From 608ef5cf3a15f235aa7b6d80924edbdb0fda365b Mon Sep 17 00:00:00 2001 From: John Reck Date: Fri, 15 Feb 2013 14:32:08 -0800 Subject: Start of photos Change-Id: Id53dd96d8873e0fe6c2685e07cd7bdc0cde7ea75 --- src/com/android/gallery3d/data/DataManager.java | 7 +- src/com/android/photos/AlbumSetFragment.java | 24 +++++ src/com/android/photos/GalleryActivity.java | 125 ++++++++++++++++++++++++ src/com/android/photos/PhotoFragment.java | 25 +++++ src/com/android/photos/PhotoSetFragment.java | 82 ++++++++++++++++ src/com/android/photos/data/MediaSetLoader.java | 89 +++++++++++++++++ src/com/android/photos/data/PhotoSetLoader.java | 70 +++++++++++++ 7 files changed, 421 insertions(+), 1 deletion(-) create mode 100644 src/com/android/photos/AlbumSetFragment.java create mode 100644 src/com/android/photos/GalleryActivity.java create mode 100644 src/com/android/photos/PhotoFragment.java create mode 100644 src/com/android/photos/PhotoSetFragment.java create mode 100644 src/com/android/photos/data/MediaSetLoader.java create mode 100644 src/com/android/photos/data/PhotoSetLoader.java diff --git a/src/com/android/gallery3d/data/DataManager.java b/src/com/android/gallery3d/data/DataManager.java index 8fcf4008a..38865e9f1 100644 --- a/src/com/android/gallery3d/data/DataManager.java +++ b/src/com/android/gallery3d/data/DataManager.java @@ -16,13 +16,13 @@ package com.android.gallery3d.data; +import android.content.Context; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import com.android.gallery3d.app.GalleryApp; import com.android.gallery3d.app.StitchingChangeListener; -import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.common.Utils; import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback; import com.android.gallery3d.data.MediaSet.ItemConsumer; @@ -65,6 +65,11 @@ public class DataManager implements StitchingChangeListener { // to prevent concurrency issue. public static final Object LOCK = new Object(); + public static DataManager from(Context context) { + GalleryApp app = (GalleryApp) context.getApplicationContext(); + return app.getDataManager(); + } + private static final String TAG = "DataManager"; // This is the path for the media set seen by the user at top level. diff --git a/src/com/android/photos/AlbumSetFragment.java b/src/com/android/photos/AlbumSetFragment.java new file mode 100644 index 000000000..2d348c248 --- /dev/null +++ b/src/com/android/photos/AlbumSetFragment.java @@ -0,0 +1,24 @@ +/* + * 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.app.Fragment; + + +public class AlbumSetFragment extends Fragment { + +} diff --git a/src/com/android/photos/GalleryActivity.java b/src/com/android/photos/GalleryActivity.java new file mode 100644 index 000000000..46b5140fb --- /dev/null +++ b/src/com/android/photos/GalleryActivity.java @@ -0,0 +1,125 @@ +/* + * 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.app.ActionBar; +import android.app.ActionBar.Tab; +import android.app.Activity; +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +import com.android.camera.CameraActivity; +import com.android.gallery3d.R; + +public class GalleryActivity extends Activity { + + private final String FTAG_PHOTOSET = "PhotoSet"; + private final String FTAG_ALBUMSET = "AlbumSet"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setupActionBar(); + } + + private void setupActionBar() { + ActionBar ab = getActionBar(); + ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + ab.setDisplayShowHomeEnabled(false); + ab.setDisplayShowTitleEnabled(false); + Tab tab = ab.newTab(); + tab.setText(R.string.tab_photos); + tab.setTabListener(new TabListener(this, + FTAG_PHOTOSET, PhotoSetFragment.class)); + ab.addTab(tab, true); + tab = ab.newTab(); + tab.setText(R.string.tab_albums); + tab.setTabListener(new TabListener(this, + FTAG_ALBUMSET, AlbumSetFragment.class)); + ab.addTab(tab); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.gallery, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_camera: + Intent intent = new Intent(this, CameraActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private static class TabListener implements ActionBar.TabListener { + private Fragment mFragment; + private final Activity mActivity; + private final String mTag; + private final Class mClass; + + /** Constructor used each time a new tab is created. + * @param activity The host Activity, used to instantiate the fragment + * @param tag The identifier tag for the fragment + * @param clz The fragment's Class, used to instantiate the fragment + */ + public TabListener(Activity activity, String tag, Class clz) { + mActivity = activity; + mTag = tag; + mClass = clz; + } + + /* The following are each of the ActionBar.TabListener callbacks */ + + @Override + public void onTabSelected(Tab tab, FragmentTransaction ft) { + // Check if the fragment is already initialized + if (mFragment == null) { + // If not, instantiate and add it to the activity + mFragment = Fragment.instantiate(mActivity, mClass.getName()); + ft.add(android.R.id.content, mFragment, mTag); + } else { + // If it exists, simply attach it in order to show it + ft.attach(mFragment); + } + } + + @Override + public void onTabUnselected(Tab tab, FragmentTransaction ft) { + if (mFragment != null) { + // Detach the fragment, because another one is being attached + ft.detach(mFragment); + } + } + + @Override + public void onTabReselected(Tab tab, FragmentTransaction ft) { + // User selected the already selected tab. Usually do nothing. + } + } +} diff --git a/src/com/android/photos/PhotoFragment.java b/src/com/android/photos/PhotoFragment.java new file mode 100644 index 000000000..3be6313f2 --- /dev/null +++ b/src/com/android/photos/PhotoFragment.java @@ -0,0 +1,25 @@ +/* + * 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.app.Fragment; + + +public class PhotoFragment extends Fragment { + +} diff --git a/src/com/android/photos/PhotoSetFragment.java b/src/com/android/photos/PhotoSetFragment.java new file mode 100644 index 000000000..e9bfce56f --- /dev/null +++ b/src/com/android/photos/PhotoSetFragment.java @@ -0,0 +1,82 @@ +/* + * 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.app.Fragment; +import android.app.LoaderManager.LoaderCallbacks; +import android.content.Loader; +import android.database.Cursor; +import android.os.Bundle; +import android.provider.MediaStore.Files.FileColumns; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorAdapter; +import android.widget.ListView; +import android.widget.SimpleCursorAdapter; + +import com.android.gallery3d.R; +import com.android.photos.data.PhotoSetLoader; + + +public class PhotoSetFragment extends Fragment implements LoaderCallbacks { + + private static final int LOADER_PHOTOSET = 1; + + private ListView mPhotoSetView; + private View mEmptyView; + private CursorAdapter mAdapter; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.photo_set, container, false); + mPhotoSetView = (ListView) root.findViewById(android.R.id.list); + mEmptyView = root.findViewById(android.R.id.empty); + mEmptyView.setVisibility(View.GONE); + mAdapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_list_item_1, null, + new String[] { FileColumns.DATA }, + new int[] { android.R.id.text1 }, 0); + mPhotoSetView.setAdapter(mAdapter); + getLoaderManager().initLoader(LOADER_PHOTOSET, null, this); + updateEmptyStatus(); + return root; + } + + private void updateEmptyStatus() { + boolean empty = (mAdapter == null || mAdapter.getCount() == 0); + mPhotoSetView.setVisibility(empty ? View.GONE : View.VISIBLE); + mEmptyView.setVisibility(empty ? View.VISIBLE : View.GONE); + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new PhotoSetLoader(getActivity()); + } + + @Override + public void onLoadFinished(Loader loader, + Cursor data) { + mAdapter.swapCursor(data); + updateEmptyStatus(); + } + + @Override + public void onLoaderReset(Loader loader) { + } +} diff --git a/src/com/android/photos/data/MediaSetLoader.java b/src/com/android/photos/data/MediaSetLoader.java new file mode 100644 index 000000000..4afb7d922 --- /dev/null +++ b/src/com/android/photos/data/MediaSetLoader.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.data; + +import android.content.AsyncTaskLoader; +import android.content.Context; + +import com.android.gallery3d.data.ContentListener; +import com.android.gallery3d.data.DataManager; +import com.android.gallery3d.data.MediaSet; +import com.android.gallery3d.data.MediaSet.SyncListener; +import com.android.gallery3d.util.Future; + +/** + * Proof of concept, don't use + */ +public class MediaSetLoader extends AsyncTaskLoader { + + private static final SyncListener sNullListener = new SyncListener() { + @Override + public void onSyncDone(MediaSet mediaSet, int resultCode) { + } + }; + + private MediaSet mMediaSet; + private Future mSyncTask = null; + private ContentListener mObserver = new ContentListener() { + @Override + public void onContentDirty() { + onContentChanged(); + } + }; + + public MediaSetLoader(Context context, String path) { + super(context); + mMediaSet = DataManager.from(getContext()).getMediaSet(path); + } + + @Override + protected void onStartLoading() { + super.onStartLoading(); + mMediaSet.addContentListener(mObserver); + mSyncTask = mMediaSet.requestSync(sNullListener); + forceLoad(); + } + + @Override + protected boolean onCancelLoad() { + if (mSyncTask != null) { + mSyncTask.cancel(); + mSyncTask = null; + } + return super.onCancelLoad(); + } + + @Override + protected void onStopLoading() { + super.onStopLoading(); + cancelLoad(); + mMediaSet.removeContentListener(mObserver); + } + + @Override + protected void onReset() { + super.onReset(); + onStopLoading(); + } + + @Override + public MediaSet loadInBackground() { + mMediaSet.loadIfDirty(); + return mMediaSet; + } + +} diff --git a/src/com/android/photos/data/PhotoSetLoader.java b/src/com/android/photos/data/PhotoSetLoader.java new file mode 100644 index 000000000..8c511a525 --- /dev/null +++ b/src/com/android/photos/data/PhotoSetLoader.java @@ -0,0 +1,70 @@ +/* + * 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.data; + +import android.content.Context; +import android.content.CursorLoader; +import android.database.ContentObserver; +import android.net.Uri; +import android.provider.MediaStore; +import android.provider.MediaStore.Files; +import android.provider.MediaStore.Files.FileColumns; + +public class PhotoSetLoader extends CursorLoader { + + private static final Uri CONTENT_URI = Files.getContentUri("external"); + private static final String[] PROJECTION = new String[] { + FileColumns._ID, + FileColumns.DATA, + FileColumns.WIDTH, + FileColumns.HEIGHT, + FileColumns.DATE_ADDED, + FileColumns.MEDIA_TYPE, + }; + private static final String SORT_ORDER = FileColumns.DATE_ADDED + " DESC"; + private static final String SELECTION = + FileColumns.MEDIA_TYPE + " == " + FileColumns.MEDIA_TYPE_IMAGE + + " OR " + + FileColumns.MEDIA_TYPE + " == " + FileColumns.MEDIA_TYPE_VIDEO; + + public static final int INDEX_ID = 0; + public static final int INDEX_DATA = 1; + public static final int INDEX_WIDTH = 2; + public static final int INDEX_HEIGHT = 3; + public static final int INDEX_DATE_ADDED = 4; + public static final int INDEX_MEDIA_TYPE = 5; + + private static final Uri GLOBAL_CONTENT_URI = Uri.parse("content://" + MediaStore.AUTHORITY + "/external/"); + private final ContentObserver mGlobalObserver = new ForceLoadContentObserver(); + + public PhotoSetLoader(Context context) { + super(context, CONTENT_URI, PROJECTION, SELECTION, null, SORT_ORDER); + } + + @Override + protected void onStartLoading() { + super.onStartLoading(); + getContext().getContentResolver().registerContentObserver(GLOBAL_CONTENT_URI, + true, mGlobalObserver); + } + + @Override + protected void onReset() { + super.onReset(); + getContext().getContentResolver().unregisterContentObserver(mGlobalObserver); + } +} -- cgit v1.2.3