Extract Instant Apps notification code into InstantAppNotifier

The code to show notifications of Instant App was in
PhoneStatsuBarPolicy. This CL moves these code into new class
named InstantAppNotifier.

Bug: 123917211
Test: build passes

Change-Id: Ib4bd988c94e2382d776c5c0b1886c8d20d3a1b16
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index e0c5e59..4a86484 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -16,53 +16,29 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-
 import android.app.ActivityManager;
-import android.app.ActivityManager.StackInfo;
 import android.app.ActivityTaskManager;
 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.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;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-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.service.notification.ZenModeConfig;
 import android.telecom.TelecomManager;
 import android.text.format.DateFormat;
-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.UiOffloadThread;
@@ -70,12 +46,8 @@
 import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.BluetoothController.Callback;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.CastController.CastDevice;
 import com.android.systemui.statusbar.policy.DataSaverController;
@@ -90,7 +62,6 @@
 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.io.PrintWriter;
 import java.io.StringWriter;
@@ -98,18 +69,22 @@
 import java.util.Locale;
 
 /**
- * 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
- * strictly doesn't need to.
+ * 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 strictly doesn't need to.
  */
-public class PhoneStatusBarPolicy implements Callback, Callbacks,
-        RotationLockControllerCallback, Listener, ZenModeController.Callback,
-        DeviceProvisionedListener, KeyguardMonitor.Callback, PrivacyItemController.Callback {
+public class PhoneStatusBarPolicy
+        implements BluetoothController.Callback,
+                CommandQueue.Callbacks,
+                RotationLockControllerCallback,
+                Listener,
+                ZenModeController.Callback,
+                DeviceProvisionedListener,
+                KeyguardMonitor.Callback,
+                PrivacyItemController.Callback {
     private static final String TAG = "PhoneStatusBarPolicy";
     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;
@@ -142,7 +117,6 @@
     private final KeyguardMonitor mKeyguardMonitor;
     private final LocationController mLocationController;
     private final PrivacyItemController mPrivacyItemController;
-    private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
 
     // Assume it's all good unless we hear otherwise.  We don't always seem
@@ -152,7 +126,6 @@
     private boolean mZenVisible;
     private boolean mVolumeVisible;
     private boolean mCurrentUserSetup;
-    private boolean mDockedStackExists;
 
     private boolean mManagedProfileIconVisible = false;
 
@@ -271,19 +244,6 @@
         mPrivacyItemController.addCallback(this);
 
         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
-        ActivityManagerWrapper.getInstance().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());
-            }
-        }
-        DockedStackExistsListener.register(exists -> {
-            mDockedStackExists = exists;
-            updateForegroundInstantApps();
-        });
     }
 
     @Override
@@ -515,169 +475,6 @@
         });
     }
 
-    private void updateForegroundInstantApps() {
-        NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
-        ArraySet<Pair<String, Integer>> notifs = new ArraySet<>(mCurrentNotifs);
-        IPackageManager pm = AppGlobals.getPackageManager();
-        mCurrentNotifs.clear();
-        mUiOffloadThread.submit(() -> {
-            try {
-                final StackInfo focusedStack =
-                        ActivityTaskManager.getService().getFocusedStackInfo();
-                if (focusedStack != null) {
-                    final int windowingMode =
-                            focusedStack.configuration.windowConfiguration.getWindowingMode();
-                    if (windowingMode == WINDOWING_MODE_FULLSCREEN
-                            || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
-                        checkStack(focusedStack, notifs, noMan, pm);
-                    }
-                }
-                if (mDockedStackExists) {
-                    checkStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED,
-                            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 windowingMode, int activityType,
-            ArraySet<Pair<String, Integer>> notifs, NotificationManager noMan, IPackageManager pm) {
-        try {
-            final StackInfo info =
-                    ActivityTaskManager.getService().getStackInfo(windowingMode, activityType);
-            checkStack(info, notifs, noMan, pm);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-    }
-    private void checkStack(StackInfo info, ArraySet<Pair<String, Integer>> notifs,
-            NotificationManager noMan, IPackageManager pm) {
-        try {
-            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.
-                // Maybe cache non-ephemeral packages?
-                ApplicationInfo appInfo = pm.getApplicationInfo(pkg,
-                        PackageManager.MATCH_UNINSTALLED_PACKAGES, info.userId);
-                if (appInfo.isInstantApp()) {
-                    postEphemeralNotif(pkg, info.userId, appInfo, noMan, info.taskIds[info.taskIds.length - 1]);
-                }
-            }
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-    }
-
-    private void postEphemeralNotif(String pkg, int userId, ApplicationInfo appInfo,
-            NotificationManager noMan, int taskId) {
-        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 helpUrl = mContext.getString(R.string.instant_apps_help_url);
-        boolean hasHelpUrl = !helpUrl.isEmpty();
-        String message = mContext.getString(hasHelpUrl
-                ? R.string.instant_apps_message_with_help
-                : R.string.instant_apps_message);
-
-        UserHandle user = UserHandle.of(userId);
-        PendingIntent appInfoAction = PendingIntent.getActivityAsUser(mContext, 0,
-                new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
-                        .setData(Uri.fromParts("package", pkg, null)), 0, null, user);
-        Action action = new Notification.Action.Builder(null, mContext.getString(R.string.app_info),
-                appInfoAction).build();
-        PendingIntent helpCenterIntent = hasHelpUrl
-                ? PendingIntent.getActivityAsUser(mContext, 0,
-                new Intent(Intent.ACTION_VIEW).setData(Uri.parse(
-                        helpUrl)),
-                0, null, user)
-                : null;
-
-        Intent browserIntent = getTaskIntent(taskId, userId);
-        Notification.Builder builder = new Notification.Builder(mContext,
-                NotificationChannels.GENERAL);
-        if (browserIntent != null && browserIntent.isWebIntent()) {
-            // Make sure that this doesn't resolve back to an instant app
-            browserIntent.setComponent(null)
-                    .setPackage(null)
-                    .addFlags(Intent.FLAG_IGNORE_EPHEMERAL)
-                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            PendingIntent pendingIntent = PendingIntent.getActivityAsUser(mContext,
-                    0 /* requestCode */, browserIntent, 0 /* flags */, null, user);
-            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)
-                    .addCategory("unique:" + System.currentTimeMillis())
-                    .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
-                    .putExtra(Intent.EXTRA_VERSION_CODE, (int) (appInfo.versionCode & 0x7fffffff))
-                    .putExtra(Intent.EXTRA_LONG_VERSION_CODE, appInfo.versionCode)
-                    .putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, pendingIntent);
-
-            PendingIntent webPendingIntent = PendingIntent.getActivityAsUser(mContext, 0,
-                    goToWebIntent, 0, null, user);
-            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(helpCenterIntent)
-                        .setColor(mContext.getColor(R.color.instant_apps_color))
-                        .setContentTitle(mContext.getString(R.string.instant_apps_title,
-                                appInfo.loadLabel(mContext.getPackageManager())))
-                        .setLargeIcon(Icon.createWithResource(pkg, appInfo.icon))
-                        .setSmallIcon(Icon.createWithResource(mContext.getPackageName(),
-                                R.drawable.instant_icon))
-                        .setContentText(message)
-                        .setStyle(new Notification.BigTextStyle().bigText(message))
-                        .setOngoing(true)
-                        .build(),
-                new UserHandle(userId));
-    }
-
-    private Intent getTaskIntent(int taskId, int userId) {
-        try {
-            final List<ActivityManager.RecentTaskInfo> tasks =
-                    ActivityTaskManager.getService().getRecentTasks(
-                            NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId).getList();
-            for (int i = 0; i < tasks.size(); i++) {
-                if (tasks.get(i).id == taskId) {
-                    return tasks.get(i).baseIntent;
-                }
-            }
-        } catch (RemoteException e) {
-            // Fall through
-        }
-        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)) {
-            mCurrentNotifs.add(key);
-            return true;
-        }
-        return false;
-    }
-
     private final SynchronousUserSwitchObserver mUserSwitchListener =
             new SynchronousUserSwitchObserver() {
                 @Override
@@ -690,7 +487,6 @@
                     mHandler.post(() -> {
                         updateAlarm();
                         updateManagedProfile();
-                        updateForegroundInstantApps();
                     });
                 }
             };
@@ -723,14 +519,12 @@
             boolean forced) {
         if (mContext.getDisplayId() == displayId) {
             updateManagedProfile();
-            updateForegroundInstantApps();
         }
     }
 
     @Override
     public void onKeyguardShowingChanged() {
         updateManagedProfile();
-        updateForegroundInstantApps();
     }
 
     @Override
@@ -743,11 +537,6 @@
     }
 
     @Override
-    public void preloadRecentApps() {
-        updateForegroundInstantApps();
-    }
-
-    @Override
     public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
         boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait(
                 mRotationLockController, mContext);
@@ -822,14 +611,6 @@
         mIconController.setIconVisibility(mSlotLocation, showLocation);
     }
 
-    private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() {
-        @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) {