| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.gallery3d.ingest; |
| |
| import android.app.Activity; |
| import android.app.ProgressDialog; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.ServiceConnection; |
| import android.mtp.MtpObjectInfo; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Message; |
| import android.util.SparseBooleanArray; |
| import android.view.ActionMode; |
| import android.view.Menu; |
| import android.view.MenuInflater; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.widget.AbsListView.MultiChoiceModeListener; |
| import android.widget.AdapterView; |
| import android.widget.AdapterView.OnItemClickListener; |
| import android.widget.GridView; |
| import android.widget.TextView; |
| |
| import com.android.gallery3d.R; |
| import com.android.gallery3d.ingest.adapter.MtpAdapter; |
| import com.android.gallery3d.ingest.ui.DateTileView; |
| |
| import java.lang.ref.WeakReference; |
| import java.util.Collection; |
| |
| public class IngestActivity extends Activity implements |
| MtpDeviceIndex.ProgressListener, ImportTask.Listener { |
| |
| private IngestService mHelperService; |
| private boolean mActive = false; |
| private GridView mGridView; |
| private MtpAdapter mAdapter; |
| private Handler mHandler; |
| private ProgressDialog mProgressDialog; |
| private ActionMode mActiveActionMode; |
| |
| private View mWarningOverlay; |
| private TextView mWarningOverlayText; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| doBindHelperService(); |
| |
| setContentView(R.layout.ingest_activity_item_list); |
| mGridView = (GridView) findViewById(R.id.ingest_gridview); |
| mAdapter = new MtpAdapter(this); |
| mGridView.setAdapter(mAdapter); |
| mGridView.setMultiChoiceModeListener(mMultiChoiceModeListener); |
| mGridView.setOnItemClickListener(mOnItemClickListener); |
| |
| mHandler = new ItemListHandler(this); |
| } |
| |
| private OnItemClickListener mOnItemClickListener = new OnItemClickListener() { |
| @Override |
| public void onItemClick(AdapterView<?> adapterView, View itemView, int position, long arg3) { |
| mGridView.setItemChecked(position, !mGridView.getCheckedItemPositions().get(position)); |
| } |
| }; |
| |
| private MultiChoiceModeListener mMultiChoiceModeListener = new MultiChoiceModeListener() { |
| private boolean mIgnoreItemCheckedStateChanges = false; |
| |
| private void updateSelectedTitle(ActionMode mode) { |
| int count = mGridView.getCheckedItemCount(); |
| mode.setTitle(getResources().getQuantityString( |
| R.plurals.number_of_items_selected, count, count)); |
| } |
| |
| @Override |
| public void onItemCheckedStateChanged(ActionMode mode, int position, long id, |
| boolean checked) { |
| if (mIgnoreItemCheckedStateChanges) return; |
| if (mAdapter.itemAtPositionIsBucket(position)) { |
| SparseBooleanArray checkedItems = mGridView.getCheckedItemPositions(); |
| mIgnoreItemCheckedStateChanges = true; |
| mGridView.setItemChecked(position, false); |
| |
| // Takes advantage of the fact that SectionIndexer imposes the |
| // need to clamp to the valid range |
| int nextSectionStart = mAdapter.getPositionForSection( |
| mAdapter.getSectionForPosition(position) + 1); |
| if (nextSectionStart == position) |
| nextSectionStart = mAdapter.getCount(); |
| |
| boolean rangeValue = false; // Value we want to set all of the bucket items to |
| |
| // Determine if all the items in the bucket are currently checked, so that we |
| // can uncheck them, otherwise we will check all items in the bucket. |
| for (int i = position + 1; i < nextSectionStart; i++) { |
| if (checkedItems.get(i) == false) { |
| rangeValue = true; |
| break; |
| } |
| } |
| |
| // Set all items in the bucket to the desired state |
| for (int i = position + 1; i < nextSectionStart; i++) { |
| if (checkedItems.get(i) != rangeValue) |
| mGridView.setItemChecked(i, rangeValue); |
| } |
| |
| mIgnoreItemCheckedStateChanges = false; |
| } |
| updateSelectedTitle(mode); |
| } |
| |
| @Override |
| public boolean onActionItemClicked(ActionMode mode, MenuItem item) { |
| switch (item.getItemId()) { |
| case R.id.import_items: |
| mHelperService.importSelectedItems( |
| mGridView.getCheckedItemPositions(), |
| mAdapter); |
| mode.finish(); |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| @Override |
| public boolean onCreateActionMode(ActionMode mode, Menu menu) { |
| MenuInflater inflater = mode.getMenuInflater(); |
| inflater.inflate(R.menu.ingest_menu_item_list_selection, menu); |
| updateSelectedTitle(mode); |
| mActiveActionMode = mode; |
| return true; |
| } |
| |
| @Override |
| public void onDestroyActionMode(ActionMode mode) { |
| mActiveActionMode = null; |
| } |
| |
| @Override |
| public boolean onPrepareActionMode(ActionMode mode, Menu menu) { |
| updateSelectedTitle(mode); |
| return false; |
| } |
| }; |
| |
| @Override |
| protected void onDestroy() { |
| super.onDestroy(); |
| doUnbindHelperService(); |
| } |
| |
| @Override |
| protected void onResume() { |
| DateTileView.refreshLocale(); |
| mActive = true; |
| if (mHelperService != null) mHelperService.setClientActivity(this); |
| updateWarningOverlay(); |
| super.onResume(); |
| } |
| |
| @Override |
| protected void onPause() { |
| if (mHelperService != null) mHelperService.setClientActivity(null); |
| mActive = false; |
| cleanupProgressDialog(); |
| super.onPause(); |
| } |
| |
| private void showWarningOverlay(int textResId) { |
| if (mWarningOverlay == null) { |
| mWarningOverlay = findViewById(R.id.ingest_warning_overlay); |
| mWarningOverlayText = |
| (TextView)mWarningOverlay.findViewById(R.id.ingest_warning_overlay_text); |
| } |
| mWarningOverlayText.setText(textResId); |
| mWarningOverlay.setVisibility(View.VISIBLE); |
| mGridView.setVisibility(View.GONE); |
| } |
| |
| private void hideWarningOverlay() { |
| if (mWarningOverlay != null) { |
| mWarningOverlay.setVisibility(View.GONE); |
| mGridView.setVisibility(View.VISIBLE); |
| } |
| } |
| |
| private void updateWarningOverlay() { |
| if (!mAdapter.deviceConnected()) { |
| showWarningOverlay(R.string.ingest_no_device); |
| } else if (mAdapter.indexReady() && mAdapter.getCount() == 0) { |
| showWarningOverlay(R.string.ingest_empty_device); |
| } else { |
| hideWarningOverlay(); |
| } |
| } |
| |
| private void UiThreadNotifyIndexChanged() { |
| mAdapter.notifyDataSetChanged(); |
| if (mActiveActionMode != null) { |
| mActiveActionMode.finish(); |
| mActiveActionMode = null; |
| } |
| updateWarningOverlay(); |
| } |
| |
| protected void notifyIndexChanged() { |
| mHandler.sendEmptyMessage(ItemListHandler.MSG_NOTIFY_CHANGED); |
| } |
| |
| private static class ProgressState { |
| String message; |
| String title; |
| int current; |
| int max; |
| |
| public void reset() { |
| title = null; |
| message = null; |
| current = 0; |
| max = 0; |
| } |
| } |
| |
| private ProgressState mProgressState = new ProgressState(); |
| |
| @Override |
| public void onObjectIndexed(MtpObjectInfo object, int numVisited) { |
| // Not guaranteed to be called on the UI thread |
| mProgressState.reset(); |
| mProgressState.max = 0; |
| mProgressState.message = getResources().getQuantityString( |
| R.plurals.ingest_number_of_items_scanned, numVisited, numVisited); |
| mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_UPDATE); |
| } |
| |
| @Override |
| public void onSorting() { |
| // Not guaranteed to be called on the UI thread |
| mProgressState.reset(); |
| mProgressState.max = 0; |
| mProgressState.message = getResources().getString(R.string.ingest_sorting); |
| mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_UPDATE); |
| } |
| |
| @Override |
| public void onIndexFinish() { |
| // Not guaranteed to be called on the UI thread |
| mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_HIDE); |
| mHandler.sendEmptyMessage(ItemListHandler.MSG_NOTIFY_CHANGED); |
| } |
| |
| @Override |
| public void onImportProgress(final int visitedCount, final int totalCount, |
| String pathIfSuccessful) { |
| // Not guaranteed to be called on the UI thread |
| mProgressState.reset(); |
| mProgressState.max = totalCount; |
| mProgressState.current = visitedCount; |
| mProgressState.title = getResources().getString(R.string.ingest_importing); |
| mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_UPDATE); |
| } |
| |
| @Override |
| public void onImportFinish(Collection<MtpObjectInfo> objectsNotImported) { |
| // Not guaranteed to be called on the UI thread |
| mHandler.sendEmptyMessage(ItemListHandler.MSG_PROGRESS_HIDE); |
| // TODO: maybe show an extra dialog listing the ones that failed |
| // importing, if any? |
| } |
| |
| private ProgressDialog getProgressDialog() { |
| if (mProgressDialog == null || !mProgressDialog.isShowing()) { |
| mProgressDialog = new ProgressDialog(this); |
| mProgressDialog.setCancelable(false); |
| } |
| return mProgressDialog; |
| } |
| |
| private void updateProgressDialog() { |
| ProgressDialog dialog = getProgressDialog(); |
| boolean indeterminate = (mProgressState.max == 0); |
| dialog.setIndeterminate(indeterminate); |
| dialog.setProgressStyle(indeterminate ? ProgressDialog.STYLE_SPINNER |
| : ProgressDialog.STYLE_HORIZONTAL); |
| if (mProgressState.title != null) { |
| dialog.setTitle(mProgressState.title); |
| } |
| if (mProgressState.message != null) { |
| dialog.setMessage(mProgressState.message); |
| } |
| if (!indeterminate) { |
| dialog.setProgress(mProgressState.current); |
| dialog.setMax(mProgressState.max); |
| } |
| if (!dialog.isShowing()) { |
| dialog.show(); |
| } |
| } |
| |
| private void cleanupProgressDialog() { |
| if (mProgressDialog != null) { |
| mProgressDialog.hide(); |
| mProgressDialog = null; |
| } |
| } |
| |
| // This is static and uses a WeakReference in order to avoid leaking the Activity |
| private static class ItemListHandler extends Handler { |
| public static final int MSG_PROGRESS_UPDATE = 0; |
| public static final int MSG_PROGRESS_HIDE = 1; |
| public static final int MSG_NOTIFY_CHANGED = 2; |
| |
| WeakReference<IngestActivity> mParentReference; |
| |
| public ItemListHandler(IngestActivity parent) { |
| super(); |
| mParentReference = new WeakReference<IngestActivity>(parent); |
| } |
| |
| public void handleMessage(Message message) { |
| IngestActivity parent = mParentReference.get(); |
| if (parent == null || !parent.mActive) |
| return; |
| switch (message.what) { |
| case MSG_PROGRESS_HIDE: |
| parent.cleanupProgressDialog(); |
| break; |
| case MSG_PROGRESS_UPDATE: |
| parent.updateProgressDialog(); |
| break; |
| case MSG_NOTIFY_CHANGED: |
| parent.UiThreadNotifyIndexChanged(); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| private ServiceConnection mHelperServiceConnection = new ServiceConnection() { |
| public void onServiceConnected(ComponentName className, IBinder service) { |
| mHelperService = ((IngestService.LocalBinder) service).getService(); |
| mHelperService.setClientActivity(IngestActivity.this); |
| mAdapter.setMtpDeviceIndex(mHelperService.getIndex()); |
| } |
| |
| public void onServiceDisconnected(ComponentName className) { |
| mHelperService = null; |
| } |
| }; |
| |
| private void doBindHelperService() { |
| bindService(new Intent(getApplicationContext(), IngestService.class), |
| mHelperServiceConnection, Context.BIND_AUTO_CREATE); |
| } |
| |
| private void doUnbindHelperService() { |
| if (mHelperService != null) { |
| mHelperService.setClientActivity(null); |
| unbindService(mHelperServiceConnection); |
| } |
| } |
| } |