Merge "Uses a notification to request auto-fill."
This commit is contained in:
committed by
Android (Google) Code Review
commit
be12db588f
@@ -501,6 +501,10 @@
|
||||
|
||||
<protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" />
|
||||
|
||||
<!-- Added in O -->
|
||||
<!-- TODO: temporary broadcast used by AutoFillManagerServiceImpl; will be removed -->
|
||||
<protected-broadcast android:name="com.android.internal.autofill.action.REQUEST_AUTOFILL" />
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- RUNTIME PERMISSIONS -->
|
||||
<!-- ====================================================================== -->
|
||||
|
||||
@@ -20,26 +20,32 @@ import static android.Manifest.permission.MANAGE_AUTO_FILL;
|
||||
import static android.content.Context.AUTO_FILL_MANAGER_SERVICE;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.ShellCallback;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.service.autofill.IAutoFillManagerService;
|
||||
import android.text.TextUtils;
|
||||
@@ -56,6 +62,7 @@ import com.android.server.SystemService;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Entry point service for auto-fill management.
|
||||
@@ -88,6 +95,9 @@ public final class AutoFillManagerService extends SystemService {
|
||||
case MSG_UNBIND:
|
||||
removeStaleServiceForUser(msg.arg1);
|
||||
return;
|
||||
case MSG_SHOW_ALL_NOTIFICATIONS:
|
||||
showAllNotifications();
|
||||
return;
|
||||
default:
|
||||
Slog.w(TAG, "Invalid message: " + msg);
|
||||
}
|
||||
@@ -131,6 +141,14 @@ public final class AutoFillManagerService extends SystemService {
|
||||
if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
|
||||
new SettingsObserver(BackgroundThread.getHandler());
|
||||
}
|
||||
if (phase == PHASE_BOOT_COMPLETED) {
|
||||
// TODO: if sent right away, the notification is not displayed. Since the notification
|
||||
// mechanism is a temporary approach anyways, just delay it..
|
||||
if (DEBUG)
|
||||
Slog.d(TAG, "Showing notifications in " + SHOW_ALL_NOTIFICATIONS_DELAY_MS + "ms");
|
||||
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ALL_NOTIFICATIONS),
|
||||
SHOW_ALL_NOTIFICATIONS_DELAY_MS);
|
||||
}
|
||||
}
|
||||
|
||||
private AutoFillManagerServiceImpl newServiceForUser(int userId) {
|
||||
@@ -224,6 +242,7 @@ public final class AutoFillManagerService extends SystemService {
|
||||
}
|
||||
mServicesCache.delete(userId);
|
||||
service.stopLocked();
|
||||
|
||||
}
|
||||
|
||||
final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub {
|
||||
@@ -287,7 +306,136 @@ public final class AutoFillManagerService extends SystemService {
|
||||
if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId);
|
||||
synchronized (mLock) {
|
||||
removeCachedServiceForUserLocked(userId);
|
||||
final ComponentName serviceComponent = getProviderForUser(userId);
|
||||
if (serviceComponent== null) {
|
||||
cancelNotificationLocked(userId);
|
||||
} else {
|
||||
showNotification(serviceComponent, userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// TODO: temporary code using a notification to request auto-fill. //
|
||||
// Will be removed once UX decide the right way to present it to the user //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO: remove from frameworks/base/core/res/AndroidManifest.xml once it's not used anymore
|
||||
private static final String NOTIFICATION_INTENT =
|
||||
"com.android.internal.autofill.action.REQUEST_AUTOFILL";
|
||||
private static final String EXTRA_USER_ID = "user_id";
|
||||
|
||||
private static final int MSG_SHOW_ALL_NOTIFICATIONS = 42;
|
||||
private static final int SHOW_ALL_NOTIFICATIONS_DELAY_MS = 5000;
|
||||
|
||||
private BroadcastReceiver mNotificationReceiver;
|
||||
|
||||
final class NotificationReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final int userId = intent.getIntExtra(EXTRA_USER_ID, -1);
|
||||
if (DEBUG) Slog.d(TAG, "Requesting autofill by notification for user " + userId);
|
||||
synchronized (mLock) {
|
||||
final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
|
||||
if (service == null) {
|
||||
Slog.w(TAG, "no auto-fill service for user " + userId);
|
||||
} else {
|
||||
service.requestAutoFill(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ComponentName getProviderForUser(int userId) {
|
||||
ComponentName serviceComponent = null;
|
||||
ServiceInfo serviceInfo = null;
|
||||
final String componentName = Settings.Secure.getStringForUser(
|
||||
mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId);
|
||||
if (!TextUtils.isEmpty(componentName)) {
|
||||
try {
|
||||
serviceComponent = ComponentName.unflattenFromString(componentName);
|
||||
serviceInfo =
|
||||
AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, userId);
|
||||
} catch (RuntimeException | RemoteException e) {
|
||||
Slog.wtf(TAG, "Bad auto-fill service name " + componentName, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component="
|
||||
+ serviceComponent + ", info: " + serviceInfo);
|
||||
if (serviceInfo == null) {
|
||||
Slog.w(TAG, "no service info for " + serviceComponent);
|
||||
return null;
|
||||
}
|
||||
return serviceComponent;
|
||||
}
|
||||
|
||||
private void showAllNotifications() {
|
||||
final UserManager userManager =
|
||||
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
||||
|
||||
final List<UserInfo> allUsers = userManager.getUsers(true);
|
||||
|
||||
for (UserInfo user : allUsers) {
|
||||
final ComponentName serviceComponent = getProviderForUser(user.id);
|
||||
if (serviceComponent != null) {
|
||||
showNotification(serviceComponent, user.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showNotification(ComponentName serviceComponent, int userId) {
|
||||
if (DEBUG) Log.d(TAG, "showNotification() for " + userId + ": " + serviceComponent);
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mNotificationReceiver == null) {
|
||||
mNotificationReceiver = new NotificationReceiver();
|
||||
mContext.registerReceiver(mNotificationReceiver,
|
||||
new IntentFilter(NOTIFICATION_INTENT));
|
||||
}
|
||||
}
|
||||
|
||||
final Intent intent = new Intent(NOTIFICATION_INTENT);
|
||||
intent.putExtra(EXTRA_USER_ID, userId);
|
||||
final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
final String packageName = serviceComponent.getPackageName();
|
||||
String providerName = null;
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
try {
|
||||
final ApplicationInfo info = pm.getApplicationInfoAsUser(packageName, 0, userId);
|
||||
if (info != null) {
|
||||
providerName = pm.getApplicationLabel(info).toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
providerName = packageName;
|
||||
}
|
||||
final String title = "AutoFill by '" + providerName + "'";
|
||||
final String subTitle = "Tap notification to auto-fill top activity for user " + userId;
|
||||
|
||||
final Notification notification = new Notification.Builder(mContext)
|
||||
.setCategory(Notification.CATEGORY_SYSTEM)
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
|
||||
.setLocalOnly(true)
|
||||
.setColor(mContext.getColor(
|
||||
com.android.internal.R.color.system_notification_accent_color))
|
||||
.setContentTitle(title)
|
||||
.setStyle(new Notification.BigTextStyle().bigText(subTitle))
|
||||
.setContentIntent(pi)
|
||||
.build();
|
||||
NotificationManager.from(mContext).notify(userId, notification);
|
||||
}
|
||||
|
||||
private void cancelNotificationLocked(int userId) {
|
||||
if (DEBUG) Log.d(TAG, "cancelNotificationLocked(): " + userId);
|
||||
NotificationManager.from(mContext).cancel(userId);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// End of temporary notification code. //
|
||||
/////////////////////////////////////////
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user