blob: 58607ba51579fbd2cadec845054488b5dbf321e1 [file] [log] [blame]
/*
* Copyright (C) 2017 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 android.view.autofill;
import static android.view.autofill.Helper.VERBOSE;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.autofill.IAutoFillManagerService;
import android.util.Log;
import android.view.View;
/**
* App entry point to the AutoFill Framework.
*/
// TODO(b/33197203): improve this javadoc
//TODO(b/33197203): restrict manager calls to activity
public final class AutoFillManager {
private static final String TAG = "AutoFillManager";
/** @hide */ public static final int FLAG_START_SESSION = 0x1;
/** @hide */ public static final int FLAG_FOCUS_GAINED = 0x2;
/** @hide */ public static final int FLAG_FOCUS_LOST = 0x4;
/** @hide */ public static final int FLAG_VALUE_CHANGED = 0x8;
private final IAutoFillManagerService mService;
private final Context mContext;
private AutoFillSession mSession;
/**
* @hide
*/
public AutoFillManager(Context context, IAutoFillManagerService service) {
mContext = context;
mService = service;
}
/**
* Called to indicate the focus on an auto-fillable {@link View} changed.
*
* @param view view whose focus changed.
* @param gainFocus whether focus was gained or lost.
*/
public void focusChanged(View view, boolean gainFocus) {
if (mSession == null) {
// Starts new session.
final Rect bounds = new Rect();
view.getBoundsOnScreen(bounds);
final AutoFillId id = getAutoFillId(view);
final AutoFillValue value = view.getAutoFillValue();
startSession(id, bounds, value);
return;
}
if (!mSession.isEnabled()) {
// Auto-fill is disabled for this session.
return;
}
// Update focus on existing session.
final Rect bounds = new Rect();
view.getBoundsOnScreen(bounds);
final AutoFillId id = getAutoFillId(view);
final AutoFillValue value = view.getAutoFillValue();
updateSession(id, bounds, value, gainFocus ? FLAG_FOCUS_GAINED : FLAG_FOCUS_LOST);
}
/**
* Called to indicate the focus on an auto-fillable virtual {@link View} changed.
*
* @param parent parent view whose focus changed.
* @param childId id identifying the virtual child inside the parent view.
* @param bounds child boundaries, relative to the top window.
* @param gainFocus whether focus was gained or lost.
*/
public void virtualFocusChanged(View parent, int childId, Rect bounds, boolean gainFocus) {
if (mSession == null) {
// Starts new session.
final AutoFillId id = getAutoFillId(parent, childId);
startSession(id, bounds, null);
return;
}
if (!mSession.isEnabled()) {
// Auto-fill is disabled for this session.
return;
}
// Update focus on existing session.
final AutoFillId id = getAutoFillId(parent, childId);
updateSession(id, bounds, null, gainFocus ? FLAG_FOCUS_GAINED : FLAG_FOCUS_LOST);
}
/**
* Called to indicate the value of an auto-fillable {@link View} changed.
*
* @param view view whose focus changed.
*/
public void valueChanged(View view) {
if (mSession == null) return;
final AutoFillId id = getAutoFillId(view);
final AutoFillValue value = view.getAutoFillValue();
updateSession(id, null, value, FLAG_VALUE_CHANGED);
}
/**
* Called to indicate the value of an auto-fillable virtual {@link View} changed.
*
* @param parent parent view whose value changed.
* @param childId id identifying the virtual child inside the parent view.
* @param value new value of the child.
*/
public void virtualValueChanged(View parent, int childId, AutoFillValue value) {
if (mSession == null) return;
final AutoFillId id = getAutoFillId(parent, childId);
updateSession(id, null, value, FLAG_VALUE_CHANGED);
}
/**
* Called to indicate the current auto-fill context should be reset.
*
* <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
* call this method after the form is submitted and another page is rendered.
*/
public void reset() {
if (mSession == null) return;
final IBinder activityToken = mSession.mToken.get();
if (activityToken == null) {
Log.wtf(TAG, "finishSession(): token already GC'ed");
return;
}
try {
mService.finishSession(activityToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} finally {
mSession = null;
}
}
/**
* Gets the current session, if any.
*
* @hide
*/
@Nullable
public AutoFillSession getSession() {
return mSession;
}
private AutoFillId getAutoFillId(View view) {
return new AutoFillId(view.getAccessibilityViewId());
}
private AutoFillId getAutoFillId(View parent, int childId) {
return new AutoFillId(parent.getAccessibilityViewId(), childId);
}
private void startSession(AutoFillId id, Rect bounds, AutoFillValue value) {
if (VERBOSE) {
Log.v(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value);
}
final IBinder activityToken = mContext.getActivityToken();
mSession = new AutoFillSession(this, activityToken);
final IBinder appCallback = mSession.getCallback().asBinder();
try {
mService.startSession(activityToken, appCallback, id, bounds, value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private void updateSession(AutoFillId id, Rect bounds, AutoFillValue value, int flags) {
if (VERBOSE) {
Log.v(TAG, "updateSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
+ ", flags=" + flags);
}
final IBinder activityToken = mSession.mToken.get();
if (activityToken == null) {
return;
}
try {
mService.updateSession(activityToken, id, bounds, value, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}