diff options
Diffstat (limited to 'packages')
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java | 564 |
1 files changed, 190 insertions, 374 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index 5771d22..6830957 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * 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. @@ -17,424 +17,240 @@ package com.android.systemui.usb; import android.app.Notification; +import android.app.Notification.Action; import android.app.NotificationManager; import android.app.PendingIntent; -import android.content.Context; import android.content.Intent; -import android.content.res.Resources; -import android.os.Environment; -import android.os.Handler; -import android.os.HandlerThread; import android.os.UserHandle; +import android.os.storage.DiskInfo; import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; -import android.provider.Settings; +import android.os.storage.VolumeInfo; import android.util.Log; +import com.android.internal.R; import com.android.systemui.SystemUI; +import java.util.List; + public class StorageNotification extends SystemUI { private static final String TAG = "StorageNotification"; - private static final boolean DEBUG = false; - - private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true; - - /** - * The notification that is shown when a USB mass storage host - * is connected. - * <p> - * This is lazily created, so use {@link #setUsbStorageNotification()}. - */ - private Notification mUsbStorageNotification; - - /** - * The notification that is shown when the following media events occur: - * - Media is being checked - * - Media is blank (or unknown filesystem) - * - Media is corrupt - * - Media is safe to unmount - * - Media is missing - * <p> - * This is lazily created, so use {@link #setMediaStorageNotification()}. - */ - private Notification mMediaStorageNotification; - private boolean mUmsAvailable; - private StorageManager mStorageManager; - private Handler mAsyncEventHandler; + private static final int NOTIF_ID = 0x53544f52; // STOR - private class StorageNotificationEventListener extends StorageEventListener { - public void onUsbMassStorageConnectionChanged(final boolean connected) { - mAsyncEventHandler.post(new Runnable() { - @Override - public void run() { - onUsbMassStorageConnectionChangedAsync(connected); - } - }); - } - public void onStorageStateChanged(final String path, - final String oldState, final String newState) { - mAsyncEventHandler.post(new Runnable() { - @Override - public void run() { - onStorageStateChangedAsync(path, oldState, newState); - } - }); + // TODO: delay some notifications to avoid bumpy fast operations + // TODO: annoy user when private media is missing + + private NotificationManager mNotificationManager; + private StorageManager mStorageManager; + + private final StorageEventListener mListener = new StorageEventListener() { + @Override + public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { + onVolumeStateChangedInternal(vol, oldState, newState); } - } + }; @Override public void start() { - mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); - final boolean connected = mStorageManager.isUsbMassStorageConnected(); - if (DEBUG) Log.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", - mUmsAvailable, Environment.getExternalStorageState())); - - HandlerThread thr = new HandlerThread("SystemUI StorageNotification"); - thr.start(); - mAsyncEventHandler = new Handler(thr.getLooper()); - - StorageNotificationEventListener listener = new StorageNotificationEventListener(); - listener.onUsbMassStorageConnectionChanged(connected); - mStorageManager.registerListener(listener); - } + mNotificationManager = mContext.getSystemService(NotificationManager.class); - private void onUsbMassStorageConnectionChangedAsync(boolean connected) { - mUmsAvailable = connected; - /* - * Even though we may have a UMS host connected, we the SD card - * may not be in a state for export. - */ - String st = Environment.getExternalStorageState(); - - if (DEBUG) Log.i(TAG, String.format("UMS connection changed to %s (media state %s)", - connected, st)); - - if (connected && (st.equals( - Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) { - /* - * No card or card being checked = don't display - */ - connected = false; - } - updateUsbMassStorageNotification(connected); - } + mStorageManager = mContext.getSystemService(StorageManager.class); + mStorageManager.registerListener(mListener); - private void onStorageStateChangedAsync(String path, String oldState, String newState) { - if (DEBUG) Log.i(TAG, String.format( - "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState)); - if (newState.equals(Environment.MEDIA_SHARED)) { - /* - * Storage is now shared. Modify the UMS notification - * for stopping UMS. - */ - Intent intent = new Intent(); - intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); - setUsbStorageNotification( - com.android.internal.R.string.usb_storage_stop_notification_title, - com.android.internal.R.string.usb_storage_stop_notification_message, - com.android.internal.R.drawable.stat_sys_warning, false, true, pi); - } else if (newState.equals(Environment.MEDIA_CHECKING)) { - /* - * Storage is now checking. Update media notification and disable - * UMS notification. - */ - setMediaStorageNotification( - com.android.internal.R.string.ext_media_checking_notification_title, - com.android.internal.R.string.ext_media_checking_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null); - updateUsbMassStorageNotification(false); - } else if (newState.equals(Environment.MEDIA_MOUNTED)) { - /* - * Storage is now mounted. Dismiss any media notifications, - * and enable UMS notification if connected. - */ - setMediaStorageNotification(0, 0, 0, false, false, null); - updateUsbMassStorageNotification(mUmsAvailable); - } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) { - /* - * Storage is now unmounted. We may have been unmounted - * because the user is enabling/disabling UMS, in which case we don't - * want to display the 'safe to unmount' notification. - */ - if (!mStorageManager.isUsbMassStorageEnabled()) { - if (oldState.equals(Environment.MEDIA_SHARED)) { - /* - * The unmount was due to UMS being enabled. Dismiss any - * media notifications, and enable UMS notification if connected - */ - setMediaStorageNotification(0, 0, 0, false, false, null); - updateUsbMassStorageNotification(mUmsAvailable); - } else { - /* - * Show safe to unmount media notification, and enable UMS - * notification if connected. - */ - if (Environment.isExternalStorageRemovable()) { - setMediaStorageNotification( - com.android.internal.R.string.ext_media_safe_unmount_notification_title, - com.android.internal.R.string.ext_media_safe_unmount_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard, true, true, null); - } else { - // This device does not have removable storage, so - // don't tell the user they can remove it. - setMediaStorageNotification(0, 0, 0, false, false, null); - } - updateUsbMassStorageNotification(mUmsAvailable); - } - } else { - /* - * The unmount was due to UMS being enabled. Dismiss any - * media notifications, and disable the UMS notification - */ - setMediaStorageNotification(0, 0, 0, false, false, null); - updateUsbMassStorageNotification(false); - } - } else if (newState.equals(Environment.MEDIA_NOFS)) { - /* - * Storage has no filesystem. Show blank media notification, - * and enable UMS notification if connected. - */ - Intent intent = new Intent(); - intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); - intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, - getVolumeByPath(mStorageManager.getVolumeList(), path)); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); - - setMediaStorageNotification( - com.android.internal.R.string.ext_media_nofs_notification_title, - com.android.internal.R.string.ext_media_nofs_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi); - updateUsbMassStorageNotification(mUmsAvailable); - } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) { - /* - * Storage is corrupt. Show corrupt media notification, - * and enable UMS notification if connected. - */ - Intent intent = new Intent(); - intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); - intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, - getVolumeByPath(mStorageManager.getVolumeList(), path)); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); - - setMediaStorageNotification( - com.android.internal.R.string.ext_media_unmountable_notification_title, - com.android.internal.R.string.ext_media_unmountable_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi); - updateUsbMassStorageNotification(mUmsAvailable); - } else if (newState.equals(Environment.MEDIA_REMOVED)) { - /* - * Storage has been removed. Show nomedia media notification, - * and disable UMS notification regardless of connection state. - */ - setMediaStorageNotification( - com.android.internal.R.string.ext_media_nomedia_notification_title, - com.android.internal.R.string.ext_media_nomedia_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard_usb, - true, false, null); - updateUsbMassStorageNotification(false); - } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) { - /* - * Storage has been removed unsafely. Show bad removal media notification, - * and disable UMS notification regardless of connection state. - */ - setMediaStorageNotification( - com.android.internal.R.string.ext_media_badremoval_notification_title, - com.android.internal.R.string.ext_media_badremoval_notification_message, - com.android.internal.R.drawable.stat_sys_warning, - true, true, null); - updateUsbMassStorageNotification(false); - } else { - Log.w(TAG, String.format("Ignoring unknown state {%s}", newState)); + // Kick current state into place + final List<VolumeInfo> vols = mStorageManager.getVolumes(); + for (VolumeInfo vol : vols) { + onVolumeStateChangedInternal(vol, vol.state, vol.state); } } - /** - * Get the corresponding StorageVolume object for a specific path. - */ - private final StorageVolume getVolumeByPath(StorageVolume[] volumes, String path) { - for (StorageVolume volume : volumes) { - if (volume.getPath().equals(path)) { - return volume; - } + public void onVolumeStateChangedInternal(VolumeInfo vol, int oldState, int newState) { + // We only care about public volumes + if (vol.type != VolumeInfo.TYPE_PUBLIC) { + return; } - Log.w(TAG, "No storage found"); - return null; - } - /** - * Update the state of the USB mass storage notification - */ - void updateUsbMassStorageNotification(boolean available) { - - if (available) { - Intent intent = new Intent(); - intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); - setUsbStorageNotification( - com.android.internal.R.string.usb_storage_notification_title, - com.android.internal.R.string.usb_storage_notification_message, - com.android.internal.R.drawable.stat_sys_data_usb, - false, true, pi); - } else { - setUsbStorageNotification(0, 0, 0, false, false, null); + Log.d(TAG, vol.toString()); + + // New state means we tear down any old notifications + mNotificationManager.cancelAsUser(vol.id, NOTIF_ID, UserHandle.ALL); + + switch (newState) { + case VolumeInfo.STATE_UNMOUNTED: + onVolumeUnmounted(vol); + break; + case VolumeInfo.STATE_MOUNTING: + onVolumeMounting(vol); + break; + case VolumeInfo.STATE_MOUNTED: + onVolumeMounted(vol); + break; + case VolumeInfo.STATE_FORMATTING: + onVolumeFormatting(vol); + break; + case VolumeInfo.STATE_UNMOUNTING: + onVolumeUnmounting(vol); + break; + case VolumeInfo.STATE_UNMOUNTABLE: + onVolumeUnmountable(vol); + break; + case VolumeInfo.STATE_REMOVED: + onVolumeRemoved(vol); + break; } } - /** - * Sets the USB storage notification. - */ - private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, - boolean sound, boolean visible, PendingIntent pi) { - - if (!visible && mUsbStorageNotification == null) { - return; - } - - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); + private void onVolumeUnmounted(VolumeInfo vol) { + // Ignored + } - if (notificationManager == null) { - return; - } + private void onVolumeMounting(VolumeInfo vol) { + final DiskInfo disk = mStorageManager.findDiskById(vol.diskId); + final CharSequence title = mContext.getString( + R.string.ext_media_checking_notification_title, disk.getDescription()); + final CharSequence text = mContext.getString( + R.string.ext_media_checking_notification_message, disk.getDescription()); + + final Notification notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard_prepare) + .setCategory(Notification.CATEGORY_PROGRESS) + .setPriority(Notification.PRIORITY_LOW) + .setOngoing(true) + .build(); + + mNotificationManager.notifyAsUser(vol.id, NOTIF_ID, notif, UserHandle.ALL); + } - if (visible) { - Resources r = Resources.getSystem(); - CharSequence title = r.getText(titleId); - CharSequence message = r.getText(messageId); - - if (mUsbStorageNotification == null) { - mUsbStorageNotification = new Notification(); - mUsbStorageNotification.icon = icon; - mUsbStorageNotification.when = 0; - } - - if (sound) { - mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; - } else { - mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; - } - - mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; - - mUsbStorageNotification.tickerText = title; - if (pi == null) { - Intent intent = new Intent(); - pi = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0, - UserHandle.CURRENT); - } - mUsbStorageNotification.color = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); - mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); - mUsbStorageNotification.visibility = Notification.VISIBILITY_PUBLIC; - mUsbStorageNotification.category = Notification.CATEGORY_SYSTEM; - - final boolean adbOn = 1 == Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.ADB_ENABLED, - 0); - - if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) { - // Pop up a full-screen alert to coach the user through enabling UMS. The average - // user has attached the device to USB either to charge the phone (in which case - // this is harmless) or transfer files, and in the latter case this alert saves - // several steps (as well as subtly indicates that you shouldn't mix UMS with other - // activities on the device). - // - // If ADB is enabled, however, we suppress this dialog (under the assumption that a - // developer (a) knows how to enable UMS, and (b) is probably using USB to install - // builds or use adb commands. - mUsbStorageNotification.fullScreenIntent = pi; - } - } + private void onVolumeMounted(VolumeInfo vol) { + final DiskInfo disk = mStorageManager.findDiskById(vol.diskId); + final Notification notif; + if (disk.isAdoptable()) { + final CharSequence title = disk.getDescription(); + final CharSequence text = mContext.getString( + R.string.ext_media_new_notification_message, disk.getDescription()); + + notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .addAction(new Action(0, mContext.getString(R.string.ext_media_init_action), + buildInitPendingIntent(vol))) + .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action), + buildUnmountPendingIntent(vol))) + .setCategory(Notification.CATEGORY_SYSTEM) + .build(); - final int notificationId = mUsbStorageNotification.icon; - if (visible) { - notificationManager.notifyAsUser(null, notificationId, mUsbStorageNotification, - UserHandle.ALL); } else { - notificationManager.cancelAsUser(null, notificationId, UserHandle.ALL); + final CharSequence title = disk.getDescription(); + final CharSequence text = mContext.getString( + R.string.ext_media_ready_notification_message, disk.getDescription()); + + notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .addAction(new Action(0, mContext.getString(R.string.ext_media_browse_action), + buildBrowsePendingIntent(vol))) + .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action), + buildUnmountPendingIntent(vol))) + .setCategory(Notification.CATEGORY_SYSTEM) + .setPriority(Notification.PRIORITY_LOW) + .build(); } + + mNotificationManager.notifyAsUser(vol.id, NOTIF_ID, notif, UserHandle.ALL); } - private synchronized boolean getMediaStorageNotificationDismissable() { - if ((mMediaStorageNotification != null) && - ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) == - Notification.FLAG_AUTO_CANCEL)) - return true; + private void onVolumeFormatting(VolumeInfo vol) { + // Ignored + } - return false; + private void onVolumeUnmounting(VolumeInfo vol) { + final DiskInfo disk = mStorageManager.findDiskById(vol.diskId); + final CharSequence title = mContext.getString( + R.string.ext_media_unmounting_notification_title, disk.getDescription()); + final CharSequence text = mContext.getString( + R.string.ext_media_unmounting_notification_message, disk.getDescription()); + + final Notification notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard_prepare) + .setCategory(Notification.CATEGORY_PROGRESS) + .setPriority(Notification.PRIORITY_LOW) + .setOngoing(true) + .build(); + + mNotificationManager.notifyAsUser(vol.id, NOTIF_ID, notif, UserHandle.ALL); } - /** - * Sets the media storage notification. - */ - private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, - boolean dismissable, PendingIntent pi) { + private void onVolumeUnmountable(VolumeInfo vol) { + final DiskInfo disk = mStorageManager.findDiskById(vol.diskId); + final CharSequence title = mContext.getString( + R.string.ext_media_unmountable_notification_title, disk.getDescription()); + final CharSequence text = mContext.getString( + R.string.ext_media_unmountable_notification_message, disk.getDescription()); - if (!visible && mMediaStorageNotification == null) { - return; - } + final Notification notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .setContentIntent(buildDetailsPendingIntent(vol)) + .setCategory(Notification.CATEGORY_ERROR) + .build(); - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); + mNotificationManager.notifyAsUser(vol.id, NOTIF_ID, notif, UserHandle.ALL); + } - if (notificationManager == null) { + private void onVolumeRemoved(VolumeInfo vol) { + if (!vol.isPrimary()) { + // Ignore non-primary media return; } - if (mMediaStorageNotification != null && visible) { - /* - * Dismiss the previous notification - we're about to - * re-use it. - */ - final int notificationId = mMediaStorageNotification.icon; - notificationManager.cancel(notificationId); - } + final DiskInfo disk = mStorageManager.findDiskById(vol.diskId); + final CharSequence title = mContext.getString( + R.string.ext_media_nomedia_notification_title, disk.getDescription()); + final CharSequence text = mContext.getString( + R.string.ext_media_nomedia_notification_message, disk.getDescription()); - if (visible) { - Resources r = Resources.getSystem(); - CharSequence title = r.getText(titleId); - CharSequence message = r.getText(messageId); - - if (mMediaStorageNotification == null) { - mMediaStorageNotification = new Notification(); - mMediaStorageNotification.when = 0; - } - - mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; - - if (dismissable) { - mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; - } else { - mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; - } - - mMediaStorageNotification.tickerText = title; - if (pi == null) { - Intent intent = new Intent(); - pi = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0, - UserHandle.CURRENT); - } - - mMediaStorageNotification.icon = icon; - mMediaStorageNotification.color = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); - mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi); - mMediaStorageNotification.visibility = Notification.VISIBILITY_PUBLIC; - mMediaStorageNotification.category = Notification.CATEGORY_SYSTEM; - } + final Notification notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .setCategory(Notification.CATEGORY_ERROR) + .build(); - final int notificationId = mMediaStorageNotification.icon; - if (visible) { - notificationManager.notifyAsUser(null, notificationId, - mMediaStorageNotification, UserHandle.ALL); - } else { - notificationManager.cancelAsUser(null, notificationId, UserHandle.ALL); - } + mNotificationManager.notifyAsUser(vol.id, NOTIF_ID, notif, UserHandle.ALL); + } + + private Notification.Builder buildNotificationBuilder(CharSequence title, CharSequence text) { + return new Notification.Builder(mContext) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true); + } + + private PendingIntent buildInitPendingIntent(VolumeInfo vol) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageWizardInit"); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); + return PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); + } + + private PendingIntent buildUnmountPendingIntent(VolumeInfo vol) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageUnmountReceiver"); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); + return PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0, UserHandle.CURRENT); + } + + private PendingIntent buildBrowsePendingIntent(VolumeInfo vol) { + final Intent intent = vol.buildBrowseIntent(); + return PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); + } + + private PendingIntent buildDetailsPendingIntent(VolumeInfo vol) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.Settings$StorageVolumeSettingsActivity"); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); + return PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); } } |