下面列出了怎么用android.view.autofill.AutofillManager的API类实例代码及写法,或者点击链接到github查看源代码。
@Override
public void onAutofillEvent(@NonNull View view, int event) {
if (view instanceof AutoCompleteTextView) {
switch (event) {
case AutofillManager.AutofillCallback.EVENT_INPUT_UNAVAILABLE:
// no break on purpose
case AutofillManager.AutofillCallback.EVENT_INPUT_HIDDEN:
if (!mAutofillReceived) {
((AutoCompleteTextView) view).showDropDown();
}
break;
case AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN:
mAutofillReceived = true;
((AutoCompleteTextView) view).setAdapter(null);
break;
default:
Log.d(TAG, "Unexpected callback: " + event);
}
}
}
private void finishIt() {
StringBuilder message = new StringBuilder(getString(R.string.message_finished))
.append("\n\n");
for (int i = 0; i < mSteps.length; i++) {
TextView label = (TextView) mSteps[i].getChildAt(0);
EditText input = (EditText) mSteps[i].getChildAt(1);
message.append(getString(R.string.message_step_description, label.getText(), input.getText()))
.append('\n');
}
mStatus.setText(message.toString());
mContainer.removeAllViews();
mFinished = true;
AutofillManager afm = getSystemService(AutofillManager.class);
if (afm != null) {
afm.commit();
}
updateButtons();
}
@Override
public void onAutofillEvent(@NonNull View view, int event) {
if (view instanceof AutoCompleteTextView) {
switch (event) {
case AutofillManager.AutofillCallback.EVENT_INPUT_UNAVAILABLE:
// no break on purpose
case AutofillManager.AutofillCallback.EVENT_INPUT_HIDDEN:
if (!mAutofillReceived) {
((AutoCompleteTextView) view).showDropDown();
}
break;
case AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN:
mAutofillReceived = true;
((AutoCompleteTextView) view).setAdapter(null);
break;
default:
Log.d(TAG, "Unexpected callback: " + event);
}
}
}
private void finishIt() {
StringBuilder message = new StringBuilder(getString(R.string.message_finished))
.append("\n\n");
for (int i = 0; i < mSteps.length; i++) {
TextView label = (TextView) mSteps[i].getChildAt(0);
EditText input = (EditText) mSteps[i].getChildAt(1);
message.append(getString(R.string.message_step_description, label.getText(), input.getText()))
.append('\n');
}
mStatus.setText(message.toString());
mContainer.removeAllViews();
mFinished = true;
AutofillManager afm = getSystemService(AutofillManager.class);
if (afm != null) {
afm.commit();
}
updateButtons();
}
public void dismiss() {
popupWindow.dismiss();
variantPopup.dismiss();
recentEmoji.persist();
variantEmoji.persist();
emojiResultReceiver.setReceiver(null);
if (originalImeOptions != -1) {
editText.setImeOptions(originalImeOptions);
final InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
inputMethodManager.restartInput(editText);
}
if (SDK_INT >= O) {
final AutofillManager autofillManager = context.getSystemService(AutofillManager.class);
if (autofillManager != null) {
autofillManager.cancel();
}
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_with_autocomplete_activity);
TextView title = findViewById(R.id.standard_login_header);
title.setText(R.string.navigation_button_anti_pattern_callbackless_autocomplete_login_label);
InfoButton info = findViewById(R.id.imageButton);
info.setInfoText(getString(R.string.anti_pattern_callbackless_autocomplete_login_info));
mLoginButton = findViewById(R.id.login);
mClearButton = findViewById(R.id.clear);
mUsernameAutoCompleteField = findViewById(R.id.usernameField);
mPasswordField = findViewById(R.id.passwordField);
mLoginButton.setOnClickListener((v) -> login());
mClearButton.setOnClickListener((v) -> {
AutofillManager afm = getSystemService(AutofillManager.class);
if (afm != null) {
afm.cancel();
}
resetFields();
});
ArrayAdapter<CharSequence> mockAutocompleteAdapter = ArrayAdapter.createFromResource
(this, R.array.mock_autocomplete_sign_in_suggestions,
android.R.layout.simple_dropdown_item_1line);
mUsernameAutoCompleteField.setAdapter(mockAutocompleteAdapter);
mUsernameAutoCompleteField.setThreshold(1);
// Show it right away
mUsernameAutoCompleteField.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
mUsernameAutoCompleteField.showDropDown();
}
});
}
@Override
public AutofillManager createService(ContextImpl ctx) throws ServiceNotFoundException {
// Get the services without throwing as this is an optional feature
IBinder b = ServiceManager.getService(Context.AUTOFILL_MANAGER_SERVICE);
IAutoFillManager service = IAutoFillManager.Stub.asInterface(b);
return new AutofillManager(ctx.getOuterContext(), service);
}
private void setCheckedId(@IdRes int id) {
boolean changed = id != mCheckedId;
mCheckedId = id;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
if (changed) {
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(this);
}
}
}
public TimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
// DatePicker is important by default, unless app developer overrode attribute.
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
}
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
final boolean isDialogMode = a.getBoolean(R.styleable.TimePicker_dialogMode, false);
final int requestedMode = a.getInt(R.styleable.TimePicker_timePickerMode, MODE_SPINNER);
a.recycle();
if (requestedMode == MODE_CLOCK && isDialogMode) {
// You want MODE_CLOCK? YOU CAN'T HANDLE MODE_CLOCK! Well, maybe
// you can depending on your screen size. Let's check...
mMode = context.getResources().getInteger(R.integer.time_picker_mode);
} else {
mMode = requestedMode;
}
switch (mMode) {
case MODE_CLOCK:
mDelegate = new TimePickerClockDelegate(
this, context, attrs, defStyleAttr, defStyleRes);
break;
case MODE_SPINNER:
default:
mDelegate = new TimePickerSpinnerDelegate(
this, context, attrs, defStyleAttr, defStyleRes);
break;
}
mDelegate.setAutoFillChangeListener((v, h, m) -> {
final AutofillManager afm = context.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(this);
}
});
}
/**
* <p>Changes the checked state of this button.</p>
*
* @param checked true to check the button, false to uncheck it
*/
@Override
public void setChecked(boolean checked) {
if (mChecked != checked) {
mCheckedFromResource = false;
mChecked = checked;
refreshDrawableState();
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
// Avoid infinite recursions if setChecked() is called from a listener
if (mBroadcasting) {
return;
}
mBroadcasting = true;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
}
if (mOnCheckedChangeWidgetListener != null) {
mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
}
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(this);
}
mBroadcasting = false;
}
}
void selectionChanged() {
// We're about to post or run the selection notifier, so we don't need
// a pending notifier.
mPendingSelectionNotifier = null;
if (mOnItemSelectedListener != null
|| AccessibilityManager.getInstance(mContext).isEnabled()) {
if (mInLayout || mBlockLayoutRequests) {
// If we are in a layout traversal, defer notification
// by posting. This ensures that the view tree is
// in a consistent state and is able to accommodate
// new layout or invalidate requests.
if (mSelectionNotifier == null) {
mSelectionNotifier = new SelectionNotifier();
} else {
removeCallbacks(mSelectionNotifier);
}
post(mSelectionNotifier);
} else {
dispatchOnItemSelected();
}
}
// Always notify AutoFillManager - it will return right away if autofill is disabled.
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(this);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_with_autocomplete_activity);
TextView title = findViewById(R.id.standard_login_header);
title.setText(R.string.navigation_button_anti_pattern_callbackless_autocomplete_login_label);
InfoButton info = findViewById(R.id.imageButton);
info.setInfoText(getString(R.string.anti_pattern_callbackless_autocomplete_login_info));
mLoginButton = findViewById(R.id.login);
mClearButton = findViewById(R.id.clear);
mUsernameAutoCompleteField = findViewById(R.id.usernameField);
mPasswordField = findViewById(R.id.passwordField);
mLoginButton.setOnClickListener((v) -> login());
mClearButton.setOnClickListener((v) -> {
AutofillManager afm = getSystemService(AutofillManager.class);
if (afm != null) {
afm.cancel();
}
resetFields();
});
ArrayAdapter<CharSequence> mockAutocompleteAdapter = ArrayAdapter.createFromResource
(this, R.array.mock_autocomplete_sign_in_suggestions,
android.R.layout.simple_dropdown_item_1line);
mUsernameAutoCompleteField.setAdapter(mockAutocompleteAdapter);
mUsernameAutoCompleteField.setThreshold(1);
// Show it right away
mUsernameAutoCompleteField.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
mUsernameAutoCompleteField.showDropDown();
}
});
}
@Override
public void onFocusChange(View view, /*hasFocus*/ boolean b) {
switch (view.getId()) {
case R.id.username:
case R.id.password:
//自动填充服务积极调用
if (b && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AutofillManager afm = getSystemService(AutofillManager.class);
if (afm != null) afm.requestAutofill(view);
}
break;
default:
}
}
public CustomVirtualView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mAutofillManager = context.getSystemService(AutofillManager.class);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.multiple_partitions_activity);
mCustomVirtualView = findViewById(R.id.custom_view);
mCredentialsPartition =
mCustomVirtualView.addPartition(getString(R.string.partition_credentials));
mCredentialsPartition.addLine("username", View.AUTOFILL_TYPE_TEXT,
getString(R.string.username_label),
" ", false, View.AUTOFILL_HINT_USERNAME);
mCredentialsPartition.addLine("password", View.AUTOFILL_TYPE_TEXT,
getString(R.string.password_label),
" ", true, View.AUTOFILL_HINT_PASSWORD);
int ccExpirationType = View.AUTOFILL_TYPE_DATE;
// TODO: add a checkbox to switch between text / date instead
Intent intent = getIntent();
if (intent != null) {
int newType = intent.getIntExtra("dateType", -1);
if (newType != -1) {
ccExpirationType = newType;
String typeMessage = getString(R.string.message_credit_card_expiration_type,
Util.getAutofillTypeAsString(ccExpirationType));
// TODO: display type in a header or proper status widget
Toast.makeText(getApplicationContext(), typeMessage, Toast.LENGTH_LONG).show();
}
}
mCcPartition = mCustomVirtualView.addPartition(getString(R.string.partition_credit_card));
mCcPartition.addLine("ccNumber", View.AUTOFILL_TYPE_TEXT,
getString(R.string.credit_card_number_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_NUMBER);
mCcPartition.addLine("ccDay", View.AUTOFILL_TYPE_TEXT,
getString(R.string.credit_card_expiration_day_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY);
mCcPartition.addLine("ccMonth", ccExpirationType,
getString(R.string.credit_card_expiration_month_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH);
mCcPartition.addLine("ccYear", View.AUTOFILL_TYPE_TEXT,
getString(R.string.credit_card_expiration_year_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR);
mCcPartition.addLine("ccDate", ccExpirationType,
getString(R.string.credit_card_expiration_date_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE);
mCcPartition.addLine("ccSecurityCode", View.AUTOFILL_TYPE_TEXT,
getString(R.string.credit_card_security_code_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE);
mAutofillManager = getSystemService(AutofillManager.class);
findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
resetFields();
mCustomVirtualView.resetPositions();
mAutofillManager.cancel();
}
});
}
private static boolean isAutofillUIShowing(View servedView) {
AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class);
return afm != null && afm.isAutofillUiShowing();
}
/** @hide */
@Override
public AutofillManager.AutofillClient getAutofillClient() {
final AutofillManager.AutofillClient client = super.getAutofillClient();
if (client != null) {
return client;
}
if (android.view.autofill.Helper.sVerbose) {
Log.v(TAG, "getAutofillClient(): null on super, trying to find activity thread");
}
// Okay, ppl use the application context when they should not. This breaks
// autofill among other things. We pick the focused activity since autofill
// interacts only with the currently focused activity and we need the fill
// client only if a call comes from the focused activity. Sigh...
final ActivityThread activityThread = ActivityThread.currentActivityThread();
if (activityThread == null) {
return null;
}
final int activityCount = activityThread.mActivities.size();
for (int i = 0; i < activityCount; i++) {
final ActivityThread.ActivityClientRecord record =
activityThread.mActivities.valueAt(i);
if (record == null) {
continue;
}
final Activity activity = record.activity;
if (activity == null) {
continue;
}
if (activity.getWindow().getDecorView().hasFocus()) {
if (android.view.autofill.Helper.sVerbose) {
Log.v(TAG, "getAutofillClient(): found activity for " + this + ": " + activity);
}
return activity;
}
}
if (android.view.autofill.Helper.sVerbose) {
Log.v(TAG, "getAutofillClient(): none of the " + activityCount + " activities on "
+ this + " have focus");
}
return null;
}
public DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
// DatePicker is important by default, unless app developer overrode attribute.
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
}
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
defStyleAttr, defStyleRes);
final boolean isDialogMode = a.getBoolean(R.styleable.DatePicker_dialogMode, false);
final int requestedMode = a.getInt(R.styleable.DatePicker_datePickerMode, MODE_SPINNER);
final int firstDayOfWeek = a.getInt(R.styleable.DatePicker_firstDayOfWeek, 0);
a.recycle();
if (requestedMode == MODE_CALENDAR && isDialogMode) {
// You want MODE_CALENDAR? YOU CAN'T HANDLE MODE_CALENDAR! Well,
// maybe you can depending on your screen size. Let's check...
mMode = context.getResources().getInteger(R.integer.date_picker_mode);
} else {
mMode = requestedMode;
}
switch (mMode) {
case MODE_CALENDAR:
mDelegate = createCalendarUIDelegate(context, attrs, defStyleAttr, defStyleRes);
break;
case MODE_SPINNER:
default:
mDelegate = createSpinnerUIDelegate(context, attrs, defStyleAttr, defStyleRes);
break;
}
if (firstDayOfWeek != 0) {
setFirstDayOfWeek(firstDayOfWeek);
}
mDelegate.setAutoFillChangeListener((v, y, m, d) -> {
final AutofillManager afm = context.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(this);
}
});
}
public CustomVirtualView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mAutofillManager = context.getSystemService(AutofillManager.class);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.multiple_partitions_activity);
mCustomVirtualView = findViewById(R.id.custom_view);
mCredentialsPartition =
mCustomVirtualView.addPartition(getString(R.string.partition_credentials));
mCredentialsPartition.addLine("username", View.AUTOFILL_TYPE_TEXT,
getString(R.string.username_label),
" ", false, View.AUTOFILL_HINT_USERNAME);
mCredentialsPartition.addLine("password", View.AUTOFILL_TYPE_TEXT,
getString(R.string.password_label),
" ", true, View.AUTOFILL_HINT_PASSWORD);
int ccExpirationType = View.AUTOFILL_TYPE_DATE;
// TODO: add a checkbox to switch between text / date instead
Intent intent = getIntent();
if (intent != null) {
int newType = intent.getIntExtra("dateType", -1);
if (newType != -1) {
ccExpirationType = newType;
String typeMessage = getString(R.string.message_credit_card_expiration_type,
Util.getAutofillTypeAsString(ccExpirationType));
// TODO: display type in a header or proper status widget
Toast.makeText(getApplicationContext(), typeMessage, Toast.LENGTH_LONG).show();
}
}
mCcPartition = mCustomVirtualView.addPartition(getString(R.string.partition_credit_card));
mCcPartition.addLine("ccNumber", View.AUTOFILL_TYPE_TEXT,
getString(R.string.credit_card_number_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_NUMBER);
mCcPartition.addLine("ccDay", View.AUTOFILL_TYPE_TEXT,
getString(R.string.credit_card_expiration_day_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY);
mCcPartition.addLine("ccMonth", ccExpirationType,
getString(R.string.credit_card_expiration_month_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH);
mCcPartition.addLine("ccYear", View.AUTOFILL_TYPE_TEXT,
getString(R.string.credit_card_expiration_year_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR);
mCcPartition.addLine("ccDate", ccExpirationType,
getString(R.string.credit_card_expiration_date_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE);
mCcPartition.addLine("ccSecurityCode", View.AUTOFILL_TYPE_TEXT,
getString(R.string.credit_card_security_code_label),
" ", true, View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE);
mAutofillManager = getSystemService(AutofillManager.class);
findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
resetFields();
mCustomVirtualView.resetPositions();
mAutofillManager.cancel();
}
});
}
private void attrHandler(final Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
// DatePicker is important by default, unless app developer overrode attribute.
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
}
}
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
final boolean isDialogMode = a.getBoolean(R.styleable.TimePicker_dtp_dialogMode, false);
final int requestedMode = a.getInt(R.styleable.TimePicker_timePickerMode, MODE_SPINNER);
a.recycle();
if (requestedMode == MODE_CLOCK && isDialogMode) {
// You want MODE_CLOCK? YOU CAN'T HANDLE MODE_CLOCK! Well, maybe
// you can depending on your screen size. Let's check...
mMode = context.getResources().getInteger(R.integer.time_picker_mode);
} else {
mMode = requestedMode;
}
switch (mMode) {
case MODE_CLOCK:
mDelegate = new TimePickerClockDelegate(
this, context, attrs, defStyleAttr, defStyleRes);
break;
case MODE_SPINNER:
default:
mDelegate = new TimePickerSpinnerDelegate(
this, context, attrs, defStyleAttr, defStyleRes);
break;
}
mDelegate.setAutoFillChangeListener(new OnTimeChangedListener() {
@Override
public void onTimeChanged(TimePicker v, int h, int m) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
final AutofillManager afm = context.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(TimePicker.this);
}
}
}
});
}
/**
* Gets the events that happened after the last
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
* call.
*
* <p>This method is typically used to keep track of previous user actions to optimize further
* requests. For example, the service might return email addresses in alphabetical order by
* default, but change that order based on the address the user picked on previous requests.
*
* <p>The history is not persisted over reboots, and it's cleared every time the service
* replies to a {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} by calling
* {@link FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)}
* (if the service doesn't call any of these methods, the history will clear out after some
* pre-defined time). Hence, the service should call {@link #getFillEventHistory()} before
* finishing the {@link FillCallback}.
*
* @return The history or {@code null} if there are no events.
*/
@Nullable public final FillEventHistory getFillEventHistory() {
final AutofillManager afm = getSystemService(AutofillManager.class);
if (afm == null) {
return null;
} else {
return afm.getFillEventHistory();
}
}