From e2bc54a09412790da5ac7d19c52b964e49e14c25 Mon Sep 17 00:00:00 2001 From: Bobby Georgescu Date: Fri, 8 Mar 2013 20:15:15 -0800 Subject: Sharing support for albums, bulk operations infrastructure Change-Id: I590f60ab85ffbd9cf9d7cd9982627604fa1b427f --- src/com/android/photos/AlbumSetFragment.java | 126 +++++++------- src/com/android/photos/GalleryActivity.java | 20 +-- src/com/android/photos/MultiChoiceManager.java | 190 +++++++++++++++++++++ src/com/android/photos/PhotoSetFragment.java | 139 +++++---------- src/com/android/photos/SelectionManager.java | 54 ++++-- .../photos/adapters/AlbumSetCursorAdapter.java | 89 ++++++++++ src/com/android/photos/data/AlbumSetLoader.java | 7 +- src/com/android/photos/data/PhotoSetLoader.java | 17 ++ src/com/android/photos/shims/LoaderCompatShim.java | 5 + src/com/android/photos/shims/MediaItemsLoader.java | 37 +++- src/com/android/photos/shims/MediaSetLoader.java | 48 +++++- 11 files changed, 534 insertions(+), 198 deletions(-) create mode 100644 src/com/android/photos/MultiChoiceManager.java create mode 100644 src/com/android/photos/adapters/AlbumSetCursorAdapter.java (limited to 'src') diff --git a/src/com/android/photos/AlbumSetFragment.java b/src/com/android/photos/AlbumSetFragment.java index 0d4fcc023..d0bc81fd6 100644 --- a/src/com/android/photos/AlbumSetFragment.java +++ b/src/com/android/photos/AlbumSetFragment.java @@ -21,42 +21,54 @@ import android.app.LoaderManager.LoaderCallbacks; import android.content.Context; import android.content.Loader; import android.database.Cursor; -import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Bundle; -import android.text.format.DateFormat; +import android.provider.MediaStore.Files.FileColumns; +import android.util.SparseBooleanArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; -import android.widget.CursorAdapter; import android.widget.GridView; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; import android.widget.Toast; import com.android.gallery3d.R; +import com.android.photos.adapters.AlbumSetCursorAdapter; import com.android.photos.data.AlbumSetLoader; import com.android.photos.shims.LoaderCompatShim; import com.android.photos.shims.MediaSetLoader; -import java.util.Date; +import java.util.ArrayList; public class AlbumSetFragment extends Fragment implements OnItemClickListener, - LoaderCallbacks { + LoaderCallbacks, MultiChoiceManager.Delegate, SelectionManager.Client { private GridView mAlbumSetView; private View mEmptyView; private AlbumSetCursorAdapter mAdapter; + private LoaderCompatShim mLoaderCompatShim; + private MultiChoiceManager mMultiChoiceManager; + private SelectionManager mSelectionManager; private static final int LOADER_ALBUMSET = 1; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mAdapter = new AlbumSetCursorAdapter(getActivity()); + Context context = getActivity(); + mAdapter = new AlbumSetCursorAdapter(context); + mMultiChoiceManager = new MultiChoiceManager(context, this); + mMultiChoiceManager.setSelectionManager(mSelectionManager); + } + + @Override + public void setSelectionManager(SelectionManager manager) { + mSelectionManager = manager; + if (mMultiChoiceManager != null) { + mMultiChoiceManager.setSelectionManager(manager); + } } @Override @@ -67,6 +79,8 @@ public class AlbumSetFragment extends Fragment implements OnItemClickListener, mEmptyView = root.findViewById(android.R.id.empty); mEmptyView.setVisibility(View.GONE); mAlbumSetView.setAdapter(mAdapter); + mAlbumSetView.setChoiceMode(GridView.CHOICE_MODE_MULTIPLE_MODAL); + mAlbumSetView.setMultiChoiceModeListener(mMultiChoiceManager); mAlbumSetView.setOnItemClickListener(this); getLoaderManager().initLoader(LOADER_ALBUMSET, null, this); updateEmptyStatus(); @@ -78,6 +92,7 @@ public class AlbumSetFragment extends Fragment implements OnItemClickListener, // TODO: Switch to AlbumSetLoader MediaSetLoader loader = new MediaSetLoader(getActivity()); mAdapter.setDrawableFactory(loader); + mLoaderCompatShim = loader; return loader; } @@ -100,63 +115,54 @@ public class AlbumSetFragment extends Fragment implements OnItemClickListener, @Override public void onItemClick(AdapterView av, View v, int pos, long id) { - Cursor c = (Cursor) av.getItemAtPosition(pos); - int albumId = c.getInt(AlbumSetLoader.INDEX_ID); - // TODO launch an activity showing the photos in the album - Toast.makeText(v.getContext(), "Clicked " + albumId, Toast.LENGTH_SHORT).show(); + if (mLoaderCompatShim == null) { + // Not fully initialized yet, discard + return; + } + Cursor item = (Cursor) mAdapter.getItem(pos); + Toast.makeText(v.getContext(), + "Tapped " + item.getInt(AlbumSetLoader.INDEX_ID), + Toast.LENGTH_SHORT).show(); } - private static class AlbumSetCursorAdapter extends CursorAdapter { + @Override + public int getItemMediaType(Object item) { + return FileColumns.MEDIA_TYPE_NONE; + } - private LoaderCompatShim mDrawableFactory; + @Override + public int getItemSupportedOperations(Object item) { + return ((Cursor) item).getInt(AlbumSetLoader.INDEX_SUPPORTED_OPERATIONS); + } - public void setDrawableFactory(LoaderCompatShim factory) { - mDrawableFactory = factory; - } - private Date mDate = new Date(); // Used for converting timestamps for display + @Override + public Object getItemAtPosition(int position) { + return mAdapter.getItem(position); + } - public AlbumSetCursorAdapter(Context context) { - super(context, null, false); - } + @Override + public ArrayList getSubItemUrisForItem(Object item) { + return mLoaderCompatShim.urisForSubItems((Cursor) item); + } - @Override - public void bindView(View v, Context context, Cursor cursor) { - TextView titleTextView = (TextView) v.findViewById( - R.id.album_set_item_title); - titleTextView.setText(cursor.getString(AlbumSetLoader.INDEX_TITLE)); - - TextView dateTextView = (TextView) v.findViewById( - R.id.album_set_item_date); - long timestamp = cursor.getLong(AlbumSetLoader.INDEX_TIMESTAMP); - if (timestamp > 0) { - mDate.setTime(timestamp); - dateTextView.setText(DateFormat.getMediumDateFormat(context).format(mDate)); - } else { - dateTextView.setText(null); - } - - ProgressBar uploadProgressBar = (ProgressBar) v.findViewById( - R.id.album_set_item_upload_progress); - if (cursor.getInt(AlbumSetLoader.INDEX_COUNT_PENDING_UPLOAD) > 0) { - uploadProgressBar.setVisibility(View.VISIBLE); - uploadProgressBar.setProgress(50); - } else { - uploadProgressBar.setVisibility(View.INVISIBLE); - } - - ImageView thumbImageView = (ImageView) v.findViewById( - R.id.album_set_item_image); - Drawable recycle = thumbImageView.getDrawable(); - Drawable drawable = mDrawableFactory.drawableForItem(cursor, recycle); - if (recycle != drawable) { - thumbImageView.setImageDrawable(drawable); - } - } + @Override + public Object getPathForItemAtPosition(int position) { + return mLoaderCompatShim.getPathForItem((Cursor) mAdapter.getItem(position)); + } - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return LayoutInflater.from(context).inflate( - R.layout.album_set_item, parent, false); - } + @Override + public void deleteItemWithPath(Object itemPath) { + mLoaderCompatShim.deleteItemWithPath(itemPath); } + + @Override + public SparseBooleanArray getSelectedItemPositions() { + return mAlbumSetView.getCheckedItemPositions(); + } + + @Override + public int getSelectedItemCount() { + return mAlbumSetView.getCheckedItemCount(); + } + } diff --git a/src/com/android/photos/GalleryActivity.java b/src/com/android/photos/GalleryActivity.java index 420fc3d51..ddf04e365 100644 --- a/src/com/android/photos/GalleryActivity.java +++ b/src/com/android/photos/GalleryActivity.java @@ -43,7 +43,7 @@ public class GalleryActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - + mSelectionManager = new SelectionManager(this); mViewPager = new ViewPager(this); mViewPager.setId(R.id.viewpager); setContentView(mViewPager); @@ -70,13 +70,6 @@ public class GalleryActivity extends Activity { outState.putInt("tab", getActionBar().getSelectedNavigationIndex()); } - protected SelectionManager getSelectionManager() { - if (mSelectionManager == null) { - mSelectionManager = new SelectionManager(this); - } - return mSelectionManager; - } - @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.gallery, menu); @@ -99,7 +92,7 @@ public class GalleryActivity extends Activity { public static class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener { - private final Context mContext; + private final GalleryActivity mActivity; private final ActionBar mActionBar; private final ViewPager mViewPager; private final ArrayList mTabs = new ArrayList(); @@ -115,9 +108,9 @@ public class GalleryActivity extends Activity { } } - public TabsAdapter(Activity activity, ViewPager pager) { + public TabsAdapter(GalleryActivity activity, ViewPager pager) { super(activity.getFragmentManager()); - mContext = activity; + mActivity = activity; mActionBar = activity.getActionBar(); mViewPager = pager; mViewPager.setAdapter(this); @@ -141,8 +134,11 @@ public class GalleryActivity extends Activity { @Override public Fragment getItem(int position) { TabInfo info = mTabs.get(position); - return Fragment.instantiate(mContext, info.clss.getName(), + Fragment item = Fragment.instantiate(mActivity, info.clss.getName(), info.args); + ((SelectionManager.Client) item).setSelectionManager( + mActivity.mSelectionManager); + return item; } @Override diff --git a/src/com/android/photos/MultiChoiceManager.java b/src/com/android/photos/MultiChoiceManager.java new file mode 100644 index 000000000..e00c842fe --- /dev/null +++ b/src/com/android/photos/MultiChoiceManager.java @@ -0,0 +1,190 @@ +/* + * 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.photos; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.util.SparseBooleanArray; +import android.view.ActionMode; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.widget.AbsListView.MultiChoiceModeListener; +import android.widget.ShareActionProvider; +import android.widget.ShareActionProvider.OnShareTargetSelectedListener; + +import com.android.gallery3d.R; +import com.android.gallery3d.data.MediaObject; + +import java.util.ArrayList; +import java.util.List; + +public class MultiChoiceManager implements MultiChoiceModeListener, + OnShareTargetSelectedListener, SelectionManager.SelectedUriSource { + + public interface Delegate { + public SparseBooleanArray getSelectedItemPositions(); + public int getSelectedItemCount(); + public int getItemMediaType(Object item); + public int getItemSupportedOperations(Object item); + public ArrayList getSubItemUrisForItem(Object item); + public Object getItemAtPosition(int position); + public Object getPathForItemAtPosition(int position); + public void deleteItemWithPath(Object itemPath); + } + + private SelectionManager mSelectionManager; + private ShareActionProvider mShareActionProvider; + private ActionMode mActionMode; + private int numSubItemsCollected = 0; + private Context mContext; + private Delegate mDelegate; + + private ArrayList mSelectedUrisArray = new ArrayList(); + + public MultiChoiceManager(Context context, Delegate delegate) { + mContext = context; + mDelegate = delegate; + } + + public void setSelectionManager(SelectionManager selectionManager) { + mSelectionManager = selectionManager; + } + + @Override + public ArrayList getSelectedShareableUris() { + return mSelectedUrisArray; + } + + private void updateSelectedTitle(ActionMode mode) { + int count = mDelegate.getSelectedItemCount(); + mode.setTitle(mContext.getResources().getQuantityString( + R.plurals.number_of_items_selected, count, count)); + } + + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, + boolean checked) { + updateSelectedTitle(mode); + Object item = mDelegate.getItemAtPosition(position); + + ArrayList subItems = mDelegate.getSubItemUrisForItem(item); + if (checked) { + mSelectedUrisArray.addAll(subItems); + numSubItemsCollected += subItems.size(); + } else { + mSelectedUrisArray.removeAll(subItems); + numSubItemsCollected -= subItems.size(); + } + + mSelectionManager.onItemSelectedStateChanged(mShareActionProvider, + mDelegate.getItemMediaType(item), + mDelegate.getItemSupportedOperations(item), + checked); + updateActionItemVisibilities(mode.getMenu(), + mSelectionManager.getSupportedOperations()); + } + + private void updateActionItemVisibilities(Menu menu, int supportedOperations) { + MenuItem shareItem = menu.findItem(R.id.menu_share); + MenuItem deleteItem = menu.findItem(R.id.menu_delete); + shareItem.setVisible((supportedOperations & MediaObject.SUPPORT_SHARE) > 0); + deleteItem.setVisible((supportedOperations & MediaObject.SUPPORT_DELETE) > 0); + } + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + mSelectionManager.setSelectedUriSource(this); + mActionMode = mode; + MenuInflater inflater = mode.getMenuInflater(); + inflater.inflate(R.menu.gallery_multiselect, menu); + MenuItem menuItem = menu.findItem(R.id.menu_share); + mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider(); + mShareActionProvider.setOnShareTargetSelectedListener(this); + updateSelectedTitle(mode); + return true; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + // onDestroyActionMode gets called when the share target was selected, + // but apparently before the ArrayList is serialized in the intent + // so we can't clear the old one here. + mSelectedUrisArray = new ArrayList(); + mSelectionManager.onClearSelection(); + mSelectionManager.setSelectedUriSource(null); + mShareActionProvider = null; + mActionMode = null; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + updateSelectedTitle(mode); + return false; + } + + @Override + public boolean onShareTargetSelected(ShareActionProvider provider, Intent intent) { + mActionMode.finish(); + return false; + } + + private static class BulkDeleteTask extends AsyncTask { + private Delegate mDelegate; + private List mPaths; + + public BulkDeleteTask(Delegate delegate, List paths) { + mDelegate = delegate; + mPaths = paths; + } + + @Override + protected Void doInBackground(Void... ignored) { + for (Object path : mPaths) { + mDelegate.deleteItemWithPath(path); + } + return null; + } + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_delete: + BulkDeleteTask deleteTask = new BulkDeleteTask(mDelegate, + getPathsForSelectedItems()); + deleteTask.execute(); + mode.finish(); + return true; + default: + return false; + } + } + + private List getPathsForSelectedItems() { + List paths = new ArrayList(); + SparseBooleanArray selected = mDelegate.getSelectedItemPositions(); + for (int i = 0; i < selected.size(); i++) { + if (selected.valueAt(i)) { + paths.add(mDelegate.getPathForItemAtPosition(i)); + } + } + return paths; + } +} diff --git a/src/com/android/photos/PhotoSetFragment.java b/src/com/android/photos/PhotoSetFragment.java index 25d80360d..b485cd051 100644 --- a/src/com/android/photos/PhotoSetFragment.java +++ b/src/com/android/photos/PhotoSetFragment.java @@ -18,41 +18,31 @@ package com.android.photos; import android.app.Fragment; import android.app.LoaderManager.LoaderCallbacks; +import android.content.Context; import android.content.Intent; import android.content.Loader; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.util.SparseBooleanArray; -import android.view.ActionMode; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.GridView; -import android.widget.ShareActionProvider; -import android.widget.ShareActionProvider.OnShareTargetSelectedListener; import com.android.gallery3d.R; import com.android.gallery3d.app.Gallery; -import com.android.gallery3d.data.MediaItem; import com.android.photos.adapters.PhotoThumbnailAdapter; import com.android.photos.data.PhotoSetLoader; import com.android.photos.shims.LoaderCompatShim; import com.android.photos.shims.MediaItemsLoader; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; -public class PhotoSetFragment extends Fragment implements LoaderCallbacks, - OnItemClickListener, SelectionManager.SelectedUriSource, MultiChoiceModeListener, - OnShareTargetSelectedListener { +public class PhotoSetFragment extends Fragment implements OnItemClickListener, + LoaderCallbacks, MultiChoiceManager.Delegate, SelectionManager.Client { private static final int LOADER_PHOTOSET = 1; @@ -62,14 +52,24 @@ public class PhotoSetFragment extends Fragment implements LoaderCallbacks mLoaderCompatShim; private PhotoThumbnailAdapter mAdapter; + private MultiChoiceManager mMultiChoiceManager; private SelectionManager mSelectionManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - GalleryActivity activity = (GalleryActivity) getActivity(); - mSelectionManager = activity.getSelectionManager(); - mAdapter = new PhotoThumbnailAdapter(activity); + Context context = getActivity(); + mAdapter = new PhotoThumbnailAdapter(context); + mMultiChoiceManager = new MultiChoiceManager(context, this); + mMultiChoiceManager.setSelectionManager(mSelectionManager); + } + + @Override + public void setSelectionManager(SelectionManager manager) { + mSelectionManager = manager; + if (mMultiChoiceManager != null) { + mMultiChoiceManager.setSelectionManager(manager); + } } @Override @@ -84,7 +84,7 @@ public class PhotoSetFragment extends Fragment implements LoaderCallbacks mSelectedUris = new HashSet(); - private ArrayList mSelectedUrisArray = new ArrayList(); - @Override - public ArrayList getSelectedShareableUris() { - mSelectedUrisArray.clear(); - mSelectedUrisArray.addAll(mSelectedUris); - return mSelectedUrisArray; - } - - public ArrayList getSelectedShareableUrisUncached() { - mSelectedUrisArray.clear(); - SparseBooleanArray selected = mPhotoSetView.getCheckedItemPositions(); - - for (int i = 0; i < selected.size(); i++) { - if (selected.valueAt(i)) { - Cursor item = mAdapter.getItem(selected.keyAt(i)); - int supported = item.getInt(PhotoSetLoader.INDEX_SUPPORTED_OPERATIONS); - if ((supported & MediaItem.SUPPORT_SHARE) > 0) { - mSelectedUrisArray.add(mLoaderCompatShim.uriForItem(item)); - } - } - } - - return mSelectedUrisArray; + public void onLoaderReset(Loader loader) { } @Override - public void onLoaderReset(Loader loader) { + public int getItemMediaType(Object item) { + return ((Cursor) item).getInt(PhotoSetLoader.INDEX_MEDIA_TYPE); } - - private ShareActionProvider mShareActionProvider; - private ActionMode mActionMode; - private boolean mSharePending = false; - - private void updateSelectedTitle(ActionMode mode) { - int count = mPhotoSetView.getCheckedItemCount(); - mode.setTitle(getResources().getQuantityString( - R.plurals.number_of_items_selected, count, count)); + @Override + public int getItemSupportedOperations(Object item) { + return ((Cursor) item).getInt(PhotoSetLoader.INDEX_SUPPORTED_OPERATIONS); } @Override - public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { - updateSelectedTitle(mode); - Cursor item = mAdapter.getItem(position); - - if (checked) { - mSelectedUris.add(mLoaderCompatShim.uriForItem(item)); - } else { - mSelectedUris.remove(mLoaderCompatShim.uriForItem(item)); - } - - mSelectionManager.onItemSelectedStateChanged(mShareActionProvider, - item.getInt(PhotoSetLoader.INDEX_MEDIA_TYPE), - item.getInt(PhotoSetLoader.INDEX_SUPPORTED_OPERATIONS), - checked); + public Object getItemAtPosition(int position) { + return mAdapter.getItem(position); } + private ArrayList mSubItemUriTemp = new ArrayList(1); @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - mSelectionManager.setSelectedUriSource(PhotoSetFragment.this); - mActionMode = mode; - MenuInflater inflater = mode.getMenuInflater(); - inflater.inflate(R.menu.gallery_multiselect, menu); - MenuItem menuItem = menu.findItem(R.id.menu_share); - mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider(); - mShareActionProvider.setOnShareTargetSelectedListener(this); - updateSelectedTitle(mode); - return true; + public ArrayList getSubItemUrisForItem(Object item) { + mSubItemUriTemp.clear(); + mSubItemUriTemp.add(mLoaderCompatShim.uriForItem((Cursor) item)); + return mSubItemUriTemp; } + @Override - public void onDestroyActionMode(ActionMode mode) { - mSelectedUris.clear(); - if (mSharePending) { - // onDestroyActionMode gets called when the share target was selected, - // but apparently before the ArrayList is serialized in the intent - // so we can't clear the old one here. - mSelectedUrisArray = new ArrayList(); - mSharePending = false; - } else { - mSelectedUrisArray.clear(); - } - mSelectionManager.onClearSelection(); - mSelectionManager.setSelectedUriSource(null); - mShareActionProvider = null; - mActionMode = null; + public Object getPathForItemAtPosition(int position) { + return mLoaderCompatShim.getPathForItem(mAdapter.getItem(position)); } @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - updateSelectedTitle(mode); - return false; + public void deleteItemWithPath(Object itemPath) { + mLoaderCompatShim.deleteItemWithPath(itemPath); } @Override - public boolean onShareTargetSelected(ShareActionProvider provider, Intent intent) { - mSharePending = true; - mActionMode.finish(); - return false; + public SparseBooleanArray getSelectedItemPositions() { + return mPhotoSetView.getCheckedItemPositions(); } @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - return false; + public int getSelectedItemCount() { + return mPhotoSetView.getCheckedItemCount(); } } diff --git a/src/com/android/photos/SelectionManager.java b/src/com/android/photos/SelectionManager.java index 979dcc7da..ce340c731 100644 --- a/src/com/android/photos/SelectionManager.java +++ b/src/com/android/photos/SelectionManager.java @@ -26,7 +26,7 @@ import android.provider.MediaStore.Files.FileColumns; import android.widget.ShareActionProvider; import com.android.gallery3d.common.ApiHelper; -import com.android.gallery3d.data.MediaItem; +import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.util.GalleryUtils; import java.util.ArrayList; @@ -41,6 +41,10 @@ public class SelectionManager { public ArrayList getSelectedShareableUris(); } + public interface Client { + public void setSelectionManager(SelectionManager manager); + } + public SelectionManager(Activity activity) { mActivity = activity; if (ApiHelper.AT_LEAST_16) { @@ -76,10 +80,10 @@ public class SelectionManager { mSelectedTotalCount += increment; mCachedShareableUris = null; - if ((itemSupportedOperations & MediaItem.SUPPORT_DELETE) > 0) { + if ((itemSupportedOperations & MediaObject.SUPPORT_DELETE) > 0) { mSelectedDeletableCount += increment; } - if ((itemSupportedOperations & MediaItem.SUPPORT_SHARE) > 0) { + if ((itemSupportedOperations & MediaObject.SUPPORT_SHARE) > 0) { mSelectedShareableCount += increment; if (itemType == FileColumns.MEDIA_TYPE_IMAGE) { mSelectedShareableImageCount += increment; @@ -93,24 +97,42 @@ public class SelectionManager { mShareIntent.setAction(null).setType(null); } else if (mSelectedShareableCount >= 1) { mCachedShareableUris = mUriSource.getSelectedShareableUris(); - if (mSelectedShareableImageCount == mSelectedShareableCount) { - mShareIntent.setType(GalleryUtils.MIME_TYPE_IMAGE); - } else if (mSelectedShareableVideoCount == mSelectedShareableCount) { - mShareIntent.setType(GalleryUtils.MIME_TYPE_VIDEO); - } else { - mShareIntent.setType(GalleryUtils.MIME_TYPE_ALL); - } - if (mSelectedShareableCount == 1) { - mShareIntent.setAction(Intent.ACTION_SEND); - mShareIntent.putExtra(Intent.EXTRA_STREAM, mCachedShareableUris.get(0)); + if (mCachedShareableUris.size() == 0) { + mShareIntent.setAction(null).setType(null); } else { - mShareIntent.setAction(Intent.ACTION_SEND_MULTIPLE); - mShareIntent.putExtra(Intent.EXTRA_STREAM, mCachedShareableUris); + if (mSelectedShareableImageCount == mSelectedShareableCount) { + mShareIntent.setType(GalleryUtils.MIME_TYPE_IMAGE); + } else if (mSelectedShareableVideoCount == mSelectedShareableCount) { + mShareIntent.setType(GalleryUtils.MIME_TYPE_VIDEO); + } else { + mShareIntent.setType(GalleryUtils.MIME_TYPE_ALL); + } + if (mCachedShareableUris.size() == 1) { + mShareIntent.setAction(Intent.ACTION_SEND); + mShareIntent.putExtra(Intent.EXTRA_STREAM, mCachedShareableUris.get(0)); + } else { + mShareIntent.setAction(Intent.ACTION_SEND_MULTIPLE); + mShareIntent.putExtra(Intent.EXTRA_STREAM, mCachedShareableUris); + } } } share.setShareIntent(mShareIntent); - // TODO update deletability, editability, etc. + // TODO update editability, etc. + } + + public int getSupportedOperations() { + if (mSelectedTotalCount == 0) { + return 0; + } + int supported = 0; + if (mSelectedDeletableCount == mSelectedTotalCount) { + supported |= MediaObject.SUPPORT_DELETE; + } + if (mSelectedShareableCount > 0) { + supported |= MediaObject.SUPPORT_SHARE; + } + return supported; } public void onClearSelection() { diff --git a/src/com/android/photos/adapters/AlbumSetCursorAdapter.java b/src/com/android/photos/adapters/AlbumSetCursorAdapter.java new file mode 100644 index 000000000..c387f8f47 --- /dev/null +++ b/src/com/android/photos/adapters/AlbumSetCursorAdapter.java @@ -0,0 +1,89 @@ +/* + * 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.photos.adapters; + +import android.content.Context; +import android.database.Cursor; +import android.graphics.drawable.Drawable; +import android.text.format.DateFormat; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorAdapter; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.android.gallery3d.R; +import com.android.photos.data.AlbumSetLoader; +import com.android.photos.shims.LoaderCompatShim; + +import java.util.Date; + +public class AlbumSetCursorAdapter extends CursorAdapter { + + private LoaderCompatShim mDrawableFactory; + + public void setDrawableFactory(LoaderCompatShim factory) { + mDrawableFactory = factory; + } + private Date mDate = new Date(); // Used for converting timestamps for display + + public AlbumSetCursorAdapter(Context context) { + super(context, null, false); + } + + @Override + public void bindView(View v, Context context, Cursor cursor) { + TextView titleTextView = (TextView) v.findViewById( + R.id.album_set_item_title); + titleTextView.setText(cursor.getString(AlbumSetLoader.INDEX_TITLE)); + + TextView dateTextView = (TextView) v.findViewById( + R.id.album_set_item_date); + long timestamp = cursor.getLong(AlbumSetLoader.INDEX_TIMESTAMP); + if (timestamp > 0) { + mDate.setTime(timestamp); + dateTextView.setText(DateFormat.getMediumDateFormat(context).format(mDate)); + } else { + dateTextView.setText(null); + } + + ProgressBar uploadProgressBar = (ProgressBar) v.findViewById( + R.id.album_set_item_upload_progress); + if (cursor.getInt(AlbumSetLoader.INDEX_COUNT_PENDING_UPLOAD) > 0) { + uploadProgressBar.setVisibility(View.VISIBLE); + uploadProgressBar.setProgress(50); + } else { + uploadProgressBar.setVisibility(View.INVISIBLE); + } + + ImageView thumbImageView = (ImageView) v.findViewById( + R.id.album_set_item_image); + Drawable recycle = thumbImageView.getDrawable(); + Drawable drawable = mDrawableFactory.drawableForItem(cursor, recycle); + if (recycle != drawable) { + thumbImageView.setImageDrawable(drawable); + } + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return LayoutInflater.from(context).inflate( + R.layout.album_set_item, parent, false); + } +} diff --git a/src/com/android/photos/data/AlbumSetLoader.java b/src/com/android/photos/data/AlbumSetLoader.java index b2b5204e6..940473255 100644 --- a/src/com/android/photos/data/AlbumSetLoader.java +++ b/src/com/android/photos/data/AlbumSetLoader.java @@ -12,6 +12,7 @@ public class AlbumSetLoader { public static final int INDEX_THUMBNAIL_HEIGHT = 5; public static final int INDEX_COUNT_PENDING_UPLOAD = 6; public static final int INDEX_COUNT = 7; + public static final int INDEX_SUPPORTED_OPERATIONS = 8; public static final String[] PROJECTION = { "_id", @@ -21,7 +22,8 @@ public class AlbumSetLoader { "thumb_width", "thumb_height", "count_pending_upload", - "_count" + "_count", + "supported_operations" }; public static final MatrixCursor MOCK = createRandomCursor(30); @@ -44,7 +46,8 @@ public class AlbumSetLoader { 0, 0, (random < .3 ? 1 : 0), - 1 + 1, + 0 }; return row; } diff --git a/src/com/android/photos/data/PhotoSetLoader.java b/src/com/android/photos/data/PhotoSetLoader.java index 72c8e93cc..56c82c4a9 100644 --- a/src/com/android/photos/data/PhotoSetLoader.java +++ b/src/com/android/photos/data/PhotoSetLoader.java @@ -29,6 +29,8 @@ import android.provider.MediaStore.Files.FileColumns; import com.android.photos.drawables.DataUriThumbnailDrawable; import com.android.photos.shims.LoaderCompatShim; +import java.util.ArrayList; + public class PhotoSetLoader extends CursorLoader implements LoaderCompatShim { public static final String SUPPORTED_OPERATIONS = "supported_operations"; @@ -95,4 +97,19 @@ public class PhotoSetLoader extends CursorLoader implements LoaderCompatShim urisForSubItems(Cursor item) { + return null; + } + + @Override + public void deleteItemWithPath(Object path) { + + } + + @Override + public Object getPathForItem(Cursor item) { + return null; + } } diff --git a/src/com/android/photos/shims/LoaderCompatShim.java b/src/com/android/photos/shims/LoaderCompatShim.java index 9da4436aa..d5bf710de 100644 --- a/src/com/android/photos/shims/LoaderCompatShim.java +++ b/src/com/android/photos/shims/LoaderCompatShim.java @@ -19,8 +19,13 @@ package com.android.photos.shims; import android.graphics.drawable.Drawable; import android.net.Uri; +import java.util.ArrayList; + public interface LoaderCompatShim { Drawable drawableForItem(T item, Drawable recycle); Uri uriForItem(T item); + ArrayList urisForSubItems(T item); + void deleteItemWithPath(Object path); + Object getPathForItem(T item); } diff --git a/src/com/android/photos/shims/MediaItemsLoader.java b/src/com/android/photos/shims/MediaItemsLoader.java index fa41c8ec8..d75823404 100644 --- a/src/com/android/photos/shims/MediaItemsLoader.java +++ b/src/com/android/photos/shims/MediaItemsLoader.java @@ -28,12 +28,16 @@ import android.util.SparseArray; import com.android.gallery3d.data.ContentListener; import com.android.gallery3d.data.DataManager; import com.android.gallery3d.data.MediaItem; +import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.MediaSet; import com.android.gallery3d.data.MediaSet.ItemConsumer; import com.android.gallery3d.data.MediaSet.SyncListener; +import com.android.gallery3d.data.Path; import com.android.gallery3d.util.Future; import com.android.photos.data.PhotoSetLoader; +import java.util.ArrayList; + /** * Returns all MediaItems in a MediaSet, wrapping them in a cursor to appear * like a PhotoSetLoader @@ -47,6 +51,7 @@ public class MediaItemsLoader extends AsyncTaskLoader implements LoaderC }; private final MediaSet mMediaSet; + private final DataManager mDataManager; private Future mSyncTask = null; private ContentListener mObserver = new ContentListener() { @Override @@ -58,14 +63,15 @@ public class MediaItemsLoader extends AsyncTaskLoader implements LoaderC public MediaItemsLoader(Context context) { super(context); - DataManager dm = DataManager.from(context); - String path = dm.getTopSetPath(DataManager.INCLUDE_ALL); - mMediaSet = dm.getMediaSet(path); + mDataManager = DataManager.from(context); + String path = mDataManager.getTopSetPath(DataManager.INCLUDE_ALL); + mMediaSet = mDataManager.getMediaSet(path); } public MediaItemsLoader(Context context, String parentPath) { super(context); - mMediaSet = DataManager.from(getContext()).getMediaSet(parentPath); + mDataManager = DataManager.from(getContext()); + mMediaSet = mDataManager.getMediaSet(parentPath); } @Override @@ -157,4 +163,27 @@ public class MediaItemsLoader extends AsyncTaskLoader implements LoaderC return mi == null ? null : mi.getContentUri(); } + @Override + public ArrayList urisForSubItems(Cursor item) { + return null; + } + + @Override + public void deleteItemWithPath(Object path) { + MediaObject o = mDataManager.getMediaObject((Path) path); + if (o != null) { + o.delete(); + } + } + + @Override + public Object getPathForItem(Cursor item) { + int index = item.getInt(PhotoSetLoader.INDEX_ID); + MediaItem mi = mMediaItems.get(index); + if (mi != null) { + return mi.getPath(); + } + return null; + } + } diff --git a/src/com/android/photos/shims/MediaSetLoader.java b/src/com/android/photos/shims/MediaSetLoader.java index 96c7485bb..d200807f9 100644 --- a/src/com/android/photos/shims/MediaSetLoader.java +++ b/src/com/android/photos/shims/MediaSetLoader.java @@ -26,7 +26,9 @@ import android.net.Uri; import com.android.gallery3d.data.ContentListener; import com.android.gallery3d.data.DataManager; import com.android.gallery3d.data.MediaItem; +import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.MediaSet; +import com.android.gallery3d.data.Path; import com.android.gallery3d.data.MediaSet.SyncListener; import com.android.gallery3d.util.Future; import com.android.photos.data.AlbumSetLoader; @@ -46,6 +48,7 @@ public class MediaSetLoader extends AsyncTaskLoader implements LoaderCom }; private final MediaSet mMediaSet; + private final DataManager mDataManager; private Future mSyncTask = null; private ContentListener mObserver = new ContentListener() { @Override @@ -58,14 +61,15 @@ public class MediaSetLoader extends AsyncTaskLoader implements LoaderCom public MediaSetLoader(Context context) { super(context); - DataManager dm = DataManager.from(context); - String path = dm.getTopSetPath(DataManager.INCLUDE_ALL); - mMediaSet = dm.getMediaSet(path); + mDataManager = DataManager.from(context); + String path = mDataManager.getTopSetPath(DataManager.INCLUDE_ALL); + mMediaSet = mDataManager.getMediaSet(path); } public MediaSetLoader(Context context, String path) { super(context); - mMediaSet = DataManager.from(getContext()).getMediaSet(path); + mDataManager = DataManager.from(getContext()); + mMediaSet = mDataManager.getMediaSet(path); } @Override @@ -111,6 +115,7 @@ public class MediaSetLoader extends AsyncTaskLoader implements LoaderCom row[AlbumSetLoader.INDEX_ID] = i; row[AlbumSetLoader.INDEX_TITLE] = m.getName(); row[AlbumSetLoader.INDEX_COUNT] = m.getMediaItemCount(); + row[AlbumSetLoader.INDEX_SUPPORTED_OPERATIONS] = m.getSupportedOperations(); MediaItem coverItem = m.getCoverMediaItem(); if (coverItem != null) { row[AlbumSetLoader.INDEX_TIMESTAMP] = coverItem.getDateInMs(); @@ -147,4 +152,39 @@ public class MediaSetLoader extends AsyncTaskLoader implements LoaderCom MediaSet ms = mMediaSet.getSubMediaSet(index); return ms == null ? null : ms.getContentUri(); } + + @Override + public ArrayList urisForSubItems(Cursor item) { + int index = item.getInt(AlbumSetLoader.INDEX_ID); + MediaSet ms = mMediaSet.getSubMediaSet(index); + if (ms == null) return null; + final ArrayList result = new ArrayList(); + ms.enumerateMediaItems(new MediaSet.ItemConsumer() { + @Override + public void consume(int index, MediaItem item) { + if (item != null) { + result.add(item.getContentUri()); + } + } + }); + return result; + } + + @Override + public void deleteItemWithPath(Object path) { + MediaObject o = mDataManager.getMediaObject((Path) path); + if (o != null) { + o.delete(); + } + } + + @Override + public Object getPathForItem(Cursor item) { + int index = item.getInt(AlbumSetLoader.INDEX_ID); + MediaSet ms = mMediaSet.getSubMediaSet(index); + if (ms != null) { + return ms.getPath(); + } + return null; + } } -- cgit v1.2.3