Loading packages/SystemUI/res/drawable/instant_icon.xml 0 → 100644 +30 −0 Original line number Diff line number Diff line <!-- Copyright (C) 2015 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. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="40dp" android:height="40dp" android:viewportWidth="2.2" android:viewportHeight="2.2"> <path android:fillColor="#FFFFFFFF" android:pathData="M.1,1.1 c0,.55 .45,1 1,1 c.55,0 1,-.45 1,-1 c0,-.55 -.45,-1 -1,-1 c-.55,0 -1,.45 -1,1z M1.15,.95 l.5,0 l-.7,1 l0.1,-.7 l-.5,0 l.7,-1 z"/> </vector> packages/SystemUI/res/values/colors.xml +2 −0 Original line number Diff line number Diff line Loading @@ -147,4 +147,6 @@ <color name="ksh_key_item_color">@color/material_grey_600</color> <color name="ksh_key_item_background">@color/material_grey_100</color> <color name="instant_apps_color">#ff4d5a64</color> </resources> packages/SystemUI/res/values/strings.xml +9 −0 Original line number Diff line number Diff line Loading @@ -1810,4 +1810,13 @@ <!-- Title for the notification channel for problems with storage (i.e. low disk). [CHAR LIMIT=NONE] --> <string name="notification_channel_storage">Storage</string> <!-- App label of the instant apps notification [CHAR LIMIT=60] --> <string name="instant_apps">Instant Apps</string> <!-- Message of the instant apps notification indicating they don't need install [CHAR LIMIT=NONE] --> <string name="instant_apps_message">Instant apps don\'t require installation.</string> <!-- Action label for launching app info on the specified app [CHAR LIMIT=20] --> <string name="app_info">App info</string> </resources> packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +141 −8 Original line number Diff line number Diff line Loading @@ -17,30 +17,57 @@ package com.android.systemui.statusbar.phone; import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.app.ActivityManager.StackId; import android.app.ActivityManager.StackInfo; import android.app.AlarmManager; import android.app.AlarmManager.AlarmClockInfo; import android.app.AppGlobals; import android.app.Notification; import android.app.Notification.Action; import android.app.Notification.BigTextStyle; import android.app.Notification.Style; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.SynchronousUserSwitchObserver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.graphics.drawable.Icon; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.Global; import android.service.notification.StatusBarNotification; import android.telecom.TelecomManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Pair; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.R.string; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.recents.Recents; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.policy.BluetoothController; Loading @@ -58,6 +85,10 @@ import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.NotificationChannels; import java.util.ArrayList; import java.util.List; /** * This class contains all of the policy about which icons are installed in the status Loading Loading @@ -96,6 +127,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, private final ZenModeController mZenController; private final DeviceProvisionedController mProvisionedController; private final KeyguardMonitor mKeyguardMonitor; private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>(); // Assume it's all good unless we hear otherwise. We don't always seem // to get broadcasts that it *is* there. Loading Loading @@ -212,6 +244,15 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mKeyguardMonitor.addCallback(this); SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this); SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskListener); // Clear out all old notifications on startup (only present in the case where sysui dies) NotificationManager noMan = mContext.getSystemService(NotificationManager.class); for (StatusBarNotification notification : noMan.getActiveNotifications()) { if (notification.getId() == SystemMessage.NOTE_INSTANT_APPS) { noMan.cancel(notification.getTag(), notification.getId()); } } } public void destroy() { Loading @@ -226,6 +267,10 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mKeyguardMonitor.removeCallback(this); SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this); mContext.unregisterReceiver(mIntentReceiver); NotificationManager noMan = mContext.getSystemService(NotificationManager.class); mCurrentNotifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS, new UserHandle(v.second))); } @Override Loading Loading @@ -423,8 +468,10 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, } private void updateManagedProfile() { if (DEBUG) Log.v(TAG, "updateManagedProfile: mManagedProfileFocused: " if (DEBUG) { Log.v(TAG, "updateManagedProfile: mManagedProfileFocused: " + mManagedProfileFocused); } final boolean showIcon; if (mManagedProfileFocused && !mKeyguardMonitor.isShowing()) { showIcon = true; Loading @@ -445,6 +492,76 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, } } private void updateForegroundInstantApps() { NotificationManager noMan = mContext.getSystemService(NotificationManager.class); ArraySet<Pair<String, Integer>> notifs = new ArraySet<>(mCurrentNotifs); IPackageManager pm = AppGlobals.getPackageManager(); mCurrentNotifs.clear(); try { int[] STACKS_TO_CHECK = new int[]{ StackId.FULLSCREEN_WORKSPACE_STACK_ID, StackId.DOCKED_STACK_ID, }; for (int i = 0; i < STACKS_TO_CHECK.length; i++) { StackInfo info = ActivityManager.getService().getStackInfo(STACKS_TO_CHECK[i]); if (info == null || info.topActivity == null) continue; String pkg = info.topActivity.getPackageName(); if (!hasNotif(notifs, pkg, info.userId)) { // TODO: Optimize by not always needing to get application info. // Maybe cache non-ephemeral packages? ApplicationInfo appInfo = pm.getApplicationInfo(pkg, 0, info.userId); if (appInfo.isInstantApp()) { postEphemeralNotif(pkg, info.userId, appInfo, noMan); } } } } catch (RemoteException e) { e.rethrowFromSystemServer(); } // Cancel all the leftover notifications that don't have a foreground process anymore. notifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS, new UserHandle(v.second))); } private void postEphemeralNotif(String pkg, int userId, ApplicationInfo appInfo, NotificationManager noMan) { final Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, mContext.getString(R.string.instant_apps)); mCurrentNotifs.add(new Pair<>(pkg, userId)); String message = mContext.getString(R.string.instant_apps_message); PendingIntent appInfoAction = PendingIntent.getActivity(mContext, 0, new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", pkg, null)), 0); // TODO: Add action for go to web as well. Action action = new Notification.Action.Builder(null, mContext.getString(R.string.app_info), appInfoAction).build(); noMan.notifyAsUser(pkg, SystemMessage.NOTE_INSTANT_APPS, new Notification.Builder(mContext, NotificationChannels.GENERAL) .addExtras(extras) .addAction(action) .setContentIntent(appInfoAction) .setColor(mContext.getColor(R.color.instant_apps_color)) .setContentTitle(appInfo.loadLabel(mContext.getPackageManager())) .setLargeIcon(Icon.createWithResource(pkg, appInfo.icon)) .setSmallIcon(Icon.createWithResource(mContext.getPackageName(), R.drawable.instant_icon)) .setContentText(message) .setOngoing(true) .build(), new UserHandle(userId)); } private boolean hasNotif(ArraySet<Pair<String, Integer>> notifs, String pkg, int userId) { Pair<String, Integer> key = new Pair<>(pkg, userId); if (notifs.remove(key)) { mCurrentNotifs.add(key); return true; } return false; } private final SynchronousUserSwitchObserver mUserSwitchListener = new SynchronousUserSwitchObserver() { @Override Loading @@ -466,6 +583,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, profileChanged(newUserId); updateQuietState(); updateManagedProfile(); updateForegroundInstantApps(); } }); } Loading Loading @@ -506,11 +624,13 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, @Override public void appTransitionStarting(long startTime, long duration, boolean forced) { updateManagedProfile(); updateForegroundInstantApps(); } @Override public void onKeyguardShowingChanged() { updateManagedProfile(); updateForegroundInstantApps(); } @Override Loading @@ -523,6 +643,11 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, updateQuietState(); } @Override public void preloadRecentApps() { updateForegroundInstantApps(); } @Override public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) { boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait( Loading Loading @@ -561,6 +686,14 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mIconController.setIconVisibility(mSlotDataSaver, isDataSaving); } private final TaskStackListener mTaskListener = new TaskStackListener() { @Override public void onTaskStackChanged() { // Listen for changes to stacks and then check which instant apps are foreground. updateForegroundInstantApps(); } }; private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading proto/src/system_messages.proto +4 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,10 @@ message SystemMessage { // Package: com.android.systemui NOTE_PLUGIN = 6; // Notify the user that instant app is running. // Package: com.android.systemui NOTE_INSTANT_APPS = 7; // Confirm that the user wants to remove the guest account. // Package: com.android.systemui NOTE_REMOVE_GUEST = 1010; Loading Loading
packages/SystemUI/res/drawable/instant_icon.xml 0 → 100644 +30 −0 Original line number Diff line number Diff line <!-- Copyright (C) 2015 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. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="40dp" android:height="40dp" android:viewportWidth="2.2" android:viewportHeight="2.2"> <path android:fillColor="#FFFFFFFF" android:pathData="M.1,1.1 c0,.55 .45,1 1,1 c.55,0 1,-.45 1,-1 c0,-.55 -.45,-1 -1,-1 c-.55,0 -1,.45 -1,1z M1.15,.95 l.5,0 l-.7,1 l0.1,-.7 l-.5,0 l.7,-1 z"/> </vector>
packages/SystemUI/res/values/colors.xml +2 −0 Original line number Diff line number Diff line Loading @@ -147,4 +147,6 @@ <color name="ksh_key_item_color">@color/material_grey_600</color> <color name="ksh_key_item_background">@color/material_grey_100</color> <color name="instant_apps_color">#ff4d5a64</color> </resources>
packages/SystemUI/res/values/strings.xml +9 −0 Original line number Diff line number Diff line Loading @@ -1810,4 +1810,13 @@ <!-- Title for the notification channel for problems with storage (i.e. low disk). [CHAR LIMIT=NONE] --> <string name="notification_channel_storage">Storage</string> <!-- App label of the instant apps notification [CHAR LIMIT=60] --> <string name="instant_apps">Instant Apps</string> <!-- Message of the instant apps notification indicating they don't need install [CHAR LIMIT=NONE] --> <string name="instant_apps_message">Instant apps don\'t require installation.</string> <!-- Action label for launching app info on the specified app [CHAR LIMIT=20] --> <string name="app_info">App info</string> </resources>
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +141 −8 Original line number Diff line number Diff line Loading @@ -17,30 +17,57 @@ package com.android.systemui.statusbar.phone; import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.app.ActivityManager.StackId; import android.app.ActivityManager.StackInfo; import android.app.AlarmManager; import android.app.AlarmManager.AlarmClockInfo; import android.app.AppGlobals; import android.app.Notification; import android.app.Notification.Action; import android.app.Notification.BigTextStyle; import android.app.Notification.Style; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.SynchronousUserSwitchObserver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.graphics.drawable.Icon; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.Global; import android.service.notification.StatusBarNotification; import android.telecom.TelecomManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Pair; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.R.string; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.recents.Recents; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.policy.BluetoothController; Loading @@ -58,6 +85,10 @@ import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.NotificationChannels; import java.util.ArrayList; import java.util.List; /** * This class contains all of the policy about which icons are installed in the status Loading Loading @@ -96,6 +127,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, private final ZenModeController mZenController; private final DeviceProvisionedController mProvisionedController; private final KeyguardMonitor mKeyguardMonitor; private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>(); // Assume it's all good unless we hear otherwise. We don't always seem // to get broadcasts that it *is* there. Loading Loading @@ -212,6 +244,15 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mKeyguardMonitor.addCallback(this); SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this); SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskListener); // Clear out all old notifications on startup (only present in the case where sysui dies) NotificationManager noMan = mContext.getSystemService(NotificationManager.class); for (StatusBarNotification notification : noMan.getActiveNotifications()) { if (notification.getId() == SystemMessage.NOTE_INSTANT_APPS) { noMan.cancel(notification.getTag(), notification.getId()); } } } public void destroy() { Loading @@ -226,6 +267,10 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mKeyguardMonitor.removeCallback(this); SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this); mContext.unregisterReceiver(mIntentReceiver); NotificationManager noMan = mContext.getSystemService(NotificationManager.class); mCurrentNotifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS, new UserHandle(v.second))); } @Override Loading Loading @@ -423,8 +468,10 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, } private void updateManagedProfile() { if (DEBUG) Log.v(TAG, "updateManagedProfile: mManagedProfileFocused: " if (DEBUG) { Log.v(TAG, "updateManagedProfile: mManagedProfileFocused: " + mManagedProfileFocused); } final boolean showIcon; if (mManagedProfileFocused && !mKeyguardMonitor.isShowing()) { showIcon = true; Loading @@ -445,6 +492,76 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, } } private void updateForegroundInstantApps() { NotificationManager noMan = mContext.getSystemService(NotificationManager.class); ArraySet<Pair<String, Integer>> notifs = new ArraySet<>(mCurrentNotifs); IPackageManager pm = AppGlobals.getPackageManager(); mCurrentNotifs.clear(); try { int[] STACKS_TO_CHECK = new int[]{ StackId.FULLSCREEN_WORKSPACE_STACK_ID, StackId.DOCKED_STACK_ID, }; for (int i = 0; i < STACKS_TO_CHECK.length; i++) { StackInfo info = ActivityManager.getService().getStackInfo(STACKS_TO_CHECK[i]); if (info == null || info.topActivity == null) continue; String pkg = info.topActivity.getPackageName(); if (!hasNotif(notifs, pkg, info.userId)) { // TODO: Optimize by not always needing to get application info. // Maybe cache non-ephemeral packages? ApplicationInfo appInfo = pm.getApplicationInfo(pkg, 0, info.userId); if (appInfo.isInstantApp()) { postEphemeralNotif(pkg, info.userId, appInfo, noMan); } } } } catch (RemoteException e) { e.rethrowFromSystemServer(); } // Cancel all the leftover notifications that don't have a foreground process anymore. notifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS, new UserHandle(v.second))); } private void postEphemeralNotif(String pkg, int userId, ApplicationInfo appInfo, NotificationManager noMan) { final Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, mContext.getString(R.string.instant_apps)); mCurrentNotifs.add(new Pair<>(pkg, userId)); String message = mContext.getString(R.string.instant_apps_message); PendingIntent appInfoAction = PendingIntent.getActivity(mContext, 0, new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", pkg, null)), 0); // TODO: Add action for go to web as well. Action action = new Notification.Action.Builder(null, mContext.getString(R.string.app_info), appInfoAction).build(); noMan.notifyAsUser(pkg, SystemMessage.NOTE_INSTANT_APPS, new Notification.Builder(mContext, NotificationChannels.GENERAL) .addExtras(extras) .addAction(action) .setContentIntent(appInfoAction) .setColor(mContext.getColor(R.color.instant_apps_color)) .setContentTitle(appInfo.loadLabel(mContext.getPackageManager())) .setLargeIcon(Icon.createWithResource(pkg, appInfo.icon)) .setSmallIcon(Icon.createWithResource(mContext.getPackageName(), R.drawable.instant_icon)) .setContentText(message) .setOngoing(true) .build(), new UserHandle(userId)); } private boolean hasNotif(ArraySet<Pair<String, Integer>> notifs, String pkg, int userId) { Pair<String, Integer> key = new Pair<>(pkg, userId); if (notifs.remove(key)) { mCurrentNotifs.add(key); return true; } return false; } private final SynchronousUserSwitchObserver mUserSwitchListener = new SynchronousUserSwitchObserver() { @Override Loading @@ -466,6 +583,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, profileChanged(newUserId); updateQuietState(); updateManagedProfile(); updateForegroundInstantApps(); } }); } Loading Loading @@ -506,11 +624,13 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, @Override public void appTransitionStarting(long startTime, long duration, boolean forced) { updateManagedProfile(); updateForegroundInstantApps(); } @Override public void onKeyguardShowingChanged() { updateManagedProfile(); updateForegroundInstantApps(); } @Override Loading @@ -523,6 +643,11 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, updateQuietState(); } @Override public void preloadRecentApps() { updateForegroundInstantApps(); } @Override public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) { boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait( Loading Loading @@ -561,6 +686,14 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mIconController.setIconVisibility(mSlotDataSaver, isDataSaving); } private final TaskStackListener mTaskListener = new TaskStackListener() { @Override public void onTaskStackChanged() { // Listen for changes to stacks and then check which instant apps are foreground. updateForegroundInstantApps(); } }; private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading
proto/src/system_messages.proto +4 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,10 @@ message SystemMessage { // Package: com.android.systemui NOTE_PLUGIN = 6; // Notify the user that instant app is running. // Package: com.android.systemui NOTE_INSTANT_APPS = 7; // Confirm that the user wants to remove the guest account. // Package: com.android.systemui NOTE_REMOVE_GUEST = 1010; Loading