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