PageRenderTime 59ms CodeModel.GetById 16ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java

https://github.com/aizuzi/platform_frameworks_base
Java | 414 lines | 260 code | 47 blank | 107 comment | 66 complexity | 3155e72fa8870d9b26e656536d1983fd MD5 | raw file
  1/*
  2 * Copyright (C) 2010 Google Inc.
  3 *
  4 * Licensed under the Apache License, Version 2.0 (the "License");
  5 * you may not use this file except in compliance with the License.
  6 * You may obtain a copy of the License at
  7 *
  8 *      http://www.apache.org/licenses/LICENSE-2.0
  9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed under the License is distributed on an "AS IS" BASIS,
 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 * See the License for the specific language governing permissions and
 14 * limitations under the License.
 15 */
 16
 17package com.android.systemui.usb;
 18
 19import android.app.Notification;
 20import android.app.NotificationManager;
 21import android.app.PendingIntent;
 22import android.content.Context;
 23import android.content.Intent;
 24import android.content.res.Resources;
 25import android.os.Environment;
 26import android.os.Handler;
 27import android.os.HandlerThread;
 28import android.os.UserHandle;
 29import android.os.storage.StorageEventListener;
 30import android.os.storage.StorageManager;
 31import android.provider.Settings;
 32import android.util.Log;
 33
 34import com.android.systemui.SystemUI;
 35
 36public class StorageNotification extends SystemUI {
 37    private static final String TAG = "StorageNotification";
 38    private static final boolean DEBUG = false;
 39
 40    private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true;
 41
 42    /**
 43     * The notification that is shown when a USB mass storage host
 44     * is connected.
 45     * <p>
 46     * This is lazily created, so use {@link #setUsbStorageNotification()}.
 47     */
 48    private Notification mUsbStorageNotification;
 49
 50    /**
 51     * The notification that is shown when the following media events occur:
 52     *     - Media is being checked
 53     *     - Media is blank (or unknown filesystem)
 54     *     - Media is corrupt
 55     *     - Media is safe to unmount
 56     *     - Media is missing
 57     * <p>
 58     * This is lazily created, so use {@link #setMediaStorageNotification()}.
 59     */
 60    private Notification   mMediaStorageNotification;
 61    private boolean        mUmsAvailable;
 62    private StorageManager mStorageManager;
 63
 64    private Handler        mAsyncEventHandler;
 65
 66    private class StorageNotificationEventListener extends StorageEventListener {
 67        public void onUsbMassStorageConnectionChanged(final boolean connected) {
 68            mAsyncEventHandler.post(new Runnable() {
 69                @Override
 70                public void run() {
 71                    onUsbMassStorageConnectionChangedAsync(connected);
 72                }
 73            });
 74        }
 75        public void onStorageStateChanged(final String path,
 76                final String oldState, final String newState) {
 77            mAsyncEventHandler.post(new Runnable() {
 78                @Override
 79                public void run() {
 80                    onStorageStateChangedAsync(path, oldState, newState);
 81                }
 82            });
 83        }
 84    }
 85
 86    @Override
 87    public void start() {
 88        mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
 89        final boolean connected = mStorageManager.isUsbMassStorageConnected();
 90        if (DEBUG) Log.d(TAG, String.format( "Startup with UMS connection %s (media state %s)",
 91                mUmsAvailable, Environment.getExternalStorageState()));
 92
 93        HandlerThread thr = new HandlerThread("SystemUI StorageNotification");
 94        thr.start();
 95        mAsyncEventHandler = new Handler(thr.getLooper());
 96
 97        StorageNotificationEventListener listener = new StorageNotificationEventListener();
 98        listener.onUsbMassStorageConnectionChanged(connected);
 99        mStorageManager.registerListener(listener);
100    }
101
102    private void onUsbMassStorageConnectionChangedAsync(boolean connected) {
103        mUmsAvailable = connected;
104        /*
105         * Even though we may have a UMS host connected, we the SD card
106         * may not be in a state for export.
107         */
108        String st = Environment.getExternalStorageState();
109
110        if (DEBUG) Log.i(TAG, String.format("UMS connection changed to %s (media state %s)",
111                connected, st));
112
113        if (connected && (st.equals(
114                Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) {
115            /*
116             * No card or card being checked = don't display
117             */
118            connected = false;
119        }
120        updateUsbMassStorageNotification(connected);
121    }
122
123    private void onStorageStateChangedAsync(String path, String oldState, String newState) {
124        if (DEBUG) Log.i(TAG, String.format(
125                "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState));
126        if (newState.equals(Environment.MEDIA_SHARED)) {
127            /*
128             * Storage is now shared. Modify the UMS notification
129             * for stopping UMS.
130             */
131            Intent intent = new Intent();
132            intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class);
133            PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
134            setUsbStorageNotification(
135                    com.android.internal.R.string.usb_storage_stop_notification_title,
136                    com.android.internal.R.string.usb_storage_stop_notification_message,
137                    com.android.internal.R.drawable.stat_sys_warning, false, true, pi);
138        } else if (newState.equals(Environment.MEDIA_CHECKING)) {
139            /*
140             * Storage is now checking. Update media notification and disable
141             * UMS notification.
142             */
143            setMediaStorageNotification(
144                    com.android.internal.R.string.ext_media_checking_notification_title,
145                    com.android.internal.R.string.ext_media_checking_notification_message,
146                    com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null);
147            updateUsbMassStorageNotification(false);
148        } else if (newState.equals(Environment.MEDIA_MOUNTED)) {
149            /*
150             * Storage is now mounted. Dismiss any media notifications,
151             * and enable UMS notification if connected.
152             */
153            setMediaStorageNotification(0, 0, 0, false, false, null);
154            updateUsbMassStorageNotification(mUmsAvailable);
155        } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) {
156            /*
157             * Storage is now unmounted. We may have been unmounted
158             * because the user is enabling/disabling UMS, in which case we don't
159             * want to display the 'safe to unmount' notification.
160             */
161            if (!mStorageManager.isUsbMassStorageEnabled()) {
162                if (oldState.equals(Environment.MEDIA_SHARED)) {
163                    /*
164                     * The unmount was due to UMS being enabled. Dismiss any
165                     * media notifications, and enable UMS notification if connected
166                     */
167                    setMediaStorageNotification(0, 0, 0, false, false, null);
168                    updateUsbMassStorageNotification(mUmsAvailable);
169                } else {
170                    /*
171                     * Show safe to unmount media notification, and enable UMS
172                     * notification if connected.
173                     */
174                    if (Environment.isExternalStorageRemovable()) {
175                        setMediaStorageNotification(
176                                com.android.internal.R.string.ext_media_safe_unmount_notification_title,
177                                com.android.internal.R.string.ext_media_safe_unmount_notification_message,
178                                com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
179                    } else {
180                        // This device does not have removable storage, so
181                        // don't tell the user they can remove it.
182                        setMediaStorageNotification(0, 0, 0, false, false, null);
183                    }
184                    updateUsbMassStorageNotification(mUmsAvailable);
185                }
186            } else {
187                /*
188                 * The unmount was due to UMS being enabled. Dismiss any
189                 * media notifications, and disable the UMS notification
190                 */
191                setMediaStorageNotification(0, 0, 0, false, false, null);
192                updateUsbMassStorageNotification(false);
193            }
194        } else if (newState.equals(Environment.MEDIA_NOFS)) {
195            /*
196             * Storage has no filesystem. Show blank media notification,
197             * and enable UMS notification if connected.
198             */
199            Intent intent = new Intent();
200            intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
201            PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
202
203            setMediaStorageNotification(
204                    com.android.internal.R.string.ext_media_nofs_notification_title,
205                    com.android.internal.R.string.ext_media_nofs_notification_message,
206                    com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
207            updateUsbMassStorageNotification(mUmsAvailable);
208        } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) {
209            /*
210             * Storage is corrupt. Show corrupt media notification,
211             * and enable UMS notification if connected.
212             */
213            Intent intent = new Intent();
214            intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
215            PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
216
217            setMediaStorageNotification(
218                    com.android.internal.R.string.ext_media_unmountable_notification_title,
219                    com.android.internal.R.string.ext_media_unmountable_notification_message,
220                    com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
221            updateUsbMassStorageNotification(mUmsAvailable);
222        } else if (newState.equals(Environment.MEDIA_REMOVED)) {
223            /*
224             * Storage has been removed. Show nomedia media notification,
225             * and disable UMS notification regardless of connection state.
226             */
227            setMediaStorageNotification(
228                    com.android.internal.R.string.ext_media_nomedia_notification_title,
229                    com.android.internal.R.string.ext_media_nomedia_notification_message,
230                    com.android.internal.R.drawable.stat_notify_sdcard_usb,
231                    true, false, null);
232            updateUsbMassStorageNotification(false);
233        } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) {
234            /*
235             * Storage has been removed unsafely. Show bad removal media notification,
236             * and disable UMS notification regardless of connection state.
237             */
238            setMediaStorageNotification(
239                    com.android.internal.R.string.ext_media_badremoval_notification_title,
240                    com.android.internal.R.string.ext_media_badremoval_notification_message,
241                    com.android.internal.R.drawable.stat_sys_warning,
242                    true, true, null);
243            updateUsbMassStorageNotification(false);
244        } else {
245            Log.w(TAG, String.format("Ignoring unknown state {%s}", newState));
246        }
247    }
248
249    /**
250     * Update the state of the USB mass storage notification
251     */
252    void updateUsbMassStorageNotification(boolean available) {
253
254        if (available) {
255            Intent intent = new Intent();
256            intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class);
257            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
258
259            PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
260            setUsbStorageNotification(
261                    com.android.internal.R.string.usb_storage_notification_title,
262                    com.android.internal.R.string.usb_storage_notification_message,
263                    com.android.internal.R.drawable.stat_sys_data_usb,
264                    false, true, pi);
265        } else {
266            setUsbStorageNotification(0, 0, 0, false, false, null);
267        }
268    }
269
270    /**
271     * Sets the USB storage notification.
272     */
273    private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon,
274            boolean sound, boolean visible, PendingIntent pi) {
275
276        if (!visible && mUsbStorageNotification == null) {
277            return;
278        }
279
280        NotificationManager notificationManager = (NotificationManager) mContext
281                .getSystemService(Context.NOTIFICATION_SERVICE);
282
283        if (notificationManager == null) {
284            return;
285        }
286
287        if (visible) {
288            Resources r = Resources.getSystem();
289            CharSequence title = r.getText(titleId);
290            CharSequence message = r.getText(messageId);
291
292            if (mUsbStorageNotification == null) {
293                mUsbStorageNotification = new Notification();
294                mUsbStorageNotification.icon = icon;
295                mUsbStorageNotification.when = 0;
296            }
297
298            if (sound) {
299                mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
300            } else {
301                mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
302            }
303
304            mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
305
306            mUsbStorageNotification.tickerText = title;
307            if (pi == null) {
308                Intent intent = new Intent();
309                pi = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0,
310                        UserHandle.CURRENT);
311            }
312
313            mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
314            final boolean adbOn = 1 == Settings.Global.getInt(
315                mContext.getContentResolver(),
316                Settings.Global.ADB_ENABLED,
317                0);
318
319            if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) {
320                // Pop up a full-screen alert to coach the user through enabling UMS. The average
321                // user has attached the device to USB either to charge the phone (in which case
322                // this is harmless) or transfer files, and in the latter case this alert saves
323                // several steps (as well as subtly indicates that you shouldn't mix UMS with other
324                // activities on the device).
325                //
326                // If ADB is enabled, however, we suppress this dialog (under the assumption that a
327                // developer (a) knows how to enable UMS, and (b) is probably using USB to install
328                // builds or use adb commands.
329                mUsbStorageNotification.fullScreenIntent = pi;
330            }
331        }
332
333        final int notificationId = mUsbStorageNotification.icon;
334        if (visible) {
335            notificationManager.notifyAsUser(null, notificationId, mUsbStorageNotification,
336                    UserHandle.ALL);
337        } else {
338            notificationManager.cancelAsUser(null, notificationId, UserHandle.ALL);
339        }
340    }
341
342    private synchronized boolean getMediaStorageNotificationDismissable() {
343        if ((mMediaStorageNotification != null) &&
344            ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
345                    Notification.FLAG_AUTO_CANCEL))
346            return true;
347
348        return false;
349    }
350
351    /**
352     * Sets the media storage notification.
353     */
354    private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
355                                                          boolean dismissable, PendingIntent pi) {
356
357        if (!visible && mMediaStorageNotification == null) {
358            return;
359        }
360
361        NotificationManager notificationManager = (NotificationManager) mContext
362                .getSystemService(Context.NOTIFICATION_SERVICE);
363
364        if (notificationManager == null) {
365            return;
366        }
367
368        if (mMediaStorageNotification != null && visible) {
369            /*
370             * Dismiss the previous notification - we're about to
371             * re-use it.
372             */
373            final int notificationId = mMediaStorageNotification.icon;
374            notificationManager.cancel(notificationId);
375        }
376
377        if (visible) {
378            Resources r = Resources.getSystem();
379            CharSequence title = r.getText(titleId);
380            CharSequence message = r.getText(messageId);
381
382            if (mMediaStorageNotification == null) {
383                mMediaStorageNotification = new Notification();
384                mMediaStorageNotification.when = 0;
385            }
386
387            mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
388
389            if (dismissable) {
390                mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
391            } else {
392                mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
393            }
394
395            mMediaStorageNotification.tickerText = title;
396            if (pi == null) {
397                Intent intent = new Intent();
398                pi = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0,
399                        UserHandle.CURRENT);
400            }
401
402            mMediaStorageNotification.icon = icon;
403            mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
404        }
405
406        final int notificationId = mMediaStorageNotification.icon;
407        if (visible) {
408            notificationManager.notifyAsUser(null, notificationId,
409                    mMediaStorageNotification, UserHandle.ALL);
410        } else {
411            notificationManager.cancelAsUser(null, notificationId, UserHandle.ALL);
412        }
413    }
414}