| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| 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.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.os.UserHandle; |
| import android.os.storage.DiskInfo; |
| import android.os.storage.StorageEventListener; |
| import android.os.storage.StorageManager; |
| 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 int NOTIF_ID = 0x53544f52; // STOR |
| |
| private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME"; |
| |
| // 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 onVolumeMetadataChanged(VolumeInfo vol) { |
| // Avoid kicking notifications when getting early metadata before |
| // mounted. If already mounted, we're being kicked because of a |
| // nickname or init'ed change. |
| if (vol.getState() == VolumeInfo.STATE_MOUNTED) { |
| onVolumeStateChangedInternal(vol, vol.getState(), vol.getState()); |
| } |
| } |
| }; |
| |
| private final BroadcastReceiver mSnoozeReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| // TODO: kick this onto background thread |
| final String volId = intent.getStringExtra(VolumeInfo.EXTRA_VOLUME_ID); |
| mStorageManager.setVolumeSnoozed(volId, true); |
| } |
| }; |
| |
| @Override |
| public void start() { |
| mNotificationManager = mContext.getSystemService(NotificationManager.class); |
| |
| mStorageManager = mContext.getSystemService(StorageManager.class); |
| mStorageManager.registerListener(mListener); |
| |
| mContext.registerReceiver(mSnoozeReceiver, new IntentFilter(ACTION_SNOOZE_VOLUME), |
| android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null); |
| |
| // Kick current state into place |
| final List<VolumeInfo> vols = mStorageManager.getVolumes(); |
| for (VolumeInfo vol : vols) { |
| onVolumeStateChangedInternal(vol, vol.getState(), vol.getState()); |
| } |
| } |
| |
| public void onVolumeStateChangedInternal(VolumeInfo vol, int oldState, int newState) { |
| // We only care about public volumes |
| if (vol.getType() != VolumeInfo.TYPE_PUBLIC) { |
| return; |
| } |
| |
| Log.d(TAG, vol.toString()); |
| |
| // New state means we tear down any old notifications |
| mNotificationManager.cancelAsUser(vol.getId(), 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; |
| } |
| } |
| |
| private void onVolumeUnmounted(VolumeInfo vol) { |
| // Ignored |
| } |
| |
| private void onVolumeMounting(VolumeInfo vol) { |
| final DiskInfo disk = mStorageManager.findDiskById(vol.getDiskId()); |
| 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.getId(), NOTIF_ID, notif, UserHandle.ALL); |
| } |
| |
| private void onVolumeMounted(VolumeInfo vol) { |
| // Don't annoy when user dismissed in past |
| if (vol.isSnoozed()) return; |
| |
| final DiskInfo disk = mStorageManager.findDiskById(vol.getDiskId()); |
| 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) |
| .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))) |
| .setDeleteIntent(buildSnoozeIntent(vol)) |
| .setCategory(Notification.CATEGORY_SYSTEM) |
| .build(); |
| |
| } else { |
| 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))) |
| .setDeleteIntent(buildSnoozeIntent(vol)) |
| .setCategory(Notification.CATEGORY_SYSTEM) |
| .setPriority(Notification.PRIORITY_LOW) |
| .build(); |
| } |
| |
| mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL); |
| } |
| |
| private void onVolumeFormatting(VolumeInfo vol) { |
| // Ignored |
| } |
| |
| private void onVolumeUnmounting(VolumeInfo vol) { |
| final DiskInfo disk = mStorageManager.findDiskById(vol.getDiskId()); |
| 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.getId(), NOTIF_ID, notif, UserHandle.ALL); |
| } |
| |
| private void onVolumeUnmountable(VolumeInfo vol) { |
| final DiskInfo disk = mStorageManager.findDiskById(vol.getDiskId()); |
| 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) |
| .setContentIntent(buildDetailsPendingIntent(vol)) |
| .setCategory(Notification.CATEGORY_ERROR) |
| .build(); |
| |
| mNotificationManager.notifyAsUser(vol.getId(), NOTIF_ID, notif, UserHandle.ALL); |
| } |
| |
| private void onVolumeRemoved(VolumeInfo vol) { |
| if (!vol.isPrimary()) { |
| // Ignore non-primary media |
| return; |
| } |
| |
| final DiskInfo disk = mStorageManager.findDiskById(vol.getDiskId()); |
| 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()); |
| |
| final Notification notif = buildNotificationBuilder(title, text) |
| .setSmallIcon(R.drawable.stat_notify_sdcard) |
| .setCategory(Notification.CATEGORY_ERROR) |
| .build(); |
| |
| mNotificationManager.notifyAsUser(vol.getId(), 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.getId()); |
| |
| final int requestKey = vol.getId().hashCode(); |
| return PendingIntent.getActivityAsUser(mContext, requestKey, intent, |
| PendingIntent.FLAG_CANCEL_CURRENT, 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.getId()); |
| |
| final int requestKey = vol.getId().hashCode(); |
| return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent, |
| PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT); |
| } |
| |
| private PendingIntent buildBrowsePendingIntent(VolumeInfo vol) { |
| final Intent intent = vol.buildBrowseIntent(); |
| |
| final int requestKey = vol.getId().hashCode(); |
| return PendingIntent.getActivityAsUser(mContext, requestKey, intent, |
| PendingIntent.FLAG_CANCEL_CURRENT, 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.getId()); |
| |
| final int requestKey = vol.getId().hashCode(); |
| return PendingIntent.getActivityAsUser(mContext, requestKey, intent, |
| PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); |
| } |
| |
| private PendingIntent buildSnoozeIntent(VolumeInfo vol) { |
| final Intent intent = new Intent(ACTION_SNOOZE_VOLUME); |
| intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId()); |
| |
| final int requestKey = vol.getId().hashCode(); |
| return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent, |
| PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT); |
| } |
| } |