Merge "Ensure profile-specific PiP notification is shown." into oc-mr1-dev

am: 66f2d56556

Change-Id: I9145bce7131e695a696e50b3e46e5c4a30a8e669
This commit is contained in:
Winson Chung
2017-09-21 16:46:20 +00:00
committed by android-build-merger
12 changed files with 105 additions and 61 deletions

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -223,7 +223,7 @@ public class PipMenuActivityController {
}
}
public void onActivityUnpinned(ComponentName topPipActivity) {
public void onActivityUnpinned() {
hideMenu();
setStartActivityRequested(false);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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();
}