blob: 47cde791c57e90f86cbdedba31e28003db8980b7 [file] [log] [blame]
/*
* 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;
}
}