OSDN Git Service

4e603bed36173f54fa5499aa4322900188fb2443
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / ingest / IngestActivity.java
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
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
17 package com.android.gallery3d.ingest;
18
19 import android.app.Activity;
20 import android.app.ProgressDialog;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.mtp.MtpObjectInfo;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.Message;
30 import android.util.SparseBooleanArray;
31 import android.view.ActionMode;
32 import android.view.Menu;
33 import android.view.MenuInflater;
34 import android.view.MenuItem;
35 import android.view.View;
36 import android.widget.AbsListView.MultiChoiceModeListener;
37 import android.widget.AdapterView;
38 import android.widget.AdapterView.OnItemClickListener;
39 import android.widget.GridView;
40 import android.widget.TextView;
41
42 import com.android.gallery3d.R;
43 import com.android.gallery3d.ingest.adapter.MtpAdapter;
44 import com.android.gallery3d.ingest.ui.DateTileView;
45
46 import java.lang.ref.WeakReference;
47 import java.util.Collection;
48
49 public class IngestActivity extends Activity implements
50         MtpDeviceIndex.ProgressListener, ImportTask.Listener {
51
52     private IngestService mHelperService;
53     private boolean mActive = false;
54     private GridView mGridView;
55     private MtpAdapter mAdapter;
56     private Handler mHandler;
57     private ProgressDialog mProgressDialog;
58     private ActionMode mActiveActionMode;
59
60     private View mWarningOverlay;
61     private TextView mWarningOverlayText;
62
63     @Override
64     protected void onCreate(Bundle savedInstanceState) {
65         super.onCreate(savedInstanceState);
66         doBindHelperService();
67
68         setContentView(R.layout.ingest_activity_item_list);
69         mGridView = (GridView) findViewById(R.id.ingest_gridview);
70         mAdapter = new MtpAdapter(this);
71         mGridView.setAdapter(mAdapter);
72         mGridView.setMultiChoiceModeListener(mMultiChoiceModeListener);
73         mGridView.setOnItemClickListener(mOnItemClickListener);
74
75         mHandler = new ItemListHandler(this);
76     }
77
78     private OnItemClickListener mOnItemClickListener = new OnItemClickListener() {
79         @Override
80         public void onItemClick(AdapterView<?> adapterView, View itemView, int position, long arg3) {
81             mGridView.setItemChecked(position, !mGridView.getCheckedItemPositions().get(position));
82         }
83     };
84
85     private MultiChoiceModeListener mMultiChoiceModeListener = new MultiChoiceModeListener() {
86         private boolean mIgnoreItemCheckedStateChanges = false;
87
88         private void updateSelectedTitle(ActionMode mode) {
89             int count = mGridView.getCheckedItemCount();
90             mode.setTitle(getResources().getQuantityString(
91                     R.plurals.number_of_items_selected, count, count));
92         }
93
94         @Override
95         public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
96                 boolean checked) {
97             if (mIgnoreItemCheckedStateChanges) return;
98             if (mAdapter.itemAtPositionIsBucket(position)) {
99                 SparseBooleanArray checkedItems = mGridView.getCheckedItemPositions();
100                 mIgnoreItemCheckedStateChanges = true;
101                 mGridView.setItemChecked(position, false);
102
103                 // Takes advantage of the fact that SectionIndexer imposes the
104                 // need to clamp to the valid range
105                 int nextSectionStart = mAdapter.getPositionForSection(
106                         mAdapter.getSectionForPosition(position) + 1);
107                 if (nextSectionStart == position)
108                     nextSectionStart = mAdapter.getCount();
109
110                 boolean rangeValue = false; // Value we want to set all of the bucket items to
111
112                 // Determine if all the items in the bucket are currently checked, so that we
113                 // can uncheck them, otherwise we will check all items in the bucket.
114                 for (int i = position + 1; i < nextSectionStart; i++) {
115                     if (checkedItems.get(i) == false) {
116                         rangeValue = true;
117                         break;
118                     }
119                 }
120
121                 // Set all items in the bucket to the desired state
122                 for (int i = position + 1; i < nextSectionStart; i++) {
123                     if (checkedItems.get(i) != rangeValue)
124                         mGridView.setItemChecked(i, rangeValue);
125                 }
126
127                 mIgnoreItemCheckedStateChanges = false;
128             }
129             updateSelectedTitle(mode);
130         }
131
132         @Override
133         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
134             switch (item.getItemId()) {
135                 case R.id.import_items:
136                     mHelperService.importSelectedItems(
137                             mGridView.getCheckedItemPositions(),
138                             mAdapter);
139                     mode.finish();
140                     return true;
141                 default:
142                     return false;
143             }
144         }
145
146         @Override
147         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
148             MenuInflater inflater = mode.getMenuInflater();
149             inflater.inflate(R.menu.ingest_menu_item_list_selection, menu);
150             updateSelectedTitle(mode);
151             mActiveActionMode = mode;
152             return true;
153         }
154
155         @Override
156         public void onDestroyActionMode(ActionMode mode) {
157             mActiveActionMode = null;
158         }
159
160         @Override
161         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
162             updateSelectedTitle(mode);
163             return false;
164         }
165     };
166
167     @Override
168     protected void onDestroy() {
169         super.onDestroy();
170         doUnbindHelperService();
171     }
172
173     @Override
174     protected void onResume() {
175         DateTileView.refreshLocale();
176         mActive = true;
177         if (mHelperService != null) mHelperService.setClientActivity(this);
178         updateWarningOverlay();
179         super.onResume();
180     }
181
182     @Override
183     protected void onPause() {
184         if (mHelperService != null) mHelperService.setClientActivity(null);
185         mActive = false;
186         cleanupProgressDialog();
187         super.onPause();
188     }
189
190     private void showWarningOverlay(int textResId) {
191         if (mWarningOverlay == null) {
192             mWarningOverlay = findViewById(R.id.ingest_warning_overlay);
193             mWarningOverlayText =
194                     (TextView)mWarningOverlay.findViewById(R.id.ingest_warning_overlay_text);
195         }
196         mWarningOverlayText.setText(textResId);
197         mWarningOverlay.setVisibility(View.VISIBLE);
198         mGridView.setVisibility(View.GONE);
199     }
200
201     private void hideWarningOverlay() {
202         if (mWarningOverlay != null) {
203             mWarningOverlay.setVisibility(View.GONE);
204             mGridView.setVisibility(View.VISIBLE);
205         }
206     }
207
208     private void updateWarningOverlay() {
209         if (!mAdapter.deviceConnected()) {
210             showWarningOverlay(R.string.ingest_no_device);
211         } else if (mAdapter.indexReady() && mAdapter.getCount() == 0) {
212             showWarningOverlay(R.string.ingest_empty_device);
213         } else {
214             hideWarningOverlay();
215         }
216     }
217
218     private void UiThreadNotifyIndexChanged() {
219         mAdapter.notifyDataSetChanged();
220         if (mActiveActionMode != null) {
221             mActiveActionMode.finish();
222             mActiveActionMode = null;
223         }
224         updateWarningOverlay();
225     }
226
227     protected void notifyIndexChanged() {
228         mHandler.sendEmptyMessage(ItemListHandler.MSG_NOTIFY_CHANGED);
229     }
230
231     private static class ProgressState {
232         String message;
233         String title;
234         int current;
235         int max;
236
237         public void reset() {
238             title = null;
239             message = null;
240             current = 0;
241             max = 0;
242         }
243     }
244
245     private ProgressState mProgressState = new ProgressState();
246
247     @Override
248     public void onObjectIndexed(MtpObjectInfo object, int numVisited) {
249         // Not guaranteed to be called on the UI thread
250         mProgressState.reset();
251         mProgressState.max = 0;
252         mProgressState.message = getResources().getQuantityString(
253                 R.plurals.ingest_number_of_items_scanned, numVisited, numVisited);
254         mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_UPDATE);
255     }
256
257     @Override
258     public void onSorting() {
259         // Not guaranteed to be called on the UI thread
260         mProgressState.reset();
261         mProgressState.max = 0;
262         mProgressState.message = getResources().getString(R.string.ingest_sorting);
263         mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_UPDATE);
264     }
265
266     @Override
267     public void onIndexFinish() {
268         // Not guaranteed to be called on the UI thread
269         mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_HIDE);
270         mHandler.sendEmptyMessage(ItemListHandler.MSG_NOTIFY_CHANGED);
271     }
272
273     @Override
274     public void onImportProgress(final int visitedCount, final int totalCount,
275             String pathIfSuccessful) {
276         // Not guaranteed to be called on the UI thread
277         mProgressState.reset();
278         mProgressState.max = totalCount;
279         mProgressState.current = visitedCount;
280         mProgressState.title = getResources().getString(R.string.ingest_importing);
281         mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_UPDATE);
282     }
283
284     @Override
285     public void onImportFinish(Collection<MtpObjectInfo> objectsNotImported) {
286         // Not guaranteed to be called on the UI thread
287         mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_HIDE);
288         // TODO: maybe show an extra dialog listing the ones that failed
289         // importing, if any?
290     }
291
292     private ProgressDialog getProgressDialog() {
293         if (mProgressDialog == null || !mProgressDialog.isShowing()) {
294             mProgressDialog = new ProgressDialog(this);
295             mProgressDialog.setCancelable(false);
296         }
297         return mProgressDialog;
298     }
299
300     private void updateProgressDialog() {
301         ProgressDialog dialog = getProgressDialog();
302         boolean indeterminate = (mProgressState.max == 0);
303         dialog.setIndeterminate(indeterminate);
304         dialog.setProgressStyle(indeterminate ? ProgressDialog.STYLE_SPINNER
305                 : ProgressDialog.STYLE_HORIZONTAL);
306         if (mProgressState.title != null) {
307             dialog.setTitle(mProgressState.title);
308         }
309         if (mProgressState.message != null) {
310             dialog.setMessage(mProgressState.message);
311         }
312         if (!indeterminate) {
313             dialog.setProgress(mProgressState.current);
314             dialog.setMax(mProgressState.max);
315         }
316         if (!dialog.isShowing()) {
317             dialog.show();
318         }
319     }
320
321     private void cleanupProgressDialog() {
322         if (mProgressDialog != null) {
323             mProgressDialog.hide();
324             mProgressDialog = null;
325         }
326     }
327
328     // This is static and uses a WeakReference in order to avoid leaking the Activity
329     private static class ItemListHandler extends Handler {
330         public static final int MSG_PROGRESS_UPDATE = 0;
331         public static final int MSG_PROGRESS_HIDE = 1;
332         public static final int MSG_NOTIFY_CHANGED = 2;
333
334         WeakReference<IngestActivity> mParentReference;
335
336         public ItemListHandler(IngestActivity parent) {
337             super();
338             mParentReference = new WeakReference<IngestActivity>(parent);
339         }
340
341         public void handleMessage(Message message) {
342             IngestActivity parent = mParentReference.get();
343             if (parent == null || !parent.mActive)
344                 return;
345             switch (message.what) {
346                 case MSG_PROGRESS_HIDE:
347                     parent.cleanupProgressDialog();
348                     break;
349                 case MSG_PROGRESS_UPDATE:
350                     parent.updateProgressDialog();
351                     break;
352                 case MSG_NOTIFY_CHANGED:
353                     parent.UiThreadNotifyIndexChanged();
354                     break;
355                 default:
356                     break;
357             }
358         }
359     }
360
361     private ServiceConnection mHelperServiceConnection = new ServiceConnection() {
362         public void onServiceConnected(ComponentName className, IBinder service) {
363             mHelperService = ((IngestService.LocalBinder) service).getService();
364             mHelperService.setClientActivity(IngestActivity.this);
365             mAdapter.setMtpDeviceIndex(mHelperService.getIndex());
366         }
367
368         public void onServiceDisconnected(ComponentName className) {
369             mHelperService = null;
370         }
371     };
372
373     private void doBindHelperService() {
374         bindService(new Intent(getApplicationContext(), IngestService.class),
375                 mHelperServiceConnection, Context.BIND_AUTO_CREATE);
376     }
377
378     private void doUnbindHelperService() {
379         if (mHelperService != null) {
380             mHelperService.setClientActivity(null);
381             unbindService(mHelperServiceConnection);
382         }
383     }
384 }