Loading packages/SystemUI/res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -1988,6 +1988,9 @@ <!-- Action label for launching app info on the specified app [CHAR LIMIT=20] --> <string name="app_info">App info</string> <!-- Action label for switching to web for an instant app [CHAR LIMIT=20] --> <string name="go_to_web">Go to web</string> <!-- Quick settings tile for toggling mobile data [CHAR LIMIT=20] --> <string name="mobile_data">Mobile data</string> Loading packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java 0 → 100644 +69 −0 Original line number Diff line number Diff line /* * 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 com.android.systemui; import android.os.RemoteException; import android.util.Log; import android.view.IDockedStackListener; import android.view.WindowManagerGlobal; import java.util.function.Consumer; /** * Utility wrapper to listen for whether or not a docked stack exists, to be * used for things like the different overview icon in that mode. */ public class DockedStackExistsListener extends IDockedStackListener.Stub { private static final String TAG = "DockedStackExistsListener"; private final Consumer<Boolean> mCallback; private DockedStackExistsListener(Consumer<Boolean> callback) { mCallback = callback; } @Override public void onDividerVisibilityChanged(boolean visible) throws RemoteException { } @Override public void onDockedStackExistsChanged(final boolean exists) throws RemoteException { mCallback.accept(exists); } @Override public void onDockedStackMinimizedChanged(boolean minimized, long animDuration, boolean isHomeStackResizable) throws RemoteException { } @Override public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration) throws RemoteException { } @Override public void onDockSideChanged(int newDockSide) throws RemoteException { } public static void register(Consumer<Boolean> callback) { try { WindowManagerGlobal.getWindowManagerService().registerDockedStackListener( new DockedStackExistsListener(callback)); } catch (RemoteException e) { Log.e(TAG, "Failed registering docked stack exists listener", e); } } } packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +5 −34 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import com.android.systemui.Dependency; import com.android.systemui.DockedStackExistsListener; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.plugins.PluginListener; Loading Loading @@ -566,40 +567,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); try { WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(new Stub() { @Override public void onDividerVisibilityChanged(boolean visible) throws RemoteException { } @Override public void onDockedStackExistsChanged(final boolean exists) throws RemoteException { mHandler.post(new Runnable() { @Override public void run() { DockedStackExistsListener.register(exists -> mHandler.post(() -> { mDockedStackExists = exists; updateRecentsIcon(); } }); } @Override public void onDockedStackMinimizedChanged(boolean minimized, long animDuration, boolean isHomeStackResizable) throws RemoteException { } @Override public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration) throws RemoteException { } @Override public void onDockSideChanged(int newDockSide) throws RemoteException { } }); } catch (RemoteException e) { Log.e(TAG, "Failed registering docked stack exists listener", e); } })); } void updateRotatedViews() { Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +80 −18 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.SynchronousUserSwitchObserver; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; Loading @@ -50,11 +51,11 @@ import android.telecom.TelecomManager; 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.DockedStackExistsListener; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.qs.tiles.DndTile; Loading Loading @@ -82,6 +83,8 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.NotificationChannels; import java.util.List; /** * This class contains all of the policy about which icons are installed in the status * bar at boot time. It goes through the normal API for icons, even though it probably Loading @@ -94,6 +97,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public static final int LOCATION_STATUS_ICON_ID = R.drawable.stat_sys_location; public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5; private final String mSlotCast; private final String mSlotHotspot; Loading Loading @@ -132,6 +136,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, private boolean mZenVisible; private boolean mVolumeVisible; private boolean mCurrentUserSetup; private boolean mDockedStackExists; private boolean mManagedProfileIconVisible = false; private boolean mManagedProfileInQuietMode = false; Loading Loading @@ -248,6 +253,10 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, noMan.cancel(notification.getTag(), notification.getId()); } } DockedStackExistsListener.register(exists -> { mDockedStackExists = exists; updateForegroundInstantApps(); }); } public void destroy() { Loading Loading @@ -495,13 +504,32 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, IPackageManager pm = AppGlobals.getPackageManager(); mCurrentNotifs.clear(); try { ArraySet<Integer> stacksToCheck = new ArraySet<>(); 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; int focusedId = ActivityManager.getService().getFocusedStackId(); if (focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID || focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID) { checkStack(StackId.FULLSCREEN_WORKSPACE_STACK_ID, notifs, noMan, pm); } if (mDockedStackExists) { checkStack(StackId.DOCKED_STACK_ID, notifs, noMan, pm); } } 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 checkStack(int stackId, ArraySet<Pair<String, Integer>> notifs, NotificationManager noMan, IPackageManager pm) { try { StackInfo info = ActivityManager.getService().getStackInfo(stackId); if (info == null || info.topActivity == null) return; String pkg = info.topActivity.getPackageName(); if (!hasNotif(notifs, pkg, info.userId)) { // TODO: Optimize by not always needing to get application info. Loading @@ -509,20 +537,16 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, ApplicationInfo appInfo = pm.getApplicationInfo(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, info.userId); if (appInfo.isInstantApp()) { postEphemeralNotif(pkg, info.userId, appInfo, noMan); } postEphemeralNotif(pkg, info.userId, appInfo, noMan, info.taskIds[info.taskIds.length - 1]); } } } 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) { NotificationManager noMan, int taskId) { final Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, mContext.getString(R.string.instant_apps)); Loading @@ -531,12 +555,39 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, 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) Intent browserIntent = getTaskIntent(taskId, userId); Notification.Builder builder = new Notification.Builder(mContext, NotificationChannels.GENERAL); if (browserIntent != null) { PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */, browserIntent, 0 /* flags */); browserIntent.setComponent(null); browserIntent.addFlags(Intent.FLAG_IGNORE_EPHEMERAL); browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ComponentName aiaComponent = null; try { aiaComponent = AppGlobals.getPackageManager().getInstantAppInstallerComponent(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } Intent goToWebIntent = new Intent() .setComponent(aiaComponent) .setAction(Intent.ACTION_VIEW) .addCategory(Intent.CATEGORY_BROWSABLE) .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName) .putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode) .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent); PendingIntent webPendingIntent = PendingIntent.getActivity(mContext, 0, goToWebIntent, 0); Action webAction = new Notification.Action.Builder(null, mContext.getString(R.string.go_to_web), webPendingIntent).build(); builder.addAction(webAction); } noMan.notifyAsUser(pkg, SystemMessage.NOTE_INSTANT_APPS, builder .addExtras(extras) .addAction(action) .setContentIntent(appInfoAction) Loading @@ -551,6 +602,17 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, new UserHandle(userId)); } private Intent getTaskIntent(int taskId, int userId) { List<ActivityManager.RecentTaskInfo> tasks = mContext.getSystemService(ActivityManager.class) .getRecentTasksForUser(NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId); for (int i = 0; i < tasks.size(); i++) { if (tasks.get(i).id == taskId) { return tasks.get(i).baseIntent; } } return null; } private boolean hasNotif(ArraySet<Pair<String, Integer>> notifs, String pkg, int userId) { Pair<String, Integer> key = new Pair<>(pkg, userId); if (notifs.remove(key)) { Loading Loading
packages/SystemUI/res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -1988,6 +1988,9 @@ <!-- Action label for launching app info on the specified app [CHAR LIMIT=20] --> <string name="app_info">App info</string> <!-- Action label for switching to web for an instant app [CHAR LIMIT=20] --> <string name="go_to_web">Go to web</string> <!-- Quick settings tile for toggling mobile data [CHAR LIMIT=20] --> <string name="mobile_data">Mobile data</string> Loading
packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java 0 → 100644 +69 −0 Original line number Diff line number Diff line /* * 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 com.android.systemui; import android.os.RemoteException; import android.util.Log; import android.view.IDockedStackListener; import android.view.WindowManagerGlobal; import java.util.function.Consumer; /** * Utility wrapper to listen for whether or not a docked stack exists, to be * used for things like the different overview icon in that mode. */ public class DockedStackExistsListener extends IDockedStackListener.Stub { private static final String TAG = "DockedStackExistsListener"; private final Consumer<Boolean> mCallback; private DockedStackExistsListener(Consumer<Boolean> callback) { mCallback = callback; } @Override public void onDividerVisibilityChanged(boolean visible) throws RemoteException { } @Override public void onDockedStackExistsChanged(final boolean exists) throws RemoteException { mCallback.accept(exists); } @Override public void onDockedStackMinimizedChanged(boolean minimized, long animDuration, boolean isHomeStackResizable) throws RemoteException { } @Override public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration) throws RemoteException { } @Override public void onDockSideChanged(int newDockSide) throws RemoteException { } public static void register(Consumer<Boolean> callback) { try { WindowManagerGlobal.getWindowManagerService().registerDockedStackListener( new DockedStackExistsListener(callback)); } catch (RemoteException e) { Log.e(TAG, "Failed registering docked stack exists listener", e); } } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +5 −34 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import com.android.systemui.Dependency; import com.android.systemui.DockedStackExistsListener; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.plugins.PluginListener; Loading Loading @@ -566,40 +567,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); try { WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(new Stub() { @Override public void onDividerVisibilityChanged(boolean visible) throws RemoteException { } @Override public void onDockedStackExistsChanged(final boolean exists) throws RemoteException { mHandler.post(new Runnable() { @Override public void run() { DockedStackExistsListener.register(exists -> mHandler.post(() -> { mDockedStackExists = exists; updateRecentsIcon(); } }); } @Override public void onDockedStackMinimizedChanged(boolean minimized, long animDuration, boolean isHomeStackResizable) throws RemoteException { } @Override public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration) throws RemoteException { } @Override public void onDockSideChanged(int newDockSide) throws RemoteException { } }); } catch (RemoteException e) { Log.e(TAG, "Failed registering docked stack exists listener", e); } })); } void updateRotatedViews() { Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +80 −18 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.SynchronousUserSwitchObserver; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; Loading @@ -50,11 +51,11 @@ import android.telecom.TelecomManager; 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.DockedStackExistsListener; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.qs.tiles.DndTile; Loading Loading @@ -82,6 +83,8 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.NotificationChannels; import java.util.List; /** * This class contains all of the policy about which icons are installed in the status * bar at boot time. It goes through the normal API for icons, even though it probably Loading @@ -94,6 +97,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public static final int LOCATION_STATUS_ICON_ID = R.drawable.stat_sys_location; public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5; private final String mSlotCast; private final String mSlotHotspot; Loading Loading @@ -132,6 +136,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, private boolean mZenVisible; private boolean mVolumeVisible; private boolean mCurrentUserSetup; private boolean mDockedStackExists; private boolean mManagedProfileIconVisible = false; private boolean mManagedProfileInQuietMode = false; Loading Loading @@ -248,6 +253,10 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, noMan.cancel(notification.getTag(), notification.getId()); } } DockedStackExistsListener.register(exists -> { mDockedStackExists = exists; updateForegroundInstantApps(); }); } public void destroy() { Loading Loading @@ -495,13 +504,32 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, IPackageManager pm = AppGlobals.getPackageManager(); mCurrentNotifs.clear(); try { ArraySet<Integer> stacksToCheck = new ArraySet<>(); 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; int focusedId = ActivityManager.getService().getFocusedStackId(); if (focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID || focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID) { checkStack(StackId.FULLSCREEN_WORKSPACE_STACK_ID, notifs, noMan, pm); } if (mDockedStackExists) { checkStack(StackId.DOCKED_STACK_ID, notifs, noMan, pm); } } 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 checkStack(int stackId, ArraySet<Pair<String, Integer>> notifs, NotificationManager noMan, IPackageManager pm) { try { StackInfo info = ActivityManager.getService().getStackInfo(stackId); if (info == null || info.topActivity == null) return; String pkg = info.topActivity.getPackageName(); if (!hasNotif(notifs, pkg, info.userId)) { // TODO: Optimize by not always needing to get application info. Loading @@ -509,20 +537,16 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, ApplicationInfo appInfo = pm.getApplicationInfo(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, info.userId); if (appInfo.isInstantApp()) { postEphemeralNotif(pkg, info.userId, appInfo, noMan); } postEphemeralNotif(pkg, info.userId, appInfo, noMan, info.taskIds[info.taskIds.length - 1]); } } } 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) { NotificationManager noMan, int taskId) { final Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, mContext.getString(R.string.instant_apps)); Loading @@ -531,12 +555,39 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, 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) Intent browserIntent = getTaskIntent(taskId, userId); Notification.Builder builder = new Notification.Builder(mContext, NotificationChannels.GENERAL); if (browserIntent != null) { PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */, browserIntent, 0 /* flags */); browserIntent.setComponent(null); browserIntent.addFlags(Intent.FLAG_IGNORE_EPHEMERAL); browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ComponentName aiaComponent = null; try { aiaComponent = AppGlobals.getPackageManager().getInstantAppInstallerComponent(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } Intent goToWebIntent = new Intent() .setComponent(aiaComponent) .setAction(Intent.ACTION_VIEW) .addCategory(Intent.CATEGORY_BROWSABLE) .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName) .putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode) .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent); PendingIntent webPendingIntent = PendingIntent.getActivity(mContext, 0, goToWebIntent, 0); Action webAction = new Notification.Action.Builder(null, mContext.getString(R.string.go_to_web), webPendingIntent).build(); builder.addAction(webAction); } noMan.notifyAsUser(pkg, SystemMessage.NOTE_INSTANT_APPS, builder .addExtras(extras) .addAction(action) .setContentIntent(appInfoAction) Loading @@ -551,6 +602,17 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, new UserHandle(userId)); } private Intent getTaskIntent(int taskId, int userId) { List<ActivityManager.RecentTaskInfo> tasks = mContext.getSystemService(ActivityManager.class) .getRecentTasksForUser(NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId); for (int i = 0; i < tasks.size(); i++) { if (tasks.get(i).id == taskId) { return tasks.get(i).baseIntent; } } return null; } private boolean hasNotif(ArraySet<Pair<String, Integer>> notifs, String pkg, int userId) { Pair<String, Integer> key = new Pair<>(pkg, userId); if (notifs.remove(key)) { Loading