UsbStorageActivity.java revision c7f8b6fcf56aeb457013832555f6358f6babee90
1/*
2 * Copyright (C) 2007 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 com.android.internal.R;
20import android.app.Activity;
21import android.app.ActivityManager;
22import android.app.AlertDialog;
23import android.app.Dialog;
24import android.content.BroadcastReceiver;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.DialogInterface.OnCancelListener;
30import android.content.pm.ApplicationInfo;
31import android.content.pm.PackageManager;
32import android.content.pm.PackageManager.NameNotFoundException;
33import android.hardware.Usb;
34import android.os.Bundle;
35import android.os.Environment;
36import android.os.Handler;
37import android.os.HandlerThread;
38import android.os.IBinder;
39import android.os.storage.IMountService;
40import android.os.storage.StorageManager;
41import android.os.storage.StorageEventListener;
42import android.os.RemoteException;
43import android.os.ServiceManager;
44import android.widget.ImageView;
45import android.widget.Button;
46import android.widget.ProgressBar;
47import android.widget.TextView;
48import android.view.View;
49import android.view.Window;
50import android.view.WindowManager;
51import android.util.Log;
52
53import java.util.List;
54
55/**
56 * This activity is shown to the user for him/her to enable USB mass storage
57 * on-demand (that is, when the USB cable is connected). It uses the alert
58 * dialog style. It will be launched from a notification.
59 */
60public class UsbStorageActivity extends Activity
61        implements View.OnClickListener, OnCancelListener {
62    private static final String TAG = "UsbStorageActivity";
63
64    private Button mMountButton;
65    private Button mUnmountButton;
66    private ProgressBar mProgressBar;
67    private TextView mBanner;
68    private TextView mMessage;
69    private ImageView mIcon;
70    private StorageManager mStorageManager = null;
71    private static final int DLG_CONFIRM_KILL_STORAGE_USERS = 1;
72    private static final int DLG_ERROR_SHARING = 2;
73    static final boolean localLOGV = false;
74    private boolean mDestroyed;
75
76    // UI thread
77    private Handler mUIHandler;
78
79    // thread for working with the storage services, which can be slow
80    private Handler mAsyncStorageHandler;
81
82    /** Used to detect when the USB cable is unplugged, so we can call finish() */
83    private BroadcastReceiver mUsbStateReceiver = new BroadcastReceiver() {
84        @Override
85        public void onReceive(Context context, Intent intent) {
86            if (intent.getAction().equals(Usb.ACTION_USB_STATE)) {
87                handleUsbStateChanged(intent);
88            }
89        }
90    };
91
92    private StorageEventListener mStorageListener = new StorageEventListener() {
93        @Override
94        public void onStorageStateChanged(String path, String oldState, String newState) {
95            final boolean on = newState.equals(Environment.MEDIA_SHARED);
96            switchDisplay(on);
97        }
98    };
99
100    @Override
101    protected void onCreate(Bundle savedInstanceState) {
102        super.onCreate(savedInstanceState);
103
104        if (mStorageManager == null) {
105            mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
106            if (mStorageManager == null) {
107                Log.w(TAG, "Failed to get StorageManager");
108            }
109        }
110
111        mUIHandler = new Handler();
112
113        HandlerThread thr = new HandlerThread("SystemUI UsbStorageActivity");
114        thr.start();
115        mAsyncStorageHandler = new Handler(thr.getLooper());
116
117        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
118        setProgressBarIndeterminateVisibility(true);
119
120        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
121        if (Environment.isExternalStorageRemovable()) {
122            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
123        }
124
125        setTitle(getString(com.android.internal.R.string.usb_storage_activity_title));
126
127        setContentView(com.android.internal.R.layout.usb_storage_activity);
128
129        mIcon = (ImageView) findViewById(com.android.internal.R.id.icon);
130        mBanner = (TextView) findViewById(com.android.internal.R.id.banner);
131        mMessage = (TextView) findViewById(com.android.internal.R.id.message);
132
133        mMountButton = (Button) findViewById(com.android.internal.R.id.mount_button);
134        mMountButton.setOnClickListener(this);
135        mUnmountButton = (Button) findViewById(com.android.internal.R.id.unmount_button);
136        mUnmountButton.setOnClickListener(this);
137        mProgressBar = (ProgressBar) findViewById(com.android.internal.R.id.progress);
138    }
139
140    @Override
141    protected void onDestroy() {
142        super.onDestroy();
143        mDestroyed = true;
144    }
145
146    private void switchDisplay(final boolean usbStorageInUse) {
147        mUIHandler.post(new Runnable() {
148            @Override
149            public void run() {
150                switchDisplayAsync(usbStorageInUse);
151            }
152        });
153    }
154
155    private void switchDisplayAsync(boolean usbStorageInUse) {
156        if (usbStorageInUse) {
157            mProgressBar.setVisibility(View.GONE);
158            mUnmountButton.setVisibility(View.VISIBLE);
159            mMountButton.setVisibility(View.GONE);
160            mIcon.setImageResource(com.android.internal.R.drawable.usb_android_connected);
161            mBanner.setText(com.android.internal.R.string.usb_storage_stop_title);
162            mMessage.setText(com.android.internal.R.string.usb_storage_stop_message);
163        } else {
164            mProgressBar.setVisibility(View.GONE);
165            mUnmountButton.setVisibility(View.GONE);
166            mMountButton.setVisibility(View.VISIBLE);
167            mIcon.setImageResource(com.android.internal.R.drawable.usb_android);
168            mBanner.setText(com.android.internal.R.string.usb_storage_title);
169            mMessage.setText(com.android.internal.R.string.usb_storage_message);
170        }
171    }
172
173    @Override
174    protected void onResume() {
175        super.onResume();
176
177        mStorageManager.registerListener(mStorageListener);
178        registerReceiver(mUsbStateReceiver, new IntentFilter(Usb.ACTION_USB_STATE));
179        try {
180            mAsyncStorageHandler.post(new Runnable() {
181                @Override
182                public void run() {
183                    switchDisplay(mStorageManager.isUsbMassStorageEnabled());
184                }
185            });
186        } catch (Exception ex) {
187            Log.e(TAG, "Failed to read UMS enable state", ex);
188        }
189    }
190
191    @Override
192    protected void onPause() {
193        super.onPause();
194
195        unregisterReceiver(mUsbStateReceiver);
196        if (mStorageManager == null && mStorageListener != null) {
197            mStorageManager.unregisterListener(mStorageListener);
198        }
199    }
200
201    private void handleUsbStateChanged(Intent intent) {
202        boolean connected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
203        if (!connected) {
204            // It was disconnected from the plug, so finish
205            finish();
206        }
207    }
208
209    private IMountService getMountService() {
210        IBinder service = ServiceManager.getService("mount");
211        if (service != null) {
212            return IMountService.Stub.asInterface(service);
213        }
214        return null;
215    }
216
217    @Override
218    public Dialog onCreateDialog(int id, Bundle args) {
219        switch (id) {
220        case DLG_CONFIRM_KILL_STORAGE_USERS:
221            return new AlertDialog.Builder(this)
222                    .setTitle(R.string.dlg_confirm_kill_storage_users_title)
223                    .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
224                        public void onClick(DialogInterface dialog, int which) {
225                            switchUsbMassStorage(true);
226                        }})
227                    .setNegativeButton(R.string.cancel, null)
228                    .setMessage(R.string.dlg_confirm_kill_storage_users_text)
229                    .setOnCancelListener(this)
230                    .create();
231        case DLG_ERROR_SHARING:
232            return new AlertDialog.Builder(this)
233                    .setTitle(R.string.dlg_error_title)
234                    .setNeutralButton(R.string.dlg_ok, null)
235                    .setMessage(R.string.usb_storage_error_message)
236                    .setOnCancelListener(this)
237                    .create();
238        }
239        return null;
240    }
241
242    private void scheduleShowDialog(final int id) {
243        mUIHandler.post(new Runnable() {
244            @Override
245            public void run() {
246                if (!mDestroyed) {
247                    removeDialog(id);
248                    showDialog(id);
249                }
250            }
251        });
252    }
253
254    private void switchUsbMassStorage(final boolean on) {
255        // things to do on the UI thread
256        mUIHandler.post(new Runnable() {
257            @Override
258            public void run() {
259                mUnmountButton.setVisibility(View.GONE);
260                mMountButton.setVisibility(View.GONE);
261
262                mProgressBar.setVisibility(View.VISIBLE);
263                // will be hidden once USB mass storage kicks in (or fails)
264            }
265        });
266
267        // things to do elsewhere
268        mAsyncStorageHandler.post(new Runnable() {
269            @Override
270            public void run() {
271                if (on) {
272                    mStorageManager.enableUsbMassStorage();
273                } else {
274                    mStorageManager.disableUsbMassStorage();
275                }
276            }
277        });
278    }
279
280    private void checkStorageUsers() {
281        mAsyncStorageHandler.post(new Runnable() {
282            @Override
283            public void run() {
284                checkStorageUsersAsync();
285            }
286        });
287    }
288
289    private void checkStorageUsersAsync() {
290        IMountService ims = getMountService();
291        if (ims == null) {
292            // Display error dialog
293            scheduleShowDialog(DLG_ERROR_SHARING);
294        }
295        String extStoragePath = Environment.getExternalStorageDirectory().toString();
296        boolean showDialog = false;
297        try {
298            int[] stUsers = ims.getStorageUsers(extStoragePath);
299            if (stUsers != null && stUsers.length > 0) {
300                showDialog = true;
301            } else {
302                // List of applications on sdcard.
303                ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
304                List<ApplicationInfo> infoList = am.getRunningExternalApplications();
305                if (infoList != null && infoList.size() > 0) {
306                    showDialog = true;
307                }
308            }
309        } catch (RemoteException e) {
310            // Display error dialog
311            scheduleShowDialog(DLG_ERROR_SHARING);
312        }
313        if (showDialog) {
314            // Display dialog to user
315            scheduleShowDialog(DLG_CONFIRM_KILL_STORAGE_USERS);
316        } else {
317            if (localLOGV) Log.i(TAG, "Enabling UMS");
318            switchUsbMassStorage(true);
319        }
320    }
321
322    public void onClick(View v) {
323        if (v == mMountButton) {
324           // Check for list of storage users and display dialog if needed.
325            checkStorageUsers();
326        } else if (v == mUnmountButton) {
327            if (localLOGV) Log.i(TAG, "Disabling UMS");
328            switchUsbMassStorage(false);
329        }
330    }
331
332    public void onCancel(DialogInterface dialog) {
333        finish();
334    }
335
336}
337