Follow sample code package-name convention for InlineFillService.

Bug: 155135358
Test: Manual. Install the application and dumpsys to check package
name.

Change-Id: Ifc338e40e6a2d37b775f6a60664231c2ce248eff
This commit is contained in:
Joanne Chung
2020-06-03 17:29:52 +08:00
parent 0500ea20fa
commit e48202f01c
9 changed files with 35 additions and 11 deletions

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.inlinefillservice;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Intent;
import android.os.Bundle;
import android.view.WindowManager;
/**
* The activity which will be open when the inline suggestion is long pressed. It shows a dialog
* that describes the source of the suggestion.
*/
public class AttributionDialogActivity extends Activity {
static final String KEY_MSG = "AttributionDialogActivity:msg";
static final String DEFAULT_MSG = "Hello";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
Intent intent = getIntent();
String msg = DEFAULT_MSG;
if (intent != null) {
msg = intent.getStringExtra(KEY_MSG);
}
Dialog dialog = createDialog(msg);
dialog.setOnDismissListener(dialog1 -> finish());
dialog.show();
}
private Dialog createDialog(String msg) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder
.setMessage("The suggestions are generated by the InlineFillService. " + msg)
.setNegativeButton(
"Settings",
(dialog, id) -> {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
})
.setPositiveButton(
"Got it",
(dialog, id) -> {
// User cancelled the dialog
});
return builder.create();
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.inlinefillservice;
import static android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcelable;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.util.ArrayMap;
import android.view.autofill.AutofillId;
import android.view.inputmethod.InlineSuggestionsRequest;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Optional;
/**
* Activity used for autofill authentication, it simply sets the dataste upon tapping OK.
*/
// TODO(b/114236837): should display a small dialog, not take the full screen
public class AuthActivity extends Activity {
private static final String EXTRA_DATASET = "dataset";
private static final String EXTRA_HINTS = "hints";
private static final String EXTRA_IDS = "ids";
private static final String EXTRA_AUTH_DATASETS = "auth_datasets";
private static final String EXTRA_INLINE_REQUEST = "inline_request";
private static int sPendingIntentId = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.auth_activity);
findViewById(R.id.yes).setOnClickListener((view) -> onYes());
findViewById(R.id.no).setOnClickListener((view) -> onNo());
}
private void onYes() {
Intent myIntent = getIntent();
Intent replyIntent = new Intent();
Dataset dataset = myIntent.getParcelableExtra(EXTRA_DATASET);
if (dataset != null) {
replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset);
} else {
String[] hints = myIntent.getStringArrayExtra(EXTRA_HINTS);
Parcelable[] ids = myIntent.getParcelableArrayExtra(EXTRA_IDS);
boolean authenticateDatasets = myIntent.getBooleanExtra(EXTRA_AUTH_DATASETS, false);
final InlineSuggestionsRequest inlineRequest =
myIntent.getParcelableExtra(EXTRA_INLINE_REQUEST);
int size = hints.length;
ArrayMap<String, AutofillId> fields = new ArrayMap<>(size);
for (int i = 0; i < size; i++) {
fields.put(hints[i], (AutofillId) ids[i]);
}
FillResponse response =
InlineFillService.createResponse(this, fields, 1, authenticateDatasets,
Optional.ofNullable(inlineRequest));
replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, response);
}
setResult(RESULT_OK, replyIntent);
finish();
}
private void onNo() {
setResult(RESULT_CANCELED);
finish();
}
public static IntentSender newIntentSenderForDataset(@NonNull Context context,
@NonNull Dataset dataset) {
return newIntentSender(context, dataset, null, null, false, null);
}
public static IntentSender newIntentSenderForResponse(@NonNull Context context,
@NonNull String[] hints, @NonNull AutofillId[] ids, boolean authenticateDatasets,
@Nullable InlineSuggestionsRequest inlineRequest) {
return newIntentSender(context, null, hints, ids, authenticateDatasets, inlineRequest);
}
private static IntentSender newIntentSender(@NonNull Context context,
@Nullable Dataset dataset, @Nullable String[] hints, @Nullable AutofillId[] ids,
boolean authenticateDatasets, @Nullable InlineSuggestionsRequest inlineRequest) {
Intent intent = new Intent(context, AuthActivity.class);
if (dataset != null) {
intent.putExtra(EXTRA_DATASET, dataset);
} else {
intent.putExtra(EXTRA_HINTS, hints);
intent.putExtra(EXTRA_IDS, ids);
intent.putExtra(EXTRA_AUTH_DATASETS, authenticateDatasets);
intent.putExtra(EXTRA_INLINE_REQUEST, inlineRequest);
}
return PendingIntent.getActivity(context, ++sPendingIntentId, intent,
PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.inlinefillservice;
import static com.example.android.inlinefillservice.InlineFillService.TAG;
import android.app.assist.AssistStructure;
import android.content.Context;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
import android.view.autofill.AutofillId;
import android.widget.Toast;
import androidx.annotation.NonNull;
import java.util.List;
import java.util.Map;
final class Helper {
/**
* Displays a toast with the given message.
*/
static void showMessage(@NonNull Context context, @NonNull CharSequence message) {
Log.i(TAG, message.toString());
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
/**
* Extracts the autofillable fields from the request through assist structure.
*/
static ArrayMap<String, AutofillId> getAutofillableFields(@NonNull FillRequest request) {
AssistStructure structure = getLatestAssistStructure(request);
return getAutofillableFields(structure);
}
/**
* Helper method to get the {@link AssistStructure} associated with the latest request
* in an autofill context.
*/
@NonNull
private static AssistStructure getLatestAssistStructure(@NonNull FillRequest request) {
List<FillContext> fillContexts = request.getFillContexts();
return fillContexts.get(fillContexts.size() - 1).getStructure();
}
/**
* Parses the {@link AssistStructure} representing the activity being autofilled, and returns a
* map of autofillable fields (represented by their autofill ids) mapped by the hint associate
* with them.
*
* <p>An autofillable field is a {@link AssistStructure.ViewNode} whose getHint(ViewNode)
* method.
*/
@NonNull
private static ArrayMap<String, AutofillId> getAutofillableFields(
@NonNull AssistStructure structure) {
ArrayMap<String, AutofillId> fields = new ArrayMap<>();
int nodes = structure.getWindowNodeCount();
for (int i = 0; i < nodes; i++) {
AssistStructure.ViewNode node = structure.getWindowNodeAt(i).getRootViewNode();
addAutofillableFields(fields, node);
}
ArrayMap<String, AutofillId> result = new ArrayMap<>();
int filedCount = fields.size();
for (int i = 0; i < filedCount; i++) {
String key = fields.keyAt(i);
AutofillId value = fields.valueAt(i);
// For fields with no hint we just use Field
if (key.equals(value.toString())) {
result.put("Field:" + i + "-", fields.valueAt(i));
} else {
result.put(key, fields.valueAt(i));
}
}
return result;
}
/**
* Adds any autofillable view from the {@link AssistStructure.ViewNode} and its descendants to
* the map.
*/
private static void addAutofillableFields(@NonNull Map<String, AutofillId> fields,
@NonNull AssistStructure.ViewNode node) {
if (node.getAutofillType() == View.AUTOFILL_TYPE_TEXT) {
if (!fields.containsValue(node.getAutofillId())) {
final String key;
if (node.getHint() != null) {
key = node.getHint().toLowerCase();
} else if (node.getAutofillHints() != null) {
key = node.getAutofillHints()[0].toLowerCase();
} else {
key = node.getAutofillId().toString();
}
fields.put(key, node.getAutofillId());
}
}
int childrenSize = node.getChildCount();
for (int i = 0; i < childrenSize; i++) {
addAutofillableFields(fields, node.getChildAt(i));
}
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.inlinefillservice;
import android.content.Context;
import android.content.IntentSender;
import android.os.CancellationSignal;
import android.service.autofill.AutofillService;
import android.service.autofill.FillCallback;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.InlinePresentation;
import android.service.autofill.SaveCallback;
import android.service.autofill.SaveInfo;
import android.service.autofill.SaveRequest;
import android.util.ArrayMap;
import android.util.Log;
import android.view.autofill.AutofillId;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.RemoteViews;
import androidx.annotation.NonNull;
import java.util.Collection;
import java.util.Optional;
/**
* A basic {@link AutofillService} implementation that only shows dynamic-generated datasets
* and supports inline suggestions.
*/
public class InlineFillService extends AutofillService {
static final String TAG = "InlineFillService";
/**
* Number of datasets sent on each request - we're simple, that value is hardcoded in our DNA!
*/
static final int NUMBER_DATASETS = 6;
private final boolean mAuthenticateResponses = false;
private final boolean mAuthenticateDatasets = false;
@Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
FillCallback callback) {
Log.d(TAG, "onFillRequest()");
final Context context = getApplicationContext();
// Find autofillable fields
ArrayMap<String, AutofillId> fields = Helper.getAutofillableFields(request);
Log.d(TAG, "autofillable fields:" + fields);
if (fields.isEmpty()) {
Helper.showMessage(context,
"InlineFillService could not figure out how to autofill this screen");
callback.onSuccess(null);
return;
}
final Optional<InlineSuggestionsRequest> inlineRequest =
InlineRequestHelper.getInlineSuggestionsRequest(request);
final int maxSuggestionsCount = InlineRequestHelper.getMaxSuggestionCount(inlineRequest,
NUMBER_DATASETS);
// Create the base response
final FillResponse response;
if (mAuthenticateResponses) {
int size = fields.size();
String[] hints = new String[size];
AutofillId[] ids = new AutofillId[size];
for (int i = 0; i < size; i++) {
hints[i] = fields.keyAt(i);
ids[i] = fields.valueAt(i);
}
IntentSender authentication = AuthActivity.newIntentSenderForResponse(this, hints,
ids, mAuthenticateDatasets, inlineRequest.orElse(null));
RemoteViews presentation = ResponseHelper.newDatasetPresentation(getPackageName(),
"Tap to auth response");
InlinePresentation inlinePresentation =
InlineRequestHelper.maybeCreateInlineAuthenticationResponse(context,
inlineRequest);
response = new FillResponse.Builder()
.setAuthentication(ids, authentication, presentation, inlinePresentation)
.build();
} else {
response = createResponse(this, fields, maxSuggestionsCount, mAuthenticateDatasets,
inlineRequest);
}
callback.onSuccess(response);
}
static FillResponse createResponse(@NonNull Context context,
@NonNull ArrayMap<String, AutofillId> fields, int numDatasets,
boolean authenticateDatasets,
@NonNull Optional<InlineSuggestionsRequest> inlineRequest) {
String packageName = context.getPackageName();
FillResponse.Builder response = new FillResponse.Builder();
// 1.Add the dynamic datasets
for (int i = 0; i < numDatasets; i++) {
if (authenticateDatasets) {
response.addDataset(ResponseHelper.newLockedDataset(context, fields, packageName, i,
inlineRequest));
} else {
response.addDataset(ResponseHelper.newUnlockedDataset(context, fields,
packageName, i, inlineRequest));
}
}
// 2. Add some inline actions
if (inlineRequest.isPresent()) {
response.addDataset(InlineRequestHelper.createInlineActionDataset(context, fields,
inlineRequest.get(), R.drawable.ic_settings));
response.addDataset(InlineRequestHelper.createInlineActionDataset(context, fields,
inlineRequest.get(), R.drawable.ic_settings));
}
// 3.Add save info
Collection<AutofillId> ids = fields.values();
AutofillId[] requiredIds = new AutofillId[ids.size()];
ids.toArray(requiredIds);
response.setSaveInfo(
// We're simple, so we're generic
new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC, requiredIds).build());
// 4.Profit!
return response.build();
}
@Override
public void onSaveRequest(SaveRequest request, SaveCallback callback) {
Log.d(TAG, "onSaveRequest()");
Helper.showMessage(getApplicationContext(), "InlineFillService doesn't support Save");
callback.onSuccess();
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.inlinefillservice;
import android.app.PendingIntent;
import android.app.slice.Slice;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.service.autofill.Dataset;
import android.service.autofill.FillRequest;
import android.service.autofill.InlinePresentation;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.view.autofill.AutofillId;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.inline.InlinePresentationSpec;
import androidx.autofill.inline.v1.InlineSuggestionUi;
import androidx.autofill.inline.v1.InlineSuggestionUi.Content;
import java.util.Optional;
public class InlineRequestHelper {
static Optional<InlineSuggestionsRequest> getInlineSuggestionsRequest(FillRequest request) {
final InlineSuggestionsRequest inlineRequest = request.getInlineSuggestionsRequest();
if (inlineRequest != null && inlineRequest.getMaxSuggestionCount() > 0
&& !inlineRequest.getInlinePresentationSpecs().isEmpty()) {
return Optional.of(inlineRequest);
}
return Optional.empty();
}
static int getMaxSuggestionCount(Optional<InlineSuggestionsRequest> inlineRequest, int max) {
if (inlineRequest.isPresent()) {
return Math.min(max, inlineRequest.get().getMaxSuggestionCount());
}
return max;
}
static InlinePresentation maybeCreateInlineAuthenticationResponse(
Context context, Optional<InlineSuggestionsRequest> inlineRequest) {
if (!inlineRequest.isPresent()) {
return null;
}
final PendingIntent attribution = createAttribution(context,
"Please tap on the chip to authenticate the Autofill response.");
final Slice slice = createSlice("Tap to auth response", null, null, null, attribution);
final InlinePresentationSpec spec = inlineRequest.get().getInlinePresentationSpecs().get(0);
return new InlinePresentation(slice, spec, false);
}
static InlinePresentation createInlineDataset(Context context,
InlineSuggestionsRequest inlineRequest, String value, int index) {
final PendingIntent attribution = createAttribution(context,
"Please tap on the chip to autofill the value:" + value);
final Slice slice = createSlice(value, null, null, null, attribution);
index = Math.min(inlineRequest.getInlinePresentationSpecs().size() - 1, index);
final InlinePresentationSpec spec = inlineRequest.getInlinePresentationSpecs().get(index);
return new InlinePresentation(slice, spec, false);
}
static Dataset createInlineActionDataset(Context context,
ArrayMap<String, AutofillId> fields,
InlineSuggestionsRequest inlineRequest, int drawable) {
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0, new Intent(context, SettingsActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT);
Dataset.Builder builder =
new Dataset.Builder()
.setInlinePresentation(createInlineAction(context, inlineRequest, drawable))
.setAuthentication(pendingIntent.getIntentSender());
for (AutofillId fieldId : fields.values()) {
builder.setValue(fieldId, null);
}
return builder.build();
}
private static InlinePresentation createInlineAction(Context context,
InlineSuggestionsRequest inlineRequest, int drawable) {
final PendingIntent attribution = createAttribution(context,
"Please tap on the chip to launch the action.");
final Icon icon = Icon.createWithResource(context, drawable);
final Slice slice = createSlice(null, null, icon, null, attribution);
// Reuse the first spec's height for the inline action size, as there isn't dedicated
// value from the request for this.
final InlinePresentationSpec spec = inlineRequest.getInlinePresentationSpecs().get(0);
return new InlinePresentation(slice, spec, true);
}
private static Slice createSlice(
String title, String subtitle, Icon startIcon, Icon endIcon,
PendingIntent attribution) {
Content.Builder builder = InlineSuggestionUi.newContentBuilder(attribution);
if (!TextUtils.isEmpty(title)) {
builder.setTitle(title);
}
if (!TextUtils.isEmpty(subtitle)) {
builder.setSubtitle(subtitle);
}
if (startIcon != null) {
builder.setStartIcon(startIcon);
}
if (endIcon != null) {
builder.setEndIcon(endIcon);
}
return builder.build().getSlice();
}
private static PendingIntent createAttribution(Context context, String msg) {
Intent intent = new Intent(context, AttributionDialogActivity.class);
intent.putExtra(AttributionDialogActivity.KEY_MSG, msg);
// Should use different request code to avoid the new intent overriding the old one.
PendingIntent pendingIntent =
PendingIntent.getActivity(
context, msg.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.inlinefillservice;
import static com.example.android.inlinefillservice.InlineFillService.TAG;
import android.content.Context;
import android.content.IntentSender;
import android.service.autofill.Dataset;
import android.service.autofill.InlinePresentation;
import android.util.Log;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.RemoteViews;
import androidx.annotation.NonNull;
import java.util.Map;
import java.util.Optional;
class ResponseHelper {
static Dataset newUnlockedDataset(@NonNull Context context,
@NonNull Map<String, AutofillId> fields, @NonNull String packageName, int index,
@NonNull Optional<InlineSuggestionsRequest> inlineRequest) {
Dataset.Builder dataset = new Dataset.Builder();
for (Map.Entry<String, AutofillId> field : fields.entrySet()) {
final String hint = field.getKey();
final AutofillId id = field.getValue();
final String value = hint + (index + 1);
// We're simple - our dataset values are hardcoded as "hintN" (for example,
// "username1", "username2") and they're displayed as such, except if they're a
// password
Log.d(TAG, "hint: " + hint);
final String displayValue = hint.contains("password") ? "password for #" + (index + 1)
: value;
final RemoteViews presentation = newDatasetPresentation(packageName, displayValue);
// Add Inline Suggestion required info.
if (inlineRequest.isPresent()) {
Log.d(TAG, "Found InlineSuggestionsRequest in FillRequest: " + inlineRequest);
final InlinePresentation inlinePresentation =
InlineRequestHelper.createInlineDataset(context, inlineRequest.get(),
displayValue, index);
dataset.setValue(id, AutofillValue.forText(value), presentation,
inlinePresentation);
} else {
dataset.setValue(id, AutofillValue.forText(value), presentation);
}
}
return dataset.build();
}
static Dataset newLockedDataset(@NonNull Context context,
@NonNull Map<String, AutofillId> fields, @NonNull String packageName, int index,
@NonNull Optional<InlineSuggestionsRequest> inlineRequest) {
Dataset unlockedDataset = ResponseHelper.newUnlockedDataset(context, fields,
packageName, index, inlineRequest);
Dataset.Builder lockedDataset = new Dataset.Builder();
for (Map.Entry<String, AutofillId> field : fields.entrySet()) {
String hint = field.getKey();
AutofillId id = field.getValue();
String value = (index + 1) + "-" + hint;
String displayValue = "Tap to auth " + value;
IntentSender authentication =
AuthActivity.newIntentSenderForDataset(context, unlockedDataset);
RemoteViews presentation = newDatasetPresentation(packageName, displayValue);
if (inlineRequest.isPresent()) {
final InlinePresentation inlinePresentation =
InlineRequestHelper.createInlineDataset(context, inlineRequest.get(),
displayValue, index);
lockedDataset.setValue(id, null, presentation, inlinePresentation)
.setAuthentication(authentication);
} else {
lockedDataset.setValue(id, null, presentation)
.setAuthentication(authentication);
}
}
return lockedDataset.build();
}
/**
* Helper method to create a dataset presentation with the givean text.
*/
@NonNull
static RemoteViews newDatasetPresentation(@NonNull String packageName,
@NonNull CharSequence text) {
RemoteViews presentation =
new RemoteViews(packageName, R.layout.list_item);
presentation.setTextViewText(R.id.text, text);
return presentation;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.inlinefillservice;
import android.app.Activity;
import android.os.Bundle;
public class SettingsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.auth_activity);
}
}