Create notification helper class
This commit is contained in:
parent
9e1458f41e
commit
10cbf1ef7a
@ -220,6 +220,8 @@ public class FDroidApp extends Application {
|
|||||||
curTheme = Preferences.get().getTheme();
|
curTheme = Preferences.get().getTheme();
|
||||||
Preferences.get().configureProxy();
|
Preferences.get().configureProxy();
|
||||||
|
|
||||||
|
NotificationHelper.init(getApplicationContext());
|
||||||
|
|
||||||
InstalledAppProviderService.compareToPackageManager(this);
|
InstalledAppProviderService.compareToPackageManager(this);
|
||||||
|
|
||||||
// If the user changes the preference to do with filtering rooted apps,
|
// If the user changes the preference to do with filtering rooted apps,
|
||||||
|
522
app/src/main/java/org/fdroid/fdroid/NotificationHelper.java
Normal file
522
app/src/main/java/org/fdroid/fdroid/NotificationHelper.java
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.support.v4.app.NotificationManagerCompat;
|
||||||
|
import android.support.v4.app.TaskStackBuilder;
|
||||||
|
import android.support.v4.util.LongSparseArray;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.text.style.StyleSpan;
|
||||||
|
|
||||||
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
import com.nostra13.universalimageloader.core.assist.ImageSize;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.data.Apk;
|
||||||
|
import org.fdroid.fdroid.data.ApkProvider;
|
||||||
|
import org.fdroid.fdroid.data.App;
|
||||||
|
import org.fdroid.fdroid.data.AppProvider;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class NotificationHelper {
|
||||||
|
|
||||||
|
private static final String BROADCAST_NOTIFICATIONS_UPDATES_CLEARED = "org.fdroid.fdroid.installer.notifications.updates.cleared";
|
||||||
|
private static final String BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED = "org.fdroid.fdroid.installer.notifications.installed.cleared";
|
||||||
|
private static final String BROADCAST_NOTIFICATIONS_NOTIFICATION_DELETED = "org.fdroid.fdroid.installer.notifications.deleted";
|
||||||
|
|
||||||
|
private static final int NOTIFY_ID_UPDATES = 4711;
|
||||||
|
private static final int NOTIFY_ID_INSTALLED = 4712;
|
||||||
|
|
||||||
|
private static final int MAX_UPDATES_TO_SHOW = 5;
|
||||||
|
private static final int MAX_INSTALLED_TO_SHOW = 10;
|
||||||
|
|
||||||
|
private static final String EXTRA_NOTIFICATION_TAG = "tag";
|
||||||
|
private static final String GROUP_UPDATES = "updates";
|
||||||
|
private static final String GROUP_INSTALLED = "installed";
|
||||||
|
|
||||||
|
public enum Status {
|
||||||
|
UpdateAvailable,
|
||||||
|
Downloading,
|
||||||
|
ReadyToInstall,
|
||||||
|
Installing,
|
||||||
|
Installed,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NotificationHelper instance;
|
||||||
|
|
||||||
|
public static void init(Context context) {
|
||||||
|
instance = new NotificationHelper(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NotificationHelper getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AppEntry {
|
||||||
|
App app;
|
||||||
|
Apk apk;
|
||||||
|
Status status;
|
||||||
|
PendingIntent intent;
|
||||||
|
int progressCurrent;
|
||||||
|
int progressMax;
|
||||||
|
|
||||||
|
AppEntry(App app, Apk apk, Status status, PendingIntent intent) {
|
||||||
|
this.app = app;
|
||||||
|
this.apk = apk;
|
||||||
|
this.status = status;
|
||||||
|
this.intent = intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getTag() {
|
||||||
|
return apk.getUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getId() {
|
||||||
|
return getTag().hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final NotificationManagerCompat notificationManager;
|
||||||
|
private HashMap<String, AppEntry> appMapping;
|
||||||
|
private boolean isBatchUpdating;
|
||||||
|
private ArrayList<AppEntry> updates;
|
||||||
|
private ArrayList<AppEntry> installed;
|
||||||
|
|
||||||
|
private NotificationHelper(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
notificationManager = NotificationManagerCompat.from(context);
|
||||||
|
|
||||||
|
// We need to listen to when notifications are cleared, so that we "forget" all that we currently know about updates
|
||||||
|
// and installs.
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(BROADCAST_NOTIFICATIONS_UPDATES_CLEARED);
|
||||||
|
filter.addAction(BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED);
|
||||||
|
filter.addAction(BROADCAST_NOTIFICATIONS_NOTIFICATION_DELETED);
|
||||||
|
BroadcastReceiver mReceiverNotificationsCleared = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
switch (intent.getAction()) {
|
||||||
|
case BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED:
|
||||||
|
clearAllInstalledInternal();
|
||||||
|
break;
|
||||||
|
case BROADCAST_NOTIFICATIONS_UPDATES_CLEARED:
|
||||||
|
clearAllUpdatesInternal();
|
||||||
|
break;
|
||||||
|
case BROADCAST_NOTIFICATIONS_NOTIFICATION_DELETED:
|
||||||
|
String id = intent.getStringExtra(EXTRA_NOTIFICATION_TAG);
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
context.registerReceiver(mReceiverNotificationsCleared, filter);
|
||||||
|
appMapping = new HashMap<>();
|
||||||
|
updates = new ArrayList<>();
|
||||||
|
installed = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setApkInternal(Apk apk, Status status, PendingIntent intent) {
|
||||||
|
if (apk == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppEntry entry = appMapping.get(apk.getUrl());
|
||||||
|
if (status == null) {
|
||||||
|
// Remove
|
||||||
|
if (entry != null) {
|
||||||
|
appMapping.remove(apk.getUrl());
|
||||||
|
notificationManager.cancel(entry.getTag(), entry.getId());
|
||||||
|
}
|
||||||
|
} else if (entry != null) {
|
||||||
|
// Update
|
||||||
|
boolean isStatusUpdate = (entry.status != status);
|
||||||
|
entry.status = status;
|
||||||
|
entry.intent = intent;
|
||||||
|
createNotificationForAppEntry(entry);
|
||||||
|
if (isStatusUpdate) {
|
||||||
|
updateSummaryNotifications();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Add
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
App app = AppProvider.Helper.findSpecificApp(resolver, apk.packageName, apk.repo);
|
||||||
|
entry = new AppEntry(app, apk, status, intent);
|
||||||
|
appMapping.put(apk.getUrl(), entry);
|
||||||
|
createNotificationForAppEntry(entry);
|
||||||
|
updateSummaryNotifications();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setApkProgressInternal(Apk apk, int max, int current) {
|
||||||
|
if (appMapping.get(apk.getUrl()) != null) {
|
||||||
|
AppEntry entry = appMapping.get(apk.getUrl());
|
||||||
|
entry.progressMax = max;
|
||||||
|
entry.progressCurrent = current;
|
||||||
|
createNotificationForAppEntry(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearAllUpdatesInternal() {
|
||||||
|
for(Iterator<Map.Entry<String, AppEntry>> it = appMapping.entrySet().iterator(); it.hasNext(); ) {
|
||||||
|
Map.Entry<String, AppEntry> entry = it.next();
|
||||||
|
if(entry.getValue().status != Status.Installed) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearAllInstalledInternal() {
|
||||||
|
for(Iterator<Map.Entry<String, AppEntry>> it = appMapping.entrySet().iterator(); it.hasNext(); ) {
|
||||||
|
Map.Entry<String, AppEntry> entry = it.next();
|
||||||
|
if(entry.getValue().status == Status.Installed) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSummaryNotifications() {
|
||||||
|
if (!isBatchUpdating) {
|
||||||
|
// Get the list of updates and installed available
|
||||||
|
updates.clear();
|
||||||
|
installed.clear();
|
||||||
|
for (Iterator<Map.Entry<String, AppEntry>> it = appMapping.entrySet().iterator(); it.hasNext(); ) {
|
||||||
|
Map.Entry<String, AppEntry> entry = it.next();
|
||||||
|
if (entry.getValue().status != Status.Installed) {
|
||||||
|
updates.add(entry.getValue());
|
||||||
|
} else {
|
||||||
|
installed.add(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder;
|
||||||
|
if (updates.size() == 0) {
|
||||||
|
// No updates, remove summary
|
||||||
|
notificationManager.cancel(GROUP_UPDATES, NOTIFY_ID_UPDATES);
|
||||||
|
} else {
|
||||||
|
builder = createUpdateSummaryNotification(updates);
|
||||||
|
notificationManager.notify(GROUP_UPDATES, NOTIFY_ID_UPDATES, builder.build());
|
||||||
|
}
|
||||||
|
if (installed.size() == 0) {
|
||||||
|
// No installed, remove summary
|
||||||
|
notificationManager.cancel(GROUP_INSTALLED, NOTIFY_ID_INSTALLED);
|
||||||
|
} else {
|
||||||
|
builder = createInstalledSummaryNotification(installed);
|
||||||
|
notificationManager.notify(GROUP_INSTALLED, NOTIFY_ID_INSTALLED, builder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNotificationForAppEntry(AppEntry entry) {
|
||||||
|
NotificationCompat.Builder builder;
|
||||||
|
if (entry.status == Status.Installed) {
|
||||||
|
builder = createInstalledNotification(entry);
|
||||||
|
} else {
|
||||||
|
builder = createUpdateNotification(entry);
|
||||||
|
}
|
||||||
|
notificationManager.notify(entry.getTag(), entry.getId(), builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an Apk to the notifications manager.
|
||||||
|
* @param apk The apk to add.
|
||||||
|
* @param status The current status of the app
|
||||||
|
* @param pendingIntent Action when notification is clicked. Can be null for default action(s)
|
||||||
|
*/
|
||||||
|
public static void setApk(Apk apk, Status status, PendingIntent pendingIntent) {
|
||||||
|
getInstance().setApkInternal(apk, status, pendingIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeApk(Apk apk) {
|
||||||
|
getInstance().setApkInternal(apk, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setApkProgress(Apk apk, int max, int current) {
|
||||||
|
getInstance().setApkProgressInternal(apk, max, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startBatchUpdates() {
|
||||||
|
getInstance().isBatchUpdating = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void endBatchUpdates() {
|
||||||
|
getInstance().isBatchUpdating = false;
|
||||||
|
getInstance().updateSummaryNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clearAllUpdates() {
|
||||||
|
getInstance().clearAllUpdatesInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clearAllInstalled() {
|
||||||
|
getInstance().clearAllInstalledInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotificationCompat.Action getAction(AppEntry entry) {
|
||||||
|
if (entry.status == Status.UpdateAvailable) {
|
||||||
|
// Make sure we have an intent to install the app. If not set, we create an intent
|
||||||
|
// to open up the app details page for the app. From there, the user can hit "install"
|
||||||
|
PendingIntent intent = entry.intent;
|
||||||
|
if (intent == null) {
|
||||||
|
intent = getAppDetailsIntent(0, entry.apk);
|
||||||
|
}
|
||||||
|
return new NotificationCompat.Action(R.drawable.ic_notify_update_24dp, "Update", intent);
|
||||||
|
} else if (entry.status == Status.Downloading || entry.status == Status.Installing) {
|
||||||
|
PendingIntent intent = entry.intent;
|
||||||
|
if (intent != null) {
|
||||||
|
return new NotificationCompat.Action(R.drawable.ic_notify_cancel_24dp, "Cancel", intent);
|
||||||
|
}
|
||||||
|
} else if (entry.status == Status.ReadyToInstall) {
|
||||||
|
// Make sure we have an intent to install the app. If not set, we create an intent
|
||||||
|
// to open up the app details page for the app. From there, the user can hit "install"
|
||||||
|
PendingIntent intent = entry.intent;
|
||||||
|
if (intent == null) {
|
||||||
|
intent = getAppDetailsIntent(0, entry.apk);
|
||||||
|
}
|
||||||
|
return new NotificationCompat.Action(R.drawable.ic_notify_install_24dp, "Install", intent);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSingleItemTitleString(App app, Status status) {
|
||||||
|
switch (status) {
|
||||||
|
case UpdateAvailable:
|
||||||
|
return "Update Available";
|
||||||
|
case Downloading:
|
||||||
|
return app.name;
|
||||||
|
case ReadyToInstall:
|
||||||
|
return "Update ready to install"; // TODO - "Update"? Should just be "ready to install"?
|
||||||
|
case Installing:
|
||||||
|
return app.name;
|
||||||
|
case Installed:
|
||||||
|
return app.name;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSingleItemContentString(App app, Status status) {
|
||||||
|
switch (status) {
|
||||||
|
case UpdateAvailable:
|
||||||
|
return app.name;
|
||||||
|
case Downloading:
|
||||||
|
return String.format("Downloading update for \"%s\"...", app.name);
|
||||||
|
case ReadyToInstall:
|
||||||
|
return app.name;
|
||||||
|
case Installing:
|
||||||
|
return String.format("Installing \"%s\"...", app.name);
|
||||||
|
case Installed:
|
||||||
|
return "Successfully installed";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMultiItemContentString(App app, Status status) {
|
||||||
|
switch (status) {
|
||||||
|
case UpdateAvailable:
|
||||||
|
return "Update available";
|
||||||
|
case Downloading:
|
||||||
|
return "Downloading update...";
|
||||||
|
case ReadyToInstall:
|
||||||
|
return "Ready to install";
|
||||||
|
case Installing:
|
||||||
|
return "Installing";
|
||||||
|
case Installed:
|
||||||
|
return "Successfully installed";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a {@link PendingIntent} for a {@link Notification} to send when it
|
||||||
|
* is clicked. {@link AppDetails} handles {@code Intent}s that are missing
|
||||||
|
* or bad {@link AppDetails#EXTRA_APPID}, so it does not need to be checked
|
||||||
|
* here.
|
||||||
|
*/
|
||||||
|
private PendingIntent getAppDetailsIntent(int requestCode, Apk apk) {
|
||||||
|
Intent notifyIntent = new Intent(context, AppDetails.class)
|
||||||
|
.putExtra(AppDetails.EXTRA_APPID, apk.packageName);
|
||||||
|
return TaskStackBuilder.create(context)
|
||||||
|
.addParentStack(AppDetails.class)
|
||||||
|
.addNextIntent(notifyIntent)
|
||||||
|
.getPendingIntent(requestCode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotificationCompat.Builder createUpdateNotification(AppEntry entry) {
|
||||||
|
App app = entry.app;
|
||||||
|
Status status = entry.status;
|
||||||
|
|
||||||
|
// TODO - async image loading
|
||||||
|
int largeIconSize = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
|
||||||
|
Bitmap iconLarge = ImageLoader.getInstance().loadImageSync(app.iconUrl, new ImageSize(largeIconSize, largeIconSize));
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder =
|
||||||
|
new NotificationCompat.Builder(context)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setLargeIcon(iconLarge)
|
||||||
|
.setSmallIcon(R.drawable.ic_stat_notify_updates)
|
||||||
|
.setContentTitle(getSingleItemTitleString(app, status))
|
||||||
|
.setContentText(getSingleItemContentString(app, status))
|
||||||
|
.setGroup(GROUP_UPDATES);
|
||||||
|
|
||||||
|
// Handle actions
|
||||||
|
//
|
||||||
|
NotificationCompat.Action action = getAction(entry);
|
||||||
|
if (action != null) {
|
||||||
|
builder.addAction(action);
|
||||||
|
// TODO - also click on whole item?
|
||||||
|
builder.setContentIntent(action.getActionIntent());
|
||||||
|
} else if (entry.intent != null) {
|
||||||
|
builder.setContentIntent(entry.intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle progress bar (for some states)
|
||||||
|
//
|
||||||
|
if (status == Status.Downloading) {
|
||||||
|
if (entry.progressMax == 0)
|
||||||
|
builder.setProgress(100, 0, true);
|
||||||
|
else
|
||||||
|
builder.setProgress(entry.progressMax, entry.progressCurrent, false);
|
||||||
|
} else if (status == Status.Installing) {
|
||||||
|
builder.setProgress(100, 0, true); // indeterminate bar
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_NOTIFICATION_DELETED);
|
||||||
|
intentDeleted.putExtra(EXTRA_NOTIFICATION_TAG, entry.getId());
|
||||||
|
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, 0);
|
||||||
|
builder.setDeleteIntent(piDeleted);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotificationCompat.Builder createUpdateSummaryNotification(ArrayList<AppEntry> updates) {
|
||||||
|
String title = String.format("%d Updates", updates.size());
|
||||||
|
StringBuilder text = new StringBuilder();
|
||||||
|
|
||||||
|
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
|
||||||
|
inboxStyle.setBigContentTitle(title);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_UPDATES_TO_SHOW && i < updates.size(); i++) {
|
||||||
|
AppEntry entry = updates.get(i);
|
||||||
|
App app = entry.app;
|
||||||
|
Status status = entry.status;
|
||||||
|
|
||||||
|
String content = getMultiItemContentString(app, status);
|
||||||
|
SpannableStringBuilder sb = new SpannableStringBuilder(app.name);
|
||||||
|
sb.setSpan(new StyleSpan(Typeface.BOLD), 0, sb.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
sb.append(" ");
|
||||||
|
sb.append(content);
|
||||||
|
inboxStyle.addLine(sb);
|
||||||
|
|
||||||
|
if (text.length() > 0)
|
||||||
|
text.append(", ");
|
||||||
|
text.append(app.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (updates.size() > MAX_UPDATES_TO_SHOW) {
|
||||||
|
// int diff = updates.size() - MAX_UPDATES_TO_SHOW;
|
||||||
|
// inboxStyle.setSummaryText(context.getString(R.string.update_notification_more, diff));
|
||||||
|
//}
|
||||||
|
|
||||||
|
inboxStyle.setSummaryText(title);
|
||||||
|
|
||||||
|
|
||||||
|
// Intent to open main app list
|
||||||
|
Intent intentObject = new Intent(context, FDroid.class);
|
||||||
|
PendingIntent piAction = PendingIntent.getActivity(context, 0, intentObject, 0);
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder =
|
||||||
|
new NotificationCompat.Builder(context)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setSmallIcon(R.drawable.ic_launcher)
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setContentText(text)
|
||||||
|
.setContentIntent(piAction)
|
||||||
|
.setStyle(inboxStyle)
|
||||||
|
.setGroup(GROUP_UPDATES)
|
||||||
|
.setGroupSummary(true);
|
||||||
|
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_UPDATES_CLEARED);
|
||||||
|
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, 0);
|
||||||
|
builder.setDeleteIntent(piDeleted);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotificationCompat.Builder createInstalledNotification(AppEntry entry) {
|
||||||
|
App app = entry.app;
|
||||||
|
|
||||||
|
int largeIconSize = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
|
||||||
|
Bitmap iconLarge = ImageLoader.getInstance().loadImageSync(app.iconUrl, new ImageSize(largeIconSize, largeIconSize));
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder =
|
||||||
|
new NotificationCompat.Builder(context)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setLargeIcon(iconLarge)
|
||||||
|
.setSmallIcon(R.drawable.ic_stat_notify_updates)
|
||||||
|
.setContentTitle(app.name)
|
||||||
|
.setContentText("Successfully Installed")
|
||||||
|
.setGroup(GROUP_INSTALLED);
|
||||||
|
|
||||||
|
PackageManager pm = context.getPackageManager();
|
||||||
|
Intent intentObject = pm.getLaunchIntentForPackage(app.packageName);
|
||||||
|
PendingIntent piAction = PendingIntent.getActivity(context, 0, intentObject, 0);
|
||||||
|
builder.setContentIntent(piAction);
|
||||||
|
|
||||||
|
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_NOTIFICATION_DELETED);
|
||||||
|
intentDeleted.putExtra(EXTRA_NOTIFICATION_TAG, entry.getId());
|
||||||
|
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, 0);
|
||||||
|
builder.setDeleteIntent(piDeleted);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotificationCompat.Builder createInstalledSummaryNotification(ArrayList<AppEntry> installed) {
|
||||||
|
String title = String.format("%d Apps Installed", installed.size());
|
||||||
|
StringBuilder text = new StringBuilder();
|
||||||
|
|
||||||
|
NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle();
|
||||||
|
bigTextStyle.setBigContentTitle(title);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_INSTALLED_TO_SHOW && i < installed.size(); i++) {
|
||||||
|
AppEntry entry = installed.get(i);
|
||||||
|
App app = entry.app;
|
||||||
|
if (text.length() > 0)
|
||||||
|
text.append(", ");
|
||||||
|
text.append(app.name);
|
||||||
|
}
|
||||||
|
bigTextStyle.bigText(text);
|
||||||
|
if (installed.size() > MAX_INSTALLED_TO_SHOW) {
|
||||||
|
int diff = installed.size() - MAX_INSTALLED_TO_SHOW;
|
||||||
|
bigTextStyle.setSummaryText(context.getString(R.string.update_notification_more, diff));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intent to open main app list
|
||||||
|
Intent intentObject = new Intent(context, FDroid.class);
|
||||||
|
PendingIntent piAction = PendingIntent.getActivity(context, 0, intentObject, 0);
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder =
|
||||||
|
new NotificationCompat.Builder(context)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setSmallIcon(R.drawable.ic_launcher)
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setContentText(text)
|
||||||
|
.setContentIntent(piAction)
|
||||||
|
.setGroup(GROUP_INSTALLED)
|
||||||
|
.setGroupSummary(true);
|
||||||
|
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED);
|
||||||
|
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, 0);
|
||||||
|
builder.setDeleteIntent(piDeleted);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
@ -520,6 +520,19 @@ public class UpdateService extends IntentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showAppUpdatesNotification(Cursor hasUpdates) {
|
private void showAppUpdatesNotification(Cursor hasUpdates) {
|
||||||
|
|
||||||
|
if (hasUpdates != null) {
|
||||||
|
hasUpdates.moveToFirst();
|
||||||
|
NotificationHelper.startBatchUpdates();
|
||||||
|
for (int i = 0; i < Math.min(MAX_UPDATES_TO_SHOW, hasUpdates.getCount()); i++) {
|
||||||
|
App app = new App(hasUpdates);
|
||||||
|
hasUpdates.moveToNext();
|
||||||
|
Apk apk = ApkProvider.Helper.findApkFromAnyRepo(this, app.packageName, app.suggestedVersionCode);
|
||||||
|
NotificationHelper.setApk(apk, NotificationHelper.Status.UpdateAvailable, null);
|
||||||
|
}
|
||||||
|
NotificationHelper.endBatchUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
Utils.debugLog(TAG, "Notifying " + hasUpdates.getCount() + " updates.");
|
Utils.debugLog(TAG, "Notifying " + hasUpdates.getCount() + " updates.");
|
||||||
|
|
||||||
final int icon = Build.VERSION.SDK_INT >= 11 ? R.drawable.ic_stat_notify_updates : R.drawable.ic_launcher;
|
final int icon = Build.VERSION.SDK_INT >= 11 ? R.drawable.ic_stat_notify_updates : R.drawable.ic_launcher;
|
||||||
|
@ -22,6 +22,7 @@ import org.apache.commons.io.FileUtils;
|
|||||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||||
import org.fdroid.fdroid.AppDetails;
|
import org.fdroid.fdroid.AppDetails;
|
||||||
import org.fdroid.fdroid.Hasher;
|
import org.fdroid.fdroid.Hasher;
|
||||||
|
import org.fdroid.fdroid.NotificationHelper;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
import org.fdroid.fdroid.compat.PackageManagerCompat;
|
import org.fdroid.fdroid.compat.PackageManagerCompat;
|
||||||
@ -98,7 +99,6 @@ public class InstallManagerService extends Service {
|
|||||||
private static final HashMap<String, App> ACTIVE_APPS = new HashMap<>(3);
|
private static final HashMap<String, App> ACTIVE_APPS = new HashMap<>(3);
|
||||||
|
|
||||||
private LocalBroadcastManager localBroadcastManager;
|
private LocalBroadcastManager localBroadcastManager;
|
||||||
private NotificationManager notificationManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service does not use binding, so no need to implement this method
|
* This service does not use binding, so no need to implement this method
|
||||||
@ -113,7 +113,6 @@ public class InstallManagerService extends Service {
|
|||||||
super.onCreate();
|
super.onCreate();
|
||||||
Utils.debugLog(TAG, "creating Service");
|
Utils.debugLog(TAG, "creating Service");
|
||||||
localBroadcastManager = LocalBroadcastManager.getInstance(this);
|
localBroadcastManager = LocalBroadcastManager.getInstance(this);
|
||||||
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
|
||||||
|
|
||||||
BroadcastReceiver br = new BroadcastReceiver() {
|
BroadcastReceiver br = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
@ -122,7 +121,7 @@ public class InstallManagerService extends Service {
|
|||||||
for (Map.Entry<String, Apk> entry : ACTIVE_APKS.entrySet()) {
|
for (Map.Entry<String, Apk> entry : ACTIVE_APKS.entrySet()) {
|
||||||
if (TextUtils.equals(packageName, entry.getValue().packageName)) {
|
if (TextUtils.equals(packageName, entry.getValue().packageName)) {
|
||||||
String urlString = entry.getKey();
|
String urlString = entry.getKey();
|
||||||
cancelNotification(urlString);
|
NotificationHelper.removeApk(getApkFromActive(urlString));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,7 +149,7 @@ public class InstallManagerService extends Service {
|
|||||||
Apk apk = getApkFromActive(urlString);
|
Apk apk = getApkFromActive(urlString);
|
||||||
DownloaderService.cancel(this, apk.getPatchObbUrl());
|
DownloaderService.cancel(this, apk.getPatchObbUrl());
|
||||||
DownloaderService.cancel(this, apk.getMainObbUrl());
|
DownloaderService.cancel(this, apk.getMainObbUrl());
|
||||||
cancelNotification(urlString);
|
NotificationHelper.removeApk(apk);
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
} else if (!ACTION_INSTALL.equals(action)) {
|
} else if (!ACTION_INSTALL.equals(action)) {
|
||||||
Utils.debugLog(TAG, "Ignoring " + intent + " as it is not an " + ACTION_INSTALL + " intent");
|
Utils.debugLog(TAG, "Ignoring " + intent + " as it is not an " + ACTION_INSTALL + " intent");
|
||||||
@ -166,7 +165,7 @@ public class InstallManagerService extends Service {
|
|||||||
&& !DownloaderService.isQueuedOrActive(urlString)) {
|
&& !DownloaderService.isQueuedOrActive(urlString)) {
|
||||||
// TODO is there a case where we should allow an active urlString to pass through?
|
// TODO is there a case where we should allow an active urlString to pass through?
|
||||||
Utils.debugLog(TAG, urlString + " finished downloading while InstallManagerService was killed.");
|
Utils.debugLog(TAG, urlString + " finished downloading while InstallManagerService was killed.");
|
||||||
cancelNotification(urlString);
|
NotificationHelper.removeApk(getApkFromActive(urlString));
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,12 +177,9 @@ public class InstallManagerService extends Service {
|
|||||||
}
|
}
|
||||||
addToActive(urlString, app, apk);
|
addToActive(urlString, app, apk);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = createNotificationBuilder(urlString, apk);
|
registerApkDownloaderReceivers(urlString);
|
||||||
notificationManager.notify(urlString.hashCode(), builder.build());
|
getObb(urlString, apk.getMainObbUrl(), apk.getMainObbFile(), apk.obbMainFileSha256);
|
||||||
|
getObb(urlString, apk.getPatchObbUrl(), apk.getPatchObbFile(), apk.obbPatchFileSha256);
|
||||||
registerApkDownloaderReceivers(urlString, builder);
|
|
||||||
getObb(urlString, apk.getMainObbUrl(), apk.getMainObbFile(), apk.obbMainFileSha256, builder);
|
|
||||||
getObb(urlString, apk.getPatchObbUrl(), apk.getPatchObbFile(), apk.obbPatchFileSha256, builder);
|
|
||||||
|
|
||||||
File apkFilePath = ApkCache.getApkDownloadPath(this, intent.getData());
|
File apkFilePath = ApkCache.getApkDownloadPath(this, intent.getData());
|
||||||
long apkFileSize = apkFilePath.length();
|
long apkFileSize = apkFilePath.length();
|
||||||
@ -217,8 +213,7 @@ public class InstallManagerService extends Service {
|
|||||||
* @see <a href="https://developer.android.com/google/play/expansion-files.html">APK Expansion Files</a>
|
* @see <a href="https://developer.android.com/google/play/expansion-files.html">APK Expansion Files</a>
|
||||||
*/
|
*/
|
||||||
private void getObb(final String urlString, String obbUrlString,
|
private void getObb(final String urlString, String obbUrlString,
|
||||||
final File obbDestFile, final String sha256,
|
final File obbDestFile, final String sha256) {
|
||||||
final NotificationCompat.Builder builder) {
|
|
||||||
if (obbDestFile == null || obbDestFile.exists() || TextUtils.isEmpty(obbUrlString)) {
|
if (obbDestFile == null || obbDestFile.exists() || TextUtils.isEmpty(obbUrlString)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -232,8 +227,7 @@ public class InstallManagerService extends Service {
|
|||||||
|
|
||||||
int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
|
int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
|
||||||
int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
||||||
builder.setProgress(totalBytes, bytesRead, false);
|
// TODO - handle obb notifications?
|
||||||
notificationManager.notify(urlString.hashCode(), builder.build());
|
|
||||||
} else if (Downloader.ACTION_COMPLETE.equals(action)) {
|
} else if (Downloader.ACTION_COMPLETE.equals(action)) {
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
File localFile = new File(intent.getStringExtra(Downloader.EXTRA_DOWNLOAD_PATH));
|
File localFile = new File(intent.getStringExtra(Downloader.EXTRA_DOWNLOAD_PATH));
|
||||||
@ -274,7 +268,7 @@ public class InstallManagerService extends Service {
|
|||||||
DownloaderService.getIntentFilter(obbUrlString));
|
DownloaderService.getIntentFilter(obbUrlString));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerApkDownloaderReceivers(String urlString, final NotificationCompat.Builder builder) {
|
private void registerApkDownloaderReceivers(String urlString) {
|
||||||
|
|
||||||
BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
|
BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
@ -284,13 +278,17 @@ public class InstallManagerService extends Service {
|
|||||||
|
|
||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
case Downloader.ACTION_STARTED:
|
case Downloader.ACTION_STARTED:
|
||||||
|
Intent intentObject = new Intent(context, InstallManagerService.class);
|
||||||
|
intentObject.setAction(ACTION_CANCEL);
|
||||||
|
intentObject.setData(downloadUri);
|
||||||
|
PendingIntent action = PendingIntent.getService(context, 0, intentObject, 0);
|
||||||
|
NotificationHelper.setApk(getApkFromActive(urlString), NotificationHelper.Status.Downloading, action);
|
||||||
// nothing to do
|
// nothing to do
|
||||||
break;
|
break;
|
||||||
case Downloader.ACTION_PROGRESS:
|
case Downloader.ACTION_PROGRESS:
|
||||||
int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
|
int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
|
||||||
int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
||||||
builder.setProgress(totalBytes, bytesRead, false);
|
NotificationHelper.setApkProgress(getApkFromActive(urlString), totalBytes, bytesRead);
|
||||||
notificationManager.notify(urlString.hashCode(), builder.build());
|
|
||||||
break;
|
break;
|
||||||
case Downloader.ACTION_COMPLETE:
|
case Downloader.ACTION_COMPLETE:
|
||||||
File localFile = new File(intent.getStringExtra(Downloader.EXTRA_DOWNLOAD_PATH));
|
File localFile = new File(intent.getStringExtra(Downloader.EXTRA_DOWNLOAD_PATH));
|
||||||
@ -298,17 +296,19 @@ public class InstallManagerService extends Service {
|
|||||||
|
|
||||||
Utils.debugLog(TAG, "download completed of " + urlString + " to " + localApkUri);
|
Utils.debugLog(TAG, "download completed of " + urlString + " to " + localApkUri);
|
||||||
|
|
||||||
|
NotificationHelper.setApk(getApkFromActive(urlString), NotificationHelper.Status.ReadyToInstall, null);
|
||||||
|
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
registerInstallerReceivers(downloadUri);
|
registerInstallerReceivers(downloadUri);
|
||||||
|
|
||||||
Apk apk = ACTIVE_APKS.get(urlString);
|
Apk apk = ACTIVE_APKS.get(urlString);
|
||||||
|
|
||||||
InstallerService.install(context, localApkUri, downloadUri, apk);
|
InstallerService.install(context, localApkUri, downloadUri, apk);
|
||||||
break;
|
break;
|
||||||
case Downloader.ACTION_INTERRUPTED:
|
case Downloader.ACTION_INTERRUPTED:
|
||||||
|
NotificationHelper.removeApk(getApkFromActive(urlString));
|
||||||
|
|
||||||
removeFromActive(urlString);
|
removeFromActive(urlString);
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
cancelNotification(urlString);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("intent action not handled!");
|
throw new RuntimeException("intent action not handled!");
|
||||||
@ -329,9 +329,10 @@ public class InstallManagerService extends Service {
|
|||||||
Apk apk;
|
Apk apk;
|
||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
case Installer.ACTION_INSTALL_STARTED:
|
case Installer.ACTION_INSTALL_STARTED:
|
||||||
// nothing to do
|
NotificationHelper.setApk(getApkFromActive(downloadUrl), NotificationHelper.Status.Installing, null);
|
||||||
break;
|
break;
|
||||||
case Installer.ACTION_INSTALL_COMPLETE:
|
case Installer.ACTION_INSTALL_COMPLETE:
|
||||||
|
NotificationHelper.setApk(getApkFromActive(downloadUrl), NotificationHelper.Status.Installed, null);
|
||||||
Apk apkComplete = removeFromActive(downloadUrl);
|
Apk apkComplete = removeFromActive(downloadUrl);
|
||||||
|
|
||||||
PackageManagerCompat.setInstaller(getPackageManager(), apkComplete.packageName);
|
PackageManagerCompat.setInstaller(getPackageManager(), apkComplete.packageName);
|
||||||
@ -339,6 +340,8 @@ public class InstallManagerService extends Service {
|
|||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
break;
|
break;
|
||||||
case Installer.ACTION_INSTALL_INTERRUPTED:
|
case Installer.ACTION_INSTALL_INTERRUPTED:
|
||||||
|
NotificationHelper.setApk(getApkFromActive(downloadUrl), NotificationHelper.Status.ReadyToInstall, null);
|
||||||
|
|
||||||
apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
||||||
String errorMessage =
|
String errorMessage =
|
||||||
intent.getStringExtra(Installer.EXTRA_ERROR_MESSAGE);
|
intent.getStringExtra(Installer.EXTRA_ERROR_MESSAGE);
|
||||||
@ -350,12 +353,7 @@ public class InstallManagerService extends Service {
|
|||||||
ContentResolver resolver = context.getContentResolver();
|
ContentResolver resolver = context.getContentResolver();
|
||||||
app = AppProvider.Helper.findSpecificApp(resolver, apk.packageName, apk.repo);
|
app = AppProvider.Helper.findSpecificApp(resolver, apk.packageName, apk.repo);
|
||||||
}
|
}
|
||||||
// show notification if app details is not visible
|
// TODO - show error
|
||||||
if (app != null && AppDetails.isAppVisible(app.packageName)) {
|
|
||||||
cancelNotification(downloadUrl);
|
|
||||||
} else {
|
|
||||||
notifyError(downloadUrl, app, errorMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
removeFromActive(downloadUrl);
|
removeFromActive(downloadUrl);
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
@ -364,14 +362,7 @@ public class InstallManagerService extends Service {
|
|||||||
apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
||||||
PendingIntent installPendingIntent =
|
PendingIntent installPendingIntent =
|
||||||
intent.getParcelableExtra(Installer.EXTRA_USER_INTERACTION_PI);
|
intent.getParcelableExtra(Installer.EXTRA_USER_INTERACTION_PI);
|
||||||
|
NotificationHelper.setApk(getApkFromActive(downloadUrl), NotificationHelper.Status.ReadyToInstall, installPendingIntent);
|
||||||
// show notification if app details is not visible
|
|
||||||
if (AppDetails.isAppVisible(apk.packageName)) {
|
|
||||||
cancelNotification(downloadUrl);
|
|
||||||
} else {
|
|
||||||
notifyDownloadComplete(apk, downloadUrl, installPendingIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("intent action not handled!");
|
throw new RuntimeException("intent action not handled!");
|
||||||
@ -383,81 +374,10 @@ public class InstallManagerService extends Service {
|
|||||||
Installer.getInstallIntentFilter(downloadUri));
|
Installer.getInstallIntentFilter(downloadUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
private NotificationCompat.Builder createNotificationBuilder(String urlString, Apk apk) {
|
|
||||||
int downloadUrlId = urlString.hashCode();
|
|
||||||
return new NotificationCompat.Builder(this)
|
|
||||||
.setAutoCancel(false)
|
|
||||||
.setOngoing(true)
|
|
||||||
.setContentIntent(getAppDetailsIntent(downloadUrlId, apk))
|
|
||||||
.setContentTitle(getString(R.string.downloading_apk, getAppName(apk)))
|
|
||||||
.addAction(R.drawable.ic_cancel_black_24dp, getString(R.string.cancel),
|
|
||||||
getCancelPendingIntent(urlString))
|
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
|
||||||
.setContentText(urlString)
|
|
||||||
.setProgress(100, 0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getAppName(Apk apk) {
|
private String getAppName(Apk apk) {
|
||||||
return ACTIVE_APPS.get(apk.packageName).name;
|
return ACTIVE_APPS.get(apk.packageName).name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a {@link PendingIntent} for a {@link Notification} to send when it
|
|
||||||
* is clicked. {@link AppDetails} handles {@code Intent}s that are missing
|
|
||||||
* or bad {@link AppDetails#EXTRA_APPID}, so it does not need to be checked
|
|
||||||
* here.
|
|
||||||
*/
|
|
||||||
private PendingIntent getAppDetailsIntent(int requestCode, Apk apk) {
|
|
||||||
Intent notifyIntent = new Intent(getApplicationContext(), AppDetails.class)
|
|
||||||
.putExtra(AppDetails.EXTRA_APPID, apk.packageName);
|
|
||||||
return TaskStackBuilder.create(getApplicationContext())
|
|
||||||
.addParentStack(AppDetails.class)
|
|
||||||
.addNextIntent(notifyIntent)
|
|
||||||
.getPendingIntent(requestCode, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Post a notification about a completed download. {@code packageName} must be a valid
|
|
||||||
* and currently in the app index database. This must create a new {@code Builder}
|
|
||||||
* instance otherwise the progress/cancel stuff does not go away.
|
|
||||||
*
|
|
||||||
* @see <a href=https://code.google.com/p/android/issues/detail?id=47809> Issue 47809:
|
|
||||||
* Removing the progress bar from a notification should cause the notification's content
|
|
||||||
* text to return to normal size</a>
|
|
||||||
*/
|
|
||||||
private void notifyDownloadComplete(Apk apk, String urlString, PendingIntent installPendingIntent) {
|
|
||||||
String title;
|
|
||||||
try {
|
|
||||||
PackageManager pm = getPackageManager();
|
|
||||||
title = String.format(getString(R.string.tap_to_update_format),
|
|
||||||
pm.getApplicationLabel(pm.getApplicationInfo(apk.packageName, 0)));
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
String name = getAppName(apk);
|
|
||||||
if (TextUtils.isEmpty(name) || name.equals(new App().name)) {
|
|
||||||
ContentResolver resolver = getContentResolver();
|
|
||||||
App app = AppProvider.Helper.findSpecificApp(resolver, apk.packageName, apk.repo,
|
|
||||||
new String[]{Schema.AppMetadataTable.Cols.NAME});
|
|
||||||
if (app == null || TextUtils.isEmpty(app.name)) {
|
|
||||||
return; // do not have a name to display, so leave notification as is
|
|
||||||
}
|
|
||||||
name = app.name;
|
|
||||||
}
|
|
||||||
title = String.format(getString(R.string.tap_to_install_format), name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int downloadUrlId = urlString.hashCode();
|
|
||||||
notificationManager.cancel(downloadUrlId);
|
|
||||||
Notification notification = new NotificationCompat.Builder(this)
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.setOngoing(false)
|
|
||||||
.setContentTitle(title)
|
|
||||||
.setContentIntent(installPendingIntent)
|
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
|
||||||
.setContentText(getString(R.string.tap_to_install))
|
|
||||||
.build();
|
|
||||||
notificationManager.notify(downloadUrlId, notification);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyError(String urlString, App app, String text) {
|
private void notifyError(String urlString, App app, String text) {
|
||||||
int downloadUrlId = urlString.hashCode();
|
int downloadUrlId = urlString.hashCode();
|
||||||
|
|
||||||
@ -493,15 +413,6 @@ public class InstallManagerService extends Service {
|
|||||||
nm.notify(downloadUrlId, builder.build());
|
nm.notify(downloadUrlId, builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel the {@link Notification} tied to {@code urlString}, which is the
|
|
||||||
* unique ID used to represent a given APK file. {@link String#hashCode()}
|
|
||||||
* converts {@code urlString} to the required {@code int}.
|
|
||||||
*/
|
|
||||||
private void cancelNotification(String urlString) {
|
|
||||||
notificationManager.cancel(urlString.hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addToActive(String urlString, App app, Apk apk) {
|
private static void addToActive(String urlString, App app, Apk apk) {
|
||||||
ACTIVE_APKS.put(urlString, apk);
|
ACTIVE_APKS.put(urlString, apk);
|
||||||
ACTIVE_APPS.put(app.packageName, app);
|
ACTIVE_APPS.put(app.packageName, app);
|
||||||
|
4
app/src/main/res/drawable/ic_notify_cancel_24dp.xml
Normal file
4
app/src/main/res/drawable/ic_notify_cancel_24dp.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="32dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="32dp">
|
||||||
|
<path android:fillColor="#30000000" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||||
|
</vector>
|
6
app/src/main/res/drawable/ic_notify_install_24dp.xml
Normal file
6
app/src/main/res/drawable/ic_notify_install_24dp.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="32dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="32dp">
|
||||||
|
<group android:pivotX="12" android:pivotY="12" android:rotation="-90">
|
||||||
|
<path android:fillColor="#30000000" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
4
app/src/main/res/drawable/ic_notify_update_24dp.xml
Normal file
4
app/src/main/res/drawable/ic_notify_update_24dp.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="32dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="32dp">
|
||||||
|
<path android:fillColor="#30000000" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
|
||||||
|
</vector>
|
Loading…
x
Reference in New Issue
Block a user