Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e6c04f94 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Broadcast hidden volumes, notification polish.

Send limited broadcast intent when certain volume state changes
occur; the only customer for now is ExternalStorageProvider.

Change notification flow to be less bumpy.  Pick USB icon based on
disk type, and avoid using "generic" disk labels.

Bug: 19993667
Change-Id: I263bc9e9aae2ae57eb4d1afe76da686aee5475fb
parent 72274366
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -64,19 +64,29 @@ public class DiskInfo implements Parcelable {
        return id;
    }

    private boolean isInteresting(String label) {
        if (TextUtils.isEmpty(label)) {
            return false;
        }
        if (label.toLowerCase().contains("generic")) {
            return false;
        }
        return true;
    }

    public String getDescription() {
        final Resources res = Resources.getSystem();
        if ((flags & FLAG_SD) != 0) {
            if (TextUtils.isEmpty(label)) {
                return res.getString(com.android.internal.R.string.storage_sd_card);
            } else {
            if (isInteresting(label)) {
                return res.getString(com.android.internal.R.string.storage_sd_card_label, label);
            } else {
                return res.getString(com.android.internal.R.string.storage_sd_card);
            }
        } else if ((flags & FLAG_USB) != 0) {
            if (TextUtils.isEmpty(label)) {
                return res.getString(com.android.internal.R.string.storage_usb_drive);
            } else {
            if (isInteresting(label)) {
                return res.getString(com.android.internal.R.string.storage_usb_drive_label, label);
            } else {
                return res.getString(com.android.internal.R.string.storage_usb_drive);
            }
        } else {
            return null;
+4 −1
Original line number Diff line number Diff line
@@ -49,7 +49,10 @@ import java.util.Objects;
 * @hide
 */
public class VolumeInfo implements Parcelable {
    public static final String EXTRA_VOLUME_ID = "android.os.storage.extra.VOLUME_ID";
    public static final String ACTION_VOLUME_STATE_CHANGED =
            "android.os.storage.action.VOLUME_STATE_CHANGED";
    public static final String EXTRA_VOLUME_ID =
            "android.os.storage.extra.VOLUME_ID";

    /** Stub volume representing internal private storage */
    public static final String ID_PRIVATE_INTERNAL = "private";
+1 −3
Original line number Diff line number Diff line
@@ -19,9 +19,7 @@

        <receiver android:name=".MountReceiver">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_MOUNTED" />
                <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
                <data android:scheme="file" />
                <action android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
            </intent-filter>
        </receiver>

+56 −49
Original line number Diff line number Diff line
@@ -100,72 +100,75 @@ public class StorageNotification extends SystemUI {

        Log.d(TAG, vol.toString());

        // New state means we tear down any old notifications
        mNotificationManager.cancelAsUser(vol.getId(), NOTIF_ID, UserHandle.ALL);

        final Notification notif;
        switch (newState) {
            case VolumeInfo.STATE_UNMOUNTED:
                onVolumeUnmounted(vol);
                notif = onVolumeUnmounted(vol);
                break;
            case VolumeInfo.STATE_CHECKING:
                onVolumeChecking(vol);
                notif = onVolumeChecking(vol);
                break;
            case VolumeInfo.STATE_MOUNTED:
            case VolumeInfo.STATE_MOUNTED_READ_ONLY:
                onVolumeMounted(vol);
                notif = onVolumeMounted(vol);
                break;
            case VolumeInfo.STATE_FORMATTING:
                onVolumeFormatting(vol);
                notif = onVolumeFormatting(vol);
                break;
            case VolumeInfo.STATE_EJECTING:
                onVolumeEjecting(vol);
                notif = onVolumeEjecting(vol);
                break;
            case VolumeInfo.STATE_UNMOUNTABLE:
                onVolumeUnmountable(vol);
                notif = onVolumeUnmountable(vol);
                break;
            case VolumeInfo.STATE_REMOVED:
                onVolumeRemoved(vol);
                notif = onVolumeRemoved(vol);
                break;
            case VolumeInfo.STATE_BAD_REMOVAL:
                onVolumeBadRemoval(vol);
                notif = onVolumeBadRemoval(vol);
                break;
            default:
                notif = null;
                break;
        }

        if (notif != null) {
            mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
        } else {
            mNotificationManager.cancelAsUser(vol.getId(), NOTIF_ID, UserHandle.ALL);
        }
    }

    private void onVolumeUnmounted(VolumeInfo vol) {
    private Notification onVolumeUnmounted(VolumeInfo vol) {
        // Ignored
        return null;
    }

    private void onVolumeChecking(VolumeInfo vol) {
    private Notification onVolumeChecking(VolumeInfo vol) {
        final DiskInfo disk = vol.getDisk();
        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)
        return buildNotificationBuilder(vol, title, text)
                .setCategory(Notification.CATEGORY_PROGRESS)
                .setPriority(Notification.PRIORITY_LOW)
                .setOngoing(true)
                .build();

        mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
    }

    private void onVolumeMounted(VolumeInfo vol) {
    private Notification onVolumeMounted(VolumeInfo vol) {
        // Don't annoy when user dismissed in past
        if (vol.isSnoozed()) return;
        if (vol.isSnoozed()) return null;

        final DiskInfo disk = vol.getDisk();
        final Notification notif;
        if (disk.isAdoptable() && !vol.isInited()) {
            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)
            return buildNotificationBuilder(vol, title, text)
                    .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),
@@ -179,8 +182,7 @@ public class StorageNotification extends SystemUI {
            final CharSequence text = mContext.getString(
                    R.string.ext_media_ready_notification_message, disk.getDescription());

            notif = buildNotificationBuilder(title, text)
                    .setSmallIcon(R.drawable.stat_notify_sdcard)
            return buildNotificationBuilder(vol, title, text)
                    .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),
@@ -190,51 +192,44 @@ public class StorageNotification extends SystemUI {
                    .setPriority(Notification.PRIORITY_LOW)
                    .build();
        }

        mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
    }

    private void onVolumeFormatting(VolumeInfo vol) {
    private Notification onVolumeFormatting(VolumeInfo vol) {
        // Ignored
        return null;
    }

    private void onVolumeEjecting(VolumeInfo vol) {
    private Notification onVolumeEjecting(VolumeInfo vol) {
        final DiskInfo disk = vol.getDisk();
        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)
        return buildNotificationBuilder(vol, title, text)
                .setCategory(Notification.CATEGORY_PROGRESS)
                .setPriority(Notification.PRIORITY_LOW)
                .setOngoing(true)
                .build();

        mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
    }

    private void onVolumeUnmountable(VolumeInfo vol) {
    private Notification onVolumeUnmountable(VolumeInfo vol) {
        final DiskInfo disk = vol.getDisk();
        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());

        final Notification notif = buildNotificationBuilder(title, text)
                .setSmallIcon(R.drawable.stat_notify_sdcard)
        return buildNotificationBuilder(vol, title, text)
                .setContentIntent(buildDetailsPendingIntent(vol))
                .setCategory(Notification.CATEGORY_ERROR)
                .build();

        mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
    }

    private void onVolumeRemoved(VolumeInfo vol) {
    private Notification onVolumeRemoved(VolumeInfo vol) {
        if (!vol.isPrimary()) {
            // Ignore non-primary media
            return;
            return null;
        }

        final DiskInfo disk = vol.getDisk();
@@ -243,18 +238,15 @@ public class StorageNotification extends SystemUI {
        final CharSequence text = mContext.getString(
                R.string.ext_media_nomedia_notification_message, disk.getDescription());

        final Notification notif = buildNotificationBuilder(title, text)
                .setSmallIcon(R.drawable.stat_notify_sdcard)
        return buildNotificationBuilder(vol, title, text)
                .setCategory(Notification.CATEGORY_ERROR)
                .build();

        mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
    }

    private void onVolumeBadRemoval(VolumeInfo vol) {
    private Notification onVolumeBadRemoval(VolumeInfo vol) {
        if (!vol.isPrimary()) {
            // Ignore non-primary media
            return;
            return null;
        }

        final DiskInfo disk = vol.getDisk();
@@ -263,16 +255,31 @@ public class StorageNotification extends SystemUI {
        final CharSequence text = mContext.getString(
                R.string.ext_media_badremoval_notification_message, disk.getDescription());

        final Notification notif = buildNotificationBuilder(title, text)
                .setSmallIcon(R.drawable.stat_notify_sdcard)
        return buildNotificationBuilder(vol, title, text)
                .setCategory(Notification.CATEGORY_ERROR)
                .build();
    }

        mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL);
    private int getSmallIcon(VolumeInfo vol) {
        if (vol.disk.isSd()) {
            switch (vol.getState()) {
                case VolumeInfo.STATE_CHECKING:
                case VolumeInfo.STATE_EJECTING:
                    return R.drawable.stat_notify_sdcard_prepare;
                default:
                    return R.drawable.stat_notify_sdcard;
            }
        } else if (vol.disk.isUsb()) {
            return R.drawable.stat_sys_data_usb;
        } else {
            return R.drawable.stat_notify_sdcard;
        }
    }

    private Notification.Builder buildNotificationBuilder(CharSequence title, CharSequence text) {
    private Notification.Builder buildNotificationBuilder(VolumeInfo vol, CharSequence title,
            CharSequence text) {
        return new Notification.Builder(mContext)
                .setSmallIcon(getSmallIcon(vol))
                .setColor(mContext.getColor(R.color.system_notification_accent_color))
                .setContentTitle(title)
                .setContentText(text)
+31 −2
Original line number Diff line number Diff line
@@ -861,7 +861,7 @@ class MountService extends IMountService.Stub
                    final int oldState = vol.state;
                    final int newState = Integer.parseInt(cooked[2]);
                    vol.state = newState;
                    onVolumeStateChangedLocked(vol, oldState, newState);
                    onVolumeStateChangedLocked(vol.clone(), oldState, newState);
                }
                break;
            }
@@ -955,8 +955,37 @@ class MountService extends IMountService.Stub
        }
    }

    private boolean isBroadcastWorthy(VolumeInfo vol) {
        switch (vol.getType()) {
            case VolumeInfo.TYPE_PUBLIC:
            case VolumeInfo.TYPE_EMULATED:
                break;
            default:
                return false;
        }

        switch (vol.getState()) {
            case VolumeInfo.STATE_MOUNTED:
            case VolumeInfo.STATE_MOUNTED_READ_ONLY:
            case VolumeInfo.STATE_EJECTING:
            case VolumeInfo.STATE_UNMOUNTED:
                break;
            default:
                return false;
        }

        return true;
    }

    private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
        mCallbacks.notifyVolumeStateChanged(vol.clone(), oldState, newState);
        mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);

        if (isBroadcastWorthy(vol)) {
            final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
            // TODO: require receiver to hold permission
            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
        }

        final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
        final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);