Merge "Adding support for async view loading in RemoteViewsAdapter"
This commit is contained in:
@@ -377,13 +377,13 @@ public class AppWidgetHostView extends FrameLayout {
|
||||
* AppWidget provider. Will animate into these new views as needed
|
||||
*/
|
||||
public void updateAppWidget(RemoteViews remoteViews) {
|
||||
applyRemoteViews(remoteViews);
|
||||
applyRemoteViews(remoteViews, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected void applyRemoteViews(RemoteViews remoteViews) {
|
||||
protected void applyRemoteViews(RemoteViews remoteViews, boolean useAsyncIfPossible) {
|
||||
if (LOGD) Log.d(TAG, "updateAppWidget called mOld=" + mOld);
|
||||
|
||||
boolean recycled = false;
|
||||
@@ -423,7 +423,7 @@ public class AppWidgetHostView extends FrameLayout {
|
||||
mLayoutId = -1;
|
||||
mViewMode = VIEW_MODE_DEFAULT;
|
||||
} else {
|
||||
if (mAsyncExecutor != null) {
|
||||
if (mAsyncExecutor != null && useAsyncIfPossible) {
|
||||
inflateAsync(remoteViews);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6284,6 +6284,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
* @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
|
||||
*/
|
||||
public void setRemoteViewsAdapter(Intent intent) {
|
||||
setRemoteViewsAdapter(intent, false);
|
||||
}
|
||||
|
||||
/** @hide **/
|
||||
public Runnable setRemoteViewsAdapterAsync(final Intent intent) {
|
||||
return new RemoteViewsAdapter.AsyncRemoteAdapterAction(this, intent);
|
||||
}
|
||||
|
||||
/** @hide **/
|
||||
@Override
|
||||
public void setRemoteViewsAdapter(Intent intent, boolean isAsync) {
|
||||
// Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
|
||||
// service handling the specified intent.
|
||||
if (mRemoteAdapter != null) {
|
||||
@@ -6296,7 +6307,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
}
|
||||
mDeferNotifyDataSetChanged = false;
|
||||
// Otherwise, create a new RemoteViewsAdapter for binding
|
||||
mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this);
|
||||
mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this, isAsync);
|
||||
if (mRemoteAdapter.isDataReady()) {
|
||||
setAdapter(mRemoteAdapter);
|
||||
}
|
||||
|
||||
@@ -975,8 +975,19 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
|
||||
* @param intent the intent used to identify the RemoteViewsService for the adapter to
|
||||
* connect to.
|
||||
*/
|
||||
@android.view.RemotableViewMethod
|
||||
@android.view.RemotableViewMethod(asyncImpl="setRemoteViewsAdapterAsync")
|
||||
public void setRemoteViewsAdapter(Intent intent) {
|
||||
setRemoteViewsAdapter(intent, false);
|
||||
}
|
||||
|
||||
/** @hide **/
|
||||
public Runnable setRemoteViewsAdapterAsync(final Intent intent) {
|
||||
return new RemoteViewsAdapter.AsyncRemoteAdapterAction(this, intent);
|
||||
}
|
||||
|
||||
/** @hide **/
|
||||
@Override
|
||||
public void setRemoteViewsAdapter(Intent intent, boolean isAsync) {
|
||||
// Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
|
||||
// service handling the specified intent.
|
||||
if (mRemoteViewsAdapter != null) {
|
||||
@@ -989,7 +1000,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
|
||||
}
|
||||
mDeferNotifyDataSetChanged = false;
|
||||
// Otherwise, create a new RemoteViewsAdapter for binding
|
||||
mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this);
|
||||
mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this, isAsync);
|
||||
if (mRemoteViewsAdapter.isDataReady()) {
|
||||
setAdapter(mRemoteViewsAdapter);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public class GridView extends AbsListView {
|
||||
* through the specified intent.
|
||||
* @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
|
||||
*/
|
||||
@android.view.RemotableViewMethod
|
||||
@android.view.RemotableViewMethod(asyncImpl="setRemoteViewsAdapterAsync")
|
||||
public void setRemoteViewsAdapter(Intent intent) {
|
||||
super.setRemoteViewsAdapter(intent);
|
||||
}
|
||||
|
||||
@@ -455,7 +455,16 @@ public class ImageView extends View {
|
||||
|
||||
/** @hide **/
|
||||
public Runnable setImageResourceAsync(@DrawableRes int resId) {
|
||||
return new ImageDrawableCallback(getContext().getDrawable(resId), null, resId);
|
||||
Drawable d = null;
|
||||
if (resId != 0) {
|
||||
try {
|
||||
d = getContext().getDrawable(resId);
|
||||
} catch (Exception e) {
|
||||
Log.w(LOG_TAG, "Unable to find resource: " + resId, e);
|
||||
resId = 0;
|
||||
}
|
||||
}
|
||||
return new ImageDrawableCallback(d, null, resId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -865,7 +874,7 @@ public class ImageView extends View {
|
||||
} catch (Exception e) {
|
||||
Log.w(LOG_TAG, "Unable to find resource: " + mResource, e);
|
||||
// Don't try again.
|
||||
mUri = null;
|
||||
mResource = 0;
|
||||
}
|
||||
} else if (mUri != null) {
|
||||
d = getDrawableFromUri(mUri);
|
||||
|
||||
@@ -448,7 +448,7 @@ public class ListView extends AbsListView {
|
||||
* through the specified intent.
|
||||
* @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
|
||||
*/
|
||||
@android.view.RemotableViewMethod
|
||||
@android.view.RemotableViewMethod(asyncImpl="setRemoteViewsAdapterAsync")
|
||||
public void setRemoteViewsAdapter(Intent intent) {
|
||||
super.setRemoteViewsAdapter(intent);
|
||||
}
|
||||
|
||||
@@ -320,6 +320,10 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean prefersAsyncApply() {
|
||||
return false;
|
||||
}
|
||||
|
||||
int viewId;
|
||||
}
|
||||
|
||||
@@ -715,20 +719,29 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
|
||||
if (target instanceof AbsListView) {
|
||||
AbsListView v = (AbsListView) target;
|
||||
v.setRemoteViewsAdapter(intent);
|
||||
v.setRemoteViewsAdapter(intent, isAsync);
|
||||
v.setRemoteViewsOnClickHandler(handler);
|
||||
} else if (target instanceof AdapterViewAnimator) {
|
||||
AdapterViewAnimator v = (AdapterViewAnimator) target;
|
||||
v.setRemoteViewsAdapter(intent);
|
||||
v.setRemoteViewsAdapter(intent, isAsync);
|
||||
v.setRemoteViewsOnClickHandler(handler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
|
||||
OnClickHandler handler) {
|
||||
SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
|
||||
copy.isAsync = true;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public String getActionName() {
|
||||
return "SetRemoteViewsAdapterIntent";
|
||||
}
|
||||
|
||||
Intent intent;
|
||||
boolean isAsync = false;
|
||||
|
||||
public final static int TAG = 10;
|
||||
}
|
||||
@@ -1461,6 +1474,11 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
// unique from the standpoint of merging.
|
||||
return "ReflectionAction" + this.methodName + this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prefersAsyncApply() {
|
||||
return this.type == URI || this.type == ICON;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1598,6 +1616,11 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
return MERGE_APPEND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prefersAsyncApply() {
|
||||
return nestedViews != null && nestedViews.prefersAsyncApply();
|
||||
}
|
||||
|
||||
RemoteViews nestedViews;
|
||||
|
||||
public final static int TAG = 4;
|
||||
@@ -1749,6 +1772,11 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prefersAsyncApply() {
|
||||
return useIcons;
|
||||
}
|
||||
|
||||
public String getActionName() {
|
||||
return "TextViewDrawableAction";
|
||||
}
|
||||
@@ -3443,6 +3471,24 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the RemoteViews contains potentially costly operations and should be
|
||||
* applied asynchronously.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean prefersAsyncApply() {
|
||||
if (mActions != null) {
|
||||
final int count = mActions.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (mActions.get(i).prefersAsyncApply()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Context getContextForResources(Context context) {
|
||||
if (mApplication != null) {
|
||||
if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
|
||||
|
||||
@@ -41,6 +41,12 @@ import android.widget.RemoteViews.OnClickHandler;
|
||||
import com.android.internal.widget.IRemoteViewsAdapterConnection;
|
||||
import com.android.internal.widget.IRemoteViewsFactory;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@@ -73,7 +79,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
private final Context mContext;
|
||||
private final Intent mIntent;
|
||||
private final int mAppWidgetId;
|
||||
private LayoutInflater mLayoutInflater;
|
||||
private final Executor mAsyncViewLoadExecutor;
|
||||
|
||||
private RemoteViewsAdapterServiceConnection mServiceConnection;
|
||||
private WeakReference<RemoteAdapterConnectionCallback> mCallback;
|
||||
private OnClickHandler mRemoteViewsOnClickHandler;
|
||||
@@ -120,15 +127,33 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
/**
|
||||
* @return whether the adapter was set or not.
|
||||
*/
|
||||
public boolean onRemoteAdapterConnected();
|
||||
boolean onRemoteAdapterConnected();
|
||||
|
||||
public void onRemoteAdapterDisconnected();
|
||||
void onRemoteAdapterDisconnected();
|
||||
|
||||
/**
|
||||
* This defers a notifyDataSetChanged on the pending RemoteViewsAdapter if it has not
|
||||
* connected yet.
|
||||
*/
|
||||
public void deferNotifyDataSetChanged();
|
||||
void deferNotifyDataSetChanged();
|
||||
|
||||
void setRemoteViewsAdapter(Intent intent, boolean isAsync);
|
||||
}
|
||||
|
||||
public static class AsyncRemoteAdapterAction implements Runnable {
|
||||
|
||||
private final RemoteAdapterConnectionCallback mCallback;
|
||||
private final Intent mIntent;
|
||||
|
||||
public AsyncRemoteAdapterAction(RemoteAdapterConnectionCallback callback, Intent intent) {
|
||||
mCallback = callback;
|
||||
mIntent = intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mCallback.setRemoteViewsAdapter(mIntent, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,7 +187,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
}
|
||||
mIsConnecting = true;
|
||||
} catch (Exception e) {
|
||||
Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage());
|
||||
Log.e("RVAServiceConnection", "bind(): " + e.getMessage());
|
||||
mIsConnecting = false;
|
||||
mIsConnected = false;
|
||||
}
|
||||
@@ -180,7 +205,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
}
|
||||
mIsConnecting = false;
|
||||
} catch (Exception e) {
|
||||
Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage());
|
||||
Log.e("RVAServiceConnection", "unbind(): " + e.getMessage());
|
||||
mIsConnecting = false;
|
||||
mIsConnected = false;
|
||||
}
|
||||
@@ -298,15 +323,29 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
* Updates this RemoteViewsFrameLayout depending on the view that was loaded.
|
||||
* @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded
|
||||
* successfully.
|
||||
* @param forceApplyAsync when true, the host will always try to inflate the view
|
||||
* asynchronously (for eg, when we are already showing the loading
|
||||
* view)
|
||||
*/
|
||||
public void onRemoteViewsLoaded(RemoteViews view, OnClickHandler handler) {
|
||||
public void onRemoteViewsLoaded(RemoteViews view, OnClickHandler handler,
|
||||
boolean forceApplyAsync) {
|
||||
setOnClickHandler(handler);
|
||||
applyRemoteViews(view);
|
||||
applyRemoteViews(view, forceApplyAsync || ((view != null) && view.prefersAsyncApply()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default loading view. Uses the size of the first row as a guide for the
|
||||
* size of the loading view.
|
||||
*/
|
||||
@Override
|
||||
protected View getDefaultView() {
|
||||
return mCache.getMetaData().createDefaultLoadingView(this);
|
||||
int viewHeight = mCache.getMetaData().getLoadingTemplate(getContext()).defaultHeight;
|
||||
// Compose the loading view text
|
||||
TextView loadingTextView = (TextView) LayoutInflater.from(getContext()).inflate(
|
||||
com.android.internal.R.layout.remote_views_adapter_default_loading_view,
|
||||
this, false);
|
||||
loadingTextView.setHeight(viewHeight);
|
||||
return loadingTextView;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -359,7 +398,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
if (refs != null) {
|
||||
// Notify all the references for that position of the newly loaded RemoteViews
|
||||
for (final RemoteViewsFrameLayout ref : refs) {
|
||||
ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler);
|
||||
ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler, true);
|
||||
if (mViewToLinkedList.containsKey(ref)) {
|
||||
mViewToLinkedList.remove(ref);
|
||||
}
|
||||
@@ -402,9 +441,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
// Used to determine how to construct loading views. If a loading view is not specified
|
||||
// by the user, then we try and load the first view, and use its height as the height for
|
||||
// the default loading view.
|
||||
RemoteViews mUserLoadingView;
|
||||
RemoteViews mFirstView;
|
||||
int mFirstViewHeight;
|
||||
LoadingViewTemplate loadingTemplate;
|
||||
|
||||
// A mapping from type id to a set of unique type ids
|
||||
private final SparseIntArray mTypeIdIndexMap = new SparseIntArray();
|
||||
@@ -418,7 +455,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
count = d.count;
|
||||
viewTypeCount = d.viewTypeCount;
|
||||
hasStableIds = d.hasStableIds;
|
||||
setLoadingViewTemplates(d.mUserLoadingView, d.mFirstView);
|
||||
loadingTemplate = d.loadingTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,20 +465,10 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
// by default there is at least one dummy view type
|
||||
viewTypeCount = 1;
|
||||
hasStableIds = true;
|
||||
mUserLoadingView = null;
|
||||
mFirstView = null;
|
||||
mFirstViewHeight = 0;
|
||||
loadingTemplate = null;
|
||||
mTypeIdIndexMap.clear();
|
||||
}
|
||||
|
||||
public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) {
|
||||
mUserLoadingView = loadingView;
|
||||
if (firstView != null) {
|
||||
mFirstView = firstView;
|
||||
mFirstViewHeight = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMappedViewType(int typeId) {
|
||||
int mappedTypeId = mTypeIdIndexMap.get(typeId, -1);
|
||||
if (mappedTypeId == -1) {
|
||||
@@ -457,33 +484,11 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
return (mappedType < viewTypeCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default loading view. Uses the size of the first row as a guide for the
|
||||
* size of the loading view.
|
||||
*/
|
||||
private synchronized View createDefaultLoadingView(ViewGroup parent) {
|
||||
final Context context = parent.getContext();
|
||||
if (mFirstViewHeight < 0) {
|
||||
try {
|
||||
View firstView = mFirstView.apply(parent.getContext(), parent);
|
||||
firstView.measure(
|
||||
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
|
||||
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
|
||||
mFirstViewHeight = firstView.getMeasuredHeight();
|
||||
} catch (Exception e) {
|
||||
float density = context.getResources().getDisplayMetrics().density;
|
||||
mFirstViewHeight = Math.round(sDefaultLoadingViewHeight * density);
|
||||
Log.w(TAG, "Error inflating first RemoteViews" + e);
|
||||
}
|
||||
mFirstView = null;
|
||||
public synchronized LoadingViewTemplate getLoadingTemplate(Context context) {
|
||||
if (loadingTemplate == null) {
|
||||
loadingTemplate = new LoadingViewTemplate(null, context);
|
||||
}
|
||||
|
||||
// Compose the loading view text
|
||||
TextView loadingTextView = (TextView) LayoutInflater.from(context).inflate(
|
||||
com.android.internal.R.layout.remote_views_adapter_default_loading_view,
|
||||
parent, false);
|
||||
loadingTextView.setHeight(mFirstViewHeight);
|
||||
return loadingTextView;
|
||||
return loadingTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,7 +777,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
}
|
||||
|
||||
public RemoteViewsAdapter(Context context, Intent intent,
|
||||
RemoteAdapterConnectionCallback callback) {
|
||||
RemoteAdapterConnectionCallback callback, boolean useAsyncLoader) {
|
||||
mContext = context;
|
||||
mIntent = intent;
|
||||
|
||||
@@ -781,7 +786,6 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
}
|
||||
|
||||
mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
|
||||
mLayoutInflater = LayoutInflater.from(context);
|
||||
mRequestedViews = new RemoteViewsFrameLayoutRefSet();
|
||||
|
||||
// Strip the previously injected app widget id from service intent
|
||||
@@ -794,6 +798,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
mWorkerThread.start();
|
||||
mWorkerQueue = new Handler(mWorkerThread.getLooper());
|
||||
mMainQueue = new Handler(Looper.myLooper(), this);
|
||||
mAsyncViewLoadExecutor = useAsyncLoader ? new HandlerThreadExecutor(mWorkerThread) : null;
|
||||
|
||||
if (sCacheRemovalThread == null) {
|
||||
sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner");
|
||||
@@ -941,10 +946,14 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
boolean hasStableIds = factory.hasStableIds();
|
||||
int viewTypeCount = factory.getViewTypeCount();
|
||||
int count = factory.getCount();
|
||||
RemoteViews loadingView = factory.getLoadingView();
|
||||
RemoteViews firstView = null;
|
||||
if ((count > 0) && (loadingView == null)) {
|
||||
firstView = factory.getViewAt(0);
|
||||
LoadingViewTemplate loadingTemplate =
|
||||
new LoadingViewTemplate(factory.getLoadingView(), mContext);
|
||||
if ((count > 0) && (loadingTemplate.remoteViews == null)) {
|
||||
RemoteViews firstView = factory.getViewAt(0);
|
||||
if (firstView != null) {
|
||||
loadingTemplate.loadFirstViewHeight(firstView, mContext,
|
||||
new HandlerThreadExecutor(mWorkerThread));
|
||||
}
|
||||
}
|
||||
final RemoteViewsMetaData tmpMetaData = mCache.getTemporaryMetaData();
|
||||
synchronized (tmpMetaData) {
|
||||
@@ -952,7 +961,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
// We +1 because the base view type is the loading view
|
||||
tmpMetaData.viewTypeCount = viewTypeCount + 1;
|
||||
tmpMetaData.count = count;
|
||||
tmpMetaData.setLoadingViewTemplates(loadingView, firstView);
|
||||
tmpMetaData.loadingTemplate = loadingTemplate;
|
||||
}
|
||||
} catch(RemoteException e) {
|
||||
processException("updateMetaData", e);
|
||||
@@ -1100,18 +1109,25 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position);
|
||||
}
|
||||
|
||||
final RemoteViewsFrameLayout layout =
|
||||
(convertView instanceof RemoteViewsFrameLayout)
|
||||
? (RemoteViewsFrameLayout) convertView
|
||||
: new RemoteViewsFrameLayout(parent.getContext(), mCache);
|
||||
final RemoteViewsFrameLayout layout;
|
||||
if (convertView instanceof RemoteViewsFrameLayout) {
|
||||
layout = (RemoteViewsFrameLayout) convertView;
|
||||
} else {
|
||||
layout = new RemoteViewsFrameLayout(parent.getContext(), mCache);
|
||||
layout.setAsyncExecutor(mAsyncViewLoadExecutor);
|
||||
}
|
||||
|
||||
if (isInCache) {
|
||||
layout.onRemoteViewsLoaded(rv, mRemoteViewsOnClickHandler);
|
||||
// Apply the view synchronously if possible, to avoid flickering
|
||||
layout.onRemoteViewsLoaded(rv, mRemoteViewsOnClickHandler, false);
|
||||
if (hasNewItems) loadNextIndexInBackground();
|
||||
} else {
|
||||
// If the views is not loaded, apply the loading view. If the loading view doesn't
|
||||
// exist, the layout will create a default view based on the firstView height.
|
||||
layout.onRemoteViewsLoaded(mCache.getMetaData().mUserLoadingView,
|
||||
mRemoteViewsOnClickHandler);
|
||||
layout.onRemoteViewsLoaded(
|
||||
mCache.getMetaData().getLoadingTemplate(mContext).remoteViews,
|
||||
mRemoteViewsOnClickHandler,
|
||||
false);
|
||||
mRequestedViews.add(position, layout);
|
||||
mCache.queueRequestedPositionToLoad(position);
|
||||
loadNextIndexInBackground();
|
||||
@@ -1285,4 +1301,58 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
|
||||
mMainQueue.removeMessages(sUnbindServiceMessageType);
|
||||
return mServiceConnection.isConnected();
|
||||
}
|
||||
|
||||
private static class HandlerThreadExecutor implements Executor {
|
||||
private final HandlerThread mThread;
|
||||
|
||||
HandlerThreadExecutor(HandlerThread thread) {
|
||||
mThread = thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable runnable) {
|
||||
if (Thread.currentThread().getId() == mThread.getId()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
new Handler(mThread.getLooper()).post(runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class LoadingViewTemplate {
|
||||
public final RemoteViews remoteViews;
|
||||
public int defaultHeight;
|
||||
|
||||
LoadingViewTemplate(RemoteViews views, Context context) {
|
||||
remoteViews = views;
|
||||
|
||||
float density = context.getResources().getDisplayMetrics().density;
|
||||
defaultHeight = Math.round(sDefaultLoadingViewHeight * density);
|
||||
}
|
||||
|
||||
public void loadFirstViewHeight(
|
||||
RemoteViews firstView, Context context, Executor executor) {
|
||||
// Inflate the first view on the worker thread
|
||||
firstView.applyAsync(context, new RemoteViewsFrameLayout(context, null), executor,
|
||||
new RemoteViews.OnViewAppliedListener() {
|
||||
@Override
|
||||
public void onViewApplied(View v) {
|
||||
try {
|
||||
v.measure(
|
||||
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
|
||||
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
|
||||
defaultHeight = v.getMeasuredHeight();
|
||||
} catch (Exception e) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
// Do nothing. The default height will stay the same.
|
||||
Log.w(TAG, "Error inflating first RemoteViews", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user