Java tutorial
/* * ================================================================================================= * Copyright (C) 2013 - 2014 Martin Albedinsky [Wolf-ITechnologies] * ================================================================================================= * Licensed under the Apache License, Version 2.0 or later (further "License" only). * ------------------------------------------------------------------------------------------------- * You may use this file only in compliance with the License. More details and copy of this License * you may obtain at * * http://www.apache.org/licenses/LICENSE-2.0 * * You can redistribute, modify or publish any part of the code written within this file but as it * is described in the License, the software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES or CONDITIONS OF ANY KIND. * * See the License for the specific language governing permissions and limitations under the License. * ================================================================================================= */ package com.wit.android.support.database.adapter; import android.content.Context; import android.content.res.Resources; import android.database.Cursor; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v4.widget.CursorAdapter; import android.view.AbsSavedState; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.wit.android.support.database.MissingDatabaseAnnotationException; import com.wit.android.support.database.annotation.CursorItemView; import com.wit.android.support.database.annotation.CursorItemViewHolder; import com.wit.android.support.database.annotation.CursorItemViewHolderFactory; import com.wit.android.support.database.util.DatabaseAnnotations; import java.lang.reflect.InvocationTargetException; /** * <h3>Class Overview</h3> * todo: description * <h3>Accepted annotations</h3> * <ul> * <li> * {@link com.wit.android.support.database.annotation.CursorItemView @CursorItemView} <b>[class - inherited]</b> * <p> * If this annotation is presented, a resource id provided by this annotation will be used to inflate * the desired view in {@link #onCreateView(int, android.database.Cursor, android.view.LayoutInflater, android.view.ViewGroup)}. * </li> * <li> * {@link com.wit.android.support.database.annotation.CursorItemViewHolderFactory @CursorItemViewHolderFactory} <b>[class - inherited]</b> * <p> * If this annotation is presented, a class provided by this annotation will be used to create instances * of holders for item views. * </li> * <li> * {@link com.wit.android.support.database.annotation.CursorItemViewHolder @CursorItemHolder} <b>[class - inherited]</b> * <p> * If this annotation is presented, a class provided by this annotation will be used to instantiate * an instance of the desired holder in {@link #onCreateViewHolder(int, android.database.Cursor, android.view.View)}. * </li> * </ul> * <h3>State saving</h3> * <pre> * public class MyCursorAdapter extends BaseCursorAdapter<Cursor> { * * // ... * * @Override * protected Parcelable onSaveInstanceState() { * final MyCursorAdapterState state = new MyCursorAdapterState(super.onSaveInstanceState()); * * // ... * // Pass here all data of this adapter which need to be saved to the state. * // ... * * return state; * } * * @Override * protected void onRestoreInstanceState(Parcelable savedState) { * if (!(savedState instanceof MyCursorAdapterState)) { * // Passed savedState is not our state, let super to process it. * super.onRestoreInstanceState(savedState); * return; * } * * final MyCursorAdapterState state = (MyCursorAdapterState) savedState; * // Pass superState to super to process it. * super.onRestoreInstanceState(savedState.getSuperState()); * * // ... * // Set here all data of this adapter which need to be restored from the savedState. * // ... * } * * // ... * * // Implementation of BaseSavedState for this adapter. * static class MyCursorAdapterState extends BaseSavedState { * * // Each implementation of saved state need to have its own CREATOR provided. * public static final Creator<MyCursorAdapterState> CREATOR = new Creator<> { * * @Override * public MyCursorAdapterState createFromParcel(Parcel source) { * return new MyCursorAdapterState(source); * } * * @Override * public MyCursorAdapterState[] newArray(int size) { * return new MyCursorAdapterState[size]; * } * } * * MyCursorAdapterState(Parcel source) { * super(source); * // Restore here state's data. * } * * // Constructor used to chain the state of inheritance hierarchies. * MyCursorAdapterState(Parcelable superState) { * super(superState); * } * * @Override * public void writeToParcel(Parcel dest, int flags) { * super.writeToParcel(dest, flags); * // Save here state's data. * } * } * } * </pre> * * @param <C> A type of the cursor of which data will be presented by a subclass of this BaseCursorAdapter. * @author Martin Albedinsky */ public abstract class BaseCursorAdapter<C extends Cursor> extends CursorAdapter implements FactoryHolderCursorAdapter { /** * Interface =================================================================================== */ /** * Constants =================================================================================== */ /** * Log TAG. */ // private static final String TAG = "BaseCursorAdapter"; /** * Flag indicating whether the debug output trough log-cat is enabled or not. */ // private static final boolean DEBUG_ENABLED = true; /** * Flag indicating whether the output trough log-cat is enabled or not. */ // private static final boolean LOG_ENABLED = true; /** * Static members ============================================================================== */ /** * Members ===================================================================================== */ /** * Context in which will be this adapter used. */ protected final Context mContext; /** * Layout inflater used to inflate new views for this adapter. */ protected final LayoutInflater mLayoutInflater; /** * Application resources. */ protected final Resources mResources; /** * Class of cursor wrapper used to wrap cursor data set of this adapter in {@link #onWrapCursor(android.database.Cursor)}. */ private Class<C> mClassOfCursorWrapper; /** * Item view type for the current {@link #getView(int, android.view.View, android.view.ViewGroup)} * iteration. */ private int mCurrentViewType; /** * Resource id of the view which should be inflated as item view. */ private int mViewRes = -1; /** * Class to be used to instantiate an instance of holder for item view. */ private Class<? extends CursorViewHolder> mClassOfHolder; /** * Factory responsible for instantiation of item view holders for this adapter. */ private CursorViewHolderFactory mHolderFactory; /** * Registered OnCursorDataSetListener callback. */ private OnCursorDataSetListener mDataSetListener; /** * Registered OnCursorDataSetActionListener callback. */ private OnCursorDataSetActionListener mDataSetActionListener; /** * Constructors ================================================================================ */ /** * Same as {@link #BaseCursorAdapter(android.content.Context, android.database.Cursor)} with * {@code null} initial cursor. */ public BaseCursorAdapter(@NonNull Context context) { this(context, (C) null); } /** * Same as {@link #BaseCursorAdapter(android.content.Context, android.database.Cursor, int)} with * {@link #FLAG_REGISTER_CONTENT_OBSERVER} flag. */ public BaseCursorAdapter(@NonNull Context context, @Nullable C cursor) { this(context, cursor, FLAG_REGISTER_CONTENT_OBSERVER); } /** * Same as {@link #BaseCursorAdapter(android.content.Context, Class, int)} with * {@link #FLAG_REGISTER_CONTENT_OBSERVER} flag. */ public BaseCursorAdapter(@NonNull Context context, @NonNull Class<C> classOfCursorWrapper) { this(context, classOfCursorWrapper, FLAG_REGISTER_CONTENT_OBSERVER); } /** * Creates a new instance of BaseCursorAdapter with {@code null} initial cursor. * <p> * If {@link com.wit.android.support.database.annotation.CursorItemView @CursorItemView}, * {@link com.wit.android.support.database.annotation.CursorItemViewHolderFactory @CursorItemViewHolderFactory}, * or {@link com.wit.android.support.database.annotation.CursorItemViewHolder @CursorItemViewHolder} * annotations are presented above a subclass of this BaseCursorAdapter, they will be processed here. * * @param context Context in which will be this adapter used. * @param classOfCursorWrapper A class of cursor wrapper used to wrap each cursor dispatched to * this adapter by {@link #swapCursor(android.database.Cursor)}. Pass * {@code null} if cursor wrapping is not needed. * @param flags Set of flags to set up cursor management behaviour. * @throws NullPointerException If the given context is invalid. */ public BaseCursorAdapter(@NonNull Context context, @NonNull Class<C> classOfCursorWrapper, int flags) { this(context, (C) null, flags); this.mClassOfCursorWrapper = classOfCursorWrapper; } /** * Creates a new instance of BaseCursorAdapter with the given initial {@code cursor} data set. * <p> * If {@link com.wit.android.support.database.annotation.CursorItemView @CursorItemView} or * {@link com.wit.android.support.database.annotation.CursorItemViewHolder @CursorItemViewHolder} * annotations are presented above a subclass of this BaseCursorAdapter, they will be processed here. * * @param context Context in which will be this adapter used. * @param cursor Initial cursor data set for this adapter. Can be {@code null}. * @param flags Set of flags to set up cursor management behaviour. * @throws NullPointerException If the given context is invalid. */ public BaseCursorAdapter(@NonNull Context context, @Nullable C cursor, int flags) { super(context, cursor, flags); this.processClassAnnotations(((Object) this).getClass()); // Set up. this.mContext = context; this.mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.mResources = context.getResources(); } /** * Methods ===================================================================================== */ /** * Public -------------------------------------------------------------------------------------- */ /** * Called to save the current state of this adapter. * * @return Saved state of this adapter or an <b>empty</b> state if this adapter does not need to * save its state. */ @NonNull public Parcelable dispatchSaveInstanceState() { return onSaveInstanceState(); } /** * Called to restore a previous state, saved by {@link #dispatchSaveInstanceState()}, of this adapter. * * @param savedState Should be the same state as obtained by {@link #dispatchSaveInstanceState()} * before. */ public void dispatchRestoreInstanceState(@NonNull Parcelable savedState) { onRestoreInstanceState(savedState); } /** * Notifies the attached OnCursorDataSetListener listeners, that the data set of this adapter has * been loaded. * <p> * Unlike {@link #notifyDataSetChanged()} this can be used to notify data set change to target * directly data loading from database not just inner data set change, like change in selection, * for such a purpose use {@link #notifyDataSetChanged()} instead. */ public void notifyDataSetLoaded() { notifyCursorDataSetLoadedInner(); } /** * This will also notify the current {@link OnCursorDataSetListener} callback if it is presented. */ @Override public void notifyDataSetChanged() { super.notifyDataSetChanged(); notifyCursorDataSetChangedInner(); } /** * This will also notify the current {@link OnCursorDataSetActionListener} callback if it is presented. */ @Override public void notifyDataSetInvalidated() { super.notifyDataSetInvalidated(); notifyCursorDataSetInvalidatedInner(); } /** */ @Nullable @Override public C getItem(int position) { return this.getItemInner(position); } /** */ @Override public final int getCurrentViewType() { return mCurrentViewType; } /** */ @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { return this.getViewInner(position, convertView, parent); } /** * @deprecated Due to optimization. Use {@link #onCreateView(int, android.database.Cursor, android.view.LayoutInflater, android.view.ViewGroup)} * instead. */ @Override @Deprecated public final View newView(Context context, Cursor cursor, ViewGroup viewGroup) { return null; } /** * @deprecated Due to optimization. Use {@link #onBindView(int, android.database.Cursor, Object)} * instead. */ @Override @Deprecated public final void bindView(View view, Context context, Cursor cursor) { } /** */ @Nullable @Override public C swapCursor(@Nullable Cursor newCursor) { return this.swapCursorInner(newCursor); } /** * Wraps the given <var>cursor</var> into wrapper specific for this cursor adapter. * * @param cursor Cursor to wrap. * @return An instance of cursor wrapper holding the given cursor or cursor itself if this cursor * adapter does not requires cursor wrapping. */ @NonNull public C wrapCursor(@NonNull Cursor cursor) { return onWrapCursor(cursor); } /** * Getters + Setters --------------------------------------------------------------------------- */ /** */ @Nullable @Override public C getCursor() { return this.getCursorInner(); } /** * Returns the context with which was this adapter created. * * @return Same context as passed during initialization. */ @NonNull public Context getContext() { return mContext; } /** * Returns layout inflater instance provided by the context passed during initialization of this * adapter. * * @return An instance of LayoutInflater. */ @NonNull public LayoutInflater getLayoutInflater() { return mLayoutInflater; } /** * Returns an application's resources provided by the context passed during initialization of * this adapter. * * @return An application's resources. */ @NonNull public Resources getResources() { return mResources; } /** * Wrapped {@link android.content.res.Resources#getString(int)} for the current resources. */ @NonNull public String getString(@StringRes int resId) { return (mResources != null) ? mResources.getString(resId) : ""; } /** * Wrapped {@link android.content.res.Resources#getString(int, Object...)} for the current resources. */ @NonNull public String getString(@StringRes int resId, @Nullable Object... args) { return (mResources != null) ? mResources.getString(resId, args) : ""; } /** * Wrapped {@link android.content.res.Resources#getText(int)} for the current resources. */ @NonNull public CharSequence getText(@StringRes int resId) { return (mResources != null) ? mResources.getText(resId) : ""; } /** * Wrapped {@link android.content.res.Resources#getText(int, java.lang.CharSequence)} for the * current resources. */ @NonNull public CharSequence getText(@StringRes int resId, @Nullable CharSequence def) { return (mResources != null) ? mResources.getText(resId, def) : ""; } /** * Registers a callback to be invoked when the data set of this adapter has been changed or * invalidated. * * @param listener Listener callback. */ public void setOnDataSetListener(@NonNull OnCursorDataSetListener listener) { this.mDataSetListener = listener; } /** * Removes the current OnCursorDataSetListener callback. */ public void removeOnDataSetListener() { this.mDataSetListener = null; } /** * Registers a callback to be invoked when there was a specific action performed above the * current data set of this adapter. * * @param listener Listener callback. */ public void setOnDataSetActionListener(@NonNull OnCursorDataSetActionListener listener) { this.mDataSetActionListener = listener; } /** * Removes the current OnCursorDataSetActionListener callback. */ public void removeOnDataSetActionListener() { this.mDataSetActionListener = null; } /** */ @Override public int getAdapterId() { return 0; } /** * Protected ----------------------------------------------------------------------------------- */ /** * Same as {@link #getCurrentViewType()}. */ protected int currentViewType() { return mCurrentViewType; } /** * Inflates a new view hierarchy from the given xml resource. * * @param resource Resource id of a view to inflate. * @param parent A parent view, to resolve correct layout params for the newly creating view. * @return The root view of the inflated view hierarchy. * @see android.view.LayoutInflater#inflate(int, android.view.ViewGroup) */ @NonNull protected final View inflate(@LayoutRes int resource, @NonNull ViewGroup parent) { return mLayoutInflater.inflate(resource, parent, false); } /** * Called from {@link #wrapCursor(android.database.Cursor)} to wrap the given <var>cursor</var>. * <p> * By default this will try to wrap the specified cursor into cursor wrapper class of which was * provided to constructor of this adapter. If no cursor wrapper class was provided, cursor itself * will be returned. * * @param cursor Cursor to wrap. * @return Wrapped cursor into cursor wrapper or cursor itself if no wrapping is needed. */ @NonNull protected C onWrapCursor(@NonNull Cursor cursor) { return this.wrapCursorInner(cursor); } /** * Moves the given <var>cursor</var> to the requested <var>position</var>. * <p> * By default this will move the given cursor to position corrected by {@link #correctCursorPosition(int)}. * <p> * This is called from {@link #getView(int, android.view.View, android.view.ViewGroup)} for the * currently iterated position. * * @param cursor Cursor which should be moved to the specified position. * @param position Position to which should be cursor moved. * @return {@code True} if the cursor has been moved to the requested position, {@code false} * otherwise. */ protected boolean moveCursorToPosition(@NonNull C cursor, int position) { return cursor.moveToPosition(correctCursorPosition(position)); } /** * Corrects the given cursor <var>position</var> according to the current data set size to ensure * that the current cursor will be moved to the correct position. * <p> * This can be useful when this cursor adapter presents also data which are not available within * the current cursor, like array of alphabetic headers, when the current cursor is sorted alphabetically * and this adapter presents above each alphabetical section a header with a letter specific for * such a section, so practically the current data set consists of two data sets, one is cursor * and other one is array of headers. * * @param position Position which should be corrected. * @return The given position. * @see #moveCursorToPosition(android.database.Cursor, int) */ protected int correctCursorPosition(int position) { return position; } /** * Invoked to create a view for an item from the current cursor data set at the specified position. * <p> * This is invoked only if <var>convertView</var> for the specified <var>position</var> in * {@link #getView(int, android.view.View, android.view.ViewGroup)} was {@code null}. * <p> * <b>Note</b>, that if {@link com.wit.android.support.database.annotation.CursorItemView @CursorItemView} * annotation is presented, a resource id provided by this annotation will be used to inflate the * requested view, otherwise implementation of this method is <b>required</b> or exception will * be thrown. * * @param position Position of the item from the current cursor data set for which should be a * new view created. * @param cursor Current cursor already moved to the specified position. * @param inflater Layout inflater which can be used to inflate the requested view. * @param parent A parent view, to resolve correct layout params for the newly creating view. * @return New instance of the requested view. * @throws MissingDatabaseAnnotationException If there is no @CursorItemView annotation presented. * @see #inflate(int, android.view.ViewGroup) */ @NonNull protected View onCreateView(int position, @NonNull C cursor, @NonNull LayoutInflater inflater, @NonNull ViewGroup parent) { if (mViewRes >= 0) { return inflater.inflate(mViewRes, parent, false); } throw new MissingDatabaseAnnotationException( "Cannot to create view for position(" + position + ") without resource id. " + "No @" + CursorItemView.class.getSimpleName() + " annotation presented."); } /** * Invoked to create a view holder for a view of an item from the current cursor data set at the * specified position. * <p> * This is invoked only if <var>convertView</var> for the specified <var>position</var> in * {@link #getView(int, android.view.View, android.view.ViewGroup)} was {@code null}, so as * view also holder need to be created. * <p> * If {@link com.wit.android.support.database.annotation.CursorItemViewHolderFactory @CursorItemViewHolderFactory} * annotation is presented, factory instantiated from the class provided by this annotation will * be used to create the requested view holder, otherwise * {@link com.wit.android.support.database.annotation.CursorItemViewHolder @CursorItemViewHolder} * annotation will be processed as described below. * <p> * If {@link com.wit.android.support.database.annotation.CursorItemViewHolder @CursorItemViewHolder} * annotation is presented, a class provided by this annotation will be used to instantiate the * requested view holder, otherwise {@code null} holder will be returned so the view created * by {@link #onCreateView(int, android.database.Cursor, android.view.LayoutInflater, android.view.ViewGroup)} * for the specified position will be passed as holder to {@link #onBindView(int, android.database.Cursor, Object)}. * * @param position Position of the item from the current data set for which should be a new view * holder created. * @param cursor Current cursor already moved to the specified position. * @param itemView An instance of the same view as obtained from * {@link #onCreateView(int, android.database.Cursor, android.view.LayoutInflater, android.view.ViewGroup)} * for the specified position. * @return New instance of the requested view holder. * @throws java.lang.IllegalStateException If the class provided by @CursorItemViewHolder annotation can not * be accessed or does not have an empty public constructor. */ @Nullable protected Object onCreateViewHolder(int position, @NonNull C cursor, @NonNull View itemView) { if (mHolderFactory != null) { final CursorViewHolder holder = mHolderFactory.createHolder(this, position, itemView); if (holder != null) { holder.create(position, itemView); return holder; } } if (mClassOfHolder != null) { CursorViewHolder holder; try { holder = mClassOfHolder.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new IllegalStateException("Failed to create view holder from class(" + mClassOfHolder + "). " + "Check if this holder class has public access and empty public constructor."); } holder.create(position, itemView); return holder; } // Return null holder, so view created by onCreateView(...) will be passed as holder to onBindView(...). return null; } /** * Invoked to set up and populate a view of an item from the current cursor data set at the specified * position. This is invoked whenever {@link #getView(int, android.view.View, android.view.ViewGroup)} * is called. * <p> * <b>Note</b>, that if {@link #onCreateViewHolder(int, android.database.Cursor, android.view.View)} * returns {@code null} for the specified <var>position</var> here passed <var>viewHolder</var> * will be the view created by {@link #onCreateView(int, android.database.Cursor, android.view.LayoutInflater, android.view.ViewGroup)} * for the specified position or just recycled view for such a position. This approach can be used, * when a view hierarchy of the specific list item is represented by one custom view, where such * a view represents a holder for all its child views. * <p> * By default this will try to bind the given <var>viewHolder</var> (if it is instanceof {@link CursorViewHolder}), * otherwise implementation of this method is <b>required</b> or exception will be thrown. * * @param position Position of the item from the current cursor data set of which view to set up. * @param cursor Current cursor already moved to the specified position. * @param viewHolder An instance of the same holder as provided by {@link #onCreateViewHolder(int, android.database.Cursor, android.view.View)} * for the specified position or converted view as holder as described above. * @throws java.lang.IllegalStateException If binding process for the specified position fails. */ protected void onBindView(int position, @NonNull C cursor, @NonNull Object viewHolder) { if (!bindViewInner(position, cursor, viewHolder)) { throw new IllegalStateException("Failed to bind view at position(" + position + "). " + viewHolder + " is not instance of CursorViewHolder."); } } /** * Called to notify, that the given <var>action</var> was performed for the specified <var>position</var>. * <p> * If {@link #onDataSetActionSelected(int, int, Object)} will not process this call, the current * {@link OnCursorDataSetActionListener} will be notified if it is presented. * * @param action Action to be dispatched. * @param position The position for which was the given action performed. * @param data Additional data for the selected action to be dispatched to the listener. */ protected void notifyDataSetActionSelected(int action, int position, @Nullable Object data) { if (!onDataSetActionSelected(action, position, data)) { notifyCursorDataSetActionSelectedInner(action, position, data); } } /** * Invoked immediately after {@link #notifyDataSetActionSelected(int, int, Object)} was called. * * @return {@code True} to indicate that this event was processed here, otherwise the current * {@link OnCursorDataSetActionListener} will be notified about this event if it is presented. */ protected boolean onDataSetActionSelected(int action, int position, @Nullable Object data) { return false; } /** * Invoked immediately after {@link #dispatchSaveInstanceState()} was called, to save the current * state of this adapter. * <p> * If you decide to override this method, do not forget to call {@code super.onSaveInstanceState()} * and pass super state obtained from the super to constructor of your {@link BaseSavedState} * implementation with such a parameter to ensure the state of all classes along the chain is saved. * * @return Return here your implementation of {@link BaseSavedState} if you want to save state of * your adapter, otherwise no implementation of this method is necessary. */ @NonNull protected Parcelable onSaveInstanceState() { return BaseSavedState.EMPTY_STATE; } /** * Called immediately after {@link #dispatchRestoreInstanceState(android.os.Parcelable)} was called, * to restore a previous state, (saved in {@link #onSaveInstanceState()}), of this adapter. * * @param savedState Before saved state of this adapter. */ protected void onRestoreInstanceState(@NonNull Parcelable savedState) { } /** * Called to process all annotation of the given <var>classOfAdapter</var>. * * @param classOfAdapter This adapter's class. */ private void processClassAnnotations(Class<?> classOfAdapter) { // Obtain item view. final CursorItemView itemView = DatabaseAnnotations.obtainAnnotationFrom(classOfAdapter, CursorItemView.class, BaseCursorAdapter.class); if (itemView != null) { this.mViewRes = itemView.value(); } // Obtain item view holder factory. final CursorItemViewHolderFactory holderFactory = DatabaseAnnotations.obtainAnnotationFrom(classOfAdapter, CursorItemViewHolderFactory.class, BaseCursorAdapter.class); if (holderFactory != null) { try { this.mHolderFactory = holderFactory.value().newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new IllegalStateException( "Failed to create view holder factory from class(" + holderFactory.value() + "). " + "Check if this factory class has public access and empty public constructor."); } } // Obtain item view holder. final CursorItemViewHolder itemViewHolder = DatabaseAnnotations.obtainAnnotationFrom(classOfAdapter, CursorItemViewHolder.class, BaseCursorAdapter.class); if (itemViewHolder != null) { this.mClassOfHolder = itemViewHolder.value(); } } /** * Called to dispatch data set loading to the current OnCursorDataSetListener callback. */ @SuppressWarnings("unchecked") void notifyCursorDataSetLoadedInner() { if (mDataSetListener != null) { mDataSetListener.onCursorDataSetLoaded(this); } } /** * Called to dispatch data set change to the current OnCursorDataSetListener callback. */ @SuppressWarnings("unchecked") void notifyCursorDataSetChangedInner() { if (mDataSetListener != null) { mDataSetListener.onCursorDataSetChanged(this); } } /** * Called to dispatch data set invalidation to the current OnCursorDataSetListener callback. */ @SuppressWarnings("unchecked") void notifyCursorDataSetInvalidatedInner() { if (mDataSetListener != null) { mDataSetListener.onCursorDataSetInvalidated(this); } } /** * Inner implementation of {@link #notifyDataSetActionSelected(int, int, Object)} to hide such an * implementation. */ @SuppressWarnings("unchecked") void notifyCursorDataSetActionSelectedInner(int action, int position, Object data) { if (mDataSetActionListener != null) { mDataSetActionListener.onCursorDataSetActionSelected(this, action, position, getItemId(position), data); } } /** * Private ------------------------------------------------------------------------------------- */ /** * Inner implementation of {@link #getCursor()} to hide such an implementation. */ @SuppressWarnings("unchecked") private C getCursorInner() { return (C) super.getCursor(); } /** * Inner implementation of {@link #getItem(int)} to hide such an implementation. */ @SuppressWarnings("unchecked") private C getItemInner(int position) { return (C) super.getItem(correctCursorPosition(position)); } /** * Inner implementation of {@link #getView(int, android.view.View, android.view.ViewGroup)} to * hide such an implementation. */ @SuppressWarnings("unchecked") private View getViewInner(int position, View convertView, ViewGroup parent) { final C cursor = getCursor(); if (cursor == null) { throw new IllegalStateException("Can not present data from the invalid cursor."); } if (!moveCursorToPosition(cursor, position)) { throw new IllegalStateException( "Can not to move the current cursor to the specified position(" + position + ")."); } View view = convertView; Object viewHolder; // Obtain current item view type. this.mCurrentViewType = getItemViewType(position); if (view == null) { // Dispatch to create new view. view = onCreateView(position, cursor, mLayoutInflater, parent); // Resolve holder for the newly created view. final Object holder = onCreateViewHolder(position, cursor, view); if (holder != null) { view.setTag(viewHolder = holder); } else { viewHolder = view; } } else { final Object holder = view.getTag(); viewHolder = holder != null ? holder : view; } // Dispatch to bind view with data. onBindView(position, cursor, viewHolder); return view; } /** * Inner implementation of {@link #swapCursor(android.database.Cursor)} to hide such an implementation. */ @SuppressWarnings("unchecked") private C swapCursorInner(Cursor newCursor) { if (newCursor != null) { return (C) super.swapCursor(onWrapCursor(newCursor)); } return (C) super.swapCursor(null); } /** * Inner implementation of {@link #onWrapCursor(android.database.Cursor)} to hide such an implementation. */ @SuppressWarnings("unchecked") private C wrapCursorInner(Cursor cursor) { if (mClassOfCursorWrapper != null) { if (mClassOfCursorWrapper.equals(cursor.getClass())) { // Do not wrap same classes. return (C) cursor; } try { return mClassOfCursorWrapper.getConstructor(Cursor.class).newInstance(cursor); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new IllegalStateException("Failed to create cursor wrapper from class(" + mClassOfCursorWrapper + "). " + "Check if this wrapper class has public access and public constructor which takes Cursor as parameter."); } } return (C) cursor; } /** * Inner implementation of {@link #onBindView(int, Cursor, Object)} to hide such an implementation. */ @SuppressWarnings("unchecked") boolean bindViewInner(int position, C cursor, Object viewHolder) { if (viewHolder instanceof CursorViewHolder) { ((CursorViewHolder<C, BaseCursorAdapter<C>>) viewHolder).bind(position, cursor, this); return true; } return false; } /** * Inner classes =============================================================================== */ /** * <h3>Class Overview</h3> * A {@link android.view.AbsSavedState} implementation that should be used by inheritance hierarchies * of {@link BaseCursorAdapter} to ensure the state of all classes along the chain is saved. * * @author Martin Albedinsky */ public static class BaseSavedState extends AbsSavedState { /** * Members ================================================================================= */ /** * Creator used to create an instance or array of instances of BaseSavedState from {@link android.os.Parcel}. */ public static final Creator<BaseSavedState> CREATOR = new Creator<BaseSavedState>() { /** */ @Override public BaseSavedState createFromParcel(Parcel source) { return new BaseSavedState(source); } /** */ @Override public BaseSavedState[] newArray(int size) { return new BaseSavedState[size]; } }; /** * Constructors ============================================================================ */ /** * Creates a new instance BaseSavedState with the given <var>superState</var> to allow * chaining of saved states in {@link #onSaveInstanceState()} and also in * {@link #onRestoreInstanceState(android.os.Parcelable)}. * * @param superState A super state obtained from {@code super.onSaveInstanceState()} * within {@code onSaveInstanceState()} of a specific {@link BaseCursorAdapter} * implementation. */ protected BaseSavedState(Parcelable superState) { super(superState); } /** * Called form {@link #CREATOR} to create an instance of BaseSavedState form the given parcel * <var>source</var>. * * @param source Parcel with data for a new instance. */ protected BaseSavedState(Parcel source) { super(source); } } }