From: Bobby Georgescu Date: Fri, 1 Feb 2013 20:57:14 +0000 (-0800) Subject: Importer: Full-screen viewing, UI refinement, refactoring X-Git-Tag: android-x86-7.1-r1~1137^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=c8a9e86919dca8938948f97efc3dcbe143e806bf;p=android-x86%2Fpackages-apps-Gallery2.git Importer: Full-screen viewing, UI refinement, refactoring Bug: 7990333 Bug: 8151814 Bug: 8037411 This CL adds or changes the following things: - Full-screen image viewing and UI for switching modes - Moved general functionality from MtpThumbnailTileView to MtpImageView to allow for reuse in full-screen image use-case - MtpBitmapCache moved from ui to data package - Orientation now respected when set in image metadata - Miscellaneous UI consistency issues fixed - Miscellaneous instability issues fixed Change-Id: I5f188b763617b693e32fedc03273d711d604922a --- diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4cb308946..78a6db1a7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -179,6 +179,7 @@ media from attached MTP devices, like cameras and camera phones --> diff --git a/res/layout/ingest_activity_item_list.xml b/res/layout/ingest_activity_item_list.xml index 23a95f658..f0e91e8e2 100644 --- a/res/layout/ingest_activity_item_list.xml +++ b/res/layout/ingest_activity_item_list.xml @@ -14,35 +14,42 @@ limitations under the License. --> - + + + android:visibility="invisible" > + + + + + + + + \ No newline at end of file diff --git a/res/menu/ingest_menu_item_list_selection.xml b/res/menu/ingest_menu_item_list_selection.xml index aaf3262ed..2f020b671 100644 --- a/res/menu/ingest_menu_item_list_selection.xml +++ b/res/menu/ingest_menu_item_list_selection.xml @@ -14,6 +14,8 @@ limitations under the License. --> + diff --git a/res/values/strings.xml b/res/values/strings.xml index d6b981528..fae3466cc 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -503,6 +503,9 @@ Grid view + + Fullscreen view + Trimming diff --git a/src/com/android/gallery3d/ingest/IngestActivity.java b/src/com/android/gallery3d/ingest/IngestActivity.java index 4e603bed3..893f59572 100644 --- a/src/com/android/gallery3d/ingest/IngestActivity.java +++ b/src/com/android/gallery3d/ingest/IngestActivity.java @@ -22,11 +22,14 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.res.Configuration; +import android.database.DataSetObserver; import android.mtp.MtpObjectInfo; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.support.v4.view.ViewPager; import android.util.SparseBooleanArray; import android.view.ActionMode; import android.view.Menu; @@ -36,12 +39,16 @@ 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.CheckBroker; import com.android.gallery3d.ingest.adapter.MtpAdapter; +import com.android.gallery3d.ingest.adapter.MtpPagerAdapter; +import com.android.gallery3d.ingest.data.MtpBitmapFetch; import com.android.gallery3d.ingest.ui.DateTileView; +import com.android.gallery3d.ingest.ui.IngestGridView; +import com.android.gallery3d.ingest.ui.IngestGridView.OnClearChoicesListener; import java.lang.ref.WeakReference; import java.util.Collection; @@ -51,14 +58,22 @@ public class IngestActivity extends Activity implements private IngestService mHelperService; private boolean mActive = false; - private GridView mGridView; + private IngestGridView mGridView; private MtpAdapter mAdapter; private Handler mHandler; private ProgressDialog mProgressDialog; private ActionMode mActiveActionMode; - private View mWarningOverlay; - private TextView mWarningOverlayText; + private View mWarningView; + private TextView mWarningText; + private int mLastCheckedPosition = 0; + + private ViewPager mFullscreenPager; + private MtpPagerAdapter mPagerAdapter; + private boolean mFullscreenPagerVisible = false; + + private MenuItem mMenuSwitcherItem; + private MenuItem mActionMenuSwitcherItem; @Override protected void onCreate(Bundle savedInstanceState) { @@ -66,18 +81,25 @@ public class IngestActivity extends Activity implements doBindHelperService(); setContentView(R.layout.ingest_activity_item_list); - mGridView = (GridView) findViewById(R.id.ingest_gridview); + mGridView = (IngestGridView) findViewById(R.id.ingest_gridview); mAdapter = new MtpAdapter(this); + mAdapter.registerDataSetObserver(mMasterObserver); mGridView.setAdapter(mAdapter); mGridView.setMultiChoiceModeListener(mMultiChoiceModeListener); mGridView.setOnItemClickListener(mOnItemClickListener); + mGridView.setOnClearChoicesListener(mPositionMappingCheckBroker); + + mFullscreenPager = (ViewPager) findViewById(R.id.ingest_view_pager); mHandler = new ItemListHandler(this); + + MtpBitmapFetch.configureForContext(this); } private OnItemClickListener mOnItemClickListener = new OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View itemView, int position, long arg3) { + mLastCheckedPosition = position; mGridView.setItemChecked(position, !mGridView.getCheckedItemPositions().get(position)); } }; @@ -124,23 +146,18 @@ public class IngestActivity extends Activity implements mGridView.setItemChecked(i, rangeValue); } + mPositionMappingCheckBroker.onBulkCheckedChange(); mIgnoreItemCheckedStateChanges = false; + } else { + mPositionMappingCheckBroker.onCheckedChange(position, checked); } + mLastCheckedPosition = position; 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; - } + return onOptionsItemSelected(item); } @Override @@ -149,12 +166,16 @@ public class IngestActivity extends Activity implements inflater.inflate(R.menu.ingest_menu_item_list_selection, menu); updateSelectedTitle(mode); mActiveActionMode = mode; + mActionMenuSwitcherItem = menu.findItem(R.id.ingest_switch_view); + setSwitcherMenuState(mActionMenuSwitcherItem, mFullscreenPagerVisible); return true; } @Override public void onDestroyActionMode(ActionMode mode) { mActiveActionMode = null; + mActionMenuSwitcherItem = null; + mHandler.sendEmptyMessage(ItemListHandler.MSG_BULK_CHECKED_CHANGE); } @Override @@ -164,6 +185,34 @@ public class IngestActivity extends Activity implements } }; + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.import_items: + if (mActiveActionMode != null) { + mHelperService.importSelectedItems( + mGridView.getCheckedItemPositions(), + mAdapter); + mActiveActionMode.finish(); + } + return true; + case R.id.ingest_switch_view: + setFullscreenPagerVisibility(!mFullscreenPagerVisible); + return true; + default: + return false; + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.ingest_menu_item_list_selection, menu); + mMenuSwitcherItem = menu.findItem(R.id.ingest_switch_view); + menu.findItem(R.id.import_items).setVisible(false); + setSwitcherMenuState(mMenuSwitcherItem, mFullscreenPagerVisible); + return true; + } + @Override protected void onDestroy() { super.onDestroy(); @@ -175,7 +224,7 @@ public class IngestActivity extends Activity implements DateTileView.refreshLocale(); mActive = true; if (mHelperService != null) mHelperService.setClientActivity(this); - updateWarningOverlay(); + updateWarningView(); super.onResume(); } @@ -187,31 +236,140 @@ public class IngestActivity extends Activity implements 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); + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + MtpBitmapFetch.configureForContext(this); + } + + private void showWarningView(int textResId) { + if (mWarningView == null) { + mWarningView = findViewById(R.id.ingest_warning_view); + mWarningText = + (TextView)mWarningView.findViewById(R.id.ingest_warning_view_text); } - mWarningOverlayText.setText(textResId); - mWarningOverlay.setVisibility(View.VISIBLE); + mWarningText.setText(textResId); + mWarningView.setVisibility(View.VISIBLE); + setFullscreenPagerVisibility(false); mGridView.setVisibility(View.GONE); } - private void hideWarningOverlay() { - if (mWarningOverlay != null) { - mWarningOverlay.setVisibility(View.GONE); - mGridView.setVisibility(View.VISIBLE); + private void hideWarningView() { + if (mWarningView != null) { + mWarningView.setVisibility(View.GONE); + setFullscreenPagerVisibility(false); } } - private void updateWarningOverlay() { + private PositionMappingCheckBroker mPositionMappingCheckBroker = new PositionMappingCheckBroker(); + + private class PositionMappingCheckBroker extends CheckBroker + implements OnClearChoicesListener { + private int mLastMappingPager = -1; + private int mLastMappingGrid = -1; + + private int mapPagerToGridPosition(int position) { + if (position != mLastMappingPager) { + mLastMappingPager = position; + mLastMappingGrid = mAdapter.translatePositionWithoutLabels(position); + } + return mLastMappingGrid; + } + + private int mapGridToPagerPosition(int position) { + if (position != mLastMappingGrid) { + mLastMappingGrid = position; + mLastMappingPager = mPagerAdapter.translatePositionWithLabels(position); + } + return mLastMappingPager; + } + + @Override + public void setItemChecked(int position, boolean checked) { + mGridView.setItemChecked(mapPagerToGridPosition(position), checked); + } + + @Override + public void onCheckedChange(int position, boolean checked) { + if (mPagerAdapter != null) { + super.onCheckedChange(mapGridToPagerPosition(position), checked); + } + } + + @Override + public boolean isItemChecked(int position) { + return mGridView.getCheckedItemPositions().get(mapPagerToGridPosition(position)); + } + + @Override + public void onClearChoices() { + onBulkCheckedChange(); + } + }; + + private DataSetObserver mMasterObserver = new DataSetObserver() { + @Override + public void onChanged() { + if (mPagerAdapter != null) mPagerAdapter.notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + if (mPagerAdapter != null) mPagerAdapter.notifyDataSetChanged(); + } + }; + + private int pickFullscreenStartingPosition() { + int firstVisiblePosition = mGridView.getFirstVisiblePosition(); + if (mLastCheckedPosition <= firstVisiblePosition + || mLastCheckedPosition > mGridView.getLastVisiblePosition()) { + return firstVisiblePosition; + } else { + return mLastCheckedPosition; + } + } + + private void setSwitcherMenuState(MenuItem menuItem, boolean inFullscreenMode) { + if (menuItem == null) return; + if (!inFullscreenMode) { + menuItem.setIcon(android.R.drawable.ic_menu_zoom); + menuItem.setTitle(R.string.switch_photo_fullscreen); + } else { + menuItem.setIcon(android.R.drawable.ic_dialog_dialer); + menuItem.setTitle(R.string.switch_photo_grid); + } + } + + private void setFullscreenPagerVisibility(boolean visible) { + mFullscreenPagerVisible = visible; + if (visible) { + if (mPagerAdapter == null) { + mPagerAdapter = new MtpPagerAdapter(this, mPositionMappingCheckBroker); + mPagerAdapter.setMtpDeviceIndex(mAdapter.getMtpDeviceIndex()); + } + mFullscreenPager.setAdapter(mPagerAdapter); + mFullscreenPager.setCurrentItem(mPagerAdapter.translatePositionWithLabels( + pickFullscreenStartingPosition()), false); + } else if (mPagerAdapter != null) { + mGridView.setSelection(mAdapter.translatePositionWithoutLabels( + mFullscreenPager.getCurrentItem())); + mFullscreenPager.setAdapter(null); + } + mGridView.setVisibility(visible ? View.INVISIBLE : View.VISIBLE); + mFullscreenPager.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + if (mActionMenuSwitcherItem != null) { + setSwitcherMenuState(mActionMenuSwitcherItem, visible); + } + setSwitcherMenuState(mMenuSwitcherItem, visible); + } + + private void updateWarningView() { if (!mAdapter.deviceConnected()) { - showWarningOverlay(R.string.ingest_no_device); + showWarningView(R.string.ingest_no_device); } else if (mAdapter.indexReady() && mAdapter.getCount() == 0) { - showWarningOverlay(R.string.ingest_empty_device); + showWarningView(R.string.ingest_empty_device); } else { - hideWarningOverlay(); + hideWarningView(); } } @@ -221,7 +379,7 @@ public class IngestActivity extends Activity implements mActiveActionMode.finish(); mActiveActionMode = null; } - updateWarningOverlay(); + updateWarningView(); } protected void notifyIndexChanged() { @@ -330,6 +488,7 @@ public class IngestActivity extends Activity implements public static final int MSG_PROGRESS_UPDATE = 0; public static final int MSG_PROGRESS_HIDE = 1; public static final int MSG_NOTIFY_CHANGED = 2; + public static final int MSG_BULK_CHECKED_CHANGE = 3; WeakReference mParentReference; @@ -352,6 +511,9 @@ public class IngestActivity extends Activity implements case MSG_NOTIFY_CHANGED: parent.UiThreadNotifyIndexChanged(); break; + case MSG_BULK_CHECKED_CHANGE: + parent.mPositionMappingCheckBroker.onBulkCheckedChange(); + break; default: break; } @@ -362,7 +524,9 @@ public class IngestActivity extends Activity implements public void onServiceConnected(ComponentName className, IBinder service) { mHelperService = ((IngestService.LocalBinder) service).getService(); mHelperService.setClientActivity(IngestActivity.this); - mAdapter.setMtpDeviceIndex(mHelperService.getIndex()); + MtpDeviceIndex index = mHelperService.getIndex(); + mAdapter.setMtpDeviceIndex(index); + if (mPagerAdapter != null) mPagerAdapter.setMtpDeviceIndex(index); } public void onServiceDisconnected(ComponentName className) { diff --git a/src/com/android/gallery3d/ingest/IngestService.java b/src/com/android/gallery3d/ingest/IngestService.java index 12b056b60..5e0ca0b68 100644 --- a/src/com/android/gallery3d/ingest/IngestService.java +++ b/src/com/android/gallery3d/ingest/IngestService.java @@ -37,7 +37,7 @@ import android.widget.Adapter; import com.android.gallery3d.R; import com.android.gallery3d.app.NotificationIds; import com.android.gallery3d.data.MtpClient; -import com.android.gallery3d.ingest.ui.MtpBitmapCache; +import com.android.gallery3d.ingest.data.MtpBitmapFetch; import com.android.gallery3d.util.BucketNames; import java.util.ArrayList; @@ -66,6 +66,7 @@ public class IngestService extends Service implements ImportTask.Listener, private boolean mRedeliverImportFinish = false; private Collection mRedeliverObjectsNotImported; private boolean mRedeliverNotifyIndexChanged = false; + private boolean mRedeliverIndexFinish = false; private NotificationManager mNotificationManager; private NotificationCompat.Builder mNotificationBuilder; private long mLastProgressIndexTime = 0; @@ -108,13 +109,19 @@ public class IngestService extends Service implements ImportTask.Listener, mRedeliverImportFinish = false; mRedeliverObjectsNotImported = null; mRedeliverNotifyIndexChanged = false; + mRedeliverIndexFinish = false; mDevice = device; mIndex.setDevice(mDevice); if (mDevice != null) { MtpDeviceInfo deviceInfo = mDevice.getDeviceInfo(); - mDevicePrettyName = deviceInfo.getModel(); - mNotificationBuilder.setContentTitle(mDevicePrettyName); - new Thread(mIndex.getIndexRunnable()).start(); + if (deviceInfo == null) { + setDevice(null); + return; + } else { + mDevicePrettyName = deviceInfo.getModel(); + mNotificationBuilder.setContentTitle(mDevicePrettyName); + new Thread(mIndex.getIndexRunnable()).start(); + } } else { mDevicePrettyName = null; } @@ -144,6 +151,10 @@ public class IngestService extends Service implements ImportTask.Listener, mClientActivity.notifyIndexChanged(); mRedeliverNotifyIndexChanged = false; } + if (mRedeliverIndexFinish) { + mClientActivity.onIndexFinish(); + mRedeliverIndexFinish = false; + } } protected void importSelectedItems(SparseBooleanArray selected, Adapter adapter) { @@ -176,8 +187,8 @@ public class IngestService extends Service implements ImportTask.Listener, public void deviceRemoved(MtpDevice device) { if (device == mDevice) { setDevice(null); + MtpBitmapFetch.onDeviceDisconnected(device); } - MtpBitmapCache.onDeviceDisconnected(device); } @Override @@ -197,6 +208,7 @@ public class IngestService extends Service implements ImportTask.Listener, @Override public void onImportFinish(Collection objectsNotImported) { + stopForeground(true); if (mClientActivity != null) { mClientActivity.onImportFinish(objectsNotImported); } else { @@ -207,7 +219,6 @@ public class IngestService extends Service implements ImportTask.Listener, mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING, mNotificationBuilder.build()); } - stopForeground(mClientActivity != null); } @Override @@ -241,6 +252,7 @@ public class IngestService extends Service implements ImportTask.Listener, .setContentText(getResources().getText(R.string.ingest_scanning_done)); mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING, mNotificationBuilder.build()); + mRedeliverIndexFinish = true; } } diff --git a/src/com/android/gallery3d/ingest/MtpDeviceIndex.java b/src/com/android/gallery3d/ingest/MtpDeviceIndex.java index 28e115ab0..e873dd1ca 100644 --- a/src/com/android/gallery3d/ingest/MtpDeviceIndex.java +++ b/src/com/android/gallery3d/ingest/MtpDeviceIndex.java @@ -180,55 +180,90 @@ public class MtpDeviceIndex { * order */ public Object get(int position, SortOrder order) { + if (mProgress != Progress.Finished) return null; if(order == SortOrder.Ascending) { - return getAscending(position); + DateBucket bucket = mBuckets[mUnifiedLookupIndex[position]]; + if (bucket.unifiedStartIndex == position) { + return bucket.bucket; + } else { + return mMtpObjects[bucket.itemsStartIndex + position - 1 + - bucket.unifiedStartIndex]; + } } else { - return getDescending(position); + int zeroIndex = mUnifiedLookupIndex.length - 1 - position; + DateBucket bucket = mBuckets[mUnifiedLookupIndex[zeroIndex]]; + if (bucket.unifiedEndIndex == zeroIndex) { + return bucket.bucket; + } else { + return mMtpObjects[bucket.itemsStartIndex + zeroIndex + - bucket.unifiedStartIndex]; + } } } /** - * @param position Index of item to fetch, where 0 is the first item in - * ascending order - * @return position-th item in ascending order + * @param position Index of item to fetch from a view of the data that doesn't + * include labels and is in the specified order + * @return position-th item in specified order, when not including labels */ - public Object getAscending(int position) { + public MtpObjectInfo getWithoutLabels(int position, SortOrder order) { if (mProgress != Progress.Finished) return null; - DateBucket bucket = mBuckets[mUnifiedLookupIndex[position]]; - if (bucket.unifiedStartIndex == position) { - return bucket.bucket; + if (order == SortOrder.Ascending) { + return mMtpObjects[position]; } else { - return bucket.get(position - 1 - bucket.unifiedStartIndex); + return mMtpObjects[mMtpObjects.length - 1 - position]; } } /** - * @param position Index of item to fetch, where 0 is the last item in - * ascending order - * @return position-th item in descending order + * Although this is O(log(number of buckets)), and thus should not be used + * in hotspots, even if the attached device has items for every day for + * a five-year timeframe, it would still only take 11 iterations at most, + * so shouldn't be a huge issue. + * @param position Index of item to map from a view of the data that doesn't + * include labels and is in the specified order + * @param order + * @return position in a view of the data that does include labels */ - public Object getDescending(int position) { - if (mProgress != Progress.Finished) return null; - int zeroIndex = mUnifiedLookupIndex.length - 1 - position; - DateBucket bucket = mBuckets[mUnifiedLookupIndex[zeroIndex]]; - if (bucket.unifiedEndIndex == zeroIndex) { - return bucket.bucket; - } else { - return bucket.get(zeroIndex - bucket.unifiedStartIndex); + public int getPositionFromPositionWithoutLabels(int position, SortOrder order) { + if (mProgress != Progress.Finished) return -1; + if (order == SortOrder.Descending) { + position = mMtpObjects.length - 1 - position; + } + int bucketNumber = 0; + int iMin = 0; + int iMax = mBuckets.length - 1; + while (iMax >= iMin) { + int iMid = (iMax + iMin) / 2; + if (mBuckets[iMid].itemsStartIndex + mBuckets[iMid].numItems <= position) { + iMin = iMid + 1; + } else if (mBuckets[iMid].itemsStartIndex > position) { + iMax = iMid - 1; + } else { + bucketNumber = iMid; + break; + } } + int mappedPos = mBuckets[bucketNumber].unifiedStartIndex + + position - mBuckets[bucketNumber].itemsStartIndex; + if (order == SortOrder.Descending) { + mappedPos = mUnifiedLookupIndex.length - 1 - mappedPos; + } + return mappedPos; } - /** - * @param position Index of item to fetch from a view of the data that doesn't - * include labels and is in ascending order - * @return position-th item in ascending order, when not including labels - */ - public MtpObjectInfo getWithoutLabels(int position, SortOrder order) { - if (mProgress != Progress.Finished) return null; - if (order == SortOrder.Ascending) { - return mMtpObjects[position]; + public int getPositionWithoutLabelsFromPosition(int position, SortOrder order) { + if (mProgress != Progress.Finished) return -1; + if(order == SortOrder.Ascending) { + DateBucket bucket = mBuckets[mUnifiedLookupIndex[position]]; + if (bucket.unifiedStartIndex == position) position++; + return bucket.itemsStartIndex + position - 1 - bucket.unifiedStartIndex; } else { - return mMtpObjects[mMtpObjects.length - 1 - position]; + int zeroIndex = mUnifiedLookupIndex.length - 1 - position; + DateBucket bucket = mBuckets[mUnifiedLookupIndex[zeroIndex]]; + if (bucket.unifiedEndIndex == zeroIndex) zeroIndex--; + return mMtpObjects.length - 1 - bucket.itemsStartIndex + - zeroIndex + bucket.unifiedStartIndex; } } @@ -288,6 +323,7 @@ public class MtpDeviceIndex { int unifiedStartIndex; int unifiedEndIndex; int itemsStartIndex; + int numItems; public DateBucket(SimpleDate bucket) { this.bucket = bucket; @@ -302,10 +338,6 @@ public class MtpDeviceIndex { Collections.sort(tempElementsList, comparator); } - public MtpObjectInfo get(int position) { - return mMtpObjects[itemsStartIndex + position]; - } - @Override public String toString() { return bucket.toString(); @@ -413,7 +445,8 @@ public class MtpDeviceIndex { currentUnifiedIndexEntry = nextUnifiedEntry; bucket.itemsStartIndex = currentItemsEntry; - for (int j = 0; j < bucket.tempElementsList.size(); j++) { + bucket.numItems = bucket.tempElementsList.size(); + for (int j = 0; j < bucket.numItems; j++) { mMtpObjects[currentItemsEntry] = bucket.tempElementsList.get(j); currentItemsEntry++; } diff --git a/src/com/android/gallery3d/ingest/adapter/CheckBroker.java b/src/com/android/gallery3d/ingest/adapter/CheckBroker.java new file mode 100644 index 000000000..6783f23c5 --- /dev/null +++ b/src/com/android/gallery3d/ingest/adapter/CheckBroker.java @@ -0,0 +1,56 @@ +/* + * 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.adapter; + +import java.util.ArrayList; +import java.util.Collection; + +public abstract class CheckBroker { + private Collection mListeners = + new ArrayList(); + + public interface OnCheckedChangedListener { + public void onCheckedChanged(int position, boolean isChecked); + public void onBulkCheckedChanged(); + } + + public abstract void setItemChecked(int position, boolean checked); + + public void onCheckedChange(int position, boolean checked) { + if (isItemChecked(position) != checked) { + for (OnCheckedChangedListener l : mListeners) { + l.onCheckedChanged(position, checked); + } + } + } + + public void onBulkCheckedChange() { + for (OnCheckedChangedListener l : mListeners) { + l.onBulkCheckedChanged(); + } + } + + public abstract boolean isItemChecked(int position); + + public void registerOnCheckedChangeListener(OnCheckedChangedListener l) { + mListeners.add(l); + } + + public void unregisterOnCheckedChangeListener(OnCheckedChangedListener l) { + mListeners.remove(l); + } +} diff --git a/src/com/android/gallery3d/ingest/adapter/MtpAdapter.java b/src/com/android/gallery3d/ingest/adapter/MtpAdapter.java index 611d880db..e8dd69f8c 100644 --- a/src/com/android/gallery3d/ingest/adapter/MtpAdapter.java +++ b/src/com/android/gallery3d/ingest/adapter/MtpAdapter.java @@ -45,8 +45,7 @@ public class MtpAdapter extends BaseAdapter implements SectionIndexer { public MtpAdapter(Activity context) { super(); mContext = context; - mInflater = (LayoutInflater)context.getSystemService - (Context.LAYOUT_INFLATER_SERVICE); + mInflater = LayoutInflater.from(context); } public void setMtpDeviceIndex(MtpDeviceIndex index) { @@ -54,6 +53,10 @@ public class MtpAdapter extends BaseAdapter implements SectionIndexer { notifyDataSetChanged(); } + public MtpDeviceIndex getMtpDeviceIndex() { + return mModel; + } + @Override public void notifyDataSetChanged() { mGeneration++; @@ -177,4 +180,13 @@ public class MtpAdapter extends BaseAdapter implements SectionIndexer { public Object[] getSections() { return getCount() > 0 ? mModel.getBuckets(mSortOrder) : null; } + + public SortOrder getSortOrder() { + return mSortOrder; + } + + public int translatePositionWithoutLabels(int position) { + if (mModel == null) return -1; + return mModel.getPositionFromPositionWithoutLabels(position, mSortOrder); + } } diff --git a/src/com/android/gallery3d/ingest/adapter/MtpPagerAdapter.java b/src/com/android/gallery3d/ingest/adapter/MtpPagerAdapter.java new file mode 100644 index 000000000..9e7abc01d --- /dev/null +++ b/src/com/android/gallery3d/ingest/adapter/MtpPagerAdapter.java @@ -0,0 +1,102 @@ +/* + * 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.adapter; + +import android.content.Context; +import android.mtp.MtpObjectInfo; +import android.support.v4.view.PagerAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.gallery3d.R; +import com.android.gallery3d.ingest.MtpDeviceIndex; +import com.android.gallery3d.ingest.MtpDeviceIndex.SortOrder; +import com.android.gallery3d.ingest.ui.MtpFullscreenView; + +public class MtpPagerAdapter extends PagerAdapter { + + private LayoutInflater mInflater; + private int mGeneration = 0; + private CheckBroker mBroker; + private MtpDeviceIndex mModel; + private SortOrder mSortOrder = SortOrder.Descending; + + private MtpFullscreenView mReusableView = null; + + public MtpPagerAdapter(Context context, CheckBroker broker) { + super(); + mInflater = LayoutInflater.from(context); + mBroker = broker; + } + + public void setMtpDeviceIndex(MtpDeviceIndex index) { + mModel = index; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mModel != null ? mModel.sizeWithoutLabels() : 0; + } + + @Override + public void notifyDataSetChanged() { + mGeneration++; + super.notifyDataSetChanged(); + } + + public int translatePositionWithLabels(int position) { + if (mModel == null) return -1; + return mModel.getPositionWithoutLabelsFromPosition(position, mSortOrder); + } + + @Override + public void finishUpdate(ViewGroup container) { + mReusableView = null; + super.finishUpdate(container); + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + MtpFullscreenView v = (MtpFullscreenView)object; + container.removeView(v); + mBroker.unregisterOnCheckedChangeListener(v); + mReusableView = v; + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + MtpFullscreenView v; + if (mReusableView != null) { + v = mReusableView; + mReusableView = null; + } else { + v = (MtpFullscreenView) mInflater.inflate(R.layout.ingest_fullsize, container, false); + } + MtpObjectInfo i = mModel.getWithoutLabels(position, mSortOrder); + v.getImageView().setMtpDeviceAndObjectInfo(mModel.getDevice(), i, mGeneration); + v.setPositionAndBroker(position, mBroker); + container.addView(v); + return v; + } +} diff --git a/src/com/android/gallery3d/ingest/data/BitmapWithMetadata.java b/src/com/android/gallery3d/ingest/data/BitmapWithMetadata.java new file mode 100644 index 000000000..bbc90f670 --- /dev/null +++ b/src/com/android/gallery3d/ingest/data/BitmapWithMetadata.java @@ -0,0 +1,29 @@ +/* + * 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.data; + +import android.graphics.Bitmap; + +public class BitmapWithMetadata { + public Bitmap bitmap; + public int rotationDegrees; + + public BitmapWithMetadata(Bitmap bitmap, int rotationDegrees) { + this.bitmap = bitmap; + this.rotationDegrees = rotationDegrees; + } +} diff --git a/src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java b/src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java new file mode 100644 index 000000000..88645e8d0 --- /dev/null +++ b/src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java @@ -0,0 +1,97 @@ +/* + * 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.data; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.mtp.MtpDevice; +import android.mtp.MtpObjectInfo; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import com.android.camera.Exif; +import com.android.gallery3d.common.Utils; +import com.android.gallery3d.data.BitmapPool; + +import java.util.ArrayList; + +public class MtpBitmapFetch { + private static final int BITMAP_POOL_SIZE = 32; + private static BitmapPool sThumbnailPool = new BitmapPool(BITMAP_POOL_SIZE); + private static int sMaxSize = 0; + + public static void recycleThumbnail(Bitmap b) { + if (b != null) { + sThumbnailPool.recycle(b); + } + } + + public static Bitmap getThumbnail(MtpDevice device, MtpObjectInfo info) { + byte[] imageBytes = device.getThumbnail(info.getObjectHandle()); + BitmapFactory.Options o = new BitmapFactory.Options(); + o.inJustDecodeBounds = true; + BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o); + if (o.outWidth == 0 || o.outHeight == 0) return null; + o.inBitmap = sThumbnailPool.getBitmap(o.outWidth, o.outHeight); + o.inMutable = true; + o.inJustDecodeBounds = false; + o.inSampleSize = 1; + return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o); + } + + public static BitmapWithMetadata getFullsize(MtpDevice device, MtpObjectInfo info) { + return getFullsize(device, info, sMaxSize); + } + + public static BitmapWithMetadata getFullsize(MtpDevice device, MtpObjectInfo info, int maxSide) { + byte[] imageBytes = device.getObject(info.getObjectHandle(), info.getCompressedSize()); + Bitmap created; + if (maxSide > 0) { + BitmapFactory.Options o = new BitmapFactory.Options(); + o.inJustDecodeBounds = true; + BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o); + int w = o.outWidth; + int h = o.outHeight; + int comp = Math.max(h, w); + int sampleSize = 1; + while ((comp >> 1) >= maxSide) { + comp = comp >> 1; + sampleSize++; + } + o.inSampleSize = sampleSize; + o.inJustDecodeBounds = false; + created = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o); + } else { + created = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); + } + if (created == null) return null; + + return new BitmapWithMetadata(created, Exif.getOrientation(imageBytes)); + } + + public static void onDeviceDisconnected(MtpDevice device) { + sThumbnailPool.clear(); + } + + public static void configureForContext(Context context) { + DisplayMetrics metrics = new DisplayMetrics(); + WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); + wm.getDefaultDisplay().getMetrics(metrics); + sMaxSize = Math.max(metrics.heightPixels, metrics.widthPixels); + } +} diff --git a/src/com/android/gallery3d/ingest/ui/IngestGridView.java b/src/com/android/gallery3d/ingest/ui/IngestGridView.java new file mode 100644 index 000000000..c821259fe --- /dev/null +++ b/src/com/android/gallery3d/ingest/ui/IngestGridView.java @@ -0,0 +1,58 @@ +/* + * 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.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.GridView; + +/** + * This just extends GridView with the ability to listen for calls + * to clearChoices() + */ +public class IngestGridView extends GridView { + + public interface OnClearChoicesListener { + public void onClearChoices(); + } + + private OnClearChoicesListener mOnClearChoicesListener = null; + + public IngestGridView(Context context) { + super(context); + } + + public IngestGridView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public IngestGridView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setOnClearChoicesListener(OnClearChoicesListener l) { + mOnClearChoicesListener = l; + } + + @Override + public void clearChoices() { + super.clearChoices(); + if (mOnClearChoicesListener != null) { + mOnClearChoicesListener.onClearChoices(); + } + } +} diff --git a/src/com/android/gallery3d/ingest/ui/MtpBitmapCache.java b/src/com/android/gallery3d/ingest/ui/MtpBitmapCache.java deleted file mode 100644 index 307531d5b..000000000 --- a/src/com/android/gallery3d/ingest/ui/MtpBitmapCache.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.ui; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.mtp.MtpDevice; -import android.util.LruCache; - -public class MtpBitmapCache extends LruCache { - private static final int PER_DEVICE_CACHE_MAX_BYTES = 4194304; - private static MtpBitmapCache sInstance; - - public synchronized static MtpBitmapCache getInstanceForDevice(MtpDevice device) { - if (sInstance == null || sInstance.mDevice != device) { - sInstance = new MtpBitmapCache(PER_DEVICE_CACHE_MAX_BYTES, device); - } - return sInstance; - } - - public synchronized static void onDeviceDisconnected(MtpDevice device) { - if (sInstance != null && sInstance.mDevice == device) { - synchronized (sInstance) { - sInstance.mDevice = null; - } - sInstance = null; - } - } - - private MtpDevice mDevice; - - private MtpBitmapCache(int maxSize, MtpDevice device) { - super(maxSize); - mDevice = device; - } - - @Override - protected int sizeOf(Integer key, Bitmap value) { - return value.getByteCount(); - } - - public Bitmap getOrCreate(Integer key) { - Bitmap b = get(key); - return b == null ? createAndInsert(key) : b; - } - - private Bitmap createAndInsert(Integer key) { - MtpDevice device; - synchronized (this) { - device = mDevice; - } - if (device == null) return null; - byte[] imageBytes = device.getThumbnail(key); - if (imageBytes == null) return null; - Bitmap created = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); - put(key, created); - return created; - } -} diff --git a/src/com/android/gallery3d/ingest/ui/MtpFullscreenView.java b/src/com/android/gallery3d/ingest/ui/MtpFullscreenView.java new file mode 100644 index 000000000..8d3884dc6 --- /dev/null +++ b/src/com/android/gallery3d/ingest/ui/MtpFullscreenView.java @@ -0,0 +1,115 @@ +/* + * 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.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.CheckBox; +import android.widget.Checkable; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.RelativeLayout; + +import com.android.gallery3d.R; +import com.android.gallery3d.ingest.adapter.CheckBroker; + +public class MtpFullscreenView extends RelativeLayout implements Checkable, + CompoundButton.OnCheckedChangeListener, CheckBroker.OnCheckedChangedListener { + + private MtpImageView mImageView; + private CheckBox mCheckbox; + private int mPosition = -1; + private CheckBroker mBroker; + + public MtpFullscreenView(Context context) { + super(context); + } + + public MtpFullscreenView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public MtpFullscreenView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mImageView = (MtpImageView) findViewById(R.id.ingest_fullsize_image); + mCheckbox = (CheckBox) findViewById(R.id.ingest_fullsize_image_checkbox); + mCheckbox.setOnCheckedChangeListener(this); + } + + @Override + public boolean isChecked() { + return mCheckbox.isChecked(); + } + + @Override + public void setChecked(boolean checked) { + mCheckbox.setChecked(checked); + } + + @Override + public void toggle() { + mCheckbox.toggle(); + } + + @Override + public void onDetachedFromWindow() { + setPositionAndBroker(-1, null); + super.onDetachedFromWindow(); + } + + public MtpImageView getImageView() { + return mImageView; + } + + public int getPosition() { + return mPosition; + } + + public void setPositionAndBroker(int position, CheckBroker b) { + if (mBroker != null) { + mBroker.unregisterOnCheckedChangeListener(this); + } + mPosition = position; + mBroker = b; + if (mBroker != null) { + setChecked(mBroker.isItemChecked(position)); + mBroker.registerOnCheckedChangeListener(this); + } + } + + @Override + public void onCheckedChanged(CompoundButton arg0, boolean isChecked) { + if (mBroker != null) mBroker.setItemChecked(mPosition, isChecked); + } + + @Override + public void onCheckedChanged(int position, boolean isChecked) { + if (position == mPosition) { + setChecked(isChecked); + } + } + + @Override + public void onBulkCheckedChanged() { + if(mBroker != null) setChecked(mBroker.isItemChecked(mPosition)); + } +} diff --git a/src/com/android/gallery3d/ingest/ui/MtpImageView.java b/src/com/android/gallery3d/ingest/ui/MtpImageView.java new file mode 100644 index 000000000..9c197851e --- /dev/null +++ b/src/com/android/gallery3d/ingest/ui/MtpImageView.java @@ -0,0 +1,130 @@ +/* + * 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.ui; + +import android.content.Context; +import android.mtp.MtpDevice; +import android.mtp.MtpObjectInfo; +import android.os.AsyncTask; +import android.util.AttributeSet; +import android.widget.ImageView; + +import com.android.gallery3d.ingest.data.BitmapWithMetadata; +import com.android.gallery3d.ingest.data.MtpBitmapFetch; + +public class MtpImageView extends ImageView { + private static final int FADE_IN_TIME_MS = 80; + + private int mObjectHandle; + private int mGeneration; + + private void init() { + showPlaceholder(); + } + + public MtpImageView(Context context) { + super(context); + init(); + } + + public MtpImageView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public MtpImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void showPlaceholder() { + setImageResource(android.R.color.transparent); + } + + private LoadMtpImageTask mTask; + + public void setMtpDeviceAndObjectInfo(MtpDevice device, MtpObjectInfo object, int gen) { + int handle = object.getObjectHandle(); + if (handle == mObjectHandle && gen == mGeneration) { + return; + } + cancelLoadingAndClear(); + showPlaceholder(); + mGeneration = gen; + mObjectHandle = handle; + mTask = new LoadMtpImageTask(device); + mTask.execute(object); + } + + protected Object fetchMtpImageDataFromDevice(MtpDevice device, MtpObjectInfo info) { + return MtpBitmapFetch.getFullsize(device, info); + } + + protected void onMtpImageDataFetchedFromDevice(Object result) { + BitmapWithMetadata bitmapWithMetadata = (BitmapWithMetadata)result; + setImageBitmap(bitmapWithMetadata.bitmap); + setRotation(bitmapWithMetadata.rotationDegrees); + } + + private class LoadMtpImageTask extends AsyncTask { + private MtpDevice mDevice; + + public LoadMtpImageTask(MtpDevice device) { + mDevice = device; + } + + @Override + protected Object doInBackground(MtpObjectInfo... args) { + Object result = null; + if (!isCancelled()) { + result = fetchMtpImageDataFromDevice(mDevice, args[0]); + } + mDevice = null; + return result; + } + + @Override + protected void onPostExecute(Object result) { + if (isCancelled() || result == null) { + return; + } + setAlpha(0f); + onMtpImageDataFetchedFromDevice(result); + animate().alpha(1f).setDuration(FADE_IN_TIME_MS); + } + + @Override + protected void onCancelled() { + } + } + + protected void cancelLoadingAndClear() { + if (mTask != null) { + mTask.cancel(true); + } + mTask = null; + animate().cancel(); + setImageResource(android.R.color.transparent); + setRotation(0); + } + + @Override + public void onDetachedFromWindow() { + cancelLoadingAndClear(); + super.onDetachedFromWindow(); + } +} diff --git a/src/com/android/gallery3d/ingest/ui/MtpThumbnailTileView.java b/src/com/android/gallery3d/ingest/ui/MtpThumbnailTileView.java index 2aeda73db..3307e78aa 100644 --- a/src/com/android/gallery3d/ingest/ui/MtpThumbnailTileView.java +++ b/src/com/android/gallery3d/ingest/ui/MtpThumbnailTileView.java @@ -22,26 +22,22 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.mtp.MtpDevice; import android.mtp.MtpObjectInfo; -import android.os.AsyncTask; import android.util.AttributeSet; -import android.view.View; import android.widget.Checkable; -import android.widget.ImageView; import com.android.gallery3d.R; +import com.android.gallery3d.ingest.data.MtpBitmapFetch; -public class MtpThumbnailTileView extends ImageView implements Checkable { - private static final int FADE_IN_TIME_MS = 80; + +public class MtpThumbnailTileView extends MtpImageView implements Checkable { private Paint mForegroundPaint; private boolean mIsChecked; - private int mObjectHandle; - private int mGeneration; + private Bitmap mBitmap; private void init() { mForegroundPaint = new Paint(); mForegroundPaint.setColor(getResources().getColor(R.color.ingest_highlight_semitransparent)); - showPlaceholder(); } public MtpThumbnailTileView(Context context) { @@ -66,9 +62,20 @@ public class MtpThumbnailTileView extends ImageView implements Checkable { } @Override + protected Object fetchMtpImageDataFromDevice(MtpDevice device, MtpObjectInfo info) { + return MtpBitmapFetch.getThumbnail(device, info); + } + + @Override + protected void onMtpImageDataFetchedFromDevice(Object result) { + mBitmap = (Bitmap)result; + setImageBitmap(mBitmap); + } + + @Override public void draw(Canvas canvas) { super.draw(canvas); - if (mIsChecked) { + if (isChecked()) { canvas.drawRect(canvas.getClipBounds(), mForegroundPaint); } } @@ -88,65 +95,12 @@ public class MtpThumbnailTileView extends ImageView implements Checkable { setChecked(!mIsChecked); } - private void showPlaceholder() { - setAlpha(0f); - } - - private LoadThumbnailTask mTask; - - public void setMtpDeviceAndObjectInfo(MtpDevice device, MtpObjectInfo object, int gen) { - int handle = object.getObjectHandle(); - if (handle == mObjectHandle && gen == mGeneration) { - return; - } - animate().cancel(); - if (mTask != null) { - mTask.cancel(true); - } - mGeneration = gen; - mObjectHandle = handle; - Bitmap thumbnail = MtpBitmapCache.getInstanceForDevice(device) - .get(handle); - if (thumbnail != null) { - setAlpha(1f); - setImageBitmap(thumbnail); - } else { - showPlaceholder(); - mTask = new LoadThumbnailTask(device); - mTask.execute(object); - } - } - - private class LoadThumbnailTask extends AsyncTask { - private MtpDevice mDevice; - - public LoadThumbnailTask(MtpDevice device) { - mDevice = device; - } - - @Override - protected Bitmap doInBackground(MtpObjectInfo... args) { - Bitmap result = null; - if (!isCancelled()) { - result = MtpBitmapCache.getInstanceForDevice(mDevice).getOrCreate( - args[0].getObjectHandle()); - } - mDevice = null; - return result; - } - - @Override - protected void onPostExecute(Bitmap result) { - if (isCancelled() || result == null) { - return; - } - setAlpha(0f); - setImageBitmap(result); - animate().alpha(1f).setDuration(FADE_IN_TIME_MS); - } - - @Override - protected void onCancelled() { + @Override + protected void cancelLoadingAndClear() { + super.cancelLoadingAndClear(); + if (mBitmap != null) { + MtpBitmapFetch.recycleThumbnail(mBitmap); + mBitmap = null; } } }