Merge "Add support for emulating external storage on devices with no SD card"
diff --git a/Android.mk b/Android.mk
index 27f9e1e..30a67e3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -163,6 +163,7 @@
core/java/com/android/internal/view/IInputMethodClient.aidl \
core/java/com/android/internal/view/IInputMethodManager.aidl \
core/java/com/android/internal/view/IInputMethodSession.aidl \
+ core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
location/java/android/location/IGeocodeProvider.aidl \
location/java/android/location/IGpsStatusListener.aidl \
location/java/android/location/IGpsStatusProvider.aidl \
diff --git a/api/current.xml b/api/current.xml
index b5435e4..76b1c9a 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -34218,6 +34218,40 @@
<parameter name="context" type="android.content.Context">
</parameter>
</method>
+<method name="notifyAppWidgetViewDataChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="appWidgetIds" type="int[]">
+</parameter>
+<parameter name="views" type="android.widget.RemoteViews">
+</parameter>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
+<method name="notifyAppWidgetViewDataChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="appWidgetId" type="int">
+</parameter>
+<parameter name="views" type="android.widget.RemoteViews">
+</parameter>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
<method name="updateAppWidget"
return="void"
abstract="false"
@@ -207092,6 +207126,28 @@
visibility="public"
>
</method>
+<method name="onRemoteAdapterConnected"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRemoteAdapterDisconnected"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onRestoreInstanceState"
return="void"
abstract="false"
@@ -207269,6 +207325,19 @@
<parameter name="listener" type="android.widget.AbsListView.RecyclerListener">
</parameter>
</method>
+<method name="setRemoteViewsAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
<method name="setScrollIndicators"
return="void"
abstract="false"
@@ -213725,6 +213794,19 @@
<parameter name="verticalSpacing" type="int">
</parameter>
</method>
+<method name="smoothScrollByOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
<field name="AUTO_FIT"
type="int"
transient="false"
@@ -216276,6 +216358,19 @@
<parameter name="y" type="int">
</parameter>
</method>
+<method name="smoothScrollByOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
<field name="CHOICE_MODE_MULTIPLE"
type="int"
transient="false"
@@ -219130,6 +219225,23 @@
<parameter name="value" type="int">
</parameter>
</method>
+<method name="setIntent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="methodName" type="java.lang.String">
+</parameter>
+<parameter name="value" type="android.content.Intent">
+</parameter>
+</method>
<method name="setLong"
return="void"
abstract="false"
@@ -219181,6 +219293,51 @@
<parameter name="indeterminate" type="boolean">
</parameter>
</method>
+<method name="setRelativeScrollPosition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="offset" type="int">
+</parameter>
+</method>
+<method name="setRemoteAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="setScrollPosition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+</method>
<method name="setShort"
return="void"
abstract="false"
@@ -219343,6 +219500,149 @@
<implements name="java.lang.annotation.Annotation">
</implements>
</class>
+<class name="RemoteViewsService"
+ extends="android.app.Service"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="RemoteViewsService"
+ type="android.widget.RemoteViewsService"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onBind"
+ return="android.os.IBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onGetViewFactory"
+ return="android.widget.RemoteViewsService.RemoteViewsFactory"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+</class>
+<interface name="RemoteViewsService.RemoteViewsFactory"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getItemId"
+ return="long"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="getLoadingView"
+ return="android.widget.RemoteViews"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getViewAt"
+ return="android.widget.RemoteViews"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="getViewTypeCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasStableIds"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onCreate"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onDestroy"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
<class name="ResourceCursorAdapter"
extends="android.widget.CursorAdapter"
abstract="true"
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index b2fc13f..6011eec 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -39,6 +39,7 @@
static final int HANDLE_UPDATE = 1;
static final int HANDLE_PROVIDER_CHANGED = 2;
+ static final int HANDLE_VIEW_DATA_CHANGED = 3;
final static Object sServiceLock = new Object();
static IAppWidgetService sService;
@@ -60,6 +61,14 @@
msg.obj = info;
msg.sendToTarget();
}
+
+ public void viewDataChanged(int appWidgetId, RemoteViews views, int viewId) {
+ Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED);
+ msg.arg1 = appWidgetId;
+ msg.arg2 = viewId;
+ msg.obj = views;
+ msg.sendToTarget();
+ }
}
class UpdateHandler extends Handler {
@@ -77,6 +86,10 @@
onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj);
break;
}
+ case HANDLE_VIEW_DATA_CHANGED: {
+ viewDataChanged(msg.arg1, (RemoteViews) msg.obj, msg.arg2);
+ break;
+ }
}
}
}
@@ -250,6 +263,16 @@
v.updateAppWidget(views);
}
}
+
+ void viewDataChanged(int appWidgetId, RemoteViews views, int viewId) {
+ AppWidgetHostView v;
+ synchronized (mViews) {
+ v = mViews.get(appWidgetId);
+ }
+ if (v != null) {
+ v.viewDataChanged(views, viewId);
+ }
+ }
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 3c19ea3..22f4266 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -32,6 +32,9 @@
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.Adapter;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
@@ -258,6 +261,22 @@
}
/**
+ * Process data-changed notifications for the specified view in the specified
+ * set of {@link RemoteViews} views.
+ */
+ void viewDataChanged(RemoteViews remoteViews, int viewId) {
+ View v = findViewById(viewId);
+ if ((v != null) && (v instanceof AdapterView<?>)) {
+ AdapterView<?> adapterView = (AdapterView<?>) v;
+ Adapter adapter = adapterView.getAdapter();
+ if (adapter instanceof BaseAdapter) {
+ BaseAdapter baseAdapter = (BaseAdapter) adapter;
+ baseAdapter.notifyDataSetChanged();
+ }
+ }
+ }
+
+ /**
* Build a {@link Context} cloned into another package name, usually for the
* purposes of reading remote resources.
*/
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 3f12bf9..5ee721f 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -288,6 +288,35 @@
}
/**
+ * Notifies the specified collection view in all the specified AppWidget instances
+ * to invalidate their currently data.
+ *
+ * @param appWidgetIds The AppWidget instances for which to notify of view data changes.
+ * @param views The RemoteViews which contains the view referenced at viewId.
+ * @param viewId The collection view id.
+ */
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, RemoteViews views, int viewId) {
+ try {
+ sService.notifyAppWidgetViewDataChanged(appWidgetIds, views, viewId);
+ }
+ catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ }
+
+ /**
+ * Notifies the specified collection view in all the specified AppWidget instance
+ * to invalidate it's currently data.
+ *
+ * @param appWidgetId The AppWidget instance for which to notify of view data changes.
+ * @param views The RemoteViews which contains the view referenced at viewId.
+ * @param viewId The collection view id.
+ */
+ public void notifyAppWidgetViewDataChanged(int appWidgetId, RemoteViews views, int viewId) {
+ notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, views, viewId);
+ }
+
+ /**
* Return a list of the AppWidget providers that are currently installed.
*/
public List<AppWidgetProviderInfo> getInstalledProviders() {
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 198c156..a6394e5 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -1017,6 +1017,15 @@
}
public static final Cursor query(ContentResolver cr, String[] projection,
+ long begin, long end, String searchQuery) {
+ Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
+ ContentUris.appendId(builder, begin);
+ ContentUris.appendId(builder, end);
+ return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED,
+ new String[] { searchQuery }, DEFAULT_SORT_ORDER);
+ }
+
+ public static final Cursor query(ContentResolver cr, String[] projection,
long begin, long end, String where, String orderBy) {
Uri.Builder builder = CONTENT_URI.buildUpon();
ContentUris.appendId(builder, begin);
@@ -1030,6 +1039,21 @@
null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
}
+ public static final Cursor query(ContentResolver cr, String[] projection, long begin,
+ long end, String searchQuery, String where, String orderBy) {
+ Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
+ ContentUris.appendId(builder, begin);
+ ContentUris.appendId(builder, end);
+ builder = builder.appendPath(searchQuery);
+ if (TextUtils.isEmpty(where)) {
+ where = WHERE_CALENDARS_SELECTED;
+ } else {
+ where = "(" + where + ") AND " + WHERE_CALENDARS_SELECTED;
+ }
+ return cr.query(builder.build(), projection, where, null,
+ orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+ }
+
/**
* The content:// style URL for this table
*/
@@ -1037,6 +1061,10 @@
"/instances/when");
public static final Uri CONTENT_BY_DAY_URI =
Uri.parse("content://" + AUTHORITY + "/instances/whenbyday");
+ public static final Uri CONTENT_SEARCH_URI = Uri.parse("content://" + AUTHORITY +
+ "/instances/search");
+ public static final Uri CONTENT_SEARCH_BY_DAY_URI =
+ Uri.parse("content://" + AUTHORITY + "/instances/searchbyday");
/**
* The default sort order for this table.
@@ -1053,7 +1081,6 @@
* required for correctness, it just adds a nice touch.
*/
public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC";
-
/**
* The beginning time of the instance, in UTC milliseconds
* <P>Type: INTEGER (long; millis since epoch)</P>
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 889d3b6..7df8487 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -675,7 +675,7 @@
public static final String CONTACT_PRESENCE = "contact_presence";
/**
- * Contact Chat Capabilities. See {@link StatusColumns#CHAT_CAPABILITY} for individual
+ * Contact Chat Capabilities. See {@link StatusUpdates} for individual
* definitions.
* <p>Type: NUMBER</p>
*/
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 23219a3..8bee174 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -927,7 +927,7 @@
Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
continue;
}
- if (name.equals("Devices")) {
+ if (name.equals("Devices") || name.equals("UUIDs")) {
StringBuilder str = new StringBuilder();
len = Integer.valueOf(properties[++i]);
for (int j = 0; j < len; j++) {
diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java
index bfa349c..2604cf9 100644
--- a/core/java/android/view/MenuItem.java
+++ b/core/java/android/view/MenuItem.java
@@ -401,6 +401,8 @@
* Sets how this item should display in the presence of an Action Bar.
*
* @param actionEnum How the item should display. One of
+ * {@link #SHOW_AS_ACTION_ALWAYS}, {@link #SHOW_AS_ACTION_IF_ROOM}, or
+ * {@link #SHOW_AS_ACTION_NEVER}. SHOW_AS_ACTION_NEVER is the default.
*
* @see android.app.ActionBar
*/
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 70c1e15..1658b2f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -19,6 +19,7 @@
import com.android.internal.R;
import android.content.Context;
+import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -71,7 +72,8 @@
*/
public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,
ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
- ViewTreeObserver.OnTouchModeChangeListener {
+ ViewTreeObserver.OnTouchModeChangeListener,
+ RemoteViewsAdapter.RemoteAdapterConnectionCallback {
/**
* Disables the transcript mode.
@@ -180,6 +182,11 @@
ListAdapter mAdapter;
/**
+ * The remote adapter containing the data to be displayed by this view to be set
+ */
+ private RemoteViewsAdapter mRemoteAdapter;
+
+ /**
* Indicates whether the list selector should be drawn on top of the children or behind
*/
boolean mDrawSelectorOnTop = false;
@@ -2893,6 +2900,42 @@
mFlingRunnable.startScroll(distance, duration);
}
+ /**
+ * Allows RemoteViews to scroll relatively to a position.
+ */
+ void smoothScrollByOffset(int position) {
+ int index = -1;
+ if (position < 0) {
+ index = getFirstVisiblePosition();
+ } else if (position > 0) {
+ index = getLastVisiblePosition();
+ }
+
+ if (index > -1) {
+ View child = getChildAt(index - getFirstVisiblePosition());
+ if (child != null) {
+ Rect visibleRect = new Rect();
+ if (child.getGlobalVisibleRect(visibleRect)) {
+ // the child is partially visible
+ int childRectArea = child.getWidth() * child.getHeight();
+ int visibleRectArea = visibleRect.width() * visibleRect.height();
+ float visibleArea = (visibleRectArea / (float) childRectArea);
+ final float visibleThreshold = 0.75f;
+ if ((position < 0) && (visibleArea < visibleThreshold)) {
+ // the top index is not perceivably visible so offset
+ // to account for showing that top index as well
+ ++index;
+ } else if ((position > 0) && (visibleArea < visibleThreshold)) {
+ // the bottom index is not perceivably visible so offset
+ // to account for showing that bottom index as well
+ --index;
+ }
+ }
+ smoothScrollToPosition(Math.max(0, Math.min(getCount(), index + position)));
+ }
+ }
+ }
+
private void createScrollingCache() {
if (mScrollingCacheEnabled && !mCachingStarted) {
setChildrenDrawnWithCacheEnabled(true);
@@ -3905,6 +3948,34 @@
}
/**
+ * Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService
+ * through the specified intent.
+ * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
+ */
+ public void setRemoteViewsAdapter(Intent intent) {
+ mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this);
+ }
+
+ /**
+ * Called back when the adapter connects to the RemoteViewsService.
+ */
+ public void onRemoteAdapterConnected() {
+ if (mRemoteAdapter != mAdapter) {
+ setAdapter(mRemoteAdapter);
+ }
+ }
+
+ /**
+ * Called back when the adapter disconnects from the RemoteViewsService.
+ */
+ public void onRemoteAdapterDisconnected() {
+ if (mRemoteAdapter == mAdapter) {
+ mRemoteAdapter = null;
+ setAdapter(null);
+ }
+ }
+
+ /**
* Sets the recycler listener to be notified whenever a View is set aside in
* the recycler for later reuse. This listener can be used to free resources
* associated to the View.
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index fe69a13..a5b3ed5 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -17,22 +17,25 @@
package android.widget;
import android.content.Context;
+import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
+import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
-import android.view.SoundEffectConstants;
import android.view.animation.GridLayoutAnimationController;
+import android.widget.RemoteViews.RemoteView;
/**
* A view that shows items in two-dimensional scrolling grid. The items in the
* grid come from the {@link ListAdapter} associated with this view.
*/
+@RemoteView
public class GridView extends AbsListView {
public static final int NO_STRETCH = 0;
public static final int STRETCH_SPACING = 1;
@@ -107,6 +110,16 @@
}
/**
+ * Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService
+ * through the specified intent.
+ * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
+ */
+ @android.view.RemotableViewMethod
+ public void setRemoteViewsAdapter(Intent intent) {
+ super.setRemoteViewsAdapter(intent);
+ }
+
+ /**
* Sets the data behind this GridView.
*
* @param adapter the adapter providing the grid's data
@@ -740,6 +753,26 @@
}
/**
+ * Smoothly scroll to the specified adapter position. The view will
+ * scroll such that the indicated position is displayed.
+ * @param position Scroll to this adapter position.
+ */
+ @android.view.RemotableViewMethod
+ public void smoothScrollToPosition(int position) {
+ super.smoothScrollToPosition(position);
+ }
+
+ /**
+ * Smoothly scroll to the specified adapter position offset. The view will
+ * scroll such that the indicated position is displayed.
+ * @param offset The amount to offset from the adapter position to scroll to.
+ */
+ @android.view.RemotableViewMethod
+ public void smoothScrollByOffset(int offset) {
+ super.smoothScrollByOffset(offset);
+ }
+
+ /**
* Fills the grid based on positioning the new selection relative to the old
* selection. The new selection will be placed at, above, or below the
* location of the new selection depending on how the selection is moving.
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 86913ae..ad9d930 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -20,6 +20,7 @@
import com.google.android.collect.Lists;
import android.content.Context;
+import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -41,6 +42,7 @@
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
+import android.widget.RemoteViews.RemoteView;
import java.util.ArrayList;
@@ -65,6 +67,7 @@
* @attr ref android.R.styleable#ListView_headerDividersEnabled
* @attr ref android.R.styleable#ListView_footerDividersEnabled
*/
+@RemoteView
public class ListView extends AbsListView {
/**
* Used to indicate a no preference for a position type.
@@ -401,6 +404,16 @@
}
/**
+ * Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService
+ * through the specified intent.
+ * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
+ */
+ @android.view.RemotableViewMethod
+ public void setRemoteViewsAdapter(Intent intent) {
+ super.setRemoteViewsAdapter(intent);
+ }
+
+ /**
* Sets the data behind this ListView.
*
* The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},
@@ -863,6 +876,25 @@
return topSelectionPixel;
}
+ /**
+ * Smoothly scroll to the specified adapter position. The view will
+ * scroll such that the indicated position is displayed.
+ * @param position Scroll to this adapter position.
+ */
+ @android.view.RemotableViewMethod
+ public void smoothScrollToPosition(int position) {
+ super.smoothScrollToPosition(position);
+ }
+
+ /**
+ * Smoothly scroll to the specified adapter position offset. The view will
+ * scroll such that the indicated position is displayed.
+ * @param offset The amount to offset from the adapter position to scroll to.
+ */
+ @android.view.RemotableViewMethod
+ public void smoothScrollByOffset(int offset) {
+ super.smoothScrollByOffset(offset);
+ }
/**
* Fills the list based on positioning the new selection relative to the old
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index fc02acf..50745dc0 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -285,6 +285,7 @@
static final int URI = 11;
static final int BITMAP = 12;
static final int BUNDLE = 13;
+ static final int INTENT = 14;
int viewId;
String methodName;
@@ -347,6 +348,9 @@
case BUNDLE:
this.value = in.readBundle();
break;
+ case INTENT:
+ this.value = Intent.CREATOR.createFromParcel(in);
+ break;
default:
break;
}
@@ -402,6 +406,9 @@
case BUNDLE:
out.writeBundle((Bundle) this.value);
break;
+ case INTENT:
+ ((Intent)this.value).writeToParcel(out, flags);
+ break;
default:
break;
}
@@ -435,6 +442,8 @@
return Bitmap.class;
case BUNDLE:
return Bundle.class;
+ case INTENT:
+ return Intent.class;
default:
return null;
}
@@ -770,6 +779,37 @@
}
/**
+ * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
+ *
+ * @param viewId The id of the view whose text should change
+ * @param intent The intent of the service which will be
+ * providing data to the RemoteViewsAdapter
+ */
+ public void setRemoteAdapter(int viewId, Intent intent) {
+ setIntent(viewId, "setRemoteViewsAdapter", intent);
+ }
+
+ /**
+ * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
+ *
+ * @param viewId The id of the view whose text should change
+ * @param position Scroll to this adapter position
+ */
+ public void setScrollPosition(int viewId, int position) {
+ setInt(viewId, "smoothScrollToPosition", position);
+ }
+
+ /**
+ * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
+ *
+ * @param viewId The id of the view whose text should change
+ * @param offset Scroll by this adapter position offset
+ */
+ public void setRelativeScrollPosition(int viewId, int offset) {
+ setInt(viewId, "smoothScrollByOffset", offset);
+ }
+
+ /**
* Call a method taking one boolean on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view whose text should change
@@ -916,6 +956,16 @@
}
/**
+ *
+ * @param viewId
+ * @param methodName
+ * @param value
+ */
+ public void setIntent(int viewId, String methodName, Intent value) {
+ addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
+ }
+
+ /**
* Inflates the view hierarchy represented by this object and applies
* all of the actions.
*
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
new file mode 100644
index 0000000..d426033
--- /dev/null
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) 2007 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 android.widget;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.MeasureSpec;
+
+import com.android.internal.widget.IRemoteViewsFactory;
+
+/**
+ * An adapter to a RemoteViewsService which fetches and caches RemoteViews
+ * to be later inflated as child views.
+ */
+/** @hide */
+public class RemoteViewsAdapter extends BaseAdapter {
+
+ private static final String LOG_TAG = "RemoteViewsAdapter";
+
+ private Context mContext;
+ private Intent mIntent;
+ private RemoteViewsAdapterServiceConnection mServiceConnection;
+ private RemoteViewsCache mViewCache;
+
+ private HandlerThread mWorkerThread;
+ // items may be interrupted within the normally processed queues
+ private Handler mWorkerQueue;
+ private Handler mMainQueue;
+ // items are never dequeued from the priority queue and must run
+ private Handler mWorkerPriorityQueue;
+ private Handler mMainPriorityQueue;
+
+ /**
+ * An interface for the RemoteAdapter to notify other classes when adapters
+ * are actually connected to/disconnected from their actual services.
+ */
+ public interface RemoteAdapterConnectionCallback {
+ public void onRemoteAdapterConnected();
+
+ public void onRemoteAdapterDisconnected();
+ }
+
+ /**
+ * The service connection that gets populated when the RemoteViewsService is
+ * bound.
+ */
+ private class RemoteViewsAdapterServiceConnection implements ServiceConnection {
+ private boolean mConnected;
+ private IRemoteViewsFactory mRemoteViewsFactory;
+ private RemoteAdapterConnectionCallback mCallback;
+
+ public RemoteViewsAdapterServiceConnection(RemoteAdapterConnectionCallback callback) {
+ mCallback = callback;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
+ mConnected = true;
+ // notifyDataSetChanged should be called first, to ensure that the
+ // views are not updated twice
+ notifyDataSetChanged();
+
+ // post a new runnable to load the appropriate data, then callback
+ mWorkerPriorityQueue.post(new Runnable() {
+ @Override
+ public void run() {
+ // we need to get the viewTypeCount specifically, so just get all the
+ // metadata
+ mViewCache.requestMetaData();
+
+ // post a runnable to call the callback on the main thread
+ mMainPriorityQueue.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mCallback != null)
+ mCallback.onRemoteAdapterConnected();
+ }
+ });
+ }
+ });
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mRemoteViewsFactory = null;
+ mConnected = false;
+ if (mCallback != null)
+ mCallback.onRemoteAdapterDisconnected();
+
+ // clear the main/worker queues
+ mMainQueue.removeMessages(0);
+ mWorkerQueue.removeMessages(0);
+ }
+
+ public IRemoteViewsFactory getRemoteViewsFactory() {
+ return mRemoteViewsFactory;
+ }
+
+ public boolean isConnected() {
+ return mConnected;
+ }
+ }
+
+ /**
+ * An internal cache of remote views.
+ */
+ private class RemoteViewsCache {
+ private RemoteViewsInfo mViewCacheInfo;
+ private RemoteViewsIndexInfo[] mViewCache;
+
+ // if a user loading view is not provided, then we create a temporary one
+ // for the user using the height of the first view
+ private RemoteViews mUserLoadingView;
+ private RemoteViews mFirstView;
+ private int mFirstViewHeight;
+
+ // determines when the current cache window needs to be updated with new
+ // items (ie. when there is not enough slack)
+ private int mViewCacheStartPosition;
+ private int mViewCacheEndPosition;
+ private int mHalfCacheSize;
+ private int mCacheSlack;
+ private final float mCacheSlackPercentage = 0.75f;
+
+ // determines whether to reorder the posted items on the worker thread
+ // so that the items in the current window can be loaded first
+ private int mPriorityLoadingWindowSize;
+ private int mPriorityLoadingWindowStart;
+ private int mPriorityLoadingWindowEnd;
+
+ // determines which way to load items in the current window based on how
+ // the window shifted last
+ private boolean mLoadUpwards;
+
+ /**
+ * The data structure stored at each index of the cache. Any member
+ * that is not invalidated persists throughout the lifetime of the cache.
+ */
+ private class RemoteViewsIndexInfo {
+ FrameLayout flipper;
+ RemoteViews view;
+ long itemId;
+ int typeId;
+
+ RemoteViewsIndexInfo() {
+ invalidate();
+ }
+
+ void set(RemoteViews v, long id) {
+ view = v;
+ itemId = id;
+ if (v != null)
+ typeId = v.getLayoutId();
+ else
+ typeId = 0;
+ }
+
+ void invalidate() {
+ view = null;
+ itemId = 0;
+ typeId = 0;
+ }
+
+ final boolean isValid() {
+ return (view != null);
+ }
+ }
+
+ /**
+ * Remote adapter metadata. Useful for when we have to lock on something
+ * before updating the metadata.
+ */
+ private class RemoteViewsInfo {
+ int count;
+ int viewTypeCount;
+ boolean hasStableIds;
+ Map<Integer, Integer> mTypeIdIndexMap;
+
+ RemoteViewsInfo() {
+ count = 0;
+ // by default there is at least one dummy view type
+ viewTypeCount = 1;
+ hasStableIds = true;
+ mTypeIdIndexMap = new HashMap<Integer, Integer>();
+ }
+ }
+
+ public RemoteViewsCache(int halfCacheSize) {
+ mHalfCacheSize = halfCacheSize;
+ mCacheSlack = Math.round(mCacheSlackPercentage * mHalfCacheSize);
+ mViewCacheStartPosition = 0;
+ mViewCacheEndPosition = -1;
+ mPriorityLoadingWindowSize = 4;
+ mPriorityLoadingWindowStart = 0;
+ mPriorityLoadingWindowEnd = 0;
+ mLoadUpwards = false;
+
+ // initialize the cache
+ mViewCacheInfo = new RemoteViewsInfo();
+ mViewCache = new RemoteViewsIndexInfo[2 * mHalfCacheSize + 1];
+ for (int i = 0; i < mViewCache.length; ++i) {
+ mViewCache[i] = new RemoteViewsIndexInfo();
+ }
+ }
+
+ private final boolean contains(int position) {
+ // take the modulo of the position
+ return (mViewCacheStartPosition <= position) && (position < mViewCacheEndPosition);
+ }
+
+ private final boolean containsAndIsValid(int position) {
+ if (contains(position)) {
+ RemoteViewsIndexInfo indexInfo = mViewCache[getCacheIndex(position)];
+ if (indexInfo.isValid()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private final int getCacheIndex(int position) {
+ return (mViewCache.length + (position % mViewCache.length)) % mViewCache.length;
+ }
+
+ public void requestMetaData() {
+ if (mServiceConnection.isConnected()) {
+ try {
+ IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+
+ // get the properties/first view (so that we can use it to
+ // measure our dummy views)
+ 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);
+ }
+ synchronized (mViewCacheInfo) {
+ RemoteViewsInfo info = mViewCacheInfo;
+ info.hasStableIds = hasStableIds;
+ info.viewTypeCount = viewTypeCount + 1;
+ info.count = count;
+ mUserLoadingView = loadingView;
+ if (firstView != null) {
+ mFirstView = firstView;
+ mFirstViewHeight = -1;
+ }
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ protected void updateRemoteViewsInfo(int position) {
+ if (mServiceConnection.isConnected()) {
+ IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+
+ // load the item information
+ RemoteViews remoteView = null;
+ long itemId = 0;
+ try {
+ remoteView = factory.getViewAt(position);
+ itemId = factory.getItemId(position);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+
+ synchronized (mViewCache) {
+ // skip if the window has moved
+ if (position < mViewCacheStartPosition || position >= mViewCacheEndPosition)
+ return;
+
+ final int positionIndex = position;
+ final int cacheIndex = getCacheIndex(position);
+ mViewCache[cacheIndex].set(remoteView, itemId);
+
+ // notify the main thread when done loading
+ // flush pending updates
+ mMainQueue.post(new Runnable() {
+ @Override
+ public void run() {
+ // swap the loader view for this view
+ synchronized (mViewCache) {
+ if (containsAndIsValid(positionIndex)) {
+ RemoteViewsIndexInfo indexInfo = mViewCache[cacheIndex];
+ FrameLayout flipper = indexInfo.flipper;
+
+ // recompose the flipper
+ View loadingView = flipper.getChildAt(0);
+ loadingView.setVisibility(View.GONE);
+ flipper.removeAllViews();
+ flipper.addView(loadingView);
+ flipper.addView(indexInfo.view.apply(mContext, flipper));
+
+ // hide the loader view and bring the new view to the front
+ flipper.requestLayout();
+ flipper.invalidate();
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+
+ private RemoteViewsIndexInfo requestCachedIndexInfo(final int position) {
+ int indicesToLoadCount = 0;
+ int[] indicesToLoad = null;
+
+ synchronized (mViewCache) {
+ indicesToLoad = new int[mViewCache.length];
+ Arrays.fill(indicesToLoad, 0);
+
+ if (containsAndIsValid(position)) {
+ // return the info if it exists in the window and is loaded
+ return mViewCache[getCacheIndex(position)];
+ }
+
+ // if necessary update the window and load the new information
+ int centerPosition = (mViewCacheEndPosition + mViewCacheStartPosition) / 2;
+ if ((mViewCacheEndPosition <= mViewCacheStartPosition) || (Math.abs(position - centerPosition) > mCacheSlack)) {
+ int newStartPosition = position - mHalfCacheSize;
+ int newEndPosition = position + mHalfCacheSize;
+
+ // prune/add before the current start position
+ int effectiveStart = Math.max(newStartPosition, 0);
+ int effectiveEnd = Math.min(newEndPosition, getCount());
+
+ mWorkerQueue.removeMessages(0);
+
+ // invalidate items in the queue
+ boolean loadFromBeginning = effectiveStart < mViewCacheStartPosition;
+ int numLoadFromBeginning = mViewCacheStartPosition - effectiveStart;
+ boolean loadFromEnd = effectiveEnd > mViewCacheEndPosition;
+ int overlapStart = Math.max(mViewCacheStartPosition, effectiveStart);
+ int overlapEnd = Math.min(Math.max(mViewCacheStartPosition, mViewCacheEndPosition), effectiveEnd);
+ for (int i = newStartPosition; i < newEndPosition; ++i) {
+ if (loadFromBeginning && (effectiveStart <= i) && (i < overlapStart)) {
+ // load new items at the beginning in reverse order
+ mViewCache[getCacheIndex(i)].invalidate();
+ indicesToLoad[indicesToLoadCount++] = effectiveStart
+ + (numLoadFromBeginning - (i - effectiveStart) - 1);
+ } else if (loadFromEnd && (overlapEnd <= i) && (i < effectiveEnd)) {
+ mViewCache[getCacheIndex(i)].invalidate();
+ indicesToLoad[indicesToLoadCount++] = i;
+ } else if ((overlapStart <= i) && (i < overlapEnd)) {
+ // load the stuff in the middle that has not already
+ // been loaded
+ if (!mViewCache[getCacheIndex(i)].isValid()) {
+ indicesToLoad[indicesToLoadCount++] = i;
+ }
+ } else {
+ // invalidate all other cache indices (outside the effective start/end)
+ mViewCache[getCacheIndex(i)].invalidate();
+ }
+ }
+
+ mViewCacheStartPosition = newStartPosition;
+ mViewCacheEndPosition = newEndPosition;
+ mPriorityLoadingWindowStart = position;
+ mPriorityLoadingWindowEnd = position + mPriorityLoadingWindowSize;
+ mLoadUpwards = loadFromBeginning && !loadFromEnd;
+ } else if (contains(position)) {
+ // prioritize items around this position so that they load first
+ if (position < mPriorityLoadingWindowStart || position > mPriorityLoadingWindowEnd) {
+ mWorkerQueue.removeMessages(0);
+
+ int index;
+ int effectiveStart = Math.max(position - mPriorityLoadingWindowSize, 0);
+ int effectiveEnd = 0;
+ synchronized (mViewCacheInfo) {
+ effectiveEnd = Math.min(position + mPriorityLoadingWindowSize - 1,
+ mViewCacheInfo.count - 1);
+ }
+
+ for (int i = 0; i < mViewCache.length; ++i) {
+ if (mLoadUpwards) {
+ index = effectiveEnd - i;
+ } else {
+ index = effectiveStart + i;
+ }
+ if (!mViewCache[getCacheIndex(index)].isValid()) {
+ indicesToLoad[indicesToLoadCount++] = index;
+ }
+ }
+
+ mPriorityLoadingWindowStart = effectiveStart;
+ mPriorityLoadingWindowEnd = position + mPriorityLoadingWindowSize;
+ }
+ }
+ }
+
+ // post items to be loaded
+ int length = 0;
+ synchronized (mViewCacheInfo) {
+ length = mViewCacheInfo.count;
+ }
+ for (int i = 0; i < indicesToLoadCount; ++i) {
+ final int index = indicesToLoad[i];
+ if (0 <= index && index < length) {
+ mWorkerQueue.post(new Runnable() {
+ @Override
+ public void run() {
+ updateRemoteViewsInfo(index);
+ }
+ });
+ }
+ }
+
+ // return null so that a dummy view can be retrieved
+ return null;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (mServiceConnection.isConnected()) {
+ // create the flipper views if necessary (we have to do this now
+ // for all the flippers while we have the reference to the parent)
+ createInitialLoadingFlipperViews(parent);
+
+ // request the item from the cache (queueing it to load if not
+ // in the cache already)
+ RemoteViewsIndexInfo indexInfo = requestCachedIndexInfo(position);
+
+ // update the flipper appropriately
+ synchronized (mViewCache) {
+ int cacheIndex = getCacheIndex(position);
+ FrameLayout flipper = mViewCache[cacheIndex].flipper;
+
+ if (indexInfo == null) {
+ // hide the item view and show the loading view
+ flipper.getChildAt(0).setVisibility(View.VISIBLE);
+ for (int i = 1; i < flipper.getChildCount(); ++i) {
+ flipper.getChildAt(i).setVisibility(View.GONE);
+ }
+ flipper.requestLayout();
+ flipper.invalidate();
+ } else {
+ // hide the loading view and show the item view
+ for (int i = 0; i < flipper.getChildCount() - 1; ++i) {
+ flipper.getChildAt(i).setVisibility(View.GONE);
+ }
+ flipper.getChildAt(flipper.getChildCount() - 1).setVisibility(View.VISIBLE);
+ flipper.requestLayout();
+ flipper.invalidate();
+ }
+ return flipper;
+ }
+ }
+ return new View(mContext);
+ }
+
+ private void createInitialLoadingFlipperViews(ViewGroup parent) {
+ // ensure that the cache has the appropriate initial flipper
+ synchronized (mViewCache) {
+ if (mViewCache[0].flipper == null) {
+ for (int i = 0; i < mViewCache.length; ++i) {
+ FrameLayout flipper = new FrameLayout(mContext);
+ if (mUserLoadingView != null) {
+ // use the user-specified loading view
+ flipper.addView(mUserLoadingView.apply(mContext, parent));
+ } else {
+ // calculate the original size of the first row for the loader view
+ synchronized (mViewCacheInfo) {
+ if (mFirstViewHeight < 0) {
+ View firstView = mFirstView.apply(mContext, parent);
+ firstView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mFirstViewHeight = firstView.getMeasuredHeight();
+ }
+ }
+
+ // construct a new loader and add it to the flipper as the fallback
+ // default view
+ TextView textView = new TextView(mContext);
+ textView.setText("Loading...");
+ textView.setHeight(mFirstViewHeight);
+ textView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
+ textView.setTextSize(18.0f);
+ textView.setTextColor(Color.argb(96, 255, 255, 255));
+ textView.setShadowLayer(2.0f, 0.0f, 1.0f, Color.BLACK);
+
+ flipper.addView(textView);
+ }
+ mViewCache[i].flipper = flipper;
+ }
+ }
+ }
+ }
+
+ public long getItemId(int position) {
+ synchronized (mViewCache) {
+ if (containsAndIsValid(position)) {
+ return mViewCache[getCacheIndex(position)].itemId;
+ }
+ }
+ return 0;
+ }
+
+ public int getItemViewType(int position) {
+ // synchronize to ensure that the type id/index map is updated synchronously
+ synchronized (mViewCache) {
+ if (containsAndIsValid(position)) {
+ int viewId = mViewCache[getCacheIndex(position)].typeId;
+ Map<Integer, Integer> typeMap = mViewCacheInfo.mTypeIdIndexMap;
+ // we +1 because the default dummy view get view type 0
+ if (typeMap.containsKey(viewId)) {
+ return typeMap.get(viewId);
+ } else {
+ int newIndex = typeMap.size() + 1;
+ typeMap.put(viewId, newIndex);
+ return newIndex;
+ }
+ }
+ }
+ // return the type of the default item
+ return 0;
+ }
+
+ public int getCount() {
+ synchronized (mViewCacheInfo) {
+ return mViewCacheInfo.count;
+ }
+ }
+
+ public int getViewTypeCount() {
+ synchronized (mViewCacheInfo) {
+ return mViewCacheInfo.viewTypeCount;
+ }
+ }
+
+ public boolean hasStableIds() {
+ synchronized (mViewCacheInfo) {
+ return mViewCacheInfo.hasStableIds;
+ }
+ }
+
+ public void flushCache() {
+ synchronized (mViewCache) {
+ // flush the internal cache and invalidate the adapter for future loads
+ mWorkerQueue.removeMessages(0);
+ mMainQueue.removeMessages(0);
+
+ for (int i = 0; i < mViewCache.length; ++i) {
+ mViewCache[i].invalidate();
+ }
+
+ mViewCacheStartPosition = 0;
+ mViewCacheEndPosition = -1;
+ }
+ }
+ }
+
+ public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
+ mContext = context;
+ mIntent = intent;
+
+ // initialize the worker thread
+ mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
+ mWorkerThread.start();
+ mWorkerQueue = new Handler(mWorkerThread.getLooper());
+ mWorkerPriorityQueue = new Handler(mWorkerThread.getLooper());
+ mMainQueue = new Handler(Looper.myLooper());
+ mMainPriorityQueue = new Handler(Looper.myLooper());
+
+ // initialize the cache and the service connection on startup
+ mViewCache = new RemoteViewsCache(25);
+ mServiceConnection = new RemoteViewsAdapterServiceConnection(callback);
+ requestBindService();
+ }
+
+ protected void finalize() throws Throwable {
+ // remember to unbind from the service when finalizing
+ unbindService();
+ }
+
+ public int getCount() {
+ requestBindService();
+ return mViewCache.getCount();
+ }
+
+ public Object getItem(int position) {
+ // disallow arbitrary object to be associated with an item for the time being
+ return null;
+ }
+
+ public long getItemId(int position) {
+ requestBindService();
+ return mViewCache.getItemId(position);
+ }
+
+ public int getItemViewType(int position) {
+ requestBindService();
+ return mViewCache.getItemViewType(position);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ requestBindService();
+ return mViewCache.getView(position, convertView, parent);
+ }
+
+ public int getViewTypeCount() {
+ requestBindService();
+ return mViewCache.getViewTypeCount();
+ }
+
+ public boolean hasStableIds() {
+ requestBindService();
+ return mViewCache.hasStableIds();
+ }
+
+ public boolean isEmpty() {
+ return getCount() <= 0;
+ }
+
+ public void notifyDataSetChanged() {
+ // flush the cache so that we can reload new items from the service
+ mViewCache.flushCache();
+ super.notifyDataSetChanged();
+ }
+
+ private boolean requestBindService() {
+ // try binding the service (which will start it if it's not already running)
+ if (!mServiceConnection.isConnected()) {
+ mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ return mServiceConnection.isConnected();
+ }
+
+ private void unbindService() {
+ if (mServiceConnection.isConnected()) {
+ mContext.unbindService(mServiceConnection);
+ }
+ }
+}
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
new file mode 100644
index 0000000..7c9d7ff
--- /dev/null
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2007 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 android.widget;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Pair;
+
+import com.android.internal.widget.IRemoteViewsFactory;
+
+/**
+ * The service to be connected to for a remote adapter to request RemoteViews. Users should
+ * extend the RemoteViewsService to provide the appropriate RemoteViewsFactory's used to
+ * populate the remote collection view (ListView, GridView, etc).
+ */
+public abstract class RemoteViewsService extends Service {
+
+ private static final String LOG_TAG = "RemoteViewsService";
+
+ // multimap implementation for reference counting
+ private HashMap<Intent, Pair<RemoteViewsFactory, Integer>> mRemoteViewFactories;
+ private final Object mLock = new Object();
+
+ /**
+ * An interface for an adapter between a remote collection view (ListView, GridView, etc) and
+ * the underlying data for that view. The implementor is responsible for making a RemoteView
+ * for each item in the data set.
+ */
+ public interface RemoteViewsFactory {
+ public void onCreate();
+ public void onDestroy();
+
+ public int getCount();
+ public RemoteViews getViewAt(int position);
+ public RemoteViews getLoadingView();
+ public int getViewTypeCount();
+ public long getItemId(int position);
+ public boolean hasStableIds();
+ }
+
+ /**
+ * A private proxy class for the private IRemoteViewsFactory interface through the
+ * public RemoteViewsFactory interface.
+ */
+ private class RemoteViewsFactoryAdapter extends IRemoteViewsFactory.Stub {
+ public RemoteViewsFactoryAdapter(RemoteViewsFactory factory) {
+ mFactory = factory;
+ }
+
+ public int getCount() {
+ return mFactory.getCount();
+ }
+ public RemoteViews getViewAt(int position) {
+ return mFactory.getViewAt(position);
+ }
+ public RemoteViews getLoadingView() {
+ return mFactory.getLoadingView();
+ }
+ public int getViewTypeCount() {
+ return mFactory.getViewTypeCount();
+ }
+ public long getItemId(int position) {
+ return mFactory.getItemId(position);
+ }
+ public boolean hasStableIds() {
+ return mFactory.hasStableIds();
+ }
+
+ private RemoteViewsFactory mFactory;
+ }
+
+ public RemoteViewsService() {
+ mRemoteViewFactories = new HashMap<Intent, Pair<RemoteViewsFactory, Integer>>();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ synchronized (mLock) {
+ // increment the reference count to the particular factory associated with this intent
+ Pair<RemoteViewsFactory, Integer> factoryRef = null;
+ RemoteViewsFactory factory = null;
+ if (!mRemoteViewFactories.containsKey(intent)) {
+ factory = onGetViewFactory(intent);
+ factoryRef = new Pair<RemoteViewsFactory, Integer>(factory, 1);
+ mRemoteViewFactories.put(intent, factoryRef);
+ factory.onCreate();
+ } else {
+ Pair<RemoteViewsFactory, Integer> oldFactoryRef = mRemoteViewFactories.get(intent);
+ factory = oldFactoryRef.first;
+ int newRefCount = oldFactoryRef.second.intValue() + 1;
+ factoryRef = new Pair<RemoteViewsFactory, Integer>(oldFactoryRef.first, newRefCount);
+ mRemoteViewFactories.put(intent, factoryRef);
+ }
+ return new RemoteViewsFactoryAdapter(factory);
+ }
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ synchronized (mLock) {
+ if (mRemoteViewFactories.containsKey(intent)) {
+ // this alleviates the user's responsibility of having to clear all factories
+ Pair<RemoteViewsFactory, Integer> oldFactoryRef = mRemoteViewFactories.get(intent);
+ int newRefCount = oldFactoryRef.second.intValue() - 1;
+ if (newRefCount <= 0) {
+ oldFactoryRef.first.onDestroy();
+ mRemoteViewFactories.remove(intent);
+ } else {
+ Pair<RemoteViewsFactory, Integer> factoryRef = new Pair<RemoteViewsFactory, Integer>(oldFactoryRef.first, newRefCount);
+ mRemoteViewFactories.put(intent, factoryRef);
+ }
+ }
+ }
+ return super.onUnbind(intent);
+ }
+
+ /**
+ * To be implemented by the derived service to generate appropriate factories for
+ * the data.
+ */
+ public abstract RemoteViewsFactory onGetViewFactory(Intent intent);
+}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
index 2ed4773..f0920d1 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
@@ -24,5 +24,6 @@
oneway interface IAppWidgetHost {
void updateAppWidget(int appWidgetId, in RemoteViews views);
void providerChanged(int appWidgetId, in AppWidgetProviderInfo info);
+ void viewDataChanged(int appWidgetId, in RemoteViews views, int viewId);
}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 496aa1a..af75d5b 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -41,6 +41,7 @@
//
void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
+ void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, in RemoteViews views, int viewId);
List<AppWidgetProviderInfo> getInstalledProviders();
AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId);
void bindAppWidgetId(int appWidgetId, in ComponentName provider);
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index b57b7a8..cd9832f 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -75,8 +75,9 @@
mCloseDrawable = a.getDrawable(
com.android.internal.R.styleable.Theme_actionBarCloseContextDrawable);
mItemMargin = mItemPadding / 2;
-
- mContentHeight = CONTENT_HEIGHT_DIP;
+
+ mContentHeight =
+ (int) (CONTENT_HEIGHT_DIP * getResources().getDisplayMetrics().density + 0.5f);
a.recycle();
}
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
new file mode 100644
index 0000000..7851347
--- /dev/null
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 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.internal.widget;
+
+import android.widget.RemoteViews;
+
+/** {@hide} */
+interface IRemoteViewsFactory {
+ int getCount();
+ RemoteViews getViewAt(int position);
+ RemoteViews getLoadingView();
+ int getViewTypeCount();
+ long getItemId(int position);
+ boolean hasStableIds();
+}
+
diff --git a/docs/html/guide/developing/eclipse-adt.jd b/docs/html/guide/developing/eclipse-adt.jd
index 66379a3..9c77ece 100644
--- a/docs/html/guide/developing/eclipse-adt.jd
+++ b/docs/html/guide/developing/eclipse-adt.jd
@@ -785,6 +785,8 @@
have first started <a href="{@docRoot}guide/developing/tools/ddms.html">DDMS</a>). </p>
+
+
<!-- TODO: clean this up and expand it to cover more wizards and features
<h3>ADT Wizards</h3>
diff --git a/libs/rs/java/ImageProcessing/res/raw/horizontal_blur.rs b/libs/rs/java/ImageProcessing/res/raw/horizontal_blur.rs
index 7b0e6bc..10815fb 100644
--- a/libs/rs/java/ImageProcessing/res/raw/horizontal_blur.rs
+++ b/libs/rs/java/ImageProcessing/res/raw/horizontal_blur.rs
@@ -5,17 +5,14 @@
#include "ip.rsh"
-uchar4 * ScratchPixel;
-
-#pragma rs export_var(ScratchPixel)
-
void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
uchar4 *output = (uchar4 *)v_out;
- const uchar4 *input = (uchar4 *)v_in;
const FilterStruct *fs = (const FilterStruct *)usrData;
+ const uchar4 *input = (const uchar4 *)rsGetElementAt(fs->ain, 0, y);
float4 blurredPixel = 0;
float4 currentPixel = 0;
+
for(int r = -fs->radius; r <= fs->radius; r ++) {
// Stepping left and right away from the pixel
int validW = x + r;
diff --git a/libs/rs/java/ImageProcessing/res/raw/horizontal_blur_bc.bc b/libs/rs/java/ImageProcessing/res/raw/horizontal_blur_bc.bc
index c9ba5d9..5920f3a 100644
--- a/libs/rs/java/ImageProcessing/res/raw/horizontal_blur_bc.bc
+++ b/libs/rs/java/ImageProcessing/res/raw/horizontal_blur_bc.bc
Binary files differ
diff --git a/libs/rs/java/ImageProcessing/res/raw/ip.rsh b/libs/rs/java/ImageProcessing/res/raw/ip.rsh
index 4073304..dea92c3 100644
--- a/libs/rs/java/ImageProcessing/res/raw/ip.rsh
+++ b/libs/rs/java/ImageProcessing/res/raw/ip.rsh
@@ -3,6 +3,8 @@
#define MAX_RADIUS 25
typedef struct {
+ rs_allocation ain;
+
float *gaussian; //[MAX_RADIUS * 2 + 1];
rs_matrix3x3 colorMat;
diff --git a/libs/rs/java/ImageProcessing/res/raw/threshold.rs b/libs/rs/java/ImageProcessing/res/raw/threshold.rs
index ecbfac4..aa6b6fa 100644
--- a/libs/rs/java/ImageProcessing/res/raw/threshold.rs
+++ b/libs/rs/java/ImageProcessing/res/raw/threshold.rs
@@ -24,7 +24,6 @@
static float inWMinInB;
static float outWMinOutB;
static float overInWMinInB;
-static FilterStruct filterStruct;
#pragma rs export_var(height, width, radius, InPixel, OutPixel, ScratchPixel, inBlack, outBlack, inWhite, outWhite, gamma, saturation, InPixel, OutPixel, ScratchPixel, vBlurScript, hBlurScript)
#pragma rs export_func(filter, filterBenchmark);
@@ -106,138 +105,70 @@
}
}
-// This needs to be inline
-static float4 levelsSaturation(float4 currentPixel) {
- float3 temp = rsMatrixMultiply(&colorMat, currentPixel.xyz);
- temp = (clamp(temp, 0.1f, 255.f) - inBlack) * overInWMinInB;
- temp = pow(temp, (float3)gamma);
- currentPixel.xyz = clamp(temp * outWMinOutB + outBlack, 0.1f, 255.f);
- return currentPixel;
-}
-
static void processNoBlur() {
- int w, h, r;
- int count = 0;
-
float inWMinInB = inWhite - inBlack;
float outWMinOutB = outWhite - outBlack;
float4 currentPixel = 0;
- for(h = 0; h < height; h ++) {
- for(w = 0; w < width; w ++) {
- uchar4 *input = InPixel + h*width + w;
+ for(int h = 0; h < height; h ++) {
+ uchar4 *input = InPixel + h*width;
+ uchar4 *output = OutPixel + h*width;
+ for(int w = 0; w < width; w ++) {
//currentPixel.xyz = convert_float3(input.xyz);
currentPixel.x = (float)(input->x);
currentPixel.y = (float)(input->y);
currentPixel.z = (float)(input->z);
- currentPixel = levelsSaturation(currentPixel);
+ float3 temp = rsMatrixMultiply(&colorMat, currentPixel.xyz);
+ temp = (clamp(temp, 0.f, 255.f) - inBlack) * overInWMinInB;
+ temp = pow(temp, (float3)gamma);
+ currentPixel.xyz = clamp(temp * outWMinOutB + outBlack, 0.f, 255.f);
- uchar4 *output = OutPixel + h*width + w;
//output.xyz = convert_uchar3(currentPixel.xyz);
output->x = (uint8_t)currentPixel.x;
output->y = (uint8_t)currentPixel.y;
output->z = (uint8_t)currentPixel.z;
output->w = input->w;
- }
- }
- rsSendToClient(&count, 1, 4, 0);
-}
-static void horizontalBlurLevels() {
- float4 blurredPixel = 0;
- float4 currentPixel = 0;
- // Horizontal blur
- int w, h, r;
- for(h = 0; h < height; h ++) {
- uchar4 *output = OutPixel + h*width;
-
- for(w = 0; w < width; w ++) {
- blurredPixel = 0;
-
- for(r = -radius; r <= radius; r ++) {
- // Stepping left and right away from the pixel
- int validW = w + r;
- // Clamp to zero and width max() isn't exposed for ints yet
- if(validW < 0) {
- validW = 0;
- }
- if(validW > width - 1) {
- validW = width - 1;
- }
- //int validW = rsClamp(w + r, 0, width - 1);
-
- uchar4 *input = InPixel + h*width + validW;
-
- float weight = gaussian[r + radius];
- currentPixel.x = (float)(input->x);
- currentPixel.y = (float)(input->y);
- currentPixel.z = (float)(input->z);
- //currentPixel.w = (float)(input->a);
-
- blurredPixel.xyz += currentPixel.xyz * weight;
- }
-
- blurredPixel = levelsSaturation(blurredPixel);
-
- output->x = (uint8_t)blurredPixel.x;
- output->y = (uint8_t)blurredPixel.y;
- output->z = (uint8_t)blurredPixel.z;
- //output->a = (uint8_t)blurredPixel.w;
+ input++;
output++;
}
}
}
-static void initStructs() {
- filterStruct.gaussian = gaussian;
- filterStruct.width = width;
- filterStruct.height = height;
- filterStruct.radius = radius;
+static void blur() {
+ computeGaussianWeights();
+
+ FilterStruct fs;
+ fs.gaussian = gaussian;
+ fs.width = width;
+ fs.height = height;
+ fs.radius = radius;
+
+ fs.ain = rsGetAllocation(InPixel);
+ rsForEach(hBlurScript, fs.ain, rsGetAllocation(ScratchPixel), &fs);
+
+ fs.ain = rsGetAllocation(ScratchPixel);
+ rsForEach(vBlurScript, fs.ain, rsGetAllocation(OutPixel), &fs);
}
void filter() {
- RS_DEBUG(height);
- RS_DEBUG(width);
RS_DEBUG(radius);
- initStructs();
-
computeColorMatrix();
- if(radius == 0) {
- processNoBlur();
- return;
+ if(radius > 0) {
+ blur();
}
-
- computeGaussianWeights();
-
- horizontalBlurLevels();
-
- rsForEach(vBlurScript,
- rsGetAllocation(InPixel),
- rsGetAllocation(OutPixel),
- &filterStruct);
+ processNoBlur();
int count = 0;
rsSendToClient(&count, 1, 4, 0);
}
void filterBenchmark() {
- initStructs();
-
- computeGaussianWeights();
-
- rsForEach(hBlurScript,
- rsGetAllocation(InPixel),
- rsGetAllocation(OutPixel),
- &filterStruct);
-
- rsForEach(vBlurScript,
- rsGetAllocation(InPixel),
- rsGetAllocation(OutPixel),
- &filterStruct);
+ blur();
int count = 0;
rsSendToClient(&count, 1, 4, 0);
diff --git a/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc b/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc
index 8f37fdc..2b5d254 100644
--- a/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc
+++ b/libs/rs/java/ImageProcessing/res/raw/threshold_bc.bc
Binary files differ
diff --git a/libs/rs/java/ImageProcessing/res/raw/vertical_blur.rs b/libs/rs/java/ImageProcessing/res/raw/vertical_blur.rs
index 846f515..f5f2d69 100644
--- a/libs/rs/java/ImageProcessing/res/raw/vertical_blur.rs
+++ b/libs/rs/java/ImageProcessing/res/raw/vertical_blur.rs
@@ -5,14 +5,10 @@
#include "ip.rsh"
-uchar4 * ScratchPixel;
-
-#pragma rs export_var(ScratchPixel)
-
void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
uchar4 *output = (uchar4 *)v_out;
- const uchar4 *input = (uchar4 *)v_in;
const FilterStruct *fs = (const FilterStruct *)usrData;
+ const uchar4 *input = (const uchar4 *)rsGetElementAt(fs->ain, x, 0);
float4 blurredPixel = 0;
float4 currentPixel = 0;
@@ -27,19 +23,21 @@
validH = fs->height - 1;
}
- uchar4 *input = ScratchPixel + validH * fs->width + x;
+ const uchar4 *i = input + validH * fs->width;
+ //const uchar4 *i = (const uchar4 *)rsGetElementAt(fs->ain, x, validH);
float weight = fs->gaussian[r + fs->radius];
- currentPixel.x = (float)(input->x);
- currentPixel.y = (float)(input->y);
- currentPixel.z = (float)(input->z);
+ currentPixel.x = (float)(i->x);
+ currentPixel.y = (float)(i->y);
+ currentPixel.z = (float)(i->z);
blurredPixel.xyz += currentPixel.xyz * weight;
#else
int validH = rsClamp(y + r, 0, height - 1);
- uchar4 *input = ScratchPixel + validH * width + x;
- blurredPixel.xyz += convert_float3(input->xyz) * gaussian[r + fs->radius];
+ validH -= y;
+ uchar4 *i = input + validH * width + x;
+ blurredPixel.xyz += convert_float3(i->xyz) * gaussian[r + fs->radius];
#endif
}
diff --git a/libs/rs/java/ImageProcessing/res/raw/vertical_blur_bc.bc b/libs/rs/java/ImageProcessing/res/raw/vertical_blur_bc.bc
index af1cd8e..be5d0e4 100644
--- a/libs/rs/java/ImageProcessing/res/raw/vertical_blur_bc.bc
+++ b/libs/rs/java/ImageProcessing/res/raw/vertical_blur_bc.bc
Binary files differ
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 21c3d74..0ed1185 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -376,10 +376,7 @@
mScratchPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapScratch);
mScriptVBlur = new ScriptC_Vertical_blur(mRS, getResources(), R.raw.vertical_blur_bc, false);
- mScriptVBlur.bind_ScratchPixel(mScratchPixelsAllocation);
-
mScriptHBlur = new ScriptC_Horizontal_blur(mRS, getResources(), R.raw.horizontal_blur_bc, false);
- mScriptHBlur.bind_ScratchPixel(mScratchPixelsAllocation);
mScript = new ScriptC_Threshold(mRS, getResources(), R.raw.threshold_bc, false);
mScript.set_width(mBitmapIn.getWidth());
@@ -431,8 +428,8 @@
android.util.Log.v("Img", "Renderscript frame time core ms " + t);
long javaTime = javaFilter();
-
mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
+ //mBenchmarkResult.setText("RS: " + t + " ms");
mRadius = oldRadius;
mScript.set_radius(mRadius);
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Horizontal_blur.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Horizontal_blur.java
index 8ee50a8..c447b9b 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Horizontal_blur.java
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Horizontal_blur.java
@@ -26,17 +26,5 @@
super(rs, resources, id, isRoot);
}
- private final static int mExportVarIdx_ScratchPixel = 0;
- private Allocation mExportVar_ScratchPixel;
- public void bind_ScratchPixel(Allocation v) {
- mExportVar_ScratchPixel = v;
- if(v == null) bindAllocation(null, mExportVarIdx_ScratchPixel);
- else bindAllocation(v, mExportVarIdx_ScratchPixel);
- }
-
- public Allocation get_ScratchPixel() {
- return mExportVar_ScratchPixel;
- }
-
}
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Vertical_blur.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Vertical_blur.java
index 0215f60..cee74d9 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Vertical_blur.java
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ScriptC_Vertical_blur.java
@@ -26,17 +26,5 @@
super(rs, resources, id, isRoot);
}
- private final static int mExportVarIdx_ScratchPixel = 0;
- private Allocation mExportVar_ScratchPixel;
- public void bind_ScratchPixel(Allocation v) {
- mExportVar_ScratchPixel = v;
- if(v == null) bindAllocation(null, mExportVarIdx_ScratchPixel);
- else bindAllocation(v, mExportVarIdx_ScratchPixel);
- }
-
- public Allocation get_ScratchPixel() {
- return mExportVar_ScratchPixel;
- }
-
}
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 68eca44..629b481 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <sys/resource.h>
+#include <sched.h>
#include <cutils/properties.h>
@@ -355,6 +356,49 @@
return NULL;
}
+void * Context::helperThreadProc(void *vrsc)
+{
+ Context *rsc = static_cast<Context *>(vrsc);
+ uint32_t idx = (uint32_t)android_atomic_inc(&rsc->mWorkers.mLaunchCount);
+
+ LOGE("helperThreadProc 1 %p idx=%i", rsc, idx);
+
+ rsc->mWorkers.mLaunchSignals[idx].init();
+ rsc->mWorkers.mNativeThreadId[idx] = gettid();
+
+ //cpu_set_t cpset[16];
+ //int ret = sched_getaffinity(rsc->mWorkers.mNativeThreadId[idx], sizeof(cpset), &cpset);
+ //LOGE("ret = %i", ret);
+
+//sched_setaffinity
+
+ setpriority(PRIO_PROCESS, rsc->mWorkers.mNativeThreadId[idx], rsc->mThreadPriority);
+ while(rsc->mRunning) {
+ rsc->mWorkers.mLaunchSignals[idx].wait();
+ if (rsc->mWorkers.mLaunchCallback) {
+ LOGE("helperThreadProc 4");
+ rsc->mWorkers.mLaunchCallback(rsc->mWorkers.mLaunchData, idx);
+ }
+ LOGE("helperThreadProc 5");
+ android_atomic_dec(&rsc->mWorkers.mRunningCount);
+ rsc->mWorkers.mCompleteSignal.set();
+ }
+ return NULL;
+}
+
+void Context::launchThreads(WorkerCallback_t cbk, void *data)
+{
+ mWorkers.mLaunchData = data;
+ mWorkers.mLaunchCallback = cbk;
+ mWorkers.mRunningCount = (int)mWorkers.mCount;
+ for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) {
+ mWorkers.mLaunchSignals[ct].set();
+ }
+ while(mWorkers.mRunningCount) {
+ mWorkers.mCompleteSignal.wait();
+ }
+}
+
void Context::setPriority(int32_t p)
{
// Note: If we put this in the proper "background" policy
@@ -371,7 +415,10 @@
// success; reset the priority as well
}
#else
- setpriority(PRIO_PROCESS, mNativeThreadId, p);
+ setpriority(PRIO_PROCESS, mNativeThreadId, p);
+ for (uint32_t ct=0; ct < mWorkers.mCount; ct++) {
+ setpriority(PRIO_PROCESS, mWorkers.mNativeThreadId[ct], p);
+ }
#endif
}
@@ -421,10 +468,26 @@
timerInit();
timerSet(RS_TIMER_INTERNAL);
- LOGV("RS Launching thread");
+ LOGV("RS Launching thread(s)");
+ mWorkers.mCount = 2;
+ mWorkers.mThreadId = (pthread_t *) calloc(mWorkers.mCount, sizeof(pthread_t));
+ mWorkers.mNativeThreadId = (pid_t *) calloc(mWorkers.mCount, sizeof(pid_t));
+ mWorkers.mLaunchSignals = new Signal[mWorkers.mCount];
+ mWorkers.mLaunchCallback = NULL;
status = pthread_create(&mThreadId, &threadAttr, threadProc, this);
if (status) {
LOGE("Failed to start rs context thread.");
+ return;
+ }
+ mWorkers.mRunningCount = 0;
+ mWorkers.mLaunchCount = 0;
+ for (uint32_t ct=0; ct < mWorkers.mCount; ct++) {
+ status = pthread_create(&mWorkers.mThreadId[ct], &threadAttr, helperThreadProc, this);
+ if (status) {
+ mWorkers.mCount = ct;
+ LOGE("Created fewer than expected number of RS threads.");
+ break;
+ }
}
while(!mRunning) {
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 06433a1..98ad3a4 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -65,6 +65,7 @@
Script * mScript;
};
+ typedef void (*WorkerCallback_t)(void *usr, uint32_t idx);
//StructuredAllocationContext mStateAllocation;
ElementState mStateElement;
@@ -172,6 +173,8 @@
bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;}
+ void launchThreads(WorkerCallback_t cbk, void *data);
+
protected:
Device *mDev;
@@ -222,6 +225,20 @@
pthread_t mThreadId;
pid_t mNativeThreadId;
+ struct Workers {
+ volatile int mRunningCount;
+ volatile int mLaunchCount;
+ uint32_t mCount;
+ pthread_t *mThreadId;
+ pid_t *mNativeThreadId;
+ Signal mCompleteSignal;
+
+ Signal *mLaunchSignals;
+ WorkerCallback_t mLaunchCallback;
+ void *mLaunchData;
+ };
+ Workers mWorkers;
+
ObjectBaseRef<Script> mRootScript;
ObjectBaseRef<ProgramFragment> mFragment;
ObjectBaseRef<ProgramVertex> mVertex;
@@ -248,6 +265,7 @@
uint32_t runRootScript();
static void * threadProc(void *);
+ static void * helperThreadProc(void *);
ANativeWindow *mWndSurface;
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index b87ac28..9693b16e 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -137,72 +137,155 @@
}
+typedef struct {
+ Context *rsc;
+ ScriptC *script;
+ const Allocation * ain;
+ Allocation * aout;
+ const void * usr;
+
+ uint32_t mSliceSize;
+ volatile int mSliceNum;
+
+ const uint8_t *ptrIn;
+ uint32_t eStrideIn;
+ uint8_t *ptrOut;
+ uint32_t eStrideOut;
+
+ uint32_t xStart;
+ uint32_t xEnd;
+ uint32_t yStart;
+ uint32_t yEnd;
+ uint32_t zStart;
+ uint32_t zEnd;
+ uint32_t arrayStart;
+ uint32_t arrayEnd;
+
+ uint32_t dimX;
+ uint32_t dimY;
+ uint32_t dimZ;
+ uint32_t dimArray;
+} MTLaunchStruct;
+typedef int (*rs_t)(const void *, void *, const void *, uint32_t, uint32_t, uint32_t, uint32_t);
+
+static void wc_xy(void *usr, uint32_t idx)
+{
+ MTLaunchStruct *mtls = (MTLaunchStruct *)usr;
+ LOGE("usr %p, idx %i", usr, idx);
+
+ while (1) {
+ uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum);
+ uint32_t yStart = mtls->yStart + slice * mtls->mSliceSize;
+ uint32_t yEnd = yStart + mtls->mSliceSize;
+ yEnd = rsMin(yEnd, mtls->yEnd);
+ if (yEnd <= yStart) {
+ return;
+ }
+
+ //LOGE("usr idx %i, x %i,%i y %i,%i", idx, mtls->xStart, mtls->xEnd, yStart, yEnd);
+
+ for (uint32_t y = yStart; y < yEnd; y++) {
+ uint32_t offset = mtls->dimX * y;
+ uint8_t *xPtrOut = mtls->ptrOut + (mtls->eStrideOut * offset);
+ const uint8_t *xPtrIn = mtls->ptrIn + (mtls->eStrideIn * offset);
+
+ for (uint32_t x = mtls->xStart; x < mtls->xEnd; x++) {
+ ((rs_t)mtls->script->mProgram.mRoot) (xPtrIn, xPtrOut, mtls->usr, x, y, 0, 0);
+ xPtrIn += mtls->eStrideIn;
+ xPtrOut += mtls->eStrideOut;
+ }
+ }
+ }
+
+}
+
void ScriptC::runForEach(Context *rsc,
const Allocation * ain,
Allocation * aout,
const void * usr,
const RsScriptCall *sc)
{
- uint32_t dimX = ain->getType()->getDimX();
- uint32_t dimY = ain->getType()->getDimY();
- uint32_t dimZ = ain->getType()->getDimZ();
- uint32_t dimA = 0;//ain->getType()->getDimArray();
+ MTLaunchStruct mtls;
+ memset(&mtls, 0, sizeof(mtls));
- uint32_t xStart = 0;
- uint32_t xEnd = 0;
- uint32_t yStart = 0;
- uint32_t yEnd = 0;
- uint32_t zStart = 0;
- uint32_t zEnd = 0;
- uint32_t arrayStart = 0;
- uint32_t arrayEnd = 0;
+ if (ain) {
+ mtls.dimX = ain->getType()->getDimX();
+ mtls.dimY = ain->getType()->getDimY();
+ mtls.dimZ = ain->getType()->getDimZ();
+ //mtls.dimArray = ain->getType()->getDimArray();
+ } else if (aout) {
+ mtls.dimX = aout->getType()->getDimX();
+ mtls.dimY = aout->getType()->getDimY();
+ mtls.dimZ = aout->getType()->getDimZ();
+ //mtls.dimArray = aout->getType()->getDimArray();
+ } else {
+ rsc->setError(RS_ERROR_BAD_SCRIPT, "rsForEach called with null allocations");
+ return;
+ }
if (!sc || (sc->xEnd == 0)) {
- xStart = 0;
- xEnd = ain->getType()->getDimX();
+ mtls.xEnd = mtls.dimX;
} else {
- rsAssert(xStart < dimX);
- rsAssert(xEnd <= dimX);
+ rsAssert(sc->xStart < mtls.dimX);
+ rsAssert(sc->xEnd <= mtls.dimX);
rsAssert(sc->xStart < sc->xEnd);
- xStart = rsMin(dimX, sc->xStart);
- xEnd = rsMin(dimX, sc->xEnd);
- if (xStart >= xEnd) return;
+ mtls.xStart = rsMin(mtls.dimX, sc->xStart);
+ mtls.xEnd = rsMin(mtls.dimX, sc->xEnd);
+ if (mtls.xStart >= mtls.xEnd) return;
}
if (!sc || (sc->yEnd == 0)) {
- yStart = 0;
- yEnd = ain->getType()->getDimY();
+ mtls.yEnd = mtls.dimY;
} else {
- rsAssert(yStart < dimY);
- rsAssert(yEnd <= dimY);
+ rsAssert(sc->yStart < mtls.dimY);
+ rsAssert(sc->yEnd <= mtls.dimY);
rsAssert(sc->yStart < sc->yEnd);
- yStart = rsMin(dimY, sc->yStart);
- yEnd = rsMin(dimY, sc->yEnd);
- if (yStart >= yEnd) return;
+ mtls.yStart = rsMin(mtls.dimY, sc->yStart);
+ mtls.yEnd = rsMin(mtls.dimY, sc->yEnd);
+ if (mtls.yStart >= mtls.yEnd) return;
}
- xEnd = rsMax((uint32_t)1, xEnd);
- yEnd = rsMax((uint32_t)1, yEnd);
- zEnd = rsMax((uint32_t)1, zEnd);
- arrayEnd = rsMax((uint32_t)1, arrayEnd);
+ mtls.xEnd = rsMax((uint32_t)1, mtls.xEnd);
+ mtls.yEnd = rsMax((uint32_t)1, mtls.yEnd);
+ mtls.zEnd = rsMax((uint32_t)1, mtls.zEnd);
+ mtls.arrayEnd = rsMax((uint32_t)1, mtls.arrayEnd);
rsAssert(ain->getType()->getDimZ() == 0);
setupScript(rsc);
Script * oldTLS = setTLS(this);
- typedef int (*rs_t)(const void *, void *, const void *, uint32_t, uint32_t, uint32_t, uint32_t);
- const uint8_t *ptrIn = (const uint8_t *)ain->getPtr();
- uint32_t eStrideIn = ain->getType()->getElementSizeBytes();
+ mtls.rsc = rsc;
+ mtls.ain = ain;
+ mtls.aout = aout;
+ mtls.script = this;
+ mtls.usr = usr;
+ mtls.mSliceSize = 10;
+ mtls.mSliceNum = 0;
- uint8_t *ptrOut = NULL;
- uint32_t eStrideOut = 0;
- if (aout) {
- ptrOut = (uint8_t *)aout->getPtr();
- eStrideOut = aout->getType()->getElementSizeBytes();
+ mtls.ptrIn = NULL;
+ mtls.eStrideIn = 0;
+ if (ain) {
+ mtls.ptrIn = (const uint8_t *)ain->getPtr();
+ mtls.eStrideIn = ain->getType()->getElementSizeBytes();
}
+ mtls.ptrOut = NULL;
+ mtls.eStrideOut = 0;
+ if (aout) {
+ mtls.ptrOut = (uint8_t *)aout->getPtr();
+ mtls.eStrideOut = aout->getType()->getElementSizeBytes();
+ }
+
+
+ {
+ LOGE("launch 1");
+ rsc->launchThreads(wc_xy, &mtls);
+ LOGE("launch 2");
+ }
+
+/*
for (uint32_t ar = arrayStart; ar < arrayEnd; ar++) {
for (uint32_t z = zStart; z < zEnd; z++) {
for (uint32_t y = yStart; y < yEnd; y++) {
@@ -221,7 +304,7 @@
}
}
-
+*/
setTLS(oldTLS);
}
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 8d9ca9f..9c29ca6 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -329,6 +329,29 @@
return a->getType()->getDimFaces();
}
+const void * SC_getElementAtX(RsAllocation va, uint32_t x)
+{
+ const Allocation *a = static_cast<const Allocation *>(va);
+ const Type *t = a->getType();
+ const uint8_t *p = (const uint8_t *)a->getPtr();
+ return &p[t->getElementSizeBytes() * x];
+}
+
+const void * SC_getElementAtXY(RsAllocation va, uint32_t x, uint32_t y)
+{
+ const Allocation *a = static_cast<const Allocation *>(va);
+ const Type *t = a->getType();
+ const uint8_t *p = (const uint8_t *)a->getPtr();
+ return &p[t->getElementSizeBytes() * (x + y*t->getDimX())];
+}
+
+const void * SC_getElementAtXYZ(RsAllocation va, uint32_t x, uint32_t y, uint32_t z)
+{
+ const Allocation *a = static_cast<const Allocation *>(va);
+ const Type *t = a->getType();
+ const uint8_t *p = (const uint8_t *)a->getPtr();
+ return &p[t->getElementSizeBytes() * (x + y*t->getDimX())];
+}
static void SC_debugF(const char *s, float f) {
@@ -350,6 +373,10 @@
LOGE("%s %i 0x%x", s, i, i);
}
+static void SC_debugP(const char *s, const void *p) {
+ LOGE("%s %p", s, p);
+}
+
static uint32_t SC_toClient(void *data, int cmdID, int len, int waitForSpace)
{
GET_TLS();
@@ -433,12 +460,18 @@
{ "rsAllocationGetDimFaces", (void *)&SC_allocGetDimFaces },
{ "rsGetAllocation", (void *)&SC_getAllocation },
+ { "_Z14rsGetElementAt13rs_allocationj", (void *)&SC_getElementAtX },
+ { "_Z14rsGetElementAt13rs_allocationjj", (void *)&SC_getElementAtXY },
+ { "_Z14rsGetElementAt13rs_allocationjjj", (void *)&SC_getElementAtXYZ },
+
+
// Debug
{ "_Z7rsDebugPKcf", (void *)&SC_debugF },
{ "_Z7rsDebugPKcDv2_f", (void *)&SC_debugFv2 },
{ "_Z7rsDebugPKcDv3_f", (void *)&SC_debugFv3 },
{ "_Z7rsDebugPKcDv4_f", (void *)&SC_debugFv4 },
{ "_Z7rsDebugPKci", (void *)&SC_debugI32 },
+ { "_Z7rsDebugPKcPKv", (void *)&SC_debugP },
//extern void __attribute__((overloadable))rsDebug(const char *, const void *);
diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h
index 0a37a5b..17feb22 100644
--- a/libs/rs/rsUtils.h
+++ b/libs/rs/rsUtils.h
@@ -30,6 +30,7 @@
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
+#include <cutils/atomic.h>
#ifndef ANDROID_RS_BUILD_FOR_HOST
#include <EGL/egl.h>
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index e11c832..bd6e5a9 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -14,6 +14,12 @@
extern uint32_t rsAllocationGetDimLOD(rs_allocation);
extern uint32_t rsAllocationGetDimFaces(rs_allocation);
+extern const void * __attribute__((overloadable))
+ rsGetElementAt(rs_allocation, uint32_t x);
+extern const void * __attribute__((overloadable))
+ rsGetElementAt(rs_allocation, uint32_t x, uint32_t y);
+extern const void * __attribute__((overloadable))
+ rsGetElementAt(rs_allocation, uint32_t x, uint32_t y, uint32_t z);
// Debugging
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 1674221..30597320 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -425,6 +425,23 @@
}
}
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, RemoteViews views, int viewId) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ for (int i=0; i<N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ notifyAppWidgetViewDataChangedInstanceLocked(id, views, viewId);
+ }
+ }
+ }
+
public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
synchronized (mAppWidgetIds) {
Provider p = lookupProviderLocked(provider);
@@ -462,6 +479,27 @@
}
}
+ void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, RemoteViews views, int viewId) {
+ // allow for stale appWidgetIds and other badness
+ // lookup also checks that the calling process can access the appWidgetId
+ // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+ if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+ id.views = views;
+
+ // is anyone listening?
+ if (id.host.callbacks != null) {
+ try {
+ // the lock is held, but this is a oneway call
+ id.host.callbacks.viewDataChanged(id.appWidgetId, views, viewId);
+ } catch (RemoteException e) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this instance.
+ id.host.callbacks = null;
+ }
+ }
+ }
+ }
+
public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
List<RemoteViews> updatedViews) {
int callingUid = enforceCallingUid(packageName);