OSDN Git Service

am edddcab2: am d8259c06: Merge "Fix ICS recording aspect ratio" into gb-ub-photos...
[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.content.res.Configuration;
26 import android.database.DataSetObserver;
27 import android.mtp.MtpObjectInfo;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Message;
32 import android.support.v4.view.ViewPager;
33 import android.util.SparseBooleanArray;
34 import android.view.ActionMode;
35 import android.view.Menu;
36 import android.view.MenuInflater;
37 import android.view.MenuItem;
38 import android.view.View;
39 import android.widget.AbsListView.MultiChoiceModeListener;
40 import android.widget.AdapterView;
41 import android.widget.AdapterView.OnItemClickListener;
42 import android.widget.TextView;
43
44 import com.android.gallery3d.R;
45 import com.android.gallery3d.ingest.adapter.CheckBroker;
46 import com.android.gallery3d.ingest.adapter.MtpAdapter;
47 import com.android.gallery3d.ingest.adapter.MtpPagerAdapter;
48 import com.android.gallery3d.ingest.data.MtpBitmapFetch;
49 import com.android.gallery3d.ingest.ui.DateTileView;
50 import com.android.gallery3d.ingest.ui.IngestGridView;
51 import com.android.gallery3d.ingest.ui.IngestGridView.OnClearChoicesListener;
52
53 import java.lang.ref.WeakReference;
54 import java.util.Collection;
55
56 public class IngestActivity extends Activity implements
57         MtpDeviceIndex.ProgressListener, ImportTask.Listener {
58
59     private IngestService mHelperService;
60     private boolean mActive = false;
61     private IngestGridView mGridView;
62     private MtpAdapter mAdapter;
63     private Handler mHandler;
64     private ProgressDialog mProgressDialog;
65     private ActionMode mActiveActionMode;
66
67     private View mWarningView;
68     private TextView mWarningText;
69     private int mLastCheckedPosition = 0;
70
71     private ViewPager mFullscreenPager;
72     private MtpPagerAdapter mPagerAdapter;
73     private boolean mFullscreenPagerVisible = false;
74
75     private MenuItem mMenuSwitcherItem;
76     private MenuItem mActionMenuSwitcherItem;
77
78     @Override
79     protected void onCreate(Bundle savedInstanceState) {
80         super.onCreate(savedInstanceState);
81         doBindHelperService();
82
83         setContentView(R.layout.ingest_activity_item_list);
84         mGridView = (IngestGridView) findViewById(R.id.ingest_gridview);
85         mAdapter = new MtpAdapter(this);
86         mAdapter.registerDataSetObserver(mMasterObserver);
87         mGridView.setAdapter(mAdapter);
88         mGridView.setMultiChoiceModeListener(mMultiChoiceModeListener);
89         mGridView.setOnItemClickListener(mOnItemClickListener);
90         mGridView.setOnClearChoicesListener(mPositionMappingCheckBroker);
91
92         mFullscreenPager = (ViewPager) findViewById(R.id.ingest_view_pager);
93
94         mHandler = new ItemListHandler(this);
95
96         MtpBitmapFetch.configureForContext(this);
97     }
98
99     private OnItemClickListener mOnItemClickListener = new OnItemClickListener() {
100         @Override
101         public void onItemClick(AdapterView<?> adapterView, View itemView, int position, long arg3) {
102             mLastCheckedPosition = position;
103             mGridView.setItemChecked(position, !mGridView.getCheckedItemPositions().get(position));
104         }
105     };
106
107     private MultiChoiceModeListener mMultiChoiceModeListener = new MultiChoiceModeListener() {
108         private boolean mIgnoreItemCheckedStateChanges = false;
109
110         private void updateSelectedTitle(ActionMode mode) {
111             int count = mGridView.getCheckedItemCount();
112             mode.setTitle(getResources().getQuantityString(
113                     R.plurals.number_of_items_selected, count, count));
114         }
115
116         @Override
117         public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
118                 boolean checked) {
119             if (mIgnoreItemCheckedStateChanges) return;
120             if (mAdapter.itemAtPositionIsBucket(position)) {
121                 SparseBooleanArray checkedItems = mGridView.getCheckedItemPositions();
122                 mIgnoreItemCheckedStateChanges = true;
123                 mGridView.setItemChecked(position, false);
124
125                 // Takes advantage of the fact that SectionIndexer imposes the
126                 // need to clamp to the valid range
127                 int nextSectionStart = mAdapter.getPositionForSection(
128                         mAdapter.getSectionForPosition(position) + 1);
129                 if (nextSectionStart == position)
130                     nextSectionStart = mAdapter.getCount();
131
132                 boolean rangeValue = false; // Value we want to set all of the bucket items to
133
134                 // Determine if all the items in the bucket are currently checked, so that we
135                 // can uncheck them, otherwise we will check all items in the bucket.
136                 for (int i = position + 1; i < nextSectionStart; i++) {
137                     if (checkedItems.get(i) == false) {
138                         rangeValue = true;
139                         break;
140                     }
141                 }
142
143                 // Set all items in the bucket to the desired state
144                 for (int i = position + 1; i < nextSectionStart; i++) {
145                     if (checkedItems.get(i) != rangeValue)
146                         mGridView.setItemChecked(i, rangeValue);
147                 }
148
149                 mPositionMappingCheckBroker.onBulkCheckedChange();
150                 mIgnoreItemCheckedStateChanges = false;
151             } else {
152                 mPositionMappingCheckBroker.onCheckedChange(position, checked);
153             }
154             mLastCheckedPosition = position;
155             updateSelectedTitle(mode);
156         }
157
158         @Override
159         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
160             return onOptionsItemSelected(item);
161         }
162
163         @Override
164         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
165             MenuInflater inflater = mode.getMenuInflater();
166             inflater.inflate(R.menu.ingest_menu_item_list_selection, menu);
167             updateSelectedTitle(mode);
168             mActiveActionMode = mode;
169             mActionMenuSwitcherItem = menu.findItem(R.id.ingest_switch_view);
170             setSwitcherMenuState(mActionMenuSwitcherItem, mFullscreenPagerVisible);
171             return true;
172         }
173
174         @Override
175         public void onDestroyActionMode(ActionMode mode) {
176             mActiveActionMode = null;
177             mActionMenuSwitcherItem = null;
178             mHandler.sendEmptyMessage(ItemListHandler.MSG_BULK_CHECKED_CHANGE);
179         }
180
181         @Override
182         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
183             updateSelectedTitle(mode);
184             return false;
185         }
186     };
187
188     public boolean onOptionsItemSelected(MenuItem item) {
189         switch (item.getItemId()) {
190             case R.id.import_items:
191                 if (mActiveActionMode != null) {
192                     mHelperService.importSelectedItems(
193                             mGridView.getCheckedItemPositions(),
194                             mAdapter);
195                     mActiveActionMode.finish();
196                 }
197                 return true;
198             case R.id.ingest_switch_view:
199                 setFullscreenPagerVisibility(!mFullscreenPagerVisible);
200                 return true;
201             default:
202                 return false;
203         }
204     }
205
206     @Override
207     public boolean onCreateOptionsMenu(Menu menu) {
208         MenuInflater inflater = getMenuInflater();
209         inflater.inflate(R.menu.ingest_menu_item_list_selection, menu);
210         mMenuSwitcherItem = menu.findItem(R.id.ingest_switch_view);
211         menu.findItem(R.id.import_items).setVisible(false);
212         setSwitcherMenuState(mMenuSwitcherItem, mFullscreenPagerVisible);
213         return true;
214     }
215
216     @Override
217     protected void onDestroy() {
218         super.onDestroy();
219         doUnbindHelperService();
220     }
221
222     @Override
223     protected void onResume() {
224         DateTileView.refreshLocale();
225         mActive = true;
226         if (mHelperService != null) mHelperService.setClientActivity(this);
227         updateWarningView();
228         super.onResume();
229     }
230
231     @Override
232     protected void onPause() {
233         if (mHelperService != null) mHelperService.setClientActivity(null);
234         mActive = false;
235         cleanupProgressDialog();
236         super.onPause();
237     }
238
239     @Override
240     public void onConfigurationChanged(Configuration newConfig) {
241         super.onConfigurationChanged(newConfig);
242         MtpBitmapFetch.configureForContext(this);
243     }
244
245     private void showWarningView(int textResId) {
246         if (mWarningView == null) {
247             mWarningView = findViewById(R.id.ingest_warning_view);
248             mWarningText =
249                     (TextView)mWarningView.findViewById(R.id.ingest_warning_view_text);
250         }
251         mWarningText.setText(textResId);
252         mWarningView.setVisibility(View.VISIBLE);
253         setFullscreenPagerVisibility(false);
254         mGridView.setVisibility(View.GONE);
255     }
256
257     private void hideWarningView() {
258         if (mWarningView != null) {
259             mWarningView.setVisibility(View.GONE);
260             setFullscreenPagerVisibility(false);
261         }
262     }
263
264     private PositionMappingCheckBroker mPositionMappingCheckBroker = new PositionMappingCheckBroker();
265
266     private class PositionMappingCheckBroker extends CheckBroker
267         implements OnClearChoicesListener {
268         private int mLastMappingPager = -1;
269         private int mLastMappingGrid = -1;
270
271         private int mapPagerToGridPosition(int position) {
272             if (position != mLastMappingPager) {
273                mLastMappingPager = position;
274                mLastMappingGrid = mAdapter.translatePositionWithoutLabels(position);
275             }
276             return mLastMappingGrid;
277         }
278
279         private int mapGridToPagerPosition(int position) {
280             if (position != mLastMappingGrid) {
281                 mLastMappingGrid = position;
282                 mLastMappingPager = mPagerAdapter.translatePositionWithLabels(position);
283             }
284             return mLastMappingPager;
285         }
286
287         @Override
288         public void setItemChecked(int position, boolean checked) {
289             mGridView.setItemChecked(mapPagerToGridPosition(position), checked);
290         }
291
292         @Override
293         public void onCheckedChange(int position, boolean checked) {
294             if (mPagerAdapter != null) {
295                 super.onCheckedChange(mapGridToPagerPosition(position), checked);
296             }
297         }
298
299         @Override
300         public boolean isItemChecked(int position) {
301             return mGridView.getCheckedItemPositions().get(mapPagerToGridPosition(position));
302         }
303
304         @Override
305         public void onClearChoices() {
306             onBulkCheckedChange();
307         }
308     };
309
310     private DataSetObserver mMasterObserver = new DataSetObserver() {
311         @Override
312         public void onChanged() {
313             if (mPagerAdapter != null) mPagerAdapter.notifyDataSetChanged();
314         }
315
316         @Override
317         public void onInvalidated() {
318             if (mPagerAdapter != null) mPagerAdapter.notifyDataSetChanged();
319         }
320     };
321
322     private int pickFullscreenStartingPosition() {
323         int firstVisiblePosition = mGridView.getFirstVisiblePosition();
324         if (mLastCheckedPosition <= firstVisiblePosition
325                 || mLastCheckedPosition > mGridView.getLastVisiblePosition()) {
326             return firstVisiblePosition;
327         } else {
328             return mLastCheckedPosition;
329         }
330     }
331
332     private void setSwitcherMenuState(MenuItem menuItem, boolean inFullscreenMode) {
333         if (menuItem == null) return;
334         if (!inFullscreenMode) {
335             menuItem.setIcon(android.R.drawable.ic_menu_zoom);
336             menuItem.setTitle(R.string.switch_photo_fullscreen);
337         } else {
338             menuItem.setIcon(android.R.drawable.ic_dialog_dialer);
339             menuItem.setTitle(R.string.switch_photo_grid);
340         }
341     }
342
343     private void setFullscreenPagerVisibility(boolean visible) {
344         mFullscreenPagerVisible = visible;
345         if (visible) {
346             if (mPagerAdapter == null) {
347                 mPagerAdapter = new MtpPagerAdapter(this, mPositionMappingCheckBroker);
348                 mPagerAdapter.setMtpDeviceIndex(mAdapter.getMtpDeviceIndex());
349             }
350             mFullscreenPager.setAdapter(mPagerAdapter);
351             mFullscreenPager.setCurrentItem(mPagerAdapter.translatePositionWithLabels(
352                     pickFullscreenStartingPosition()), false);
353         } else if (mPagerAdapter != null) {
354             mGridView.setSelection(mAdapter.translatePositionWithoutLabels(
355                     mFullscreenPager.getCurrentItem()));
356             mFullscreenPager.setAdapter(null);
357         }
358         mGridView.setVisibility(visible ? View.INVISIBLE : View.VISIBLE);
359         mFullscreenPager.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
360         if (mActionMenuSwitcherItem != null) {
361             setSwitcherMenuState(mActionMenuSwitcherItem, visible);
362         }
363         setSwitcherMenuState(mMenuSwitcherItem, visible);
364     }
365
366     private void updateWarningView() {
367         if (!mAdapter.deviceConnected()) {
368             showWarningView(R.string.ingest_no_device);
369         } else if (mAdapter.indexReady() && mAdapter.getCount() == 0) {
370             showWarningView(R.string.ingest_empty_device);
371         } else {
372             hideWarningView();
373         }
374     }
375
376     private void UiThreadNotifyIndexChanged() {
377         mAdapter.notifyDataSetChanged();
378         if (mActiveActionMode != null) {
379             mActiveActionMode.finish();
380             mActiveActionMode = null;
381         }
382         updateWarningView();
383     }
384
385     protected void notifyIndexChanged() {
386         mHandler.sendEmptyMessage(ItemListHandler.MSG_NOTIFY_CHANGED);
387     }
388
389     private static class ProgressState {
390         String message;
391         String title;
392         int current;
393         int max;
394
395         public void reset() {
396             title = null;
397             message = null;
398             current = 0;
399             max = 0;
400         }
401     }
402
403     private ProgressState mProgressState = new ProgressState();
404
405     @Override
406     public void onObjectIndexed(MtpObjectInfo object, int numVisited) {
407         // Not guaranteed to be called on the UI thread
408         mProgressState.reset();
409         mProgressState.max = 0;
410         mProgressState.message = getResources().getQuantityString(
411                 R.plurals.ingest_number_of_items_scanned, numVisited, numVisited);
412         mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_UPDATE);
413     }
414
415     @Override
416     public void onSorting() {
417         // Not guaranteed to be called on the UI thread
418         mProgressState.reset();
419         mProgressState.max = 0;
420         mProgressState.message = getResources().getString(R.string.ingest_sorting);
421         mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_UPDATE);
422     }
423
424     @Override
425     public void onIndexFinish() {
426         // Not guaranteed to be called on the UI thread
427         mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_HIDE);
428         mHandler.sendEmptyMessage(ItemListHandler.MSG_NOTIFY_CHANGED);
429     }
430
431     @Override
432     public void onImportProgress(final int visitedCount, final int totalCount,
433             String pathIfSuccessful) {
434         // Not guaranteed to be called on the UI thread
435         mProgressState.reset();
436         mProgressState.max = totalCount;
437         mProgressState.current = visitedCount;
438         mProgressState.title = getResources().getString(R.string.ingest_importing);
439         mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_UPDATE);
440     }
441
442     @Override
443     public void onImportFinish(Collection<MtpObjectInfo> objectsNotImported,
444             int numVisited) {
445         // Not guaranteed to be called on the UI thread
446         mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_HIDE);
447         // TODO: maybe show an extra dialog listing the ones that failed
448         // importing, if any?
449     }
450
451     private ProgressDialog getProgressDialog() {
452         if (mProgressDialog == null || !mProgressDialog.isShowing()) {
453             mProgressDialog = new ProgressDialog(this);
454             mProgressDialog.setCancelable(false);
455         }
456         return mProgressDialog;
457     }
458
459     private void updateProgressDialog() {
460         ProgressDialog dialog = getProgressDialog();
461         boolean indeterminate = (mProgressState.max == 0);
462         dialog.setIndeterminate(indeterminate);
463         dialog.setProgressStyle(indeterminate ? ProgressDialog.STYLE_SPINNER
464                 : ProgressDialog.STYLE_HORIZONTAL);
465         if (mProgressState.title != null) {
466             dialog.setTitle(mProgressState.title);
467         }
468         if (mProgressState.message != null) {
469             dialog.setMessage(mProgressState.message);
470         }
471         if (!indeterminate) {
472             dialog.setProgress(mProgressState.current);
473             dialog.setMax(mProgressState.max);
474         }
475         if (!dialog.isShowing()) {
476             dialog.show();
477         }
478     }
479
480     private void cleanupProgressDialog() {
481         if (mProgressDialog != null) {
482             mProgressDialog.hide();
483             mProgressDialog = null;
484         }
485     }
486
487     // This is static and uses a WeakReference in order to avoid leaking the Activity
488     private static class ItemListHandler extends Handler {
489         public static final int MSG_PROGRESS_UPDATE = 0;
490         public static final int MSG_PROGRESS_HIDE = 1;
491         public static final int MSG_NOTIFY_CHANGED = 2;
492         public static final int MSG_BULK_CHECKED_CHANGE = 3;
493
494         WeakReference<IngestActivity> mParentReference;
495
496         public ItemListHandler(IngestActivity parent) {
497             super();
498             mParentReference = new WeakReference<IngestActivity>(parent);
499         }
500
501         public void handleMessage(Message message) {
502             IngestActivity parent = mParentReference.get();
503             if (parent == null || !parent.mActive)
504                 return;
505             switch (message.what) {
506                 case MSG_PROGRESS_HIDE:
507                     parent.cleanupProgressDialog();
508                     break;
509                 case MSG_PROGRESS_UPDATE:
510                     parent.updateProgressDialog();
511                     break;
512                 case MSG_NOTIFY_CHANGED:
513                     parent.UiThreadNotifyIndexChanged();
514                     break;
515                 case MSG_BULK_CHECKED_CHANGE:
516                     parent.mPositionMappingCheckBroker.onBulkCheckedChange();
517                     break;
518                 default:
519                     break;
520             }
521         }
522     }
523
524     private ServiceConnection mHelperServiceConnection = new ServiceConnection() {
525         public void onServiceConnected(ComponentName className, IBinder service) {
526             mHelperService = ((IngestService.LocalBinder) service).getService();
527             mHelperService.setClientActivity(IngestActivity.this);
528             MtpDeviceIndex index = mHelperService.getIndex();
529             mAdapter.setMtpDeviceIndex(index);
530             if (mPagerAdapter != null) mPagerAdapter.setMtpDeviceIndex(index);
531         }
532
533         public void onServiceDisconnected(ComponentName className) {
534             mHelperService = null;
535         }
536     };
537
538     private void doBindHelperService() {
539         bindService(new Intent(getApplicationContext(), IngestService.class),
540                 mHelperServiceConnection, Context.BIND_AUTO_CREATE);
541     }
542
543     private void doUnbindHelperService() {
544         if (mHelperService != null) {
545             mHelperService.setClientActivity(null);
546             unbindService(mHelperServiceConnection);
547         }
548     }
549 }