Merge "Ensure profile-specific PiP notification is shown." into oc-mr1-dev
This commit is contained in:
@@ -30,7 +30,7 @@ oneway interface ITaskStackListener {
|
||||
void onTaskStackChanged();
|
||||
|
||||
/** Called whenever an Activity is moved to the pinned stack from another stack. */
|
||||
void onActivityPinned(String packageName, int taskId);
|
||||
void onActivityPinned(String packageName, int userId, int taskId);
|
||||
|
||||
/** Called whenever an Activity is moved from the pinned stack to another stack. */
|
||||
void onActivityUnpinned();
|
||||
|
||||
@@ -31,7 +31,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPinned(String packageName, int taskId) throws RemoteException {
|
||||
public void onActivityPinned(String packageName, int userId, int taskId)
|
||||
throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.IPinnedStackController;
|
||||
import android.view.IPinnedStackListener;
|
||||
import android.view.IWindowManager;
|
||||
@@ -70,11 +71,11 @@ public class PipManager implements BasePipManager {
|
||||
*/
|
||||
TaskStackListener mTaskStackListener = new TaskStackListener() {
|
||||
@Override
|
||||
public void onActivityPinned(String packageName, int taskId) {
|
||||
public void onActivityPinned(String packageName, int userId, int taskId) {
|
||||
mTouchHandler.onActivityPinned();
|
||||
mMediaController.onActivityPinned();
|
||||
mMenuController.onActivityPinned();
|
||||
mNotificationController.onActivityPinned(packageName,
|
||||
mNotificationController.onActivityPinned(packageName, userId,
|
||||
true /* deferUntilAnimationEnds */);
|
||||
|
||||
SystemServicesProxy.getInstance(mContext).setPipVisibility(true);
|
||||
@@ -82,13 +83,15 @@ public class PipManager implements BasePipManager {
|
||||
|
||||
@Override
|
||||
public void onActivityUnpinned() {
|
||||
ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext,
|
||||
mActivityManager);
|
||||
mMenuController.onActivityUnpinned(topPipActivity);
|
||||
mTouchHandler.onActivityUnpinned(topPipActivity);
|
||||
mNotificationController.onActivityUnpinned(topPipActivity);
|
||||
final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPinnedActivity(
|
||||
mContext, mActivityManager);
|
||||
final ComponentName topActivity = topPipActivityInfo.first;
|
||||
final int userId = topActivity != null ? topPipActivityInfo.second : 0;
|
||||
mMenuController.onActivityUnpinned();
|
||||
mTouchHandler.onActivityUnpinned(topActivity);
|
||||
mNotificationController.onActivityUnpinned(topActivity, userId);
|
||||
|
||||
SystemServicesProxy.getInstance(mContext).setPipVisibility(topPipActivity != null);
|
||||
SystemServicesProxy.getInstance(mContext).setPipVisibility(topActivity != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -230,7 +230,7 @@ public class PipMediaController {
|
||||
private void resolveActiveMediaController(List<MediaController> controllers) {
|
||||
if (controllers != null) {
|
||||
final ComponentName topActivity = PipUtils.getTopPinnedActivity(mContext,
|
||||
mActivityManager);
|
||||
mActivityManager).first;
|
||||
if (topActivity != null) {
|
||||
for (int i = 0; i < controllers.size(); i++) {
|
||||
final MediaController controller = controllers.get(i);
|
||||
|
||||
@@ -223,7 +223,7 @@ public class PipMenuActivityController {
|
||||
}
|
||||
}
|
||||
|
||||
public void onActivityUnpinned(ComponentName topPipActivity) {
|
||||
public void onActivityUnpinned() {
|
||||
hideMenu();
|
||||
setStartActivityRequested(false);
|
||||
}
|
||||
|
||||
@@ -35,10 +35,15 @@ import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.UserHandle;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SystemUI;
|
||||
@@ -57,22 +62,29 @@ public class PipNotificationController {
|
||||
private IActivityManager mActivityManager;
|
||||
private AppOpsManager mAppOpsManager;
|
||||
private NotificationManager mNotificationManager;
|
||||
private IconDrawableFactory mIconDrawableFactory;
|
||||
|
||||
private PipMotionHelper mMotionHelper;
|
||||
|
||||
// Used when building a deferred notification
|
||||
private String mDeferredNotificationPackageName;
|
||||
private int mDeferredNotificationUserId;
|
||||
|
||||
private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
|
||||
@Override
|
||||
public void onOpChanged(String op, String packageName) {
|
||||
try {
|
||||
// Dismiss the PiP once the user disables the app ops setting for that package
|
||||
final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
|
||||
packageName, 0);
|
||||
if (mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, packageName)
|
||||
!= MODE_ALLOWED) {
|
||||
mMotionHelper.dismissPip();
|
||||
final Pair<ComponentName, Integer> topPipActivityInfo =
|
||||
PipUtils.getTopPinnedActivity(mContext, mActivityManager);
|
||||
if (topPipActivityInfo.first != null) {
|
||||
final ApplicationInfo appInfo = mContext.getPackageManager()
|
||||
.getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second);
|
||||
if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) &&
|
||||
mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid,
|
||||
packageName) != MODE_ALLOWED) {
|
||||
mMotionHelper.dismissPip();
|
||||
}
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
// Unregister the listener if the package can't be found
|
||||
@@ -88,16 +100,18 @@ public class PipNotificationController {
|
||||
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
mNotificationManager = NotificationManager.from(context);
|
||||
mMotionHelper = motionHelper;
|
||||
mIconDrawableFactory = IconDrawableFactory.newInstance(context);
|
||||
}
|
||||
|
||||
public void onActivityPinned(String packageName, boolean deferUntilAnimationEnds) {
|
||||
public void onActivityPinned(String packageName, int userId, boolean deferUntilAnimationEnds) {
|
||||
// Clear any existing notification
|
||||
mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
|
||||
|
||||
if (deferUntilAnimationEnds) {
|
||||
mDeferredNotificationPackageName = packageName;
|
||||
mDeferredNotificationUserId = userId;
|
||||
} else {
|
||||
showNotificationForApp(mDeferredNotificationPackageName);
|
||||
showNotificationForApp(packageName, userId);
|
||||
}
|
||||
|
||||
// Register for changes to the app ops setting for this package while it is in PiP
|
||||
@@ -106,22 +120,25 @@ public class PipNotificationController {
|
||||
|
||||
public void onPinnedStackAnimationEnded() {
|
||||
if (mDeferredNotificationPackageName != null) {
|
||||
showNotificationForApp(mDeferredNotificationPackageName);
|
||||
showNotificationForApp(mDeferredNotificationPackageName, mDeferredNotificationUserId);
|
||||
mDeferredNotificationPackageName = null;
|
||||
mDeferredNotificationUserId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void onActivityUnpinned(ComponentName topPipActivity) {
|
||||
public void onActivityUnpinned(ComponentName topPipActivity, int userId) {
|
||||
// Unregister for changes to the previously PiP'ed package
|
||||
unregisterAppOpsListener();
|
||||
|
||||
// Reset the deferred notification package
|
||||
mDeferredNotificationPackageName = null;
|
||||
mDeferredNotificationUserId = 0;
|
||||
|
||||
if (topPipActivity != null) {
|
||||
// onActivityUnpinned() is only called after the transition is complete, so we don't
|
||||
// need to defer until the animation ends to update the notification
|
||||
onActivityPinned(topPipActivity.getPackageName(), false /* deferUntilAnimationEnds */);
|
||||
onActivityPinned(topPipActivity.getPackageName(), userId,
|
||||
false /* deferUntilAnimationEnds */);
|
||||
} else {
|
||||
mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
|
||||
}
|
||||
@@ -130,20 +147,27 @@ public class PipNotificationController {
|
||||
/**
|
||||
* Builds and shows the notification for the given app.
|
||||
*/
|
||||
private void showNotificationForApp(String packageName) {
|
||||
private void showNotificationForApp(String packageName, int userId) {
|
||||
// Build a new notification
|
||||
final Notification.Builder builder =
|
||||
new Notification.Builder(mContext, NotificationChannels.GENERAL)
|
||||
.setLocalOnly(true)
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.pip_notification_icon)
|
||||
.setColor(mContext.getColor(
|
||||
com.android.internal.R.color.system_notification_accent_color));
|
||||
if (updateNotificationForApp(builder, packageName)) {
|
||||
SystemUI.overrideNotificationAppName(mContext, builder);
|
||||
try {
|
||||
final UserHandle user = UserHandle.of(userId);
|
||||
final Context userContext = mContext.createPackageContextAsUser(
|
||||
mContext.getPackageName(), 0, user);
|
||||
final Notification.Builder builder =
|
||||
new Notification.Builder(userContext, NotificationChannels.GENERAL)
|
||||
.setLocalOnly(true)
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.pip_notification_icon)
|
||||
.setColor(mContext.getColor(
|
||||
com.android.internal.R.color.system_notification_accent_color));
|
||||
if (updateNotificationForApp(builder, packageName, user)) {
|
||||
SystemUI.overrideNotificationAppName(mContext, builder);
|
||||
|
||||
// Show the new notification
|
||||
mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
|
||||
// Show the new notification
|
||||
mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(TAG, "Could not show notification for application", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,33 +175,33 @@ public class PipNotificationController {
|
||||
* Updates the notification builder with app-specific information, returning whether it was
|
||||
* successful.
|
||||
*/
|
||||
private boolean updateNotificationForApp(Notification.Builder builder, String packageName) {
|
||||
private boolean updateNotificationForApp(Notification.Builder builder, String packageName,
|
||||
UserHandle user) throws NameNotFoundException {
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
final ApplicationInfo appInfo;
|
||||
try {
|
||||
appInfo = pm.getApplicationInfo(packageName, 0);
|
||||
appInfo = pm.getApplicationInfoAsUser(packageName, 0, user.getIdentifier());
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(TAG, "Could not update notification for application", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (appInfo != null) {
|
||||
final String appName = pm.getApplicationLabel(appInfo).toString();
|
||||
final String appName = pm.getUserBadgedLabel(pm.getApplicationLabel(appInfo), user)
|
||||
.toString();
|
||||
final String message = mContext.getString(R.string.pip_notification_message, appName);
|
||||
final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
|
||||
Uri.fromParts("package", packageName, null));
|
||||
settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
|
||||
settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
|
||||
final Icon appIcon = appInfo.icon != 0
|
||||
? Icon.createWithResource(packageName, appInfo.icon)
|
||||
: Icon.createWithResource(Resources.getSystem(),
|
||||
com.android.internal.R.drawable.sym_def_app_icon);
|
||||
|
||||
final Drawable iconDrawable = mIconDrawableFactory.getBadgedIcon(appInfo);
|
||||
builder.setContentTitle(mContext.getString(R.string.pip_notification_title, appName))
|
||||
.setContentText(message)
|
||||
.setContentIntent(PendingIntent.getActivity(mContext, packageName.hashCode(),
|
||||
settingsIntent, FLAG_CANCEL_CURRENT))
|
||||
.setContentIntent(PendingIntent.getActivityAsUser(mContext, packageName.hashCode(),
|
||||
settingsIntent, FLAG_CANCEL_CURRENT, null, user))
|
||||
.setStyle(new Notification.BigTextStyle().bigText(message))
|
||||
.setLargeIcon(appIcon);
|
||||
.setLargeIcon(createBitmap(iconDrawable).createAshmemBitmap());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -191,4 +215,17 @@ public class PipNotificationController {
|
||||
private void unregisterAppOpsListener() {
|
||||
mAppOpsManager.stopWatchingMode(mAppOpsChangedListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bakes a drawable into a bitmap.
|
||||
*/
|
||||
private Bitmap createBitmap(Drawable d) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
|
||||
Config.ARGB_8888);
|
||||
Canvas c = new Canvas(bitmap);
|
||||
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
|
||||
d.draw(c);
|
||||
c.setBitmap(null);
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,16 +24,17 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
public class PipUtils {
|
||||
|
||||
private static final String TAG = "PipUtils";
|
||||
|
||||
/**
|
||||
* @return the ComponentName of the top non-SystemUI activity in the pinned stack, or null if
|
||||
* none exists.
|
||||
* @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
|
||||
* The component name may be null if no such activity exists.
|
||||
*/
|
||||
public static ComponentName getTopPinnedActivity(Context context,
|
||||
public static Pair<ComponentName, Integer> getTopPinnedActivity(Context context,
|
||||
IActivityManager activityManager) {
|
||||
try {
|
||||
final String sysUiPackageName = context.getPackageName();
|
||||
@@ -44,13 +45,13 @@ public class PipUtils {
|
||||
ComponentName cn = ComponentName.unflattenFromString(
|
||||
pinnedStackInfo.taskNames[i]);
|
||||
if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) {
|
||||
return cn;
|
||||
return new Pair<>(cn, pinnedStackInfo.taskUserIds[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Unable to get pinned stack.");
|
||||
}
|
||||
return null;
|
||||
return new Pair<>(null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,7 +654,7 @@ public class PipManager implements BasePipManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPinned(String packageName, int taskId) {
|
||||
public void onActivityPinned(String packageName, int userId, int taskId) {
|
||||
if (DEBUG) Log.d(TAG, "onActivityPinned()");
|
||||
if (!checkCurrentUserId(mContext, DEBUG)) {
|
||||
return;
|
||||
|
||||
@@ -173,7 +173,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPinned(String packageName, int taskId) {
|
||||
public void onActivityPinned(String packageName, int userId, int taskId) {
|
||||
// Check this is for the right user
|
||||
if (!checkCurrentUserId(mContext, false /* debug */)) {
|
||||
return;
|
||||
|
||||
@@ -176,7 +176,7 @@ public class SystemServicesProxy {
|
||||
public void onTaskStackChangedBackground() { }
|
||||
public void onTaskStackChanged() { }
|
||||
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
|
||||
public void onActivityPinned(String packageName, int taskId) { }
|
||||
public void onActivityPinned(String packageName, int userId, int taskId) { }
|
||||
public void onActivityUnpinned() { }
|
||||
public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
|
||||
public void onPinnedStackAnimationStarted() { }
|
||||
@@ -231,9 +231,10 @@ public class SystemServicesProxy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPinned(String packageName, int taskId) throws RemoteException {
|
||||
public void onActivityPinned(String packageName, int userId, int taskId)
|
||||
throws RemoteException {
|
||||
mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
|
||||
mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, taskId, 0, packageName).sendToTarget();
|
||||
mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, userId, taskId, packageName).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1387,7 +1388,8 @@ public class SystemServicesProxy {
|
||||
}
|
||||
case ON_ACTIVITY_PINNED: {
|
||||
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
|
||||
mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1);
|
||||
mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1,
|
||||
msg.arg2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3042,7 +3042,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
|
||||
resumeFocusedStackTopActivityLocked();
|
||||
|
||||
mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName,
|
||||
mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName, r.userId,
|
||||
r.getTask().taskId);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ class TaskChangeNotificationController {
|
||||
};
|
||||
|
||||
private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> {
|
||||
l.onActivityPinned((String) m.obj, m.arg1);
|
||||
l.onActivityPinned((String) m.obj, m.arg1, m.arg2);
|
||||
};
|
||||
|
||||
private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> {
|
||||
@@ -278,10 +278,10 @@ class TaskChangeNotificationController {
|
||||
}
|
||||
|
||||
/** Notifies all listeners when an Activity is pinned. */
|
||||
void notifyActivityPinned(String packageName, int taskId) {
|
||||
void notifyActivityPinned(String packageName, int userId, int taskId) {
|
||||
mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
|
||||
final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
|
||||
taskId, 0, packageName);
|
||||
userId, taskId, packageName);
|
||||
forAllLocalListeners(mNotifyActivityPinned, msg);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user