Add ongoing notification for instant apps

Show a BTW notification when instant app is top of either of the
stacks.

Bug: 35152138
Test: Open ephemeral app, look at notifications.
Change-Id: Id992dd893a6632e9b95948a9e11167762f42bc49
This commit is contained in:
Jason Monk
2017-02-16 17:19:12 -05:00
parent 65febdd300
commit 324b8867e4
5 changed files with 186 additions and 8 deletions

View File

@@ -0,0 +1,30 @@
<!--
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>

View File

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

View File

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

View File

@@ -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;
@@ -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
@@ -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.
@@ -163,7 +195,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
}
// TTY status
mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode, null);
mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode, null);
mIconController.setIconVisibility(mSlotTty, false);
// bluetooth status
@@ -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() {
@@ -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
@@ -423,8 +468,10 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
}
private void updateManagedProfile() {
if (DEBUG) Log.v(TAG, "updateManagedProfile: mManagedProfileFocused: "
+ mManagedProfileFocused);
if (DEBUG) {
Log.v(TAG, "updateManagedProfile: mManagedProfileFocused: "
+ mManagedProfileFocused);
}
final boolean showIcon;
if (mManagedProfileFocused && !mKeyguardMonitor.isShowing()) {
showIcon = true;
@@ -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
@@ -466,6 +583,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
profileChanged(newUserId);
updateQuietState();
updateManagedProfile();
updateForegroundInstantApps();
}
});
}
@@ -497,20 +615,22 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
private final NextAlarmController.NextAlarmChangeCallback mNextAlarmCallback =
new NextAlarmController.NextAlarmChangeCallback() {
@Override
public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
updateAlarm();
}
};
@Override
public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
updateAlarm();
}
};
@Override
public void appTransitionStarting(long startTime, long duration, boolean forced) {
updateManagedProfile();
updateForegroundInstantApps();
}
@Override
public void onKeyguardShowingChanged() {
updateManagedProfile();
updateForegroundInstantApps();
}
@Override
@@ -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(
@@ -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) {

View File

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