Bug: 111214100 Change-Id: If3f82bfac740f4f3cf8312f396943f2ebd447c12 Test: Notifications are showing correctly after new apps installed
348 lines
14 KiB
Java
348 lines
14 KiB
Java
/*
|
|
* Copyright 2018 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.packageinstaller;
|
|
|
|
import android.annotation.NonNull;
|
|
import android.app.Notification;
|
|
import android.app.NotificationChannel;
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.pm.PackageItemInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.content.res.Resources;
|
|
import android.graphics.drawable.Icon;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.provider.Settings;
|
|
import android.util.Log;
|
|
|
|
/**
|
|
* A util class that handle and post new app installed notifications.
|
|
*/
|
|
class PackageInstalledNotificationUtils {
|
|
private static final String TAG = PackageInstalledNotificationUtils.class.getSimpleName();
|
|
|
|
private static final String NEW_APP_INSTALLED_CHANNEL_ID_PREFIX = "INSTALLER:";
|
|
private static final String META_DATA_INSTALLER_NOTIFICATION_SMALL_ICON_KEY =
|
|
"com.android.packageinstaller.notification.smallIcon";
|
|
private static final String META_DATA_INSTALLER_NOTIFICATION_COLOR_KEY =
|
|
"com.android.packageinstaller.notification.color";
|
|
|
|
private static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f;
|
|
|
|
private final Context mContext;
|
|
private final NotificationManager mNotificationManager;
|
|
|
|
private final String mInstallerPackage;
|
|
private final String mInstallerAppLabel;
|
|
private final Icon mInstallerAppSmallIcon;
|
|
private final Integer mInstallerAppColor;
|
|
|
|
private final String mInstalledPackage;
|
|
private final String mInstalledAppLabel;
|
|
private final Icon mInstalledAppLargeIcon;
|
|
|
|
private final String mChannelId;
|
|
|
|
PackageInstalledNotificationUtils(@NonNull Context context, @NonNull String installerPackage,
|
|
@NonNull String installedPackage) {
|
|
mContext = context;
|
|
mNotificationManager = context.getSystemService(NotificationManager.class);
|
|
ApplicationInfo installerAppInfo;
|
|
ApplicationInfo installedAppInfo;
|
|
|
|
try {
|
|
installerAppInfo = context.getPackageManager().getApplicationInfo(installerPackage,
|
|
PackageManager.GET_META_DATA);
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
// Should not happen
|
|
throw new IllegalStateException("Unable to get application info: " + installerPackage);
|
|
}
|
|
try {
|
|
installedAppInfo = context.getPackageManager().getApplicationInfo(installedPackage,
|
|
PackageManager.GET_META_DATA);
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
// Should not happen
|
|
throw new IllegalStateException("Unable to get application info: " + installedPackage);
|
|
}
|
|
mInstallerPackage = installerPackage;
|
|
mInstallerAppLabel = getAppLabel(context, installerAppInfo, installerPackage);
|
|
mInstallerAppSmallIcon = getAppNotificationIcon(context, installerAppInfo);
|
|
mInstallerAppColor = getAppNotificationColor(context, installerAppInfo);
|
|
|
|
mInstalledPackage = installedPackage;
|
|
mInstalledAppLabel = getAppLabel(context, installedAppInfo, installerPackage);
|
|
mInstalledAppLargeIcon = getAppLargeIcon(installedAppInfo);
|
|
|
|
mChannelId = NEW_APP_INSTALLED_CHANNEL_ID_PREFIX + installerPackage;
|
|
}
|
|
|
|
/**
|
|
* Get app label from app's manifest.
|
|
*
|
|
* @param context A context of the current app
|
|
* @param appInfo Application info of targeted app
|
|
* @param packageName Package name of targeted app
|
|
* @return The label of targeted application, or package name if label is not found
|
|
*/
|
|
private static String getAppLabel(@NonNull Context context, @NonNull ApplicationInfo appInfo,
|
|
@NonNull String packageName) {
|
|
CharSequence label = appInfo.loadSafeLabel(context.getPackageManager(),
|
|
DEFAULT_MAX_LABEL_SIZE_PX,
|
|
PackageItemInfo.SAFE_LABEL_FLAG_TRIM
|
|
| PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE).toString();
|
|
if (label != null) {
|
|
return label.toString();
|
|
}
|
|
return packageName;
|
|
}
|
|
|
|
/**
|
|
* The app icon from app's manifest.
|
|
*
|
|
* @param appInfo Application info of targeted app
|
|
* @return App icon of targeted app, or Android default app icon if icon is not found
|
|
*/
|
|
private static Icon getAppLargeIcon(@NonNull ApplicationInfo appInfo) {
|
|
if (appInfo.icon != 0) {
|
|
return Icon.createWithResource(appInfo.packageName, appInfo.icon);
|
|
} else {
|
|
return Icon.createWithResource("android", android.R.drawable.sym_def_app_icon);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get notification icon from installer's manifest meta-data.
|
|
*
|
|
* @param context A context of the current app
|
|
* @param appInfo Installer application info
|
|
* @return Notification icon that listed in installer's manifest meta-data.
|
|
* If icon is not found in meta-data, then it returns Android default download icon.
|
|
*/
|
|
private static Icon getAppNotificationIcon(@NonNull Context context,
|
|
@NonNull ApplicationInfo appInfo) {
|
|
if (appInfo.metaData == null) {
|
|
return Icon.createWithResource(context, R.drawable.ic_file_download);
|
|
}
|
|
|
|
int iconResId = appInfo.metaData.getInt(
|
|
META_DATA_INSTALLER_NOTIFICATION_SMALL_ICON_KEY, 0);
|
|
if (iconResId != 0) {
|
|
return Icon.createWithResource(appInfo.packageName, iconResId);
|
|
}
|
|
return Icon.createWithResource(context, R.drawable.ic_file_download);
|
|
}
|
|
|
|
/**
|
|
* Get notification color from installer's manifest meta-data.
|
|
*
|
|
* @param context A context of the current app
|
|
* @param appInfo Installer application info
|
|
* @return Notification color that listed in installer's manifest meta-data, or null if
|
|
* meta-data is not found.
|
|
*/
|
|
private static Integer getAppNotificationColor(@NonNull Context context,
|
|
@NonNull ApplicationInfo appInfo) {
|
|
if (appInfo.metaData == null) {
|
|
return null;
|
|
}
|
|
|
|
int colorResId = appInfo.metaData.getInt(
|
|
META_DATA_INSTALLER_NOTIFICATION_COLOR_KEY, 0);
|
|
if (colorResId != 0) {
|
|
try {
|
|
PackageManager pm = context.getPackageManager();
|
|
Resources resources = pm.getResourcesForApplication(appInfo.packageName);
|
|
return resources.getColor(colorResId, context.getTheme());
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
Log.e(TAG, "Error while loading notification color: " + colorResId + " for "
|
|
+ appInfo.packageName);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static Intent getAppDetailIntent(@NonNull String packageName) {
|
|
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
|
intent.setData(Uri.fromParts("package", packageName, null));
|
|
return intent;
|
|
}
|
|
|
|
private static Intent resolveIntent(@NonNull Context context, @NonNull Intent i) {
|
|
ResolveInfo result = context.getPackageManager().resolveActivity(i, 0);
|
|
if (result == null) {
|
|
return null;
|
|
}
|
|
return new Intent(i.getAction()).setClassName(result.activityInfo.packageName,
|
|
result.activityInfo.name);
|
|
}
|
|
|
|
private static Intent getAppStoreLink(@NonNull Context context,
|
|
@NonNull String installerPackageName, @NonNull String packageName) {
|
|
Intent intent = new Intent(Intent.ACTION_SHOW_APP_INFO)
|
|
.setPackage(installerPackageName);
|
|
|
|
Intent result = resolveIntent(context, intent);
|
|
if (result != null) {
|
|
result.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
|
|
return result;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Create notification channel for showing apps installed notifications.
|
|
*/
|
|
private void createChannel() {
|
|
NotificationChannel channel = new NotificationChannel(mChannelId, mInstallerAppLabel,
|
|
NotificationManager.IMPORTANCE_DEFAULT);
|
|
channel.setDescription(
|
|
mContext.getString(R.string.app_installed_notification_channel_description));
|
|
channel.enableVibration(false);
|
|
channel.setSound(null, null);
|
|
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
|
channel.setBlockableSystem(true);
|
|
|
|
mNotificationManager.createNotificationChannel(channel);
|
|
}
|
|
|
|
/**
|
|
* Returns a pending intent when user clicks on apps installed notification.
|
|
* It should launch the app if possible, otherwise it will return app store's app page.
|
|
* If app store's app page is not available, it will return Android app details page.
|
|
*/
|
|
private PendingIntent getInstalledAppLaunchIntent() {
|
|
Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(mInstalledPackage);
|
|
|
|
// If installed app does not have a launch intent, bring user to app store page
|
|
if (intent == null) {
|
|
intent = getAppStoreLink(mContext, mInstallerPackage, mInstalledPackage);
|
|
}
|
|
|
|
// If app store cannot handle this, bring user to app settings page
|
|
if (intent == null) {
|
|
intent = getAppDetailIntent(mInstalledPackage);
|
|
}
|
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
return PendingIntent.getActivity(mContext,
|
|
0 /* request code */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
}
|
|
|
|
/**
|
|
* Returns a pending intent that starts installer's launch intent.
|
|
* If it doesn't have a launch intent, it will return installer's Android app details page.
|
|
*/
|
|
private PendingIntent getInstallerEntranceIntent() {
|
|
Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(mInstallerPackage);
|
|
|
|
// If installer does not have a launch intent, bring user to app settings page
|
|
if (intent == null) {
|
|
intent = getAppDetailIntent(mInstallerPackage);
|
|
}
|
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
return PendingIntent.getActivity(mContext,
|
|
0 /* request code */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
}
|
|
|
|
/**
|
|
* Returns a notification builder for grouped notifications.
|
|
*/
|
|
private Notification.Builder getGroupNotificationBuilder() {
|
|
PendingIntent contentIntent = getInstallerEntranceIntent();
|
|
|
|
Bundle extras = new Bundle();
|
|
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, mInstallerAppLabel);
|
|
|
|
Notification.Builder builder =
|
|
new Notification.Builder(mContext, mChannelId)
|
|
.setSmallIcon(mInstallerAppSmallIcon)
|
|
.setGroup(mChannelId)
|
|
.setExtras(extras)
|
|
.setLocalOnly(true)
|
|
.setCategory(Notification.CATEGORY_STATUS)
|
|
.setContentIntent(contentIntent)
|
|
.setGroupSummary(true);
|
|
|
|
if (mInstallerAppColor != null) {
|
|
builder.setColor(mInstallerAppColor);
|
|
}
|
|
return builder;
|
|
}
|
|
|
|
/**
|
|
* Returns notification build for individual installed applications.
|
|
*/
|
|
private Notification.Builder getAppInstalledNotificationBuilder() {
|
|
PendingIntent contentIntent = getInstalledAppLaunchIntent();
|
|
|
|
Bundle extras = new Bundle();
|
|
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, mInstallerAppLabel);
|
|
|
|
String tickerText = String.format(
|
|
mContext.getString(R.string.notification_installation_success_status),
|
|
mInstalledAppLabel);
|
|
|
|
Notification.Builder builder =
|
|
new Notification.Builder(mContext, mChannelId)
|
|
.setAutoCancel(true)
|
|
.setSmallIcon(mInstallerAppSmallIcon)
|
|
.setContentTitle(mInstalledAppLabel)
|
|
.setContentText(mContext.getString(
|
|
R.string.notification_installation_success_message))
|
|
.setContentIntent(contentIntent)
|
|
.setTicker(tickerText)
|
|
.setCategory(Notification.CATEGORY_STATUS)
|
|
.setShowWhen(true)
|
|
.setWhen(System.currentTimeMillis())
|
|
.setLocalOnly(true)
|
|
.setGroup(mChannelId)
|
|
.addExtras(extras)
|
|
.setStyle(new Notification.BigTextStyle());
|
|
|
|
if (mInstalledAppLargeIcon != null) {
|
|
builder.setLargeIcon(mInstalledAppLargeIcon);
|
|
}
|
|
if (mInstallerAppColor != null) {
|
|
builder.setColor(mInstallerAppColor);
|
|
}
|
|
return builder;
|
|
}
|
|
|
|
/**
|
|
* Post new app installed notification.
|
|
*/
|
|
void postAppInstalledNotification() {
|
|
createChannel();
|
|
|
|
// Post app installed notification
|
|
Notification.Builder appNotificationBuilder = getAppInstalledNotificationBuilder();
|
|
mNotificationManager.notify(mInstalledPackage, mInstalledPackage.hashCode(),
|
|
appNotificationBuilder.build());
|
|
|
|
// Post installer group notification
|
|
Notification.Builder groupNotificationBuilder = getGroupNotificationBuilder();
|
|
mNotificationManager.notify(mInstallerPackage, mInstallerPackage.hashCode(),
|
|
groupNotificationBuilder.build());
|
|
}
|
|
}
|