/*
* Copyright (C) 2006 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 org.robolectric.shadows;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.os.Handler;
import android.util.Config;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.FilterQueryProvider;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import java.util.ArrayList;
import java.util.List;
import static android.widget.CursorAdapter.FLAG_AUTO_REQUERY;
import static android.widget.CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER;
@Implements(CursorAdapter.class)
public class ShadowCursorAdapter extends ShadowBaseAdapter {
@RealObject CursorAdapter realCursorAdapter;
private List<View> views = new ArrayList<>();
@Implementation
public View getView(int position, View convertView, ViewGroup parent) {
// if the cursor is null OR there are no views to dispense return null
if (this.mCursor == null || views.size() == 0 ) {
return null;
}
if (convertView != null) {
return convertView;
}
return views.get(position);
}
/**
* Sets a list of views to be returned for successive calls to {@link #getView(int, View, ViewGroup)}.
*
* @param views The list of views
*/
public void setViews(List<View> views) {
this.views = views;
}
protected boolean mDataValid;
protected boolean mAutoRequery;
protected Cursor mCursor;
protected Context mContext;
protected int mRowIDColumn;
protected ChangeObserver mChangeObserver;
protected DataSetObserver mDataSetObserver;
protected FilterQueryProvider mFilterQueryProvider;
public void __constructor__(Context context, Cursor c) {
initialize(context, c, FLAG_AUTO_REQUERY);
}
public void __constructor__(Context context, Cursor c, boolean autoRequery) {
initialize(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
}
// Recommended constructor for API level 11+
public void __constructor__(Context context, Cursor c, int flags) {
initialize(context, c, flags);
}
// renamed from Android source so as not to conflict with RobolectricWiringTest
private void initialize(Context context, Cursor c, int flags) {
boolean cursorPresent = c != null;
if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
flags |= FLAG_REGISTER_CONTENT_OBSERVER;
mAutoRequery = true;
}
mCursor = c;
mDataValid = cursorPresent;
mContext = context;
mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
mChangeObserver = new ChangeObserver();
mDataSetObserver = new MyDataSetObserver();
}
if (cursorPresent) {
if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
}
}
@Implementation
public Cursor getCursor() {
return mCursor;
}
@Implementation
public int getCount() {
if (mDataValid && mCursor != null) {
return mCursor.getCount();
} else {
return 0;
}
}
@Implementation
public Object getItem(int position) {
if (mDataValid && mCursor != null) {
mCursor.moveToPosition(position);
return mCursor;
} else {
return null;
}
}
@Implementation
public long getItemId(int position) {
if (mDataValid && mCursor != null) {
this.mCursor.getColumnIndexOrThrow("_id");
if (mCursor.moveToPosition(position)) {
return mCursor.getLong(mRowIDColumn);
} else {
return 0;
}
} else {
return 0;
}
}
@Implementation
public boolean hasStableIds() {
return true;
}
@Implementation
public Cursor swapCursor(Cursor cursor) {
if (cursor == mCursor) {
return null;
}
Cursor old = mCursor;
if (mCursor != null) {
if (mChangeObserver != null) mCursor.unregisterContentObserver(mChangeObserver);
if (mDataSetObserver != null) mCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = cursor;
if (cursor != null) {
if (mChangeObserver != null) cursor.registerContentObserver(mChangeObserver);
if (mDataSetObserver != null) cursor.registerDataSetObserver(mDataSetObserver);
mRowIDColumn = cursor.getColumnIndexOrThrow("_id");
mDataValid = true;
// notify the observers about the new cursor
realCursorAdapter.notifyDataSetChanged();
} else {
mRowIDColumn = -1;
mDataValid = false;
// notify the observers about the lack of a data set
realCursorAdapter.notifyDataSetInvalidated();
}
return old;
}
@Implementation
public void changeCursor(Cursor newCursor) {
Cursor old = swapCursor(newCursor);
if (old != null) {
old.close();
}
}
@Implementation
public CharSequence convertToString(Cursor cursor) {
return cursor == null ? "" : cursor.toString();
}
@Implementation
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (mFilterQueryProvider != null) {
return mFilterQueryProvider.runQuery(constraint);
}
return mCursor;
}
@Implementation
public FilterQueryProvider getFilterQueryProvider() {
return mFilterQueryProvider;
}
@Implementation
public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {
mFilterQueryProvider = filterQueryProvider;
}
protected void onContentChangedInternal() {
if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
mDataValid = mCursor.requery();
}
}
private class ChangeObserver extends ContentObserver {
public ChangeObserver() {
super(new Handler());
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
@Override
public void onChange(boolean selfChange) {
onContentChangedInternal();
}
}
private class MyDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
mDataValid = true;
realCursorAdapter.notifyDataSetChanged();
}
@Override
public void onInvalidated() {
mDataValid = false;
realCursorAdapter.notifyDataSetInvalidated();
}
}
}