diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-11 12:11:56 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-11 12:11:56 -0700 |
commit | c39a6e0c51e182338deb8b63d07933b585134929 (patch) | |
tree | e55fc5bd38b1eb8fb4851a0fe1cc264a7fe2f245 /core/java | |
parent | b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54 (diff) | |
download | frameworks_base-c39a6e0c51e182338deb8b63d07933b585134929.zip frameworks_base-c39a6e0c51e182338deb8b63d07933b585134929.tar.gz frameworks_base-c39a6e0c51e182338deb8b63d07933b585134929.tar.bz2 |
auto import from //branches/cupcake/...@137873
Diffstat (limited to 'core/java')
46 files changed, 1476 insertions, 1158 deletions
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index d447eb2..15e0a4d 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -31,6 +31,7 @@ import android.database.Cursor; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -125,7 +126,14 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // support for AutoCompleteTextView suggestions display private SuggestionsAdapter mSuggestionsAdapter; - + private Handler mHandler = new Handler(); + private Runnable mInstallSuggestionAdapter = new Runnable() { + public void run() { + if (mSearchTextField != null) { + mSearchTextField.setAdapter(mSuggestionsAdapter); + } + } + }; /** * Constructor - fires it up and makes it look like the search UI. @@ -253,7 +261,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchTextField.setAdapter(mSuggestionsAdapter); mSearchTextField.setText(initialQuery); } else { - mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable); + mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable, + mHandler, mInstallSuggestionAdapter); mSearchTextField.setAdapter(mSuggestionsAdapter); // finally, load the user's initial text (which may trigger suggestions) @@ -1296,10 +1305,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // These private variables are shared by the filter thread and must be protected private WeakReference<Cursor> mRecentCursor = new WeakReference<Cursor>(null); private boolean mNonUserQuery = false; + private Handler mHandler; + private Runnable mInstallSuggestionAdapter; - public SuggestionsAdapter(Context context, SearchableInfo searchable) { + public SuggestionsAdapter(Context context, SearchableInfo searchable, + Handler handler, Runnable installSuggestionAdapter) { super(context, -1, null, null, null); mSearchable = searchable; + mHandler = handler; + mInstallSuggestionAdapter = installSuggestionAdapter; // set up provider resources (gives us icons, etc.) Context activityContext = mSearchable.getActivityContext(mContext); @@ -1414,7 +1428,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } // Now actually set up the cursor, columns, and the list view changeCursorAndColumns(c, from, to); - setViewResource(layout); + setViewResource(layout); + // Force the underlying ListView to discard and reload all layouts + // (Note, this could be optimized for cases where layout/cursor remain same) + mHandler.post(mInstallSuggestionAdapter); + } else { // Provide some help for developers instead of just silently discarding Log.w(LOG_TAG, "Suggestions cursor discarded due to missing required columns."); diff --git a/core/java/android/gadget/GadgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 3d88b58..10c2b02 100644 --- a/core/java/android/gadget/GadgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -1,5 +1,4 @@ /* - * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +13,7 @@ * limitations under the License. */ -package android.gadget; +package android.appwidget; import android.content.Context; import android.os.Handler; @@ -29,35 +28,35 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import com.android.internal.gadget.IGadgetHost; -import com.android.internal.gadget.IGadgetService; +import com.android.internal.appwidget.IAppWidgetHost; +import com.android.internal.appwidget.IAppWidgetService; /** - * GadgetHost provides the interaction with the Gadget Service for apps, - * like the home screen, that want to embed gadgets in their UI. + * AppWidgetHost provides the interaction with the AppWidget service for apps, + * like the home screen, that want to embed AppWidgets in their UI. */ -public class GadgetHost { +public class AppWidgetHost { static final int HANDLE_UPDATE = 1; static final int HANDLE_PROVIDER_CHANGED = 2; static Object sServiceLock = new Object(); - static IGadgetService sService; + static IAppWidgetService sService; Context mContext; String mPackageName; - class Callbacks extends IGadgetHost.Stub { - public void updateGadget(int gadgetId, RemoteViews views) { + class Callbacks extends IAppWidgetHost.Stub { + public void updateAppWidget(int appWidgetId, RemoteViews views) { Message msg = mHandler.obtainMessage(HANDLE_UPDATE); - msg.arg1 = gadgetId; + msg.arg1 = appWidgetId; msg.obj = views; msg.sendToTarget(); } - public void providerChanged(int gadgetId, GadgetProviderInfo info) { + public void providerChanged(int appWidgetId, AppWidgetProviderInfo info) { Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED); - msg.arg1 = gadgetId; + msg.arg1 = appWidgetId; msg.obj = info; msg.sendToTarget(); } @@ -71,11 +70,11 @@ public class GadgetHost { public void handleMessage(Message msg) { switch (msg.what) { case HANDLE_UPDATE: { - updateGadgetView(msg.arg1, (RemoteViews)msg.obj); + updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj); break; } case HANDLE_PROVIDER_CHANGED: { - onProviderChanged(msg.arg1, (GadgetProviderInfo)msg.obj); + onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj); break; } } @@ -86,22 +85,22 @@ public class GadgetHost { int mHostId; Callbacks mCallbacks = new Callbacks(); - HashMap<Integer,GadgetHostView> mViews = new HashMap(); + HashMap<Integer,AppWidgetHostView> mViews = new HashMap(); - public GadgetHost(Context context, int hostId) { + public AppWidgetHost(Context context, int hostId) { mContext = context; mHostId = hostId; mHandler = new UpdateHandler(context.getMainLooper()); synchronized (sServiceLock) { if (sService == null) { - IBinder b = ServiceManager.getService(Context.GADGET_SERVICE); - sService = IGadgetService.Stub.asInterface(b); + IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE); + sService = IAppWidgetService.Stub.asInterface(b); } } } /** - * Start receiving onGadgetChanged calls for your gadgets. Call this when your activity + * Start receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity * becomes visible, i.e. from onStart() in your Activity. */ public void startListening() { @@ -120,12 +119,12 @@ public class GadgetHost { final int N = updatedIds.length; for (int i=0; i<N; i++) { - updateGadgetView(updatedIds[i], updatedViews.get(i)); + updateAppWidgetView(updatedIds[i], updatedViews.get(i)); } } /** - * Stop receiving onGadgetChanged calls for your gadgets. Call this when your activity is + * Stop receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity is * no longer visible, i.e. from onStop() in your Activity. */ public void stopListening() { @@ -138,16 +137,16 @@ public class GadgetHost { } /** - * Get a gadgetId for a host in the calling process. + * Get a appWidgetId for a host in the calling process. * - * @return a gadgetId + * @return a appWidgetId */ - public int allocateGadgetId() { + public int allocateAppWidgetId() { try { if (mPackageName == null) { mPackageName = mContext.getPackageName(); } - return sService.allocateGadgetId(mPackageName, mHostId); + return sService.allocateAppWidgetId(mPackageName, mHostId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -155,13 +154,13 @@ public class GadgetHost { } /** - * Stop listening to changes for this gadget. + * Stop listening to changes for this AppWidget. */ - public void deleteGadgetId(int gadgetId) { + public void deleteAppWidgetId(int appWidgetId) { synchronized (mViews) { - mViews.remove(gadgetId); + mViews.remove(appWidgetId); try { - sService.deleteGadgetId(gadgetId); + sService.deleteAppWidgetId(appWidgetId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -170,10 +169,10 @@ public class GadgetHost { } /** - * Remove all records about this host from the gadget manager. + * Remove all records about this host from the AppWidget manager. * <ul> * <li>Call this when initializing your database, as it might be because of a data wipe.</li> - * <li>Call this to have the gadget manager release all resources associated with your + * <li>Call this to have the AppWidget manager release all resources associated with your * host. Any future calls about this host will cause the records to be re-allocated.</li> * </ul> */ @@ -190,7 +189,7 @@ public class GadgetHost { * Remove all records about all hosts for your package. * <ul> * <li>Call this when initializing your database, as it might be because of a data wipe.</li> - * <li>Call this to have the gadget manager release all resources associated with your + * <li>Call this to have the AppWidget manager release all resources associated with your * host. Any future calls about this host will cause the records to be re-allocated.</li> * </ul> */ @@ -203,45 +202,45 @@ public class GadgetHost { } } - public final GadgetHostView createView(Context context, int gadgetId, - GadgetProviderInfo gadget) { - GadgetHostView view = onCreateView(context, gadgetId, gadget); - view.setGadget(gadgetId, gadget); + public final AppWidgetHostView createView(Context context, int appWidgetId, + AppWidgetProviderInfo appWidget) { + AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget); + view.setAppWidget(appWidgetId, appWidget); synchronized (mViews) { - mViews.put(gadgetId, view); + mViews.put(appWidgetId, view); } RemoteViews views = null; try { - views = sService.getGadgetViews(gadgetId); + views = sService.getAppWidgetViews(appWidgetId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } - view.updateGadget(views); + view.updateAppWidget(views); return view; } /** - * Called to create the GadgetHostView. Override to return a custom subclass if you + * Called to create the AppWidgetHostView. Override to return a custom subclass if you * need it. {@more} */ - protected GadgetHostView onCreateView(Context context, int gadgetId, - GadgetProviderInfo gadget) { - return new GadgetHostView(context); + protected AppWidgetHostView onCreateView(Context context, int appWidgetId, + AppWidgetProviderInfo appWidget) { + return new AppWidgetHostView(context); } /** - * Called when the gadget provider for a gadget has been upgraded to a new apk. + * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk. */ - protected void onProviderChanged(int gadgetId, GadgetProviderInfo gadget) { + protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) { } - void updateGadgetView(int gadgetId, RemoteViews views) { - GadgetHostView v; + void updateAppWidgetView(int appWidgetId, RemoteViews views) { + AppWidgetHostView v; synchronized (mViews) { - v = mViews.get(gadgetId); + v = mViews.get(appWidgetId); } if (v != null) { - v.updateGadget(views); + v.updateAppWidget(views); } } } diff --git a/core/java/android/gadget/GadgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 5cbd988..be0f96e 100644 --- a/core/java/android/gadget/GadgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.gadget; +package android.appwidget; import android.content.Context; import android.content.pm.PackageManager; @@ -37,12 +37,12 @@ import android.widget.RemoteViews; import android.widget.TextView; /** - * Provides the glue to show gadget views. This class offers automatic animation + * Provides the glue to show AppWidget views. This class offers automatic animation * between updates, and will try recycling old views for each incoming * {@link RemoteViews}. */ -public class GadgetHostView extends FrameLayout { - static final String TAG = "GadgetHostView"; +public class AppWidgetHostView extends FrameLayout { + static final String TAG = "AppWidgetHostView"; static final boolean LOGD = false; static final boolean CROSSFADE = false; @@ -53,7 +53,7 @@ public class GadgetHostView extends FrameLayout { static final int FADE_DURATION = 1000; - // When we're inflating the initialLayout for a gadget, we only allow + // When we're inflating the initialLayout for a AppWidget, we only allow // views that are allowed in RemoteViews. static final LayoutInflater.Filter sInflaterFilter = new LayoutInflater.Filter() { public boolean onLoadClass(Class clazz) { @@ -63,8 +63,8 @@ public class GadgetHostView extends FrameLayout { Context mContext; - int mGadgetId; - GadgetProviderInfo mInfo; + int mAppWidgetId; + AppWidgetProviderInfo mInfo; View mView; int mViewMode = VIEW_MODE_NOINIT; int mLayoutId = -1; @@ -75,44 +75,44 @@ public class GadgetHostView extends FrameLayout { /** * Create a host view. Uses default fade animations. */ - public GadgetHostView(Context context) { + public AppWidgetHostView(Context context) { this(context, android.R.anim.fade_in, android.R.anim.fade_out); } /** * Create a host view. Uses specified animations when pushing - * {@link #updateGadget(RemoteViews)}. + * {@link #updateAppWidget(RemoteViews)}. * * @param animationIn Resource ID of in animation to use * @param animationOut Resource ID of out animation to use */ - public GadgetHostView(Context context, int animationIn, int animationOut) { + public AppWidgetHostView(Context context, int animationIn, int animationOut) { super(context); mContext = context; } /** - * Set the gadget that will be displayed by this view. + * Set the AppWidget that will be displayed by this view. */ - public void setGadget(int gadgetId, GadgetProviderInfo info) { - mGadgetId = gadgetId; + public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) { + mAppWidgetId = appWidgetId; mInfo = info; } - public int getGadgetId() { - return mGadgetId; + public int getAppWidgetId() { + return mAppWidgetId; } - public GadgetProviderInfo getGadgetInfo() { + public AppWidgetProviderInfo getAppWidgetInfo() { return mInfo; } /** * Process a set of {@link RemoteViews} coming in as an update from the - * gadget provider. Will animate into these new views as needed. + * AppWidget provider. Will animate into these new views as needed. */ - public void updateGadget(RemoteViews remoteViews) { - if (LOGD) Log.d(TAG, "updateGadget called mOld=" + mOld); + public void updateAppWidget(RemoteViews remoteViews) { + if (LOGD) Log.d(TAG, "updateAppWidget called mOld=" + mOld); boolean recycled = false; View content = null; @@ -180,7 +180,7 @@ public class GadgetHostView extends FrameLayout { // We've already done this -- nothing to do. return ; } - Log.w(TAG, "updateGadget couldn't find any view, using error view", exception); + Log.w(TAG, "updateAppWidget couldn't find any view, using error view", exception); content = getErrorView(); mViewMode = VIEW_MODE_ERROR; } @@ -263,7 +263,7 @@ public class GadgetHostView extends FrameLayout { } /** - * Inflate and return the default layout requested by gadget provider. + * Inflate and return the default layout requested by AppWidget provider. */ protected View getDefaultView() { View defaultView = null; @@ -288,7 +288,7 @@ public class GadgetHostView extends FrameLayout { } if (exception != null && LOGD) { - Log.w(TAG, "Error inflating gadget " + mInfo, exception); + Log.w(TAG, "Error inflating AppWidget " + mInfo, exception); } if (defaultView == null) { diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java new file mode 100644 index 0000000..3b10ed2 --- /dev/null +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.appwidget; + +import android.content.ComponentName; +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import android.widget.RemoteViews; + +import com.android.internal.appwidget.IAppWidgetService; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.WeakHashMap; + +/** + * Updates AppWidget state; gets information about installed AppWidget providers and other + * AppWidget related state. + */ +public class AppWidgetManager { + static final String TAG = "AppWidgetManager"; + + /** + * Send this from your {@link AppWidgetHost} activity when you want to pick an AppWidget to display. + * The AppWidget picker activity will be launched. + * <p> + * You must supply the following extras: + * <table> + * <tr> + * <td>{@link #EXTRA_APPWIDGET_ID}</td> + * <td>A newly allocated appWidgetId, which will be bound to the AppWidget provider + * once the user has selected one.</td> + * </tr> + * </table> + * + * <p> + * The system will respond with an onActivityResult call with the following extras in + * the intent: + * <table> + * <tr> + * <td>{@link #EXTRA_APPWIDGET_ID}</td> + * <td>The appWidgetId that you supplied in the original intent.</td> + * </tr> + * </table> + * <p> + * When you receive the result from the AppWidget pick activity, if the resultCode is + * {@link android.app.Activity#RESULT_OK}, an AppWidget has been selected. You should then + * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its configuration + * activity. If {@link android.app.Activity#RESULT_CANCELED} is returned, you should delete + * the appWidgetId. + * + * @see #ACTION_APPWIDGET_CONFIGURE + */ + public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK"; + + /** + * Sent when it is time to configure your AppWidget while it is being added to a host. + * This action is not sent as a broadcast to the AppWidget provider, but as a startActivity + * to the activity specified in the {@link AppWidgetProviderInfo AppWidgetProviderInfo meta-data}. + * + * <p> + * The intent will contain the following extras: + * <table> + * <tr> + * <td>{@link #EXTRA_APPWIDGET_ID}</td> + * <td>The appWidgetId to configure.</td> + * </tr> + * </table> + * + * <p>If you return {@link android.app.Activity#RESULT_OK} using + * {@link android.app.Activity#setResult Activity.setResult()}, the AppWidget will be added, + * and you will receive an {@link #ACTION_APPWIDGET_UPDATE} broadcast for this AppWidget. + * If you return {@link android.app.Activity#RESULT_CANCELED}, the host will cancel the add + * and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED} broadcast. + */ + public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE"; + + /** + * An intent extra that contains one appWidgetId. + * <p> + * The value will be an int that can be retrieved like this: + * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/AppWidgetHostActivity.java getExtra_EXTRA_APPWIDGET_ID} + */ + public static final String EXTRA_APPWIDGET_ID = "appWidgetId"; + + /** + * An intent extra that contains multiple appWidgetIds. + * <p> + * The value will be an int array that can be retrieved like this: + * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/TestAppWidgetProvider.java getExtra_EXTRA_APPWIDGET_IDS} + */ + public static final String EXTRA_APPWIDGET_IDS = "appWidgetIds"; + + /** + * A sentiel value that the AppWidget manager will never return as a appWidgetId. + */ + public static final int INVALID_APPWIDGET_ID = 0; + + /** + * Sent when it is time to update your AppWidget. + * + * <p>This may be sent in response to a new instance for this AppWidget provider having + * been instantiated, the requested {@link AppWidgetProviderInfo#updatePeriodMillis update interval} + * having lapsed, or the system booting. + * + * <p> + * The intent will contain the following extras: + * <table> + * <tr> + * <td>{@link #EXTRA_APPWIDGET_IDS}</td> + * <td>The appWidgetIds to update. This may be all of the AppWidgets created for this + * provider, or just a subset. The system tries to send updates for as few AppWidget + * instances as possible.</td> + * </tr> + * </table> + * + * @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) + */ + public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE"; + + /** + * Sent when an instance of an AppWidget is deleted from its host. + * + * @see AppWidgetProvider#onDeleted AppWidgetProvider.onDeleted(Context context, int[] appWidgetIds) + */ + public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED"; + + /** + * Sent when an instance of an AppWidget is removed from the last host. + * + * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context) + */ + public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED"; + + /** + * Sent when an instance of an AppWidget is added to a host for the first time. + * This broadcast is sent at boot time if there is a AppWidgetHost installed with + * an instance for this provider. + * + * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context) + */ + public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED"; + + /** + * Field for the manifest meta-data tag. + * + * @see AppWidgetProviderInfo + */ + public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider"; + + static WeakHashMap<Context, WeakReference<AppWidgetManager>> sManagerCache = new WeakHashMap(); + static IAppWidgetService sService; + + Context mContext; + + /** + * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context + * Context} object. + */ + public static AppWidgetManager getInstance(Context context) { + synchronized (sManagerCache) { + if (sService == null) { + IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE); + sService = IAppWidgetService.Stub.asInterface(b); + } + + WeakReference<AppWidgetManager> ref = sManagerCache.get(context); + AppWidgetManager result = null; + if (ref != null) { + result = ref.get(); + } + if (result == null) { + result = new AppWidgetManager(context); + sManagerCache.put(context, new WeakReference(result)); + } + return result; + } + } + + private AppWidgetManager(Context context) { + mContext = context; + } + + /** + * Set the RemoteViews to use for the specified appWidgetIds. + * + * <p> + * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast, + * and outside of the handler. + * This method will only work when called from the uid that owns the AppWidget provider. + * + * @param appWidgetIds The AppWidget instances for which to set the RemoteViews. + * @param views The RemoteViews object to show. + */ + public void updateAppWidget(int[] appWidgetIds, RemoteViews views) { + try { + sService.updateAppWidgetIds(appWidgetIds, views); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Set the RemoteViews to use for the specified appWidgetId. + * + * <p> + * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast, + * and outside of the handler. + * This method will only work when called from the uid that owns the AppWidget provider. + * + * @param appWidgetId The AppWidget instance for which to set the RemoteViews. + * @param views The RemoteViews object to show. + */ + public void updateAppWidget(int appWidgetId, RemoteViews views) { + updateAppWidget(new int[] { appWidgetId }, views); + } + + /** + * Set the RemoteViews to use for all AppWidget instances for the supplied AppWidget provider. + * + * <p> + * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast, + * and outside of the handler. + * This method will only work when called from the uid that owns the AppWidget provider. + * + * @param provider The {@link ComponentName} for the {@link + * android.content.BroadcastReceiver BroadcastReceiver} provider + * for your AppWidget. + * @param views The RemoteViews object to show. + */ + public void updateAppWidget(ComponentName provider, RemoteViews views) { + try { + sService.updateAppWidgetProvider(provider, views); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Return a list of the AppWidget providers that are currently installed. + */ + public List<AppWidgetProviderInfo> getInstalledProviders() { + try { + return sService.getInstalledProviders(); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Get the available info about the AppWidget. + * + * @return A appWidgetId. If the appWidgetId has not been bound to a provider yet, or + * you don't have access to that appWidgetId, null is returned. + */ + public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) { + try { + return sService.getAppWidgetInfo(appWidgetId); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Set the component for a given appWidgetId. + * + * <p class="note">You need the APPWIDGET_LIST permission. This method is to be used by the + * AppWidget picker. + * + * @param appWidgetId The AppWidget instance for which to set the RemoteViews. + * @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget + * provider for this AppWidget. + */ + public void bindAppWidgetId(int appWidgetId, ComponentName provider) { + try { + sService.bindAppWidgetId(appWidgetId, provider); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Get the list of appWidgetIds that have been bound to the given AppWidget + * provider. + * + * @param provider The {@link android.content.BroadcastReceiver} that is the + * AppWidget provider to find appWidgetIds for. + */ + public int[] getAppWidgetIds(ComponentName provider) { + try { + return sService.getAppWidgetIds(provider); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } +} + diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java new file mode 100755 index 0000000..f70de9c --- /dev/null +++ b/core/java/android/appwidget/AppWidgetProvider.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.appwidget; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +/** + * A conveience class to aid in implementing an AppWidget provider. + * Everything you can do with AppWidgetProvider, you can do with a regular {@link BroadcastReceiver}. + * AppWidgetProvider merely parses the relevant fields out of the Intent that is received in + * {@link #onReceive(Context,Intent) onReceive(Context,Intent)}, and calls hook methods + * with the received extras. + * + * <p>Extend this class and override one or more of the {@link #onUpdate}, {@link #onDeleted}, + * {@link #onEnabled} or {@link #onDisabled} methods to implement your own AppWidget functionality. + * + * <h3>Sample Code</h3> + * For an example of how to write a AppWidget provider, see the + * <a href="{@toroot}reference/android/appwidget/package-descr.html#providers">android.appwidget + * package overview</a>. + */ +public class AppWidgetProvider extends BroadcastReceiver { + /** + * Constructor to initialize AppWidgetProvider. + */ + public AppWidgetProvider() { + } + + /** + * Implements {@link BroadcastReceiver#onReceive} to dispatch calls to the various + * other methods on AppWidgetProvider. + * + * @param context The Context in which the receiver is running. + * @param intent The Intent being received. + */ + // BEGIN_INCLUDE(onReceive) + public void onReceive(Context context, Intent intent) { + // Protect against rogue update broadcasts (not really a security issue, + // just filter bad broacasts out so subclasses are less likely to crash). + String action = intent.getAction(); + if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) { + Bundle extras = intent.getExtras(); + if (extras != null) { + int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS); + if (appWidgetIds != null && appWidgetIds.length > 0) { + this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds); + } + } + } + else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) { + Bundle extras = intent.getExtras(); + if (extras != null) { + int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS); + if (appWidgetIds != null && appWidgetIds.length > 0) { + this.onDeleted(context, appWidgetIds); + } + } + } + else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) { + this.onEnabled(context); + } + else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) { + this.onDisabled(context); + } + } + // END_INCLUDE(onReceive) + + /** + * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} broadcast when + * this AppWidget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews} + * for a set of AppWidgets. Override this method to implement your own AppWidget functionality. + * + * {@more} + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * @param appWidgetManager A {@link AppWidgetManager} object you can call {@link + * AppWidgetManager#updateAppWidget} on. + * @param appWidgetIds The appWidgetIds for which an update is needed. Note that this + * may be all of the AppWidget instances for this provider, or just + * a subset of them. + * + * @see AppWidgetManager#ACTION_APPWIDGET_UPDATE + */ + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + } + + /** + * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_DELETED} broadcast when + * one or more AppWidget instances have been deleted. Override this method to implement + * your own AppWidget functionality. + * + * {@more} + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * @param appWidgetIds The appWidgetIds that have been deleted from their host. + * + * @see AppWidgetManager#ACTION_APPWIDGET_DELETED + */ + public void onDeleted(Context context, int[] appWidgetIds) { + } + + /** + * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_ENABLED} broadcast when + * the a AppWidget for this provider is instantiated. Override this method to implement your + * own AppWidget functionality. + * + * {@more} + * When the last AppWidget for this provider is deleted, + * {@link AppWidgetManager#ACTION_APPWIDGET_DISABLED} is sent by the AppWidget manager, and + * {@link #onDisabled} is called. If after that, an AppWidget for this provider is created + * again, onEnabled() will be called again. + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * + * @see AppWidgetManager#ACTION_APPWIDGET_ENABLED + */ + public void onEnabled(Context context) { + } + + /** + * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_DISABLED} broadcast, which + * is sent when the last AppWidget instance for this provider is deleted. Override this method + * to implement your own AppWidget functionality. + * + * {@more} + * + * @param context The {@link android.content.Context Context} in which this receiver is + * running. + * + * @see AppWidgetManager#ACTION_APPWIDGET_DISABLED + */ + public void onDisabled(Context context) { + } +} diff --git a/core/java/android/gadget/GadgetProviderInfo.aidl b/core/java/android/appwidget/AppWidgetProviderInfo.aidl index 589f886..82b3ada 100644 --- a/core/java/android/gadget/GadgetProviderInfo.aidl +++ b/core/java/android/appwidget/AppWidgetProviderInfo.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.gadget; +package android.appwidget; -parcelable GadgetProviderInfo; +parcelable AppWidgetProviderInfo; diff --git a/core/java/android/gadget/GadgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java index 95c0432..8530c35 100644 --- a/core/java/android/gadget/GadgetProviderInfo.java +++ b/core/java/android/appwidget/AppWidgetProviderInfo.java @@ -14,21 +14,21 @@ * limitations under the License. */ -package android.gadget; +package android.appwidget; import android.os.Parcel; import android.os.Parcelable; import android.content.ComponentName; /** - * Describes the meta data for an installed gadget provider. The fields in this class - * correspond to the fields in the <code><gadget-provider></code> xml tag. + * Describes the meta data for an installed AppWidget provider. The fields in this class + * correspond to the fields in the <code><appwidget-provider></code> xml tag. */ -public class GadgetProviderInfo implements Parcelable { +public class AppWidgetProviderInfo implements Parcelable { /** - * Identity of this gadget component. This component should be a {@link - * android.content.BroadcastReceiver}, and it will be sent the Gadget intents - * {@link android.gadget as described in the gadget package documentation}. + * Identity of this AppWidget component. This component should be a {@link + * android.content.BroadcastReceiver}, and it will be sent the AppWidget intents + * {@link android.appwidget as described in the AppWidget package documentation}. * * <p>This field corresponds to the <code>android:name</code> attribute in * the <code><receiver></code> element in the AndroidManifest.xml file. @@ -36,50 +36,50 @@ public class GadgetProviderInfo implements Parcelable { public ComponentName provider; /** - * Minimum width of the gadget, in dp. + * Minimum width of the AppWidget, in dp. * * <p>This field corresponds to the <code>android:minWidth</code> attribute in - * the gadget meta-data file. + * the AppWidget meta-data file. */ public int minWidth; /** - * Minimum height of the gadget, in dp. + * Minimum height of the AppWidget, in dp. * * <p>This field corresponds to the <code>android:minHeight</code> attribute in - * the gadget meta-data file. + * the AppWidget meta-data file. */ public int minHeight; /** - * How often, in milliseconds, that this gadget wants to be updated. - * The gadget manager may place a limit on how often a gadget is updated. + * How often, in milliseconds, that this AppWidget wants to be updated. + * The AppWidget manager may place a limit on how often a AppWidget is updated. * * <p>This field corresponds to the <code>android:updatePeriodMillis</code> attribute in - * the gadget meta-data file. + * the AppWidget meta-data file. */ public int updatePeriodMillis; /** - * The resource id of the initial layout for this gadget. This should be - * displayed until the RemoteViews for the gadget is available. + * The resource id of the initial layout for this AppWidget. This should be + * displayed until the RemoteViews for the AppWidget is available. * * <p>This field corresponds to the <code>android:initialLayout</code> attribute in - * the gadget meta-data file. + * the AppWidget meta-data file. */ public int initialLayout; /** - * The activity to launch that will configure the gadget. + * The activity to launch that will configure the AppWidget. * * <p>This class name of field corresponds to the <code>android:configure</code> attribute in - * the gadget meta-data file. The package name always corresponds to the package containing - * the gadget provider. + * the AppWidget meta-data file. The package name always corresponds to the package containing + * the AppWidget provider. */ public ComponentName configure; /** - * The label to display to the user in the gadget picker. If not supplied in the + * The label to display to the user in the AppWidget picker. If not supplied in the * xml, the application label will be used. * * <p>This field corresponds to the <code>android:label</code> attribute in @@ -88,7 +88,7 @@ public class GadgetProviderInfo implements Parcelable { public String label; /** - * The icon to display for this gadget in the gadget picker. If not supplied in the + * The icon to display for this AppWidget in the AppWidget picker. If not supplied in the * xml, the application icon will be used. * * <p>This field corresponds to the <code>android:icon</code> attribute in @@ -96,13 +96,13 @@ public class GadgetProviderInfo implements Parcelable { */ public int icon; - public GadgetProviderInfo() { + public AppWidgetProviderInfo() { } /** - * Unflatten the GadgetProviderInfo from a parcel. + * Unflatten the AppWidgetProviderInfo from a parcel. */ - public GadgetProviderInfo(Parcel in) { + public AppWidgetProviderInfo(Parcel in) { if (0 != in.readInt()) { this.provider = new ComponentName(in); } @@ -144,24 +144,24 @@ public class GadgetProviderInfo implements Parcelable { } /** - * Parcelable.Creator that instantiates GadgetProviderInfo objects + * Parcelable.Creator that instantiates AppWidgetProviderInfo objects */ - public static final Parcelable.Creator<GadgetProviderInfo> CREATOR - = new Parcelable.Creator<GadgetProviderInfo>() + public static final Parcelable.Creator<AppWidgetProviderInfo> CREATOR + = new Parcelable.Creator<AppWidgetProviderInfo>() { - public GadgetProviderInfo createFromParcel(Parcel parcel) + public AppWidgetProviderInfo createFromParcel(Parcel parcel) { - return new GadgetProviderInfo(parcel); + return new AppWidgetProviderInfo(parcel); } - public GadgetProviderInfo[] newArray(int size) + public AppWidgetProviderInfo[] newArray(int size) { - return new GadgetProviderInfo[size]; + return new AppWidgetProviderInfo[size]; } }; public String toString() { - return "GadgetProviderInfo(provider=" + this.provider + ")"; + return "AppWidgetProviderInfo(provider=" + this.provider + ")"; } } diff --git a/core/java/android/appwidget/package.html b/core/java/android/appwidget/package.html new file mode 100644 index 0000000..b6cd9c7 --- /dev/null +++ b/core/java/android/appwidget/package.html @@ -0,0 +1,136 @@ +<body> +<p>Android allows applications to publish views to be embedded in other applications. These +views are called widgets, and are published by "AppWidget providers." The component that can +contain widgets is called a "AppWidget host." +</p> +<h3><a href="package-descr.html#providers">AppWidget Providers</a></h3> +<ul> + <li><a href="package-descr.html#provider_manifest">Declaring a widget in the AndroidManifest</a></li> + <li><a href="package-descr.html#provider_meta_data">Adding the AppWidgetProviderInfo meta-data</a></li> + <li><a href="package-descr.html#provider_AppWidgetProvider">Using the AppWidgetProvider class</a></li> + <li><a href="package-descr.html#provider_configuration">AppWidget Configuration UI</a></li> + <li><a href="package-descr.html#provider_broadcasts">AppWidget Broadcast Intents</a></li> +</ul> +<h3><a href="package-descr.html#">AppWidget Hosts</a></h3> + + +{@more} + + +<h2><a name="providers"></a>AppWidget Providers</h2> +<p> +Any application can publish widgets. All an application needs to do to publish a widget is +to have a {@link android.content.BroadcastReceiver} that receives the {@link +android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE AppWidgetManager.ACTION_APPWIDGET_UPDATE} intent, +and provide some meta-data about the widget. Android provides the +{@link android.appwidget.AppWidgetProvider} class, which extends BroadcastReceiver, as a convenience +class to aid in handling the broadcasts. + +<h3><a name="provider_manifest"></a>Declaring a widget in the AndroidManifest</h3> + +<p> +First, declare the {@link android.content.BroadcastReceiver} in your application's +<code>AndroidManifest.xml</code> file. + +{@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/AndroidManifest.xml AppWidgetProvider} + +<p> +The <b><code><receiver></b> element has the following attributes: +<ul> + <li><b><code>android:name</code> -</b> which specifies the + {@link android.content.BroadcastReceiver} or {@link android.appwidget.AppWidgetProvider} + class.</li> + <li><b><code>android:label</code> -</b> which specifies the string resource that + will be shown by the widget picker as the label.</li> + <li><b><code>android:icon</code> -</b> which specifies the drawable resource that + will be shown by the widget picker as the icon.</li> +</ul> + +<p> +The <b><code><intent-filter></b> element tells the {@link android.content.pm.PackageManager} +that this {@link android.content.BroadcastReceiver} receives the {@link +android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE AppWidgetManager.ACTION_APPWIDGET_UPDATE} broadcast. +The widget manager will send other broadcasts directly to your widget provider as required. +It is only necessary to explicitly declare that you accept the {@link +android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE AppWidgetManager.ACTION_APPWIDGET_UPDATE} broadcast. + +<p> +The <b><code><meta-data></code></b> element tells the widget manager which xml resource to +read to find the {@link android.appwidget.AppWidgetProviderInfo} for your widget provider. It has the following +attributes: +<ul> + <li><b><code>android:name="android.appwidget.provider"</code> -</b> identifies this meta-data + as the {@link android.appwidget.AppWidgetProviderInfo} descriptor.</li> + <li><b><code>android:resource</code> -</b> is the xml resource to use as that descriptor.</li> +</ul> + + +<h3><a name="provider_meta_data"></a>Adding the {@link android.appwidget.AppWidgetProviderInfo AppWidgetProviderInfo} meta-data</h3> + +<p> +For a widget, the values in the {@link android.appwidget.AppWidgetProviderInfo} structure are supplied +in an XML resource. In the example above, the xml resource is referenced with +<code>android:resource="@xml/appwidget_info"</code>. That XML file would go in your application's +directory at <code>res/xml/appwidget_info.xml</code>. Here is a simple example. + +{@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/res/xml/appwidget_info.xml AppWidgetProviderInfo} + +<p> +The attributes are as documented in the {@link android.appwidget.AppWidgetProviderInfo GagetInfo} class. (86400000 milliseconds means once per day) + + +<h3><a name="provider_AppWidgetProvider"></a>Using the {@link android.appwidget.AppWidgetProvider AppWidgetProvider} class</h3> + +<p>The AppWidgetProvider class is the easiest way to handle the widget provider intent broadcasts. +See the <code>src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.java</code> +sample class in ApiDemos for an example. + +<p class="note">Keep in mind that since the the AppWidgetProvider is a BroadcastReceiver, +your process is not guaranteed to keep running after the callback methods return. See +<a href="../../../guide/topics/fundamentals.html#broadlife">Application Fundamentals > +Broadcast Receiver Lifecycle</a> for more information. + + + +<h3><a name="provider_configuration"></a>AppWidget Configuration UI</h3> + +<p> +Widget hosts have the ability to start a configuration activity when a widget is instantiated. +The activity should be declared as normal in AndroidManifest.xml, and it should be listed in +the AppWidgetProviderInfo XML file in the <code>android:configure</code> attribute. + +<p>The activity you specified will be launched with the {@link +android.appwidget.AppWidgetManager#ACTION_APPWIDGET_CONFIGURE} action. See the documentation for that +action for more info. + +<p>See the <code>src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java</code> +sample class in ApiDemos for an example. + + + +<h3><a name="providers_broadcasts"></a>AppWidget Broadcast Intents</h3> + +<p>{@link android.appwidget.AppWidgetProvider} is just a convenience class. If you would like +to receive the widget broadcasts directly, you can. The four intents you need to care about are: +<ul> + <li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE}</li> + <li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_DELETED}</li> + <li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_ENABLED}</li> + <li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_DISABLED}</li> +</ul> + +<p>By way of example, the implementation of +{@link android.appwidget.AppWidgetProvider#onReceive} is quite simple:</p> + +{@sample frameworks/base/core/java/android/appwidget/AppWidgetProvider.java onReceive} + + +<h2>AppWidget Hosts</h3> +<p>Widget hosts are the containers in which widgets can be placed. Most of the look and feel +details are left up to the widget hosts. For example, the home screen has one way of viewing +widgets, but the lock screen could also contain widgets, and it would have a different way of +adding, removing and otherwise managing widgets.</p> +<p>For more information on implementing your own widget host, see the +{@link android.appwidget.AppWidgetHost AppWidgetHost} class.</p> +</body> + diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index e0fe533..9a0dc9f 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1250,12 +1250,12 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a - * {@blink android.gadget.GadgetManager} for accessing wallpapers. + * {@blink android.appwidget.AppWidgetManager} for accessing AppWidgets. * * @hide * @see #getSystemService */ - public static final String GADGET_SERVICE = "gadget"; + public static final String APPWIDGET_SERVICE = "appwidget"; /** * Determine whether the given permission is allowed for a particular diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 824fd9b..bb80e10 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -525,7 +525,6 @@ import java.util.Set; * <li> {@link #CATEGORY_INFO} * <li> {@link #CATEGORY_HOME} * <li> {@link #CATEGORY_PREFERENCE} - * <li> {@link #CATEGORY_GADGET} * <li> {@link #CATEGORY_TEST} * </ul> * @@ -1544,12 +1543,6 @@ public class Intent implements Parcelable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_TAB = "android.intent.category.TAB"; /** - * This activity can be embedded inside of another activity that is hosting - * gadgets. - */ - @SdkConstant(SdkConstantType.INTENT_CATEGORY) - public static final String CATEGORY_GADGET = "android.intent.category.GADGET"; - /** * Should be displayed in the top-level launcher. */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) @@ -1579,9 +1572,6 @@ public class Intent implements Parcelable { public static final String CATEGORY_DEVELOPMENT_PREFERENCE = "android.intent.category.DEVELOPMENT_PREFERENCE"; /** * Capable of running inside a parent activity container. - * - * <p>Note: being removed in favor of more explicit categories such as - * CATEGORY_GADGET */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_EMBED = "android.intent.category.EMBED"; diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 0f3f270..453a83d 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -304,7 +304,6 @@ public class ColorStateList implements Parcelable { for (int i=0; i<N; i++) { dest.writeIntArray(mStateSpecs[i]); } - dest.writeArray(mStateSpecs); dest.writeIntArray(mColors); } diff --git a/core/java/android/gadget/GadgetManager.java b/core/java/android/gadget/GadgetManager.java deleted file mode 100644 index d2c4055..0000000 --- a/core/java/android/gadget/GadgetManager.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.gadget; - -import android.content.ComponentName; -import android.content.Context; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.util.Log; -import android.widget.RemoteViews; - -import com.android.internal.gadget.IGadgetService; - -import java.lang.ref.WeakReference; -import java.util.List; -import java.util.WeakHashMap; - -/** - * Updates gadget state; gets information about installed gadget providers and other - * gadget related state. - */ -public class GadgetManager { - static final String TAG = "GadgetManager"; - - /** - * Send this from your gadget host activity when you want to pick a gadget to display. - * The gadget picker activity will be launched. - * <p> - * You must supply the following extras: - * <table> - * <tr> - * <td>{@link #EXTRA_GADGET_ID}</td> - * <td>A newly allocated gadgetId, which will be bound to the gadget provider - * once the user has selected one.</td> - * </tr> - * </table> - * - * <p> - * The system will respond with an onActivityResult call with the following extras in - * the intent: - * <table> - * <tr> - * <td>{@link #EXTRA_GADGET_ID}</td> - * <td>The gadgetId that you supplied in the original intent.</td> - * </tr> - * </table> - * <p> - * When you receive the result from the gadget pick activity, if the resultCode is - * {@link android.app.Activity#RESULT_OK}, a gadget has been selected. You should then - * check the GadgetProviderInfo for the returned gadget, and if it has one, launch its configuration - * activity. If {@link android.app.Activity#RESULT_CANCELED} is returned, you should delete - * the gadgetId. - * - * @see #ACTION_GADGET_CONFIGURE - */ - public static final String ACTION_GADGET_PICK = "android.gadget.action.GADGET_PICK"; - - /** - * Sent when it is time to configure your gadget while it is being added to a host. - * This action is not sent as a broadcast to the gadget provider, but as a startActivity - * to the activity specified in the {@link GadgetProviderInfo GadgetProviderInfo meta-data}. - * - * <p> - * The intent will contain the following extras: - * <table> - * <tr> - * <td>{@link #EXTRA_GADGET_ID}</td> - * <td>The gadgetId to configure.</td> - * </tr> - * </table> - * - * <p>If you return {@link android.app.Activity#RESULT_OK} using - * {@link android.app.Activity#setResult Activity.setResult()}, the gadget will be added, - * and you will receive an {@link #ACTION_GADGET_UPDATE} broadcast for this gadget. - * If you return {@link android.app.Activity#RESULT_CANCELED}, the host will cancel the add - * and not display this gadget, and you will receive a {@link #ACTION_GADGET_DELETED} broadcast. - */ - public static final String ACTION_GADGET_CONFIGURE = "android.gadget.action.GADGET_CONFIGURE"; - - /** - * An intent extra that contains one gadgetId. - * <p> - * The value will be an int that can be retrieved like this: - * {@sample frameworks/base/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/GadgetHostActivity.java getExtra_EXTRA_GADGET_ID} - */ - public static final String EXTRA_GADGET_ID = "gadgetId"; - - /** - * An intent extra that contains multiple gadgetIds. - * <p> - * The value will be an int array that can be retrieved like this: - * {@sample frameworks/base/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/TestGadgetProvider.java getExtra_EXTRA_GADGET_IDS} - */ - public static final String EXTRA_GADGET_IDS = "gadgetIds"; - - /** - * A sentiel value that the gadget manager will never return as a gadgetId. - */ - public static final int INVALID_GADGET_ID = 0; - - /** - * Sent when it is time to update your gadget. - * - * <p>This may be sent in response to a new instance for this gadget provider having - * been instantiated, the requested {@link GadgetProviderInfo#updatePeriodMillis update interval} - * having lapsed, or the system booting. - * - * <p> - * The intent will contain the following extras: - * <table> - * <tr> - * <td>{@link #EXTRA_GADGET_IDS}</td> - * <td>The gadgetIds to update. This may be all of the gadgets created for this - * provider, or just a subset. The system tries to send updates for as few gadget - * instances as possible.</td> - * </tr> - * </table> - * - * @see GadgetProvider#onUpdate GadgetProvider.onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds) - */ - public static final String ACTION_GADGET_UPDATE = "android.gadget.action.GADGET_UPDATE"; - - /** - * Sent when an instance of a gadget is deleted from its host. - * - * @see GadgetProvider#onDeleted GadgetProvider.onDeleted(Context context, int[] gadgetIds) - */ - public static final String ACTION_GADGET_DELETED = "android.gadget.action.GADGET_DELETED"; - - /** - * Sent when an instance of a gadget is removed from the last host. - * - * @see GadgetProvider#onEnabled GadgetProvider.onEnabled(Context context) - */ - public static final String ACTION_GADGET_DISABLED = "android.gadget.action.GADGET_DISABLED"; - - /** - * Sent when an instance of a gadget is added to a host for the first time. - * This broadcast is sent at boot time if there is a gadget host installed with - * an instance for this provider. - * - * @see GadgetProvider#onEnabled GadgetProvider.onEnabled(Context context) - */ - public static final String ACTION_GADGET_ENABLED = "android.gadget.action.GADGET_ENABLED"; - - /** - * Field for the manifest meta-data tag. - * - * @see GadgetProviderInfo - */ - public static final String META_DATA_GADGET_PROVIDER = "android.gadget.provider"; - - static WeakHashMap<Context, WeakReference<GadgetManager>> sManagerCache = new WeakHashMap(); - static IGadgetService sService; - - Context mContext; - - /** - * Get the GadgetManager instance to use for the supplied {@link android.content.Context - * Context} object. - */ - public static GadgetManager getInstance(Context context) { - synchronized (sManagerCache) { - if (sService == null) { - IBinder b = ServiceManager.getService(Context.GADGET_SERVICE); - sService = IGadgetService.Stub.asInterface(b); - } - - WeakReference<GadgetManager> ref = sManagerCache.get(context); - GadgetManager result = null; - if (ref != null) { - result = ref.get(); - } - if (result == null) { - result = new GadgetManager(context); - sManagerCache.put(context, new WeakReference(result)); - } - return result; - } - } - - private GadgetManager(Context context) { - mContext = context; - } - - /** - * Set the RemoteViews to use for the specified gadgetIds. - * - * <p> - * It is okay to call this method both inside an {@link #ACTION_GADGET_UPDATE} broadcast, - * and outside of the handler. - * This method will only work when called from the uid that owns the gadget provider. - * - * @param gadgetIds The gadget instances for which to set the RemoteViews. - * @param views The RemoteViews object to show. - */ - public void updateGadget(int[] gadgetIds, RemoteViews views) { - try { - sService.updateGadgetIds(gadgetIds, views); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } - - /** - * Set the RemoteViews to use for the specified gadgetId. - * - * <p> - * It is okay to call this method both inside an {@link #ACTION_GADGET_UPDATE} broadcast, - * and outside of the handler. - * This method will only work when called from the uid that owns the gadget provider. - * - * @param gadgetId The gadget instance for which to set the RemoteViews. - * @param views The RemoteViews object to show. - */ - public void updateGadget(int gadgetId, RemoteViews views) { - updateGadget(new int[] { gadgetId }, views); - } - - /** - * Set the RemoteViews to use for all gadget instances for the supplied gadget provider. - * - * <p> - * It is okay to call this method both inside an {@link #ACTION_GADGET_UPDATE} broadcast, - * and outside of the handler. - * This method will only work when called from the uid that owns the gadget provider. - * - * @param provider The {@link ComponentName} for the {@link - * android.content.BroadcastReceiver BroadcastReceiver} provider - * for your gadget. - * @param views The RemoteViews object to show. - */ - public void updateGadget(ComponentName provider, RemoteViews views) { - try { - sService.updateGadgetProvider(provider, views); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } - - /** - * Return a list of the gadget providers that are currently installed. - */ - public List<GadgetProviderInfo> getInstalledProviders() { - try { - return sService.getInstalledProviders(); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } - - /** - * Get the available info about the gadget. - * - * @return A gadgetId. If the gadgetId has not been bound to a provider yet, or - * you don't have access to that gadgetId, null is returned. - */ - public GadgetProviderInfo getGadgetInfo(int gadgetId) { - try { - return sService.getGadgetInfo(gadgetId); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } - - /** - * Set the component for a given gadgetId. - * - * <p class="note">You need the GADGET_LIST permission. This method is to be used by the - * gadget picker. - * - * @param gadgetId The gadget instance for which to set the RemoteViews. - * @param provider The {@link android.content.BroadcastReceiver} that will be the gadget - * provider for this gadget. - */ - public void bindGadgetId(int gadgetId, ComponentName provider) { - try { - sService.bindGadgetId(gadgetId, provider); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } - - /** - * Get the list of gadgetIds that have been bound to the given gadget - * provider. - * - * @param provider The {@link android.content.BroadcastReceiver} that is the - * gadget provider to find gadgetIds for. - */ - public int[] getGadgetIds(ComponentName provider) { - try { - return sService.getGadgetIds(provider); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } -} - diff --git a/core/java/android/gadget/GadgetProvider.java b/core/java/android/gadget/GadgetProvider.java deleted file mode 100755 index 7e10e78..0000000 --- a/core/java/android/gadget/GadgetProvider.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.gadget; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; - -/** - * A conveience class to aid in implementing a gadget provider. - * Everything you can do with GadgetProvider, you can do with a regular {@link BroadcastReceiver}. - * GadgetProvider merely parses the relevant fields out of the Intent that is received in - * {@link #onReceive(Context,Intent) onReceive(Context,Intent)}, and calls hook methods - * with the received extras. - * - * <p>Extend this class and override one or more of the {@link #onUpdate}, {@link #onDeleted}, - * {@link #onEnabled} or {@link #onDisabled} methods to implement your own gadget functionality. - * - * <h3>Sample Code</h3> - * For an example of how to write a gadget provider, see the - * <a href="{@toroot}reference/android/gadget/package-descr.html#providers">android.gadget - * package overview</a>. - */ -public class GadgetProvider extends BroadcastReceiver { - /** - * Constructor to initialize GadgetProvider. - */ - public GadgetProvider() { - } - - /** - * Implements {@link BroadcastReceiver#onReceive} to dispatch calls to the various - * other methods on GadgetProvider. - * - * @param context The Context in which the receiver is running. - * @param intent The Intent being received. - */ - // BEGIN_INCLUDE(onReceive) - public void onReceive(Context context, Intent intent) { - // Protect against rogue update broadcasts (not really a security issue, - // just filter bad broacasts out so subclasses are less likely to crash). - String action = intent.getAction(); - if (GadgetManager.ACTION_GADGET_UPDATE.equals(action)) { - Bundle extras = intent.getExtras(); - if (extras != null) { - int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS); - if (gadgetIds != null && gadgetIds.length > 0) { - this.onUpdate(context, GadgetManager.getInstance(context), gadgetIds); - } - } - } - else if (GadgetManager.ACTION_GADGET_DELETED.equals(action)) { - Bundle extras = intent.getExtras(); - if (extras != null) { - int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS); - if (gadgetIds != null && gadgetIds.length > 0) { - this.onDeleted(context, gadgetIds); - } - } - } - else if (GadgetManager.ACTION_GADGET_ENABLED.equals(action)) { - this.onEnabled(context); - } - else if (GadgetManager.ACTION_GADGET_DISABLED.equals(action)) { - this.onDisabled(context); - } - } - // END_INCLUDE(onReceive) - - /** - * Called in response to the {@link GadgetManager#ACTION_GADGET_UPDATE} broadcast when - * this gadget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews} - * for a set of gadgets. Override this method to implement your own gadget functionality. - * - * {@more} - * - * @param context The {@link android.content.Context Context} in which this receiver is - * running. - * @param gadgetManager A {@link GadgetManager} object you can call {@link - * GadgetManager#updateGadget} on. - * @param gadgetIds The gadgetsIds for which an update is needed. Note that this - * may be all of the gadget instances for this provider, or just - * a subset of them. - * - * @see GadgetManager#ACTION_GADGET_UPDATE - */ - public void onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds) { - } - - /** - * Called in response to the {@link GadgetManager#ACTION_GADGET_DELETED} broadcast when - * one or more gadget instances have been deleted. Override this method to implement - * your own gadget functionality. - * - * {@more} - * - * @param context The {@link android.content.Context Context} in which this receiver is - * running. - * @param gadgetIds The gadgetsIds that have been deleted from their host. - * - * @see GadgetManager#ACTION_GADGET_DELETED - */ - public void onDeleted(Context context, int[] gadgetIds) { - } - - /** - * Called in response to the {@link GadgetManager#ACTION_GADGET_ENABLED} broadcast when - * the a gadget for this provider is instantiated. Override this method to implement your - * own gadget functionality. - * - * {@more} - * When the last gadget for this provider is deleted, - * {@link GadgetManager#ACTION_GADGET_DISABLED} is sent by the gadget manager, and - * {@link #onDisabled} is called. If after that, a gadget for this provider is created - * again, onEnabled() will be called again. - * - * @param context The {@link android.content.Context Context} in which this receiver is - * running. - * - * @see GadgetManager#ACTION_GADGET_ENABLED - */ - public void onEnabled(Context context) { - } - - /** - * Called in response to the {@link GadgetManager#ACTION_GADGET_DISABLED} broadcast, which - * is sent when the last gadget instance for this provider is deleted. Override this method - * to implement your own gadget functionality. - * - * {@more} - * - * @param context The {@link android.content.Context Context} in which this receiver is - * running. - * - * @see GadgetManager#ACTION_GADGET_DISABLED - */ - public void onDisabled(Context context) { - } -} diff --git a/core/java/android/gadget/package.html b/core/java/android/gadget/package.html deleted file mode 100644 index 4c04396..0000000 --- a/core/java/android/gadget/package.html +++ /dev/null @@ -1,136 +0,0 @@ -<body> -<p>Android allows applications to publish views to be embedded in other applications. These -views are called gadgets, and are published by "gadget providers." The component that can -contain gadgets is called a "gadget host." -</p> -<h3><a href="package-descr.html#providers">Gadget Providers</a></h3> -<ul> - <li><a href="package-descr.html#provider_manifest">Declaring a gadget in the AndroidManifest</a></li> - <li><a href="package-descr.html#provider_meta_data">Adding the GadgetProviderInfo meta-data</a></li> - <li><a href="package-descr.html#provider_GadgetProvider">Using the GadgetProvider class</a></li> - <li><a href="package-descr.html#provider_configuration">Gadget Configuration UI</a></li> - <li><a href="package-descr.html#provider_broadcasts">Gadget Broadcast Intents</a></li> -</ul> -<h3><a href="package-descr.html#">Gadget Hosts</a></h3> - - -{@more} - - -<h2><a name="providers"></a>Gadget Providers</h2> -<p> -Any application can publish gadgets. All an application needs to do to publish a gadget is -to have a {@link android.content.BroadcastReceiver} that receives the {@link -android.gadget.GadgetManager#ACTION_GADGET_UPDATE GadgetManager.ACTION_GADGET_UPDATE} intent, -and provide some meta-data about the gadget. Android provides the -{@link android.gadget.GadgetProvider} class, which extends BroadcastReceiver, as a convenience -class to aid in handling the broadcasts. - -<h3><a name="provider_manifest"></a>Declaring a gadget in the AndroidManifest</h3> - -<p> -First, declare the {@link android.content.BroadcastReceiver} in your application's -<code>AndroidManifest.xml</code> file. - -{@sample frameworks/base/tests/gadgets/GadgetHostTest/AndroidManifest.xml GadgetProvider} - -<p> -The <b><code><receiver></b> element has the following attributes: -<ul> - <li><b><code>android:name</code> -</b> which specifies the - {@link android.content.BroadcastReceiver} or {@link android.gadget.GadgetProvider} - class.</li> - <li><b><code>android:label</code> -</b> which specifies the string resource that - will be shown by the gadget picker as the label.</li> - <li><b><code>android:icon</code> -</b> which specifies the drawable resource that - will be shown by the gadget picker as the icon.</li> -</ul> - -<p> -The <b><code><intent-filter></b> element tells the {@link android.content.pm.PackageManager} -that this {@link android.content.BroadcastReceiver} receives the {@link -android.gadget.GadgetManager#ACTION_GADGET_UPDATE GadgetManager.ACTION_GADGET_UPDATE} broadcast. -The gadget manager will send other broadcasts directly to your gadget provider as required. -It is only necessary to explicitly declare that you accept the {@link -android.gadget.GadgetManager#ACTION_GADGET_UPDATE GadgetManager.ACTION_GADGET_UPDATE} broadcast. - -<p> -The <b><code><meta-data></code></b> element tells the gadget manager which xml resource to -read to find the {@link android.gadget.GadgetProviderInfo} for your gadget provider. It has the following -attributes: -<ul> - <li><b><code>android:name="android.gadget.provider"</code> -</b> identifies this meta-data - as the {@link android.gadget.GadgetProviderInfo} descriptor.</li> - <li><b><code>android:resource</code> -</b> is the xml resource to use as that descriptor.</li> -</ul> - - -<h3><a name="provider_meta_data"></a>Adding the {@link android.gadget.GadgetProviderInfo GadgetProviderInfo} meta-data</h3> - -<p> -For a gadget, the values in the {@link android.gadget.GadgetProviderInfo} structure are supplied -in an XML resource. In the example above, the xml resource is referenced with -<code>android:resource="@xml/gadget_info"</code>. That XML file would go in your application's -directory at <code>res/xml/gadget_info.xml</code>. Here is a simple example. - -{@sample frameworks/base/tests/gadgets/GadgetHostTest/res/xml/gadget_info.xml GadgetProviderInfo} - -<p> -The attributes are as documented in the {@link android.gadget.GadgetProviderInfo GagetInfo} class. (86400000 milliseconds means once per day) - - -<h3><a name="provider_GadgetProvider"></a>Using the {@link android.gadget.GadgetProvider GadgetProvider} class</h3> - -<p>The GadgetProvider class is the easiest way to handle the gadget provider intent broadcasts. -See the <code>src/com/example/android/apis/gadget/ExampleGadgetProvider.java</code> -sample class in ApiDemos for an example. - -<p class="note">Keep in mind that since the the GadgetProvider is a BroadcastReceiver, -your process is not guaranteed to keep running after the callback methods return. See -<a href="../../../guide/topics/fundamentals.html#broadlife">Application Fundamentals > -Broadcast Receiver Lifecycle</a> for more information. - - - -<h3><a name="provider_configuration"></a>Gadget Configuration UI</h3> - -<p> -Gadget hosts have the ability to start a configuration activity when a gadget is instantiated. -The activity should be declared as normal in AndroidManifest.xml, and it should be listed in -the GadgetProviderInfo XML file in the <code>android:configure</code> attribute. - -<p>The activity you specified will be launched with the {@link -android.gadget.GadgetManager#ACTION_GADGET_CONFIGURE} action. See the documentation for that -action for more info. - -<p>See the <code>src/com/example/android/apis/gadget/ExampleGadgetConfigure.java</code> -sample class in ApiDemos for an example. - - - -<h3><a name="providers_broadcasts"></a>Gadget Broadcast Intents</h3> - -<p>{@link android.gadget.GadgetProvider} is just a convenience class. If you would like -to receive the gadget broadcasts directly, you can. The four intents you need to care about are: -<ul> - <li>{@link android.gadget.GadgetManager#ACTION_GADGET_UPDATE}</li> - <li>{@link android.gadget.GadgetManager#ACTION_GADGET_DELETED}</li> - <li>{@link android.gadget.GadgetManager#ACTION_GADGET_ENABLED}</li> - <li>{@link android.gadget.GadgetManager#ACTION_GADGET_DISABLED}</li> -</ul> - -<p>By way of example, the implementation of -{@link android.gadget.GadgetProvider#onReceive} is quite simple:</p> - -{@sample frameworks/base/core/java/android/gadget/GadgetProvider.java onReceive} - - -<h2>Gadget Hosts</h3> -<p>Gadget hosts are the containers in which gadgets can be placed. Most of the look and feel -details are left up to the gadget hosts. For example, the home screen has one way of viewing -gadgets, but the lock screen could also contain gadgets, and it would have a different way of -adding, removing and otherwise managing gadgets.</p> -<p>For more information on implementing your own gadget host, see the -{@link android.gadget.GadgetHost GadgetHost} class.</p> -</body> - diff --git a/core/java/android/inputmethodservice/ExtractButton.java b/core/java/android/inputmethodservice/ExtractButton.java new file mode 100644 index 0000000..d6fe38d --- /dev/null +++ b/core/java/android/inputmethodservice/ExtractButton.java @@ -0,0 +1,30 @@ +package android.inputmethodservice; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.Button; + +/*** + * Specialization of {@link Button} that ignores the window not being focused. + */ +class ExtractButton extends Button { + public ExtractButton(Context context) { + super(context, null); + } + + public ExtractButton(Context context, AttributeSet attrs) { + super(context, attrs, com.android.internal.R.attr.buttonStyle); + } + + public ExtractButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Pretend like the window this view is in always has focus, so it will + * highlight when selected. + */ + @Override public boolean hasWindowFocus() { + return this.isEnabled() ? true : false; + } +} diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 34d5695..32270c4 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1398,10 +1398,17 @@ public class InputMethodService extends AbstractInputMethodService { int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) { final ExtractEditText eet = mExtractEditText; - if (eet != null && mExtractedText != null) { + if (eet != null && isFullscreenMode() && mExtractedText != null) { final int off = mExtractedText.startOffset; eet.startInternalChanges(); - eet.setSelection(newSelStart-off, newSelEnd-off); + newSelStart -= off; + newSelEnd -= off; + final int len = eet.getText().length(); + if (newSelStart < 0) newSelStart = 0; + else if (newSelStart > len) newSelStart = len; + if (newSelEnd < 0) newSelEnd = 0; + else if (newSelEnd > len) newSelEnd = len; + eet.setSelection(newSelStart, newSelEnd); eet.finishInternalChanges(); } } diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java index 3bffaec..d0bd2a5 100644 --- a/core/java/android/provider/Contacts.java +++ b/core/java/android/provider/Contacts.java @@ -1423,7 +1423,42 @@ public class Contacts { */ public static final String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE"; - + + /** + * Takes as input a data URI with a mailto: or tel: scheme. If a single + * contact exists with the given data it will be shown. If no contact + * exists, a dialog will ask the user if they want to create a new + * contact with the provided details filled in. If multiple contacts + * share the data the user will be prompted to pick which contact they + * want to view. + * <p> + * For <code>mailto:</code> URIs, the scheme specific portion must be a + * raw email address, such as one built using + * {@link Uri#fromParts(String, String, String)}. + * <p> + * For <code>tel:</code> URIs, the scheme specific portion is compared + * to existing numbers using the standard caller ID lookup algorithm. + * The number must be properly encoded, for example using + * {@link Uri#fromParts(String, String, String)}. + * <p> + * Any extras from the {@link Insert} class will be passed along to the + * create activity if there are no contacts to show. + * <p> + * Passing true for the {@link #EXTRA_FORCE_CREATE} extra will skip + * prompting the user when the contact doesn't exist. + */ + public static final String SHOW_OR_CREATE_CONTACT = + "com.android.contacts.action.SHOW_OR_CREATE_CONTACT"; + + /** + * Used with {@link #SHOW_OR_CREATE_CONTACT} to force creating a new contact if no matching + * contact found. Otherwise, default behavior is to prompt user with dialog before creating. + * + * <P>Type: BOOLEAN</P> + */ + public static final String EXTRA_FORCE_CREATE = + "com.android.contacts.action.FORCE_CREATE"; + /** * Intents related to the Contacts app UI. */ diff --git a/core/java/android/provider/Gmail.java b/core/java/android/provider/Gmail.java index 5b3c223..cc03968 100644 --- a/core/java/android/provider/Gmail.java +++ b/core/java/android/provider/Gmail.java @@ -1540,7 +1540,15 @@ public final class Gmail { /** Returns the number of unread conversation with a given label. */ public int getNumUnreadConversations(long labelId) { - return getLabelIdValues(labelId).getAsInteger(LabelColumns.NUM_UNREAD_CONVERSATIONS); + Integer unreadConversations = + getLabelIdValues(labelId).getAsInteger(LabelColumns.NUM_UNREAD_CONVERSATIONS); + // There seems to be a race condition here that can get the label maps into a bad + // state and lose state on a particular label. + if (unreadConversations == null) { + return 0; + } else { + return unreadConversations; + } } /** diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index f8bc765..3aa4078 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -208,6 +208,15 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (mAudioDevices == null) { return BluetoothError.ERROR; } + // ignore if there are any active sinks + if (lookupSinksMatchingStates(new int[] { + BluetoothA2dp.STATE_CONNECTING, + BluetoothA2dp.STATE_CONNECTED, + BluetoothA2dp.STATE_PLAYING, + BluetoothA2dp.STATE_DISCONNECTING}).size() != 0) { + return BluetoothError.ERROR; + } + String path = lookupPath(address); if (path == null) { path = createHeadsetNative(address); diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 6f5513a..e271909 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -287,6 +287,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } else { intent = new Intent(BluetoothIntent.DISABLED_ACTION); } + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent, BLUETOOTH_PERM); mEnableThread = null; diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index e4ebcca..8e77eed 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -52,6 +52,7 @@ class BluetoothEventLoop { private Context mContext; private static final int EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 1; + private static final int EVENT_RESTART_BLUETOOTH = 2; // The time (in millisecs) to delay the pairing attempt after the first // auto pairing attempt fails. We use an exponential delay with @@ -74,6 +75,10 @@ class BluetoothEventLoop { return; } break; + case EVENT_RESTART_BLUETOOTH: + mBluetoothService.disable(); + mBluetoothService.enable(null); + break; } } }; @@ -372,6 +377,13 @@ class BluetoothEventLoop { } } + private void onRestartRequired() { + if (mBluetoothService.isEnabled()) { + Log.e(TAG, "*** A serious error occured (did hcid crash?) - restarting Bluetooth ***"); + mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH); + } + } + private static void log(String msg) { Log.d(TAG, msg); } diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java index 3f8288c..21bc2a6 100644 --- a/core/java/android/text/method/QwertyKeyListener.java +++ b/core/java/android/text/method/QwertyKeyListener.java @@ -298,8 +298,10 @@ public class QwertyKeyListener extends BaseKeyListener { content.removeSpan(repl[0]); // only cancel the autocomplete if the cursor is at the end of - // the replaced span - if (selStart == en) { + // the replaced span (or after it, because the user is + // backspacing over the space after the word, not the word + // itself). + if (selStart >= en) { content.setSpan(TextKeyListener.INHIBIT_REPLACEMENT, en, en, Spannable.SPAN_POINT_POINT); content.replace(st, en, old); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d78320a..c3e00c4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1511,8 +1511,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback { @ViewDebug.ExportedProperty int mUserPaddingBottom; - private int mOldWidthMeasureSpec = Integer.MIN_VALUE; - private int mOldHeightMeasureSpec = Integer.MIN_VALUE; + /** + * @hide + */ + int mOldWidthMeasureSpec = Integer.MIN_VALUE; + /** + * @hide + */ + int mOldHeightMeasureSpec = Integer.MIN_VALUE; private Resources mResources = null; @@ -2661,9 +2667,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @attr ref android.R.styleable#View_visibility */ @ViewDebug.ExportedProperty(mapping = { - @ViewDebug.IntToString(from = 0, to = "VISIBLE"), - @ViewDebug.IntToString(from = 4, to = "INVISIBLE"), - @ViewDebug.IntToString(from = 8, to = "GONE") + @ViewDebug.IntToString(from = VISIBLE, to = "VISIBLE"), + @ViewDebug.IntToString(from = INVISIBLE, to = "INVISIBLE"), + @ViewDebug.IntToString(from = GONE, to = "GONE") }) public int getVisibility() { return mViewFlags & VISIBILITY_MASK; @@ -3291,8 +3297,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * when ViewDebug.SYSTEM_PROPERTY_CAPTURE_VIEW) is set */ private static void captureViewInfo(String subTag, View v) { - if (v == null || - SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_VIEW, 0) == 0) { + if (v == null || SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_VIEW, 0) == 0) { return; } ViewDebug.dumpCapturedView(subTag, v); diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 883c7bd..0a043bd 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -17,9 +17,12 @@ package android.view; import android.util.Log; +import android.util.DisplayMetrics; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.os.Environment; +import android.os.Debug; import java.io.File; import java.io.BufferedWriter; @@ -43,6 +46,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.AccessibleObject; /** * Various debugging/tracing tools related to {@link View} and the view hierarchy. @@ -73,7 +77,7 @@ public class ViewDebug { * when it is set, we log key events, touch/motion and trackball events */ static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent"; - + /** * This annotation can be used to mark fields and methods to be dumped by * the view server. Only non-void methods with no arguments can be annotated @@ -113,6 +117,27 @@ public class ViewDebug { IntToString[] mapping() default { }; /** + * A mapping can be defined to map array indices to specific strings. + * A mapping can be used to see human readable values for the indices + * of an array: + * + * <pre> + * @ViewDebug.ExportedProperty(mapping = { + * @ViewDebug.IntToString(from = 0, to = "INVALID"), + * @ViewDebug.IntToString(from = 1, to = "FIRST"), + * @ViewDebug.IntToString(from = 2, to = "SECOND") + * }) + * private int[] mElements; + * <pre> + * + * @return An array of int to String mappings + * + * @see android.view.ViewDebug.IntToString + * @see #mapping() + */ + IntToString[] indexMapping() default { }; + + /** * When deep export is turned on, this property is not dumped. Instead, the * properties contained in this property are dumped. Each child property * is prefixed with the name of this property. @@ -187,10 +212,12 @@ public class ViewDebug { private static final String REMOTE_COMMAND_DUMP = "DUMP"; private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE"; private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT"; + private static final String REMOTE_PROFILE = "PROFILE"; private static HashMap<Class<?>, Field[]> sFieldsForClasses; private static HashMap<Class<?>, Method[]> sMethodsForClasses; - + private static HashMap<AccessibleObject, ExportedProperty> sAnnotations; + /** * Defines the type of hierarhcy trace to output to the hierarchy traces file. */ @@ -347,6 +374,7 @@ public class ViewDebug { } File recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/"); + //noinspection ResultOfMethodCallIgnored recyclerDump.mkdirs(); recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".recycler"); @@ -450,6 +478,7 @@ public class ViewDebug { } File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/"); + //noinspection ResultOfMethodCallIgnored hierarchyDump.mkdirs(); hierarchyDump = new File(hierarchyDump, prefix + ".traces"); @@ -497,6 +526,7 @@ public class ViewDebug { sHierarchyTraces = null; File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/"); + //noinspection ResultOfMethodCallIgnored hierarchyDump.mkdirs(); hierarchyDump = new File(hierarchyDump, sHierarchyTracePrefix + ".tree"); @@ -538,32 +568,41 @@ public class ViewDebug { invalidate(view, params[0]); } else if (REMOTE_COMMAND_REQUEST_LAYOUT.equalsIgnoreCase(command)) { requestLayout(view, params[0]); + } else if (REMOTE_PROFILE.equalsIgnoreCase(command)) { + profile(view, clientStream, params[0]); } } } - private static View findViewByHashCode(View root, String parameter) { - final String[] ids = parameter.split("@"); - final String className = ids[0]; - final int hashCode = Integer.parseInt(ids[1], 16); + private static View findView(View root, String parameter) { + // Look by type/hashcode + if (parameter.indexOf('@') != -1) { + final String[] ids = parameter.split("@"); + final String className = ids[0]; + final int hashCode = Integer.parseInt(ids[1], 16); - View view = root.getRootView(); - if (view instanceof ViewGroup) { - return findView((ViewGroup) view, className, hashCode); + View view = root.getRootView(); + if (view instanceof ViewGroup) { + return findView((ViewGroup) view, className, hashCode); + } + } else { + // Look by id + final int id = root.getResources().getIdentifier(parameter, null, null); + return root.getRootView().findViewById(id); } return null; } private static void invalidate(View root, String parameter) { - final View view = findViewByHashCode(root, parameter); + final View view = findView(root, parameter); if (view != null) { view.postInvalidate(); } } private static void requestLayout(View root, String parameter) { - final View view = findViewByHashCode(root, parameter); + final View view = findView(root, parameter); if (view != null) { root.post(new Runnable() { public void run() { @@ -573,19 +612,139 @@ public class ViewDebug { } } - private static void capture(View root, final OutputStream clientStream, String parameter) + private static void profile(View root, OutputStream clientStream, String parameter) throws IOException { + final View view = findView(root, parameter); + BufferedWriter out = null; + try { + out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024); + + if (view != null) { + final long durationMeasure = profileViewOperation(view, new ViewOperation<Void>() { + public Void[] pre() { + forceLayout(view); + return null; + } + + private void forceLayout(View view) { + view.forceLayout(); + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + final int count = group.getChildCount(); + for (int i = 0; i < count; i++) { + forceLayout(group.getChildAt(i)); + } + } + } + + public void run(Void... data) { + view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec); + } + + public void post(Void... data) { + } + }); + + final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() { + public Void[] pre() { + return null; + } + + public void run(Void... data) { + view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom); + } + + public void post(Void... data) { + } + }); + + final long durationDraw = profileViewOperation(view, new ViewOperation<Object>() { + public Object[] pre() { + final DisplayMetrics metrics = view.getResources().getDisplayMetrics(); + final Bitmap bitmap = Bitmap.createBitmap(metrics.widthPixels, + metrics.heightPixels, Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(bitmap); + return new Object[] { bitmap, canvas }; + } + + public void run(Object... data) { + view.draw((Canvas) data[1]); + } + + public void post(Object... data) { + ((Bitmap) data[0]).recycle(); + } + }); + + out.write(String.valueOf(durationMeasure)); + out.write(' '); + out.write(String.valueOf(durationLayout)); + out.write(' '); + out.write(String.valueOf(durationDraw)); + out.newLine(); + } else { + out.write("-1 -1 -1"); + out.newLine(); + } + } catch (Exception e) { + android.util.Log.w("View", "Problem profiling the view:", e); + } finally { + if (out != null) { + out.close(); + } + } + } + + interface ViewOperation<T> { + T[] pre(); + void run(T... data); + void post(T... data); + } + + private static <T> long profileViewOperation(View view, final ViewOperation<T> operation) { final CountDownLatch latch = new CountDownLatch(1); - final View captureView = findViewByHashCode(root, parameter); + final long[] duration = new long[1]; + + view.post(new Runnable() { + public void run() { + try { + T[] data = operation.pre(); + long start = Debug.threadCpuTimeNanos(); + operation.run(data); + duration[0] = Debug.threadCpuTimeNanos() - start; + operation.post(data); + } finally { + latch.countDown(); + } + } + }); + + try { + latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Log.w("View", "Could not complete the profiling of the view " + view); + Thread.currentThread().interrupt(); + return -1; + } + + return duration[0]; + } + + private static void capture(View root, final OutputStream clientStream, String parameter) + throws IOException { + + final View captureView = findView(root, parameter); if (captureView != null) { + final CountDownLatch latch = new CountDownLatch(1); final Bitmap[] cache = new Bitmap[1]; final boolean hasCache = captureView.isDrawingCacheEnabled(); final boolean willNotCache = captureView.willNotCacheDrawing(); if (willNotCache) { + // TODO: Should happen on the UI thread captureView.setWillNotCacheDrawing(false); } @@ -619,12 +778,15 @@ public class ViewDebug { } } } catch (InterruptedException e) { - Log.w("View", "Could not complete the capture of the view " + captureView); + Log.w("View", "Could not complete the capture of the view " + captureView); + Thread.currentThread().interrupt(); } finally { if (willNotCache) { + // TODO: Should happen on the UI thread captureView.setWillNotCacheDrawing(true); } if (!hasCache) { + // TODO: Should happen on the UI thread captureView.destroyDrawingCache(); } } @@ -642,6 +804,8 @@ public class ViewDebug { } out.write("DONE."); out.newLine(); + } catch (Exception e) { + android.util.Log.w("View", "Problem dumping the view:", e); } finally { if (out != null) { out.close(); @@ -713,7 +877,12 @@ public class ViewDebug { if (sFieldsForClasses == null) { sFieldsForClasses = new HashMap<Class<?>, Field[]>(); } + if (sAnnotations == null) { + sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512); + } + final HashMap<Class<?>, Field[]> map = sFieldsForClasses; + final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations; Field[] fields = map.get(klass); if (fields != null) { @@ -729,6 +898,7 @@ public class ViewDebug { if (field.isAnnotationPresent(ExportedProperty.class)) { field.setAccessible(true); foundFields.add(field); + annotations.put(field, field.getAnnotation(ExportedProperty.class)); } } @@ -740,9 +910,14 @@ public class ViewDebug { private static Method[] getExportedPropertyMethods(Class<?> klass) { if (sMethodsForClasses == null) { - sMethodsForClasses = new HashMap<Class<?>, Method[]>(); + sMethodsForClasses = new HashMap<Class<?>, Method[]>(100); + } + if (sAnnotations == null) { + sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512); } + final HashMap<Class<?>, Method[]> map = sMethodsForClasses; + final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations; Method[] methods = map.get(klass); if (methods != null) { @@ -756,10 +931,11 @@ public class ViewDebug { for (int i = 0; i < count; i++) { final Method method = methods[i]; if (method.getParameterTypes().length == 0 && - method.isAnnotationPresent(ExportedProperty.class) && - method.getReturnType() != Void.class) { + method.isAnnotationPresent(ExportedProperty.class) && + method.getReturnType() != Void.class) { method.setAccessible(true); foundMethods.add(method); + annotations.put(method, method.getAnnotation(ExportedProperty.class)); } } @@ -799,20 +975,10 @@ public class ViewDebug { final Class<?> returnType = method.getReturnType(); if (returnType == int.class) { - ExportedProperty property = method.getAnnotation(ExportedProperty.class); + final ExportedProperty property = sAnnotations.get(method); if (property.resolveId() && view instanceof View) { - final Resources resources = ((View) view).getContext().getResources(); final int id = (Integer) methodValue; - if (id >= 0) { - try { - methodValue = resources.getResourceTypeName(id) + '/' + - resources.getResourceEntryName(id); - } catch (Resources.NotFoundException e) { - methodValue = "UNKNOWN"; - } - } else { - methodValue = "NO_ID"; - } + methodValue = resolveId(view, id); } else { final IntToString[] mapping = property.mapping(); if (mapping.length > 0) { @@ -833,28 +999,22 @@ public class ViewDebug { } } } + } else if (returnType == int[].class) { + final ExportedProperty property = sAnnotations.get(method); + final int[] array = (int[]) methodValue; + final String valuePrefix = prefix + method.getName() + '_'; + final String suffix = "()"; + + exportUnrolledArray(view, out, property, array, valuePrefix, suffix); } else if (!returnType.isPrimitive()) { - ExportedProperty property = method.getAnnotation(ExportedProperty.class); + final ExportedProperty property = sAnnotations.get(method); if (property.deepExport()) { dumpViewProperties(methodValue, out, prefix + property.prefix()); continue; } } - out.write(prefix); - out.write(method.getName()); - out.write("()="); - - if (methodValue != null) { - final String value = methodValue.toString().replace("\n", "\\n"); - out.write(String.valueOf(value.length())); - out.write(","); - out.write(value); - } else { - out.write("4,null"); - } - - out.write(' '); + writeEntry(out, prefix, method.getName(), "()", methodValue); } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } @@ -875,20 +1035,10 @@ public class ViewDebug { final Class<?> type = field.getType(); if (type == int.class) { - ExportedProperty property = field.getAnnotation(ExportedProperty.class); + final ExportedProperty property = sAnnotations.get(field); if (property.resolveId() && view instanceof View) { - final Resources resources = ((View) view).getContext().getResources(); final int id = field.getInt(view); - if (id >= 0) { - try { - fieldValue = resources.getResourceTypeName(id) + '/' + - resources.getResourceEntryName(id); - } catch (Resources.NotFoundException e) { - fieldValue = "UNKNOWN"; - } - } else { - fieldValue = "NO_ID"; - } + fieldValue = resolveId(view, id); } else { final IntToString[] mapping = property.mapping(); if (mapping.length > 0) { @@ -907,8 +1057,18 @@ public class ViewDebug { } } } + } else if (type == int[].class) { + final ExportedProperty property = sAnnotations.get(field); + final int[] array = (int[]) field.get(view); + final String valuePrefix = prefix + field.getName() + '_'; + final String suffix = ""; + + exportUnrolledArray(view, out, property, array, valuePrefix, suffix); + + // We exit here! + return; } else if (!type.isPrimitive()) { - ExportedProperty property = field.getAnnotation(ExportedProperty.class); + final ExportedProperty property = sAnnotations.get(field); if (property.deepExport()) { dumpViewProperties(field.get(view), out, prefix + property.prefix()); continue; @@ -917,24 +1077,100 @@ public class ViewDebug { if (fieldValue == null) { fieldValue = field.get(view); - } + } - out.write(prefix); - out.write(field.getName()); - out.write('='); + writeEntry(out, prefix, field.getName(), "", fieldValue); + } catch (IllegalAccessException e) { + } + } + } - if (fieldValue != null) { - final String value = fieldValue.toString().replace("\n", "\\n"); - out.write(String.valueOf(value.length())); - out.write(","); - out.write(value); - } else { - out.write("4,null"); + private static void writeEntry(BufferedWriter out, String prefix, String name, + String suffix, Object value) throws IOException { + + out.write(prefix); + out.write(name); + out.write(suffix); + out.write("="); + writeValue(out, value); + out.write(' '); + } + + private static void exportUnrolledArray(Object view, BufferedWriter out, + ExportedProperty property, int[] array, String prefix, String suffix) + throws IOException { + + final IntToString[] indexMapping = property.indexMapping(); + final boolean hasIndexMapping = indexMapping.length > 0; + + final IntToString[] mapping = property.mapping(); + final boolean hasMapping = mapping.length > 0; + + final boolean resolveId = property.resolveId() && view instanceof View; + final int valuesCount = array.length; + + for (int j = 0; j < valuesCount; j++) { + String name; + String value; + + final int intValue = array[j]; + + name = String.valueOf(j); + if (hasIndexMapping) { + int mappingCount = indexMapping.length; + for (int k = 0; k < mappingCount; k++) { + final IntToString mapped = indexMapping[k]; + if (mapped.from() == j) { + name = mapped.to(); + break; + } } + } - out.write(' '); - } catch (IllegalAccessException e) { + value = String.valueOf(intValue); + if (hasMapping) { + int mappingCount = mapping.length; + for (int k = 0; k < mappingCount; k++) { + final IntToString mapped = mapping[k]; + if (mapped.from() == intValue) { + value = mapped.to(); + break; + } + } + } + + if (resolveId) { + value = (String) resolveId(view, intValue); } + + writeEntry(out, prefix, name, suffix, value); + } + } + + private static Object resolveId(Object view, int id) { + Object fieldValue; + final Resources resources = ((View) view).getContext().getResources(); + if (id >= 0) { + try { + fieldValue = resources.getResourceTypeName(id) + '/' + + resources.getResourceEntryName(id); + } catch (Resources.NotFoundException e) { + fieldValue = "id/0x" + Integer.toHexString(id); + } + } else { + fieldValue = "NO_ID"; + } + return fieldValue; + } + + private static void writeValue(BufferedWriter out, Object value) throws IOException { + if (value != null) { + String output = value.toString().replace("\n", "\\n"); + out.write(String.valueOf(output.length())); + out.write(","); + out.write(output); + } else { + out.write("4,null"); } } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 0f15b17..220869c 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -54,7 +54,7 @@ import android.view.animation.Animation; * window manager is a very low-level system service, there are few other * system services you can call with this lock held. It is explicitly okay to * make calls into the package manager and power manager; it is explicitly not - * okay to make calls into the activity manager. Note that + * okay to make calls into the activity manager or most other services. Note that * {@link android.content.Context#checkPermission(String, int, int)} and * variations require calling into the activity manager. * <dt> Li <dd> Called with the input thread lock held. This lock can be @@ -748,7 +748,7 @@ public interface WindowManagerPolicy { * ActivityInfo.SCREEN_ORIENTATION_PORTRAIT}), return a surface * rotation. */ - public int rotationForOrientation(int orientation, int lastRotation, + public int rotationForOrientationLw(int orientation, int lastRotation, boolean displayEnabled); /** @@ -781,16 +781,16 @@ public interface WindowManagerPolicy { */ public boolean isCheekPressedAgainstScreen(MotionEvent ev); - public void setCurrentOrientation(int newOrientation); + public void setCurrentOrientationLw(int newOrientation); /** * Call from application to perform haptic feedback on its window. */ - public boolean performHapticFeedback(WindowState win, int effectId, boolean always); + public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always); /** * Called when we have stopped keeping the screen on because a window * requesting this is no longer visible. */ - public void screenOnStopped(); + public void screenOnStoppedLw(); } diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 4e98591..b4c5b72 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -174,6 +174,14 @@ public final class InputMethodInfo implements Parcelable { } /** + * Return the raw information about the Service implementing this + * input method. Do not modify the returned object. + */ + public ServiceInfo getServiceInfo() { + return mService.serviceInfo; + } + + /** * Return the component of the service that implements this input * method. */ diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 7f2142e..4de9eef 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -921,6 +921,7 @@ public final class InputMethodManager { startInputInner(); } }); + return; } // Okay we are now ready to call into the served view and have it diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index 8d4b220..f9fb0b0 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -376,6 +376,11 @@ class LoadListener extends Handler implements EventHandler { } } } + + // if there is buffered data, commit them in the end + boolean needToCommit = mAuthHeader != null && !mustAuthenticate + && mNativeLoader != 0 && !mDataBuilder.isEmpty(); + // it is only here that we can reset the last mAuthHeader object // (if existed) and start a new one!!! mAuthHeader = null; @@ -410,6 +415,10 @@ class LoadListener extends Handler implements EventHandler { } } commitHeadersCheckRedirect(); + + if (needToCommit) { + commitLoad(); + } } /** diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java index efc131f..9de97c9 100644 --- a/core/java/android/webkit/TextDialog.java +++ b/core/java/android/webkit/TextDialog.java @@ -540,7 +540,9 @@ import java.util.ArrayList; public void setSingleLine(boolean single) { int inputType = EditorInfo.TYPE_CLASS_TEXT; if (!single) { - inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; + inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE + | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES + | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; } mSingle = single; setHorizontallyScrolling(single); diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 4e2b2ab..65544d4 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -134,7 +134,7 @@ public class WebSettings { private int mDefaultFixedFontSize = 13; private boolean mLoadsImagesAutomatically = true; private boolean mBlockNetworkImage = false; - private boolean mBlockNetworkLoads = false; + private boolean mBlockNetworkLoads; private boolean mJavaScriptEnabled = false; private boolean mPluginsEnabled = false; private boolean mJavaScriptCanOpenWindowsAutomatically = false; @@ -248,7 +248,9 @@ public class WebSettings { mUserAgent = getCurrentUserAgent(); mUseDefaultUserAgent = true; - verifyNetworkAccess(); + mBlockNetworkLoads = mContext.checkPermission( + "android.permission.INTERNET", android.os.Process.myPid(), + android.os.Process.myUid()) != PackageManager.PERMISSION_GRANTED; } /** @@ -835,7 +837,7 @@ public class WebSettings { private void verifyNetworkAccess() { if (!mBlockNetworkLoads) { if (mContext.checkPermission("android.permission.INTERNET", - android.os.Process.myPid(), 0) != + android.os.Process.myPid(), android.os.Process.myUid()) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException ("Permission denied - " + diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index dc39b90..f61ce40 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.DialogInterface.OnCancelListener; -import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -46,9 +45,6 @@ import android.util.AttributeSet; import android.util.Config; import android.util.EventLog; import android.util.Log; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -68,12 +64,9 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; -import android.widget.RelativeLayout; import android.widget.Scroller; import android.widget.Toast; import android.widget.ZoomButtonsController; -import android.widget.ZoomControls; -import android.widget.FrameLayout; import android.widget.AdapterView.OnItemClickListener; import java.io.File; @@ -113,57 +106,6 @@ public class WebView extends AbsoluteLayout static final boolean DEBUG = false; static final boolean LOGV_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; - private class ExtendedZoomControls extends FrameLayout { - public ExtendedZoomControls(Context context, AttributeSet attrs) { - super(context, attrs); - LayoutInflater inflater = (LayoutInflater) - context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true); - mZoomControls = (ZoomControls) findViewById(com.android.internal.R.id.zoomControls); - mZoomMagnify = (ImageView) findViewById(com.android.internal.R.id.zoomMagnify); - } - - public void show(boolean showZoom, boolean canZoomOut) { - mZoomControls.setVisibility(showZoom ? View.VISIBLE : View.GONE); - mZoomMagnify.setVisibility(canZoomOut ? View.VISIBLE : View.GONE); - fade(View.VISIBLE, 0.0f, 1.0f); - } - - public void hide() { - fade(View.GONE, 1.0f, 0.0f); - } - - private void fade(int visibility, float startAlpha, float endAlpha) { - AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha); - anim.setDuration(500); - startAnimation(anim); - setVisibility(visibility); - } - - public void setIsZoomMagnifyEnabled(boolean isEnabled) { - mZoomMagnify.setEnabled(isEnabled); - } - - public boolean hasFocus() { - return mZoomControls.hasFocus() || mZoomMagnify.hasFocus(); - } - - public void setOnZoomInClickListener(OnClickListener listener) { - mZoomControls.setOnZoomInClickListener(listener); - } - - public void setOnZoomOutClickListener(OnClickListener listener) { - mZoomControls.setOnZoomOutClickListener(listener); - } - - public void setOnZoomMagnifyClickListener(OnClickListener listener) { - mZoomMagnify.setOnClickListener(listener); - } - - ZoomControls mZoomControls; - ImageView mZoomMagnify; - } - /** * Transportation object for returning WebView across thread boundaries. */ @@ -281,8 +223,10 @@ public class WebView extends AbsoluteLayout */ // pre-computed square of ViewConfiguration.getScaledTouchSlop() private int mTouchSlopSquare; - // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop() + // pre-computed square of the density adjusted double tap slop private int mDoubleTapSlopSquare; + // pre-computed density adjusted navigation slop + private int mNavSlop; // This should be ViewConfiguration.getTapTimeout() // But system time out is 100ms, which is too short for the browser. // In the browser, if it switches out of tap too soon, jump tap won't work. @@ -295,9 +239,6 @@ public class WebView extends AbsoluteLayout private static final int LONG_PRESS_TIMEOUT = 1000; // needed to avoid flinging after a pause of no movement private static final int MIN_FLING_TIME = 250; - // The time that the Zoom Controls are visible before fading away - private static final long ZOOM_CONTROLS_TIMEOUT = - ViewConfiguration.getZoomControlsTimeout(); // The amount of content to overlap between two screens when going through // pages with the space bar, in pixels. private static final int PAGE_SCROLL_OVERLAP = 24; @@ -337,10 +278,6 @@ public class WebView extends AbsoluteLayout private boolean mWrapContent; - // The View containing the zoom controls - private ExtendedZoomControls mZoomControls; - private Runnable mZoomControlRunnable; - // true if we should call webcore to draw the content, false means we have // requested something but it isn't ready to draw yet. private WebViewCore.FocusData mFocusData; @@ -550,6 +487,7 @@ public class WebView extends AbsoluteLayout private ZoomButtonsController mZoomButtonsController; private ImageView mZoomOverviewButton; + private ImageView mZoomFitPageButton; // These keep track of the center point of the zoom. They are used to // determine the point around which we should zoom. @@ -564,28 +502,13 @@ public class WebView extends AbsoluteLayout // in this callback } - public void onOverview() { - mZoomButtonsController.setVisible(false); - zoomScrollOut(); - if (mLogEvent) { - Checkin.updateStats(mContext.getContentResolver(), - Checkin.Stats.Tag.BROWSER_ZOOM_OVERVIEW, 1, 0.0); - } - } - public void onVisibilityChanged(boolean visible) { if (visible) { switchOutDrawHistory(); - mZoomButtonsController.setOverviewVisible(true); - updateButtonsEnabled(); + updateZoomButtonsEnabled(); } } - private void updateButtonsEnabled() { - mZoomButtonsController.setZoomInEnabled(mActualScale < mMaxZoomScale); - mZoomButtonsController.setZoomOutEnabled(mActualScale > mMinZoomScale); - } - public void onZoom(boolean zoomIn) { if (zoomIn) { zoomIn(); @@ -593,7 +516,7 @@ public class WebView extends AbsoluteLayout zoomOut(); } - updateButtonsEnabled(); + updateZoomButtonsEnabled(); } }; @@ -633,8 +556,49 @@ public class WebView extends AbsoluteLayout mFocusData.mX = 0; mFocusData.mY = 0; mScroller = new Scroller(context); + + initZoomController(context); + } + + private void initZoomController(Context context) { + // Create the buttons controller mZoomButtonsController = new ZoomButtonsController(context, this); mZoomButtonsController.setCallback(mZoomListener); + + // Create the accessory buttons + LayoutInflater inflater = + (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + ViewGroup container = mZoomButtonsController.getContainer(); + inflater.inflate(com.android.internal.R.layout.zoom_browser_accessory_buttons, container); + mZoomOverviewButton = + (ImageView) container.findViewById(com.android.internal.R.id.zoom_page_overview); + mZoomOverviewButton.setOnClickListener( + new View.OnClickListener() { + public void onClick(View v) { + mZoomButtonsController.setVisible(false); + zoomScrollOut(); + if (mLogEvent) { + Checkin.updateStats(mContext.getContentResolver(), + Checkin.Stats.Tag.BROWSER_ZOOM_OVERVIEW, 1, 0.0); + } + } + }); + mZoomFitPageButton = + (ImageView) container.findViewById(com.android.internal.R.id.zoom_fit_page); + mZoomFitPageButton.setOnClickListener( + new View.OnClickListener() { + public void onClick(View v) { + zoomWithPreview(1f); + updateZoomButtonsEnabled(); + } + }); + } + + private void updateZoomButtonsEnabled() { + mZoomButtonsController.setZoomInEnabled(mActualScale < mMaxZoomScale); + mZoomButtonsController.setZoomOutEnabled(mActualScale > mMinZoomScale); + mZoomFitPageButton.setEnabled(mActualScale != 1); + mZoomOverviewButton.setEnabled(canZoomScrollOut()); } private void init() { @@ -647,9 +611,16 @@ public class WebView extends AbsoluteLayout final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); mTouchSlopSquare = slop * slop; mMinLockSnapReverseDistance = slop; - final int doubleTapslop = ViewConfiguration.get(getContext()) - .getScaledDoubleTapSlop(); + // use twice the line height, 32 based on our current default font, for + // the double tap slop as the ViewConfiguration's double tap slop, 100, + // is too big for the Browser + final int doubleTapslop = (int) (32 * getContext().getResources() + .getDisplayMetrics().density); mDoubleTapSlopSquare = doubleTapslop * doubleTapslop; + // use one line height, 16 based on our current default font, for how + // far we allow a touch be away from the edge of a link + mNavSlop = (int) (16 * getContext().getResources() + .getDisplayMetrics().density); } /* package */ boolean onSavePassword(String schemePlusHost, String username, @@ -1380,13 +1351,7 @@ public class WebView extends AbsoluteLayout return; } clearTextEntry(); - ExtendedZoomControls zoomControls = (ExtendedZoomControls) - getZoomControls(); - zoomControls.show(true, canZoomScrollOut()); - zoomControls.requestFocus(); - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - mPrivateHandler.postDelayed(mZoomControlRunnable, - ZOOM_CONTROLS_TIMEOUT); + mZoomButtonsController.setVisible(true); } /** @@ -1498,7 +1463,8 @@ public class WebView extends AbsoluteLayout private static int pinLoc(int x, int viewMax, int docMax) { // Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax); if (docMax < viewMax) { // the doc has room on the sides for "blank" - x = -(viewMax - docMax) >> 1; + // pin the short document to the top/left of the screen + x = 0; // Log.d(LOGTAG, "--- center " + x); } else if (x < 0) { x = 0; @@ -2589,11 +2555,8 @@ public class WebView extends AbsoluteLayout private void startZoomScrollOut() { setHorizontalScrollBarEnabled(false); setVerticalScrollBarEnabled(false); - if (mZoomControlRunnable != null) { - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - } - if (mZoomControls != null) { - mZoomControls.hide(); + if (mZoomButtonsController.isVisible()) { + mZoomButtonsController.setVisible(false); } int width = getViewWidth(); int height = getViewHeight(); @@ -3263,6 +3226,7 @@ public class WebView extends AbsoluteLayout // Clean up the zoom controller mZoomButtonsController.setVisible(false); + ZoomButtonsController.finishZoomTutorial(mContext, false); } // Implementation for OnHierarchyChangeListener @@ -3518,22 +3482,29 @@ public class WebView extends AbsoluteLayout nativeMoveSelection(viewToContent(mSelectX) , viewToContent(mSelectY), false); mTouchSelection = mExtendSelection = true; - } else if (!ZoomButtonsController.useOldZoom(mContext) && - mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP) && - (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare)) { - // Found doubletap, invoke the zoom controller + } else if (!ZoomButtonsController.useOldZoom(mContext) + && mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) { mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); - int contentX = viewToContent((int) mLastTouchX + mScrollX); - int contentY = viewToContent((int) mLastTouchY + mScrollY); - if (inEditingMode()) { - mTextEntry.updateCachedTextfield(); - } - nativeClearFocus(contentX, contentY); - if (mLogEvent) { - EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION, - (eventTime - mLastTouchUpTime), eventTime); + if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) { + // Found doubletap, invoke the zoom controller + int contentX = viewToContent((int) mLastTouchX + + mScrollX); + int contentY = viewToContent((int) mLastTouchY + + mScrollY); + if (inEditingMode()) { + mTextEntry.updateCachedTextfield(); + } + nativeClearFocus(contentX, contentY); + if (mLogEvent) { + EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION, + (eventTime - mLastTouchUpTime), eventTime); + } + return mZoomButtonsController.handleDoubleTapEvent(ev); + } else { + // commit the short press action + doShortPress(); + // continue, mTouchMode should be still TOUCH_INIT_MODE } - return mZoomButtonsController.handleDoubleTapEvent(ev); } else { mTouchMode = TOUCH_INIT_MODE; mPreventDrag = mForwardTouchEvents; @@ -3678,20 +3649,6 @@ public class WebView extends AbsoluteLayout mLastTouchTime = eventTime; mUserScroll = true; } - - if (ZoomButtonsController.useOldZoom(mContext)) { - boolean showPlusMinus = mMinZoomScale < mMaxZoomScale; - boolean showMagnify = canZoomScrollOut(); - if (mZoomControls != null && (showPlusMinus || showMagnify)) { - if (mZoomControls.getVisibility() == View.VISIBLE) { - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - } else { - mZoomControls.show(showPlusMinus, showMagnify); - } - mPrivateHandler.postDelayed(mZoomControlRunnable, - ZOOM_CONTROLS_TIMEOUT); - } - } if (done) { // return false to indicate that we can't pan out of the // view space @@ -3706,8 +3663,7 @@ public class WebView extends AbsoluteLayout mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); if (getSettings().supportZoom()) { mPrivateHandler.sendMessageDelayed(mPrivateHandler - .obtainMessage(RELEASE_SINGLE_TAP, - new Boolean(true)), + .obtainMessage(RELEASE_SINGLE_TAP), DOUBLE_TAP_TIMEOUT); } else { // do short press now @@ -3755,8 +3711,7 @@ public class WebView extends AbsoluteLayout // as tap instead of short press. mTouchMode = TOUCH_INIT_MODE; mPrivateHandler.sendMessageDelayed(mPrivateHandler - .obtainMessage(RELEASE_SINGLE_TAP, - new Boolean(true)), + .obtainMessage(RELEASE_SINGLE_TAP), DOUBLE_TAP_TIMEOUT); } else { mTouchMode = TOUCH_DONE_MODE; @@ -4178,42 +4133,41 @@ public class WebView extends AbsoluteLayout } /** + * An InvisibleView is an invisible, zero-sized View for backwards + * compatibility + */ + private final class InvisibleView extends View { + + private InvisibleView(Context context) { + super(context); + setVisibility(GONE); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(0, 0); + } + + @Override + public void draw(Canvas canvas) { + } + + @Override + protected void dispatchDraw(Canvas canvas) { + } + } + + /** * Returns a view containing zoom controls i.e. +/- buttons. The caller is * in charge of installing this view to the view hierarchy. This view will * become visible when the user starts scrolling via touch and fade away if * the user does not interact with it. + * <p/> + * From 1.5, WebView change to use ZoomButtonsController. This will return + * an invisible dummy view for backwards compatibility. */ public View getZoomControls() { - if (!getSettings().supportZoom()) { - Log.w(LOGTAG, "This WebView doesn't support zoom."); - return null; - } - if (mZoomControls == null) { - mZoomControls = createZoomControls(); - - /* - * need to be set to VISIBLE first so that getMeasuredHeight() in - * {@link #onSizeChanged()} can return the measured value for proper - * layout. - */ - mZoomControls.setVisibility(View.VISIBLE); - mZoomControlRunnable = new Runnable() { - public void run() { - - /* Don't dismiss the controls if the user has - * focus on them. Wait and check again later. - */ - if (!mZoomControls.hasFocus()) { - mZoomControls.hide(); - } else { - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - mPrivateHandler.postDelayed(mZoomControlRunnable, - ZOOM_CONTROLS_TIMEOUT); - } - } - }; - } - return mZoomControls; + return new InvisibleView(mContext); } /** @@ -4236,46 +4190,6 @@ public class WebView extends AbsoluteLayout return zoomWithPreview(mActualScale * 0.8f); } - private ExtendedZoomControls createZoomControls() { - ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext - , null); - zoomControls.setOnZoomInClickListener(new OnClickListener() { - public void onClick(View v) { - // reset time out - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - mPrivateHandler.postDelayed(mZoomControlRunnable, - ZOOM_CONTROLS_TIMEOUT); - zoomIn(); - } - }); - zoomControls.setOnZoomOutClickListener(new OnClickListener() { - public void onClick(View v) { - // reset time out - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - mPrivateHandler.postDelayed(mZoomControlRunnable, - ZOOM_CONTROLS_TIMEOUT); - zoomOut(); - } - }); - zoomControls.setOnZoomMagnifyClickListener(new OnClickListener() { - public void onClick(View v) { - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - mPrivateHandler.postDelayed(mZoomControlRunnable, - ZOOM_CONTROLS_TIMEOUT); - zoomScrollOut(); - } - }); - return zoomControls; - } - - // This used to be the value returned by ViewConfiguration.getTouchSlop(). - // We pass this to the navigation cache to find where the user clicked. - // TouchSlop has been increased for other purposes, but for the - // navigation cache it is too big, and may result in clicking the wrong - // spot. This is a concern when the cache is out of date, and clicking - // finds a node which is wrong but nearby. - private static final int NAV_SLOP = 12; - private void updateSelection() { if (mNativeClass == 0) { return; @@ -4283,9 +4197,8 @@ public class WebView extends AbsoluteLayout // mLastTouchX and mLastTouchY are the point in the current viewport int contentX = viewToContent((int) mLastTouchX + mScrollX); int contentY = viewToContent((int) mLastTouchY + mScrollY); - int contentSize = NAV_SLOP; - Rect rect = new Rect(contentX - contentSize, contentY - contentSize, - contentX + contentSize, contentY + contentSize); + Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop, + contentX + mNavSlop, contentY + mNavSlop); // If we were already focused on a textfield, update its cache. if (inEditingMode()) { mTextEntry.updateCachedTextfield(); @@ -4298,8 +4211,7 @@ public class WebView extends AbsoluteLayout View v = mTextEntry; int x = viewToContent((v.getLeft() + v.getRight()) >> 1); int y = viewToContent((v.getTop() + v.getBottom()) >> 1); - int contentSize = NAV_SLOP; - nativeMotionUp(x, y, contentSize, true); + nativeMotionUp(x, y, mNavSlop, true); } } @@ -4311,8 +4223,7 @@ public class WebView extends AbsoluteLayout // mLastTouchX and mLastTouchY are the point in the current viewport int contentX = viewToContent((int) mLastTouchX + mScrollX); int contentY = viewToContent((int) mLastTouchY + mScrollY); - int contentSize = NAV_SLOP; - if (nativeMotionUp(contentX, contentY, contentSize, true)) { + if (nativeMotionUp(contentX, contentY, mNavSlop, true)) { if (mLogEvent) { Checkin.updateStats(mContext.getContentResolver(), Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0); @@ -4538,9 +4449,7 @@ public class WebView extends AbsoluteLayout } case RELEASE_SINGLE_TAP: { mTouchMode = TOUCH_DONE_MODE; - if ((Boolean)msg.obj) { - doShortPress(); - } + doShortPress(); break; } case SWITCH_TO_ENTER: diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 6ab088d..e10ffa1 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -271,11 +271,6 @@ final class WebViewCore { static native String nativeFindAddress(String addr); /** - * Rebuild the nav cache if the dom changed. - */ - private native void nativeCheckNavCache(); - - /** * Empty the picture set. */ private native void nativeClearContent(); @@ -1298,7 +1293,6 @@ final class WebViewCore { mViewportMinimumScale == 0 ? nativeGetContentMinPrefWidth() : 0, 0, draw).sendToTarget(); - nativeCheckNavCache(); if (mWebkitScrollX != 0 || mWebkitScrollY != 0) { // as we have the new picture, try to sync the scroll position Message.obtain(mWebView.mPrivateHandler, diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index f517dc9..7fc96fc 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -41,10 +41,8 @@ import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewTreeObserver; -import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputConnectionWrapper; import android.view.inputmethod.InputMethodManager; import android.view.ContextMenu.ContextMenuInfo; @@ -2917,11 +2915,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - // XXX we need to have the text filter created, so we can get an - // InputConnection to proxy to. Unfortunately this means we pretty - // much need to make it as soon as a list view gets focus. - createTextFilter(false); - return mTextFilter.onCreateInputConnection(outAttrs); + if (isTextFilterEnabled()) { + // XXX we need to have the text filter created, so we can get an + // InputConnection to proxy to. Unfortunately this means we pretty + // much need to make it as soon as a list view gets focus. + createTextFilter(false); + return mTextFilter.onCreateInputConnection(outAttrs); + } + return null; } /** @@ -3015,7 +3016,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * filtering as the text changes. */ public void onTextChanged(CharSequence s, int start, int before, int count) { - if (mPopup != null) { + if (mPopup != null && isTextFilterEnabled()) { int length = s.length(); boolean showing = mPopup.isShowing(); if (!showing && length > 0) { diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index c65a3ce..c28210d 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -25,6 +25,8 @@ import android.view.ViewGroup; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Comparator; +import java.util.Collections; /** * A ListAdapter that manages a ListView backed by an array of arbitrary @@ -227,6 +229,17 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { } /** + * Sorts the content of this adapter using the specified comparator. + * + * @param comparator The comparator used to sort the objects contained + * in this adapter. + */ + public void sort(Comparator<? super T> comparator) { + Collections.sort(mObjects, comparator); + if (mNotifyOnChange) notifyDataSetChanged(); + } + + /** * {@inheritDoc} */ @Override diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 0c1c72a..7b9670b 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -461,14 +461,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe // by ensuring it has focus and getting its window out // of touch mode. mDropDownList.requestFocusFromTouch(); - if (false) { - // Update whether the pop-up is in front of or behind - // the input method, depending on whether the user has - // moved down in it. - mPopup.setInputMethodMode(curIndex > 0 - ? PopupWindow.INPUT_METHOD_NOT_NEEDED - : PopupWindow.INPUT_METHOD_NEEDED); - } mPopup.update(); switch (keyCode) { @@ -504,6 +496,10 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe boolean handled = super.onKeyDown(keyCode, event); mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN; + if (handled && isPopupShowing() && mDropDownList != null) { + clearListSelection(); + } + return handled; } @@ -833,7 +829,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mPopup.update(getDropDownAnchorView(), mDropDownHorizontalOffset, mDropDownVerticalOffset, widthSpec, height); } else { - int widthSpec; if (mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT) { mPopup.setWindowLayoutMode(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); @@ -1119,6 +1114,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe protected int[] onCreateDrawableState(int extraSpace) { int[] res = super.onCreateDrawableState(extraSpace); + //noinspection ConstantIfStatement if (false) { StringBuilder sb = new StringBuilder("Created drawable state: ["); for (int i=0; i<res.length; i++) { diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index c2f3a85..aced533 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -126,7 +126,7 @@ public class ListView extends AbsListView { private SparseBooleanArray mCheckStates; // used for temporary calculations. - private Rect mTempRect = new Rect(); + private final Rect mTempRect = new Rect(); // the single allocated result per list view; kinda cheesey but avoids // allocating these thingies too often. @@ -1656,7 +1656,8 @@ public class ListView extends AbsListView { } p.viewType = mAdapter.getItemViewType(position); - if (recycled || (p.recycledHeaderFooter && p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) { + if (recycled || (p.recycledHeaderFooter && + p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) { attachViewToParent(child, flowDown ? -1 : 0, p); } else { if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { @@ -2643,6 +2644,7 @@ public class ListView extends AbsListView { final int listBottom = getHeight() - mListPadding.bottom; final int listTop = mListPadding.top; + final AbsListView.RecycleBin recycleBin = mRecycler; if (amount < 0) { // shifted items up @@ -2670,8 +2672,13 @@ public class ListView extends AbsListView { // top views may be panned off screen View first = getChildAt(0); while (first.getBottom() < listTop) { - removeViewInLayout(first); - mRecycler.addScrapView(first); + AbsListView.LayoutParams layoutParams = (LayoutParams) first.getLayoutParams(); + if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) { + removeViewInLayout(first); + recycleBin.addScrapView(first); + } else { + detachViewFromParent(first); + } first = getChildAt(0); mFirstPosition++; } @@ -2696,8 +2703,13 @@ public class ListView extends AbsListView { // bottom view may be panned off screen while (last.getTop() > listBottom) { - removeViewInLayout(last); - mRecycler.addScrapView(last); + AbsListView.LayoutParams layoutParams = (LayoutParams) last.getLayoutParams(); + if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) { + removeViewInLayout(last); + recycleBin.addScrapView(last); + } else { + detachViewFromParent(last); + } last = getChildAt(--lastIndex); } } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 53db77e..a4f729f 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -837,7 +837,7 @@ public class PopupWindow { if (!mTouchable) { curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; } - if (mTouchable) { + if (mOutsideTouchable) { curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; } if (!mClippingEnabled) { diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index ba63ec3..52c421c 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -22,6 +22,7 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.Gravity; +import android.view.ViewDebug; import android.widget.RemoteViews.RemoteView; import android.graphics.Rect; import com.android.internal.R; @@ -803,13 +804,33 @@ public class RelativeLayout extends ViewGroup { * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical */ public static class LayoutParams extends ViewGroup.MarginLayoutParams { + @ViewDebug.ExportedProperty(resolveId = true, indexMapping = { + @ViewDebug.IntToString(from = ABOVE, to = "above"), + @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"), + @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"), + @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"), + @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"), + @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"), + @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"), + @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"), + @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"), + @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"), + @ViewDebug.IntToString(from = BELOW, to = "below"), + @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"), + @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"), + @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"), + @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"), + @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf") + }, mapping = { @ViewDebug.IntToString(from = TRUE, to = "true") }) private int[] mRules = new int[VERB_COUNT]; + private int mLeft, mTop, mRight, mBottom; /** * When true, uses the parent as the anchor if the anchor doesn't exist or if * the anchor's visibility is GONE. */ + @ViewDebug.ExportedProperty public boolean alignWithParent; public LayoutParams(Context c, AttributeSet attrs) { diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java index e77c4ca..e0f1bb4 100644 --- a/core/java/android/widget/SlidingDrawer.java +++ b/core/java/android/widget/SlidingDrawer.java @@ -358,15 +358,17 @@ public class SlidingDrawer extends ViewGroup { } if (action == MotionEvent.ACTION_DOWN) { - if (mOnDrawerScrollListener != null) { - mOnDrawerScrollListener.onScrollStarted(); - } mTracking = true; handle.setPressed(true); // Must be called before prepareTracking() prepareContent(); + // Must be called after prepareContent() + if (mOnDrawerScrollListener != null) { + mOnDrawerScrollListener.onScrollStarted(); + } + if (mVertical) { final int top = mHandle.getTop(); mTouchDelta = (int) y - top; @@ -447,6 +449,8 @@ public class SlidingDrawer extends ViewGroup { } else { animateOpen(vertical ? top : left); } + } else { + performFling(vertical ? top : left, velocity, false); } } else { @@ -762,11 +766,11 @@ public class SlidingDrawer extends ViewGroup { * @see #toggle() */ public void animateClose() { + prepareContent(); final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener; if (scrollListener != null) { scrollListener.onScrollStarted(); } - prepareContent(); animateClose(mVertical ? mHandle.getTop() : mHandle.getLeft()); if (scrollListener != null) { scrollListener.onScrollEnded(); @@ -783,11 +787,11 @@ public class SlidingDrawer extends ViewGroup { * @see #toggle() */ public void animateOpen() { + prepareContent(); final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener; if (scrollListener != null) { scrollListener.onScrollStarted(); } - prepareContent(); animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft()); if (scrollListener != null) { scrollListener.onScrollEnded(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 8271a9a..426d711 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3242,6 +3242,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } }; mPopup.setFocusable(false); + // The user is entering text, so the input method is needed. We + // don't want the popup to be displayed on top of it. + mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); } TextView tv = (TextView) mPopup.getContentView(); diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index ec6f88b..4c5df2f 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -302,15 +302,15 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { private MediaPlayer.OnErrorListener mErrorListener = new MediaPlayer.OnErrorListener() { - public boolean onError(MediaPlayer mp, int a, int b) { - Log.d(TAG, "Error: " + a + "," + b); + public boolean onError(MediaPlayer mp, int framework_err, int impl_err) { + Log.d(TAG, "Error: " + framework_err + "," + impl_err); if (mMediaController != null) { mMediaController.hide(); } /* If an error handler has been supplied, use it and finish. */ if (mOnErrorListener != null) { - if (mOnErrorListener.onError(mMediaPlayer, a, b)) { + if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) { return true; } } @@ -322,9 +322,17 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { */ if (getWindowToken() != null) { Resources r = mContext.getResources(); + int messageId; + + if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) { + messageId = com.android.internal.R.string.VideoView_error_text_invalid_progressive_playback; + } else { + messageId = com.android.internal.R.string.VideoView_error_text_unknown; + } + new AlertDialog.Builder(mContext) .setTitle(com.android.internal.R.string.VideoView_error_title) - .setMessage(com.android.internal.R.string.VideoView_error_text_unknown) + .setMessage(messageId) .setPositiveButton(com.android.internal.R.string.VideoView_error_button, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java index 1ba2dce..6729fd1 100644 --- a/core/java/android/widget/ZoomButtonsController.java +++ b/core/java/android/widget/ZoomButtonsController.java @@ -36,6 +36,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.View.OnClickListener; @@ -61,7 +62,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi // TODO: scaled to density private static final int ZOOM_CONTROLS_TOUCH_PADDING = 20; - + private Context mContext; private WindowManager mWindowManager; @@ -84,7 +85,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi private int[] mContainerLocation = new int[2]; private ZoomControls mControls; - + /** * The view (or null) that should receive touch events. This will get set if * the touch down hits the container. It will be reset on the touch up. @@ -100,7 +101,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi * up/cancel, and then set the owner's touch listener to null. */ private boolean mReleaseTouchListenerOnUp; - + private boolean mIsSecondTapDown; private boolean mIsVisible; @@ -145,7 +146,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi * we delay the setVisible(true) call until it is not null. */ private static final int MSG_POST_SET_VISIBLE = 4; - + private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -157,7 +158,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi case MSG_DISMISS_ZOOM_CONTROLS: setVisible(false); break; - + case MSG_POST_SET_VISIBLE: if (mOwnerView.getWindowToken() == null) { // Doh, it is still null, throw an exception @@ -179,19 +180,19 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi mContainer = createContainer(); } - + public void setZoomInEnabled(boolean enabled) { mControls.setIsZoomInEnabled(enabled); } - + public void setZoomOutEnabled(boolean enabled) { mControls.setIsZoomOutEnabled(enabled); } - + public void setZoomSpeed(long speed) { mControls.setZoomSpeed(speed); } - + private FrameLayout createContainer() { LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.BOTTOM | Gravity.CENTER; @@ -202,19 +203,18 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi lp.width = LayoutParams.FILL_PARENT; lp.type = LayoutParams.TYPE_APPLICATION_PANEL; lp.format = PixelFormat.TRANSPARENT; - // TODO: make a new animation for this - lp.windowAnimations = com.android.internal.R.style.Animation_InputMethodFancy; + lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons; mContainerLayoutParams = lp; - + FrameLayout container = new FrameLayout(mContext); container.setLayoutParams(lp); container.setMeasureAllChildren(true); container.setOnKeyListener(this); - + LayoutInflater inflater = (LayoutInflater) mContext .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(com.android.internal.R.layout.zoom_magnify, container); - + inflater.inflate(com.android.internal.R.layout.zoom_container, container); + mControls = (ZoomControls) container.findViewById(com.android.internal.R.id.zoomControls); mControls.setOnZoomInClickListener(new OnClickListener() { public void onClick(View v) { @@ -229,38 +229,24 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi } }); - View overview = container.findViewById(com.android.internal.R.id.zoomMagnify); - overview.setVisibility(View.GONE); - overview.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); - if (mCallback != null) mCallback.onOverview(); - } - }); - return container; } - + public void setCallback(OnZoomListener callback) { mCallback = callback; } public void setFocusable(boolean focusable) { if (focusable) { - mContainerLayoutParams.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE; + mContainerLayoutParams.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE; } else { mContainerLayoutParams.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; } - + if (mIsVisible) { mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); } } - - public void setOverviewVisible(boolean visible) { - mContainer.findViewById(com.android.internal.R.id.zoomMagnify) - .setVisibility(visible ? View.VISIBLE : View.GONE); - } public boolean isVisible() { return mIsVisible; @@ -269,7 +255,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi public void setVisible(boolean visible) { if (!useThisZoom(mContext)) return; - + if (visible) { if (mOwnerView.getWindowToken() == null) { /* @@ -282,7 +268,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi } return; } - + dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); } @@ -350,14 +336,10 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi * * @return TODO */ - public FrameLayout getContainer() { + public ViewGroup getContainer() { return mContainer; } - public int getZoomControlsId() { - return mControls.getId(); - } - private void dismissControlsDelayed(int delay) { mHandler.removeMessages(MSG_DISMISS_ZOOM_CONTROLS); mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_CONTROLS, delay); @@ -372,7 +354,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi */ public boolean handleDoubleTapEvent(MotionEvent event) { if (!useThisZoom(mContext)) return false; - + int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { @@ -428,7 +410,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi // The second tap can no longer be down mIsSecondTapDown = false; } - + if (mReleaseTouchListenerOnUp) { // The controls were dismissed but we need to throw away all events until the up if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { @@ -443,7 +425,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi // TODO: optimize this (it ends up removing message and queuing another) dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); - + View targetView = mTouchTargetView; switch (action) { @@ -556,7 +538,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi com.android.internal.R.layout.alert_dialog_simple_text, null) .findViewById(android.R.id.text1); textView.setText(com.android.internal.R.string.tutorial_double_tap_to_zoom_message_short); - + sTutorialDialog = new AlertDialog.Builder(context) .setView(textView) .setIcon(0) @@ -603,7 +585,7 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi // Temporary methods for different zoom types static int getZoomType(Context context) { - return Settings.System.getInt(context.getContentResolver(), "zoom", 1); + return Settings.System.getInt(context.getContentResolver(), "zoom", 2); } public static boolean useOldZoom(Context context) { @@ -618,6 +600,5 @@ public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyLi void onCenter(int x, int y); void onVisibilityChanged(boolean visible); void onZoom(boolean zoomIn); - void onOverview(); } } diff --git a/core/java/com/android/internal/gadget/IGadgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl index e7b5a1e..2ed4773 100644 --- a/core/java/com/android/internal/gadget/IGadgetHost.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.android.internal.gadget; +package com.android.internal.appwidget; import android.content.ComponentName; -import android.gadget.GadgetProviderInfo; +import android.appwidget.AppWidgetProviderInfo; import android.widget.RemoteViews; /** {@hide} */ -oneway interface IGadgetHost { - void updateGadget(int gadgetId, in RemoteViews views); - void providerChanged(int gadgetId, in GadgetProviderInfo info); +oneway interface IAppWidgetHost { + void updateAppWidget(int appWidgetId, in RemoteViews views); + void providerChanged(int appWidgetId, in AppWidgetProviderInfo info); } diff --git a/core/java/com/android/internal/gadget/IGadgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl index 9c66b95..496aa1a 100644 --- a/core/java/com/android/internal/gadget/IGadgetService.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl @@ -14,37 +14,37 @@ * limitations under the License. */ -package com.android.internal.gadget; +package com.android.internal.appwidget; import android.content.ComponentName; -import android.gadget.GadgetProviderInfo; -import com.android.internal.gadget.IGadgetHost; +import android.appwidget.AppWidgetProviderInfo; +import com.android.internal.appwidget.IAppWidgetHost; import android.widget.RemoteViews; /** {@hide} */ -interface IGadgetService { +interface IAppWidgetService { // - // for GadgetHost + // for AppWidgetHost // - int[] startListening(IGadgetHost host, String packageName, int hostId, + int[] startListening(IAppWidgetHost host, String packageName, int hostId, out List<RemoteViews> updatedViews); void stopListening(int hostId); - int allocateGadgetId(String packageName, int hostId); - void deleteGadgetId(int gadgetId); + int allocateAppWidgetId(String packageName, int hostId); + void deleteAppWidgetId(int appWidgetId); void deleteHost(int hostId); void deleteAllHosts(); - RemoteViews getGadgetViews(int gadgetId); + RemoteViews getAppWidgetViews(int appWidgetId); // - // for GadgetManager + // for AppWidgetManager // - void updateGadgetIds(in int[] gadgetIds, in RemoteViews views); - void updateGadgetProvider(in ComponentName provider, in RemoteViews views); - List<GadgetProviderInfo> getInstalledProviders(); - GadgetProviderInfo getGadgetInfo(int gadgetId); - void bindGadgetId(int gadgetId, in ComponentName provider); - int[] getGadgetIds(in ComponentName provider); + void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views); + void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views); + List<AppWidgetProviderInfo> getInstalledProviders(); + AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId); + void bindAppWidgetId(int appWidgetId, in ComponentName provider); + int[] getAppWidgetIds(in ComponentName provider); } diff --git a/core/java/com/android/internal/gadget/package.html b/core/java/com/android/internal/appwidget/package.html index db6f78b..db6f78b 100644 --- a/core/java/com/android/internal/gadget/package.html +++ b/core/java/com/android/internal/appwidget/package.html diff --git a/core/java/com/android/internal/widget/NumberPicker.java b/core/java/com/android/internal/widget/NumberPicker.java index 1647c20..2f08c8d 100644 --- a/core/java/com/android/internal/widget/NumberPicker.java +++ b/core/java/com/android/internal/widget/NumberPicker.java @@ -30,6 +30,7 @@ import android.view.View.OnFocusChangeListener; import android.view.View.OnLongClickListener; import android.widget.TextView; import android.widget.LinearLayout; +import android.widget.EditText; import com.android.internal.R; @@ -76,7 +77,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener, } }; - private final TextView mText; + private final EditText mText; private final InputFilter mNumberInputFilter; private String[] mDisplayedValues; @@ -117,7 +118,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener, mDecrementButton.setOnLongClickListener(this); mDecrementButton.setNumberPicker(this); - mText = (TextView) findViewById(R.id.timepicker_input); + mText = (EditText) findViewById(R.id.timepicker_input); mText.setOnFocusChangeListener(this); mText.setFilters(new InputFilter[] {inputFilter}); mText.setRawInputType(InputType.TYPE_CLASS_NUMBER); @@ -188,11 +189,8 @@ public class NumberPicker extends LinearLayout implements OnClickListener, } public void onClick(View v) { - - /* The text view may still have focus so clear it's focus which will - * trigger the on focus changed and any typed values to be pulled. - */ - mText.clearFocus(); + validateInput(mText); + if (!mText.hasFocus()) mText.requestFocus(); // now perform the increment/decrement if (R.id.increment == v.getId()) { @@ -239,6 +237,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener, } else { mText.setText(mDisplayedValues[mCurrent - mStart]); } + mText.setSelection(mText.getText().length()); } private void validateCurrentView(CharSequence str) { @@ -257,16 +256,20 @@ public class NumberPicker extends LinearLayout implements OnClickListener, * has valid values. */ if (!hasFocus) { - String str = String.valueOf(((TextView) v).getText()); - if ("".equals(str)) { - - // Restore to the old value as we don't allow empty values - updateView(); - } else { - - // Check the new value and ensure it's in range - validateCurrentView(str); - } + validateInput(v); + } + } + + private void validateInput(View v) { + String str = String.valueOf(((TextView) v).getText()); + if ("".equals(str)) { + + // Restore to the old value as we don't allow empty values + updateView(); + } else { + + // Check the new value and ensure it's in range + validateCurrentView(str); } } |