Messaging: Vendor chips, colorpicker, photoviewer, and vcard in-tree
Clone no longer depends on LineageOS gitlinks; the libraries used by the app live in this repo. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Google Inc.
|
||||
* Licensed to 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.ex.photo.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import android.util.Log;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.ex.photo.provider.PhotoContract;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Page adapter for use with an BaseCursorLoader. Unlike other cursor adapters, this has no
|
||||
* observers for automatic refresh. Instead, it depends upon external mechanisms to provide
|
||||
* the update signal.
|
||||
*/
|
||||
public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter {
|
||||
private static final String TAG = "BaseCursorPagerAdapter";
|
||||
|
||||
protected Context mContext;
|
||||
protected Cursor mCursor;
|
||||
protected int mRowIDColumn;
|
||||
/** Mapping of row ID to cursor position */
|
||||
protected SparseIntArray mItemPosition;
|
||||
/** Mapping of instantiated object to row ID */
|
||||
protected final HashMap<Object, Integer> mObjectRowMap = new HashMap<Object, Integer>();
|
||||
|
||||
/**
|
||||
* Constructor that always enables auto-requery.
|
||||
*
|
||||
* @param c The cursor from which to get the data.
|
||||
* @param context The context
|
||||
*/
|
||||
public BaseCursorPagerAdapter(Context context, FragmentManager fm, Cursor c) {
|
||||
super(fm);
|
||||
init(context, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a fragment for the data pointed to by the cursor
|
||||
*
|
||||
* @param context Interface to application's global information
|
||||
* @param cursor The cursor from which to get the data. The cursor is already
|
||||
* moved to the correct position.
|
||||
* @return the newly created fragment.
|
||||
*/
|
||||
public abstract Fragment getItem(Context context, Cursor cursor, int position);
|
||||
|
||||
// TODO: This shouldn't just return null - maybe it needs to wait for a cursor to be supplied?
|
||||
// See b/7103023
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
if (mCursor != null && moveCursorTo(position)) {
|
||||
return getItem(mContext, mCursor, position);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (mCursor != null) {
|
||||
return mCursor.getCount();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(View container, int position) {
|
||||
if (mCursor == null) {
|
||||
throw new IllegalStateException("this should only be called when the cursor is valid");
|
||||
}
|
||||
|
||||
final Integer rowId;
|
||||
if (moveCursorTo(position)) {
|
||||
rowId = mCursor.getString(mRowIDColumn).hashCode();
|
||||
} else {
|
||||
rowId = null;
|
||||
}
|
||||
|
||||
// Create the fragment and bind cursor data
|
||||
final Object obj = super.instantiateItem(container, position);
|
||||
if (obj != null) {
|
||||
mObjectRowMap.put(obj, rowId);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(View container, int position, Object object) {
|
||||
mObjectRowMap.remove(object);
|
||||
|
||||
super.destroyItem(container, position, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemPosition(Object object) {
|
||||
final Integer rowId = mObjectRowMap.get(object);
|
||||
if (rowId == null || mItemPosition == null) {
|
||||
return POSITION_NONE;
|
||||
}
|
||||
|
||||
final int position = mItemPosition.get(rowId, POSITION_NONE);
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if data is valid
|
||||
*/
|
||||
public boolean isDataValid() {
|
||||
return mCursor != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cursor.
|
||||
*/
|
||||
public Cursor getCursor() {
|
||||
return mCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data item associated with the specified position in the data set.
|
||||
*/
|
||||
public Object getDataItem(int position) {
|
||||
if (mCursor != null && moveCursorTo(position)) {
|
||||
return mCursor;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the row id associated with the specified position in the list.
|
||||
*/
|
||||
public long getItemId(int position) {
|
||||
if (mCursor != null && moveCursorTo(position)) {
|
||||
return mCursor.getString(mRowIDColumn).hashCode();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap in a new Cursor, returning the old Cursor.
|
||||
*
|
||||
* @param newCursor The new cursor to be used.
|
||||
* @return Returns the previously set Cursor, or null if there was not one.
|
||||
* If the given new Cursor is the same instance is the previously set
|
||||
* Cursor, null is also returned.
|
||||
*/
|
||||
public Cursor swapCursor(Cursor newCursor) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "swapCursor old=" + (mCursor == null ? -1 : mCursor.getCount()) +
|
||||
"; new=" + (newCursor == null ? -1 : newCursor.getCount()));
|
||||
}
|
||||
|
||||
if (newCursor == mCursor) {
|
||||
return null;
|
||||
}
|
||||
Cursor oldCursor = mCursor;
|
||||
mCursor = newCursor;
|
||||
if (newCursor != null) {
|
||||
mRowIDColumn = newCursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI);
|
||||
} else {
|
||||
mRowIDColumn = -1;
|
||||
}
|
||||
|
||||
setItemPosition();
|
||||
notifyDataSetChanged(); // notify the observers about the new cursor
|
||||
return oldCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the cursor into a CharSequence. Subclasses should override this
|
||||
* method to convert their results. The default implementation returns an
|
||||
* empty String for null values or the default String representation of
|
||||
* the value.
|
||||
*
|
||||
* @param cursor the cursor to convert to a CharSequence
|
||||
* @return a CharSequence representing the value
|
||||
*/
|
||||
public CharSequence convertToString(Cursor cursor) {
|
||||
return cursor == null ? "" : cursor.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String makeFragmentName(int viewId, int index) {
|
||||
if (moveCursorTo(index)) {
|
||||
return "android:pager:" + viewId + ":" + mCursor.getString(mRowIDColumn).hashCode();
|
||||
} else {
|
||||
return super.makeFragmentName(viewId, index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the cursor to the given position
|
||||
*
|
||||
* @return {@code true} if the cursor's position was set. Otherwise, {@code false}.
|
||||
*/
|
||||
private boolean moveCursorTo(int position) {
|
||||
if (mCursor != null && !mCursor.isClosed()) {
|
||||
return mCursor.moveToPosition(position);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the adapter.
|
||||
*/
|
||||
private void init(Context context, Cursor c) {
|
||||
boolean cursorPresent = c != null;
|
||||
mCursor = c;
|
||||
mContext = context;
|
||||
mRowIDColumn = cursorPresent
|
||||
? mCursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #mItemPosition} instance variable with the current mapping of
|
||||
* row id to cursor position.
|
||||
*/
|
||||
private void setItemPosition() {
|
||||
if (mCursor == null || mCursor.isClosed()) {
|
||||
mItemPosition = null;
|
||||
return;
|
||||
}
|
||||
|
||||
SparseIntArray itemPosition = new SparseIntArray(mCursor.getCount());
|
||||
|
||||
mCursor.moveToPosition(-1);
|
||||
while (mCursor.moveToNext()) {
|
||||
final int rowId = mCursor.getString(mRowIDColumn).hashCode();
|
||||
final int position = mCursor.getPosition();
|
||||
|
||||
itemPosition.append(rowId, position);
|
||||
}
|
||||
mItemPosition = itemPosition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Google Inc.
|
||||
* Licensed to 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.ex.photo.adapters;
|
||||
|
||||
import android.os.Parcelable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.collection.LruCache;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* NOTE: This is a direct copy of {@link androidx.fragment.app.FragmentPagerAdapter}
|
||||
* with four very important modifications.
|
||||
* <p>
|
||||
* <ol>
|
||||
* <li>The method {@link #makeFragmentName(int, int)} is declared "protected"
|
||||
* in our class. We need to be able to re-define the fragment's name according to data
|
||||
* only available to sub-classes.</li>
|
||||
* <li>The method {@link #isViewFromObject(View, Object)} has been reimplemented to search
|
||||
* the entire view hierarchy for the given view.</li>
|
||||
* <li>In method {@link #destroyItem(View, int, Object)}, the fragment is detached and
|
||||
* added to a cache. If the fragment is evicted from the cache, it will be deleted.
|
||||
* An album may contain thousands of photos and we want to avoid having thousands of
|
||||
* fragments.</li>
|
||||
* </ol>
|
||||
*/
|
||||
public abstract class BaseFragmentPagerAdapter extends PagerAdapter {
|
||||
/** The default size of {@link #mFragmentCache} */
|
||||
private static final int DEFAULT_CACHE_SIZE = 5;
|
||||
private static final String TAG = "FragmentPagerAdapter";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final FragmentManager mFragmentManager;
|
||||
private FragmentTransaction mCurTransaction = null;
|
||||
private Fragment mCurrentPrimaryItem = null;
|
||||
/** A cache to store detached fragments before they are removed */
|
||||
private LruCache<String, Fragment> mFragmentCache = new FragmentCache(DEFAULT_CACHE_SIZE);
|
||||
|
||||
public BaseFragmentPagerAdapter(androidx.fragment.app.FragmentManager fm) {
|
||||
mFragmentManager = fm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Fragment associated with a specified position.
|
||||
*/
|
||||
public abstract Fragment getItem(int position);
|
||||
|
||||
@Override
|
||||
public void startUpdate(View container) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(View container, int position) {
|
||||
if (mCurTransaction == null) {
|
||||
mCurTransaction = mFragmentManager.beginTransaction();
|
||||
}
|
||||
|
||||
// Do we already have this fragment?
|
||||
String name = makeFragmentName(container.getId(), position);
|
||||
|
||||
// Remove item from the cache
|
||||
mFragmentCache.remove(name);
|
||||
|
||||
Fragment fragment = mFragmentManager.findFragmentByTag(name);
|
||||
if (fragment != null) {
|
||||
if (DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment);
|
||||
mCurTransaction.attach(fragment);
|
||||
} else {
|
||||
fragment = getItem(position);
|
||||
if(fragment == null) {
|
||||
if (DEBUG) Log.e(TAG, "NPE workaround for getItem(). See b/7103023");
|
||||
return null;
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
|
||||
mCurTransaction.add(container.getId(), fragment,
|
||||
makeFragmentName(container.getId(), position));
|
||||
}
|
||||
if (fragment != mCurrentPrimaryItem) {
|
||||
fragment.setMenuVisibility(false);
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(View container, int position, Object object) {
|
||||
if (mCurTransaction == null) {
|
||||
mCurTransaction = mFragmentManager.beginTransaction();
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "Detaching item #" + position + ": f=" + object
|
||||
+ " v=" + ((Fragment)object).getView());
|
||||
|
||||
Fragment fragment = (Fragment) object;
|
||||
String name = fragment.getTag();
|
||||
if (name == null) {
|
||||
// We prefer to get the name directly from the fragment, but, if the fragment is
|
||||
// detached before the add transaction is committed, this could be 'null'. In
|
||||
// that case, generate a name so we can still cache the fragment.
|
||||
name = makeFragmentName(container.getId(), position);
|
||||
}
|
||||
|
||||
mFragmentCache.put(name, fragment);
|
||||
mCurTransaction.detach(fragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrimaryItem(View container, int position, Object object) {
|
||||
Fragment fragment = (Fragment) object;
|
||||
if (fragment != mCurrentPrimaryItem) {
|
||||
if (mCurrentPrimaryItem != null) {
|
||||
mCurrentPrimaryItem.setMenuVisibility(false);
|
||||
}
|
||||
if (fragment != null) {
|
||||
fragment.setMenuVisibility(true);
|
||||
}
|
||||
mCurrentPrimaryItem = fragment;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpdate(View container) {
|
||||
if (mCurTransaction != null && !mFragmentManager.isDestroyed()) {
|
||||
mCurTransaction.commitAllowingStateLoss();
|
||||
mCurTransaction = null;
|
||||
mFragmentManager.executePendingTransactions();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
// Ascend the tree to determine if the view is a child of the fragment
|
||||
View root = ((Fragment) object).getView();
|
||||
for (Object v = view; v instanceof View; v = ((View) v).getParent()) {
|
||||
if (v == root) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable saveState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreState(Parcelable state, ClassLoader loader) {
|
||||
}
|
||||
|
||||
/** Creates a name for the fragment */
|
||||
protected String makeFragmentName(int viewId, int index) {
|
||||
return "android:switcher:" + viewId + ":" + index;
|
||||
}
|
||||
|
||||
/**
|
||||
* A cache of detached fragments.
|
||||
*/
|
||||
private class FragmentCache extends LruCache<String, Fragment> {
|
||||
public FragmentCache(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void entryRemoved(boolean evicted, String key,
|
||||
Fragment oldValue, Fragment newValue) {
|
||||
// remove the fragment if it's evicted OR it's replaced by a new fragment
|
||||
if (evicted || (newValue != null && oldValue != newValue)) {
|
||||
mCurTransaction.remove(oldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Google Inc.
|
||||
* Licensed to 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.ex.photo.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.collection.SimpleArrayMap;
|
||||
|
||||
import com.android.ex.photo.Intents;
|
||||
import com.android.ex.photo.Intents.PhotoViewIntentBuilder;
|
||||
import com.android.ex.photo.fragments.PhotoViewFragment;
|
||||
import com.android.ex.photo.provider.PhotoContract.PhotoViewColumns;
|
||||
import com.android.ex.photo.provider.PhotoContract.PhotoQuery;
|
||||
|
||||
/**
|
||||
* Pager adapter for the photo view
|
||||
*/
|
||||
public class PhotoPagerAdapter extends BaseCursorPagerAdapter {
|
||||
protected SimpleArrayMap<String, Integer> mColumnIndices =
|
||||
new SimpleArrayMap<String, Integer>(PhotoQuery.PROJECTION.length);
|
||||
protected final float mMaxScale;
|
||||
protected boolean mDisplayThumbsFullScreen;
|
||||
|
||||
public PhotoPagerAdapter(
|
||||
Context context, androidx.fragment.app.FragmentManager fm, Cursor c,
|
||||
float maxScale, boolean thumbsFullScreen) {
|
||||
super(context, fm, c);
|
||||
mMaxScale = maxScale;
|
||||
mDisplayThumbsFullScreen = thumbsFullScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(Context context, Cursor cursor, int position) {
|
||||
final String photoUri = getPhotoUri(cursor);
|
||||
final String thumbnailUri = getThumbnailUri(cursor);
|
||||
final String contentDescription = getPhotoName(cursor);
|
||||
boolean loading = shouldShowLoadingIndicator(cursor);
|
||||
boolean onlyShowSpinner = false;
|
||||
if(photoUri == null && loading) {
|
||||
onlyShowSpinner = true;
|
||||
}
|
||||
|
||||
// create new PhotoViewFragment
|
||||
final PhotoViewIntentBuilder builder =
|
||||
Intents.newPhotoViewFragmentIntentBuilder(mContext, getPhotoViewFragmentClass());
|
||||
builder
|
||||
.setResolvedPhotoUri(photoUri)
|
||||
.setThumbnailUri(thumbnailUri)
|
||||
.setContentDescription(contentDescription)
|
||||
.setDisplayThumbsFullScreen(mDisplayThumbsFullScreen)
|
||||
.setMaxInitialScale(mMaxScale);
|
||||
|
||||
return createPhotoViewFragment(builder.build(), position, onlyShowSpinner);
|
||||
}
|
||||
|
||||
protected Class<? extends PhotoViewFragment> getPhotoViewFragmentClass() {
|
||||
return PhotoViewFragment.class;
|
||||
}
|
||||
|
||||
protected PhotoViewFragment createPhotoViewFragment(
|
||||
Intent intent, int position, boolean onlyShowSpinner) {
|
||||
return PhotoViewFragment.newInstance(intent, position, onlyShowSpinner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor swapCursor(Cursor newCursor) {
|
||||
mColumnIndices.clear();
|
||||
|
||||
if (newCursor != null) {
|
||||
for(String column : PhotoQuery.PROJECTION) {
|
||||
mColumnIndices.put(column, newCursor.getColumnIndexOrThrow(column));
|
||||
}
|
||||
|
||||
for(String column : PhotoQuery.OPTIONAL_COLUMNS) {
|
||||
int index = newCursor.getColumnIndex(column);
|
||||
if (index != -1) {
|
||||
mColumnIndices.put(column, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.swapCursor(newCursor);
|
||||
}
|
||||
|
||||
public String getPhotoUri(Cursor cursor) {
|
||||
return getString(cursor, PhotoViewColumns.CONTENT_URI);
|
||||
}
|
||||
|
||||
public String getThumbnailUri(Cursor cursor) {
|
||||
return getString(cursor, PhotoViewColumns.THUMBNAIL_URI);
|
||||
}
|
||||
|
||||
public String getContentType(Cursor cursor) {
|
||||
return getString(cursor, PhotoViewColumns.CONTENT_TYPE);
|
||||
}
|
||||
|
||||
public String getPhotoName(Cursor cursor) {
|
||||
return getString(cursor, PhotoViewColumns.NAME);
|
||||
}
|
||||
|
||||
public boolean shouldShowLoadingIndicator(Cursor cursor) {
|
||||
String value = getString(cursor, PhotoViewColumns.LOADING_INDICATOR);
|
||||
if (value == null) {
|
||||
return false;
|
||||
} else {
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
}
|
||||
|
||||
private String getString(Cursor cursor, String column) {
|
||||
if (mColumnIndices.containsKey(column)) {
|
||||
return cursor.getString(mColumnIndices.get(column));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user