OSDN Git Service

Sharing support for albums, bulk operations infrastructure
authorBobby Georgescu <georgescu@google.com>
Sat, 9 Mar 2013 04:15:15 +0000 (20:15 -0800)
committerBobby Georgescu <georgescu@google.com>
Wed, 13 Mar 2013 21:20:31 +0000 (14:20 -0700)
Change-Id: I590f60ab85ffbd9cf9d7cd9982627604fa1b427f

12 files changed:
res/menu/gallery_multiselect.xml
src/com/android/photos/AlbumSetFragment.java
src/com/android/photos/GalleryActivity.java
src/com/android/photos/MultiChoiceManager.java [new file with mode: 0644]
src/com/android/photos/PhotoSetFragment.java
src/com/android/photos/SelectionManager.java
src/com/android/photos/adapters/AlbumSetCursorAdapter.java [new file with mode: 0644]
src/com/android/photos/data/AlbumSetLoader.java
src/com/android/photos/data/PhotoSetLoader.java
src/com/android/photos/shims/LoaderCompatShim.java
src/com/android/photos/shims/MediaItemsLoader.java
src/com/android/photos/shims/MediaSetLoader.java

index 3fbb6a7..22350a0 100644 (file)
@@ -1,7 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <menu xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:id="@+id/menu_delete"
+            android:icon="@android:drawable/ic_menu_delete"
+            android:title="@string/delete"
+            android:visible="false"
+            android:showAsAction="ifRoom" />
     <item android:id="@+id/menu_share"
           android:title="@string/share"
           android:showAsAction="ifRoom"
+          android:visible="false"
           android:actionProviderClass="android.widget.ShareActionProvider" />
 </menu>
\ No newline at end of file
index 0d4fcc0..d0bc81f 100644 (file)
@@ -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<Cursor> {
+    LoaderCallbacks<Cursor>, MultiChoiceManager.Delegate, SelectionManager.Client {
 
     private GridView mAlbumSetView;
     private View mEmptyView;
     private AlbumSetCursorAdapter mAdapter;
+    private LoaderCompatShim<Cursor> 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<Cursor> mDrawableFactory;
+    @Override
+    public int getItemSupportedOperations(Object item) {
+        return ((Cursor) item).getInt(AlbumSetLoader.INDEX_SUPPORTED_OPERATIONS);
+    }
 
-        public void setDrawableFactory(LoaderCompatShim<Cursor> 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<Uri> 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();
+    }
+
 }
index 420fc3d..ddf04e3 100644 (file)
@@ -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<TabInfo> mTabs = new ArrayList<TabInfo>();
@@ -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 (file)
index 0000000..e00c842
--- /dev/null
@@ -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<Uri> 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<Uri> mSelectedUrisArray = new ArrayList<Uri>();
+
+    public MultiChoiceManager(Context context, Delegate delegate) {
+        mContext = context;
+        mDelegate = delegate;
+    }
+
+    public void setSelectionManager(SelectionManager selectionManager) {
+        mSelectionManager = selectionManager;
+    }
+
+    @Override
+    public ArrayList<Uri> 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<Uri> 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<Uri>();
+        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<Void, Void, Void> {
+        private Delegate mDelegate;
+        private List<Object> mPaths;
+
+        public BulkDeleteTask(Delegate delegate, List<Object> 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<Object> getPathsForSelectedItems() {
+        List<Object> paths = new ArrayList<Object>();
+        SparseBooleanArray selected = mDelegate.getSelectedItemPositions();
+        for (int i = 0; i < selected.size(); i++) {
+            if (selected.valueAt(i)) {
+                paths.add(mDelegate.getPathForItemAtPosition(i));
+            }
+        }
+        return paths;
+    }
+}
index 25d8036..b485cd0 100644 (file)
@@ -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<Cursor>,
-        OnItemClickListener, SelectionManager.SelectedUriSource, MultiChoiceModeListener,
-        OnShareTargetSelectedListener {
+public class PhotoSetFragment extends Fragment implements OnItemClickListener,
+    LoaderCallbacks<Cursor>, MultiChoiceManager.Delegate, SelectionManager.Client  {
 
     private static final int LOADER_PHOTOSET = 1;
 
@@ -62,14 +52,24 @@ public class PhotoSetFragment extends Fragment implements LoaderCallbacks<Cursor
     private boolean mInitialLoadComplete = false;
     private LoaderCompatShim<Cursor> 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<Cursor
         mEmptyView.setVisibility(View.GONE);
         mPhotoSetView.setAdapter(mAdapter);
         mPhotoSetView.setChoiceMode(GridView.CHOICE_MODE_MULTIPLE_MODAL);
-        mPhotoSetView.setMultiChoiceModeListener(this);
+        mPhotoSetView.setMultiChoiceModeListener(mMultiChoiceManager);
         getLoaderManager().initLoader(LOADER_PHOTOSET, null, this);
         updateEmptyStatus();
         return root;
@@ -129,112 +129,51 @@ public class PhotoSetFragment extends Fragment implements LoaderCallbacks<Cursor
         updateEmptyStatus();
     }
 
-    private Set<Uri> mSelectedUris = new HashSet<Uri>();
-    private ArrayList<Uri> mSelectedUrisArray = new ArrayList<Uri>();
-
     @Override
-    public ArrayList<Uri> getSelectedShareableUris() {
-        mSelectedUrisArray.clear();
-        mSelectedUrisArray.addAll(mSelectedUris);
-        return mSelectedUrisArray;
-    }
-
-    public ArrayList<Uri> 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<Cursor> loader) {
     }
 
     @Override
-    public void onLoaderReset(Loader<Cursor> 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<Uri> mSubItemUriTemp = new ArrayList<Uri>(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<Uri> 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<Uri>();
-            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();
     }
 }
index 979dcc7..ce340c7 100644 (file)
@@ -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<Uri> 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 (file)
index 0000000..c387f8f
--- /dev/null
@@ -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<Cursor> mDrawableFactory;
+
+    public void setDrawableFactory(LoaderCompatShim<Cursor> 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);
+    }
+}
index b2b5204..9404732 100644 (file)
@@ -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;
     }
index 72c8e93..56c82c4 100644 (file)
@@ -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<Cursor> {
 
     public static final String SUPPORTED_OPERATIONS = "supported_operations";
@@ -95,4 +97,19 @@ public class PhotoSetLoader extends CursorLoader implements LoaderCompatShim<Cur
     public Uri uriForItem(Cursor item) {
         return null;
     }
+
+    @Override
+    public ArrayList<Uri> urisForSubItems(Cursor item) {
+        return null;
+    }
+
+    @Override
+    public void deleteItemWithPath(Object path) {
+
+    }
+
+    @Override
+    public Object getPathForItem(Cursor item) {
+        return null;
+    }
 }
index 9da4436..d5bf710 100644 (file)
@@ -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<T> {
     Drawable drawableForItem(T item, Drawable recycle);
     Uri uriForItem(T item);
+    ArrayList<Uri> urisForSubItems(T item);
+    void deleteItemWithPath(Object path);
+    Object getPathForItem(T item);
 }
index fa41c8e..d758234 100644 (file)
@@ -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<Cursor> implements LoaderC
     };
 
     private final MediaSet mMediaSet;
+    private final DataManager mDataManager;
     private Future<Integer> mSyncTask = null;
     private ContentListener mObserver = new ContentListener() {
         @Override
@@ -58,14 +63,15 @@ public class MediaItemsLoader extends AsyncTaskLoader<Cursor> 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<Cursor> implements LoaderC
         return mi == null ? null : mi.getContentUri();
     }
 
+    @Override
+    public ArrayList<Uri> 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;
+    }
+
 }
index 96c7485..d200807 100644 (file)
@@ -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<Cursor> implements LoaderCom
     };
 
     private final MediaSet mMediaSet;
+    private final DataManager mDataManager;
     private Future<Integer> mSyncTask = null;
     private ContentListener mObserver = new ContentListener() {
         @Override
@@ -58,14 +61,15 @@ public class MediaSetLoader extends AsyncTaskLoader<Cursor> 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<Cursor> 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<Cursor> implements LoaderCom
         MediaSet ms = mMediaSet.getSubMediaSet(index);
         return ms == null ? null : ms.getContentUri();
     }
+
+    @Override
+    public ArrayList<Uri> urisForSubItems(Cursor item) {
+        int index = item.getInt(AlbumSetLoader.INDEX_ID);
+        MediaSet ms = mMediaSet.getSubMediaSet(index);
+        if (ms == null) return null;
+        final ArrayList<Uri> result = new ArrayList<Uri>();
+        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;
+    }
 }