From c39a6e0c51e182338deb8b63d07933b585134929 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 11 Mar 2009 12:11:56 -0700 Subject: auto import from //branches/cupcake/...@137873 --- core/java/android/app/SearchDialog.java | 26 +- core/java/android/appwidget/AppWidgetHost.java | 248 ++++++++++++++ core/java/android/appwidget/AppWidgetHostView.java | 312 +++++++++++++++++ core/java/android/appwidget/AppWidgetManager.java | 320 ++++++++++++++++++ core/java/android/appwidget/AppWidgetProvider.java | 154 +++++++++ .../android/appwidget/AppWidgetProviderInfo.aidl | 19 ++ .../android/appwidget/AppWidgetProviderInfo.java | 168 +++++++++ core/java/android/appwidget/package.html | 136 ++++++++ core/java/android/content/Context.java | 4 +- core/java/android/content/Intent.java | 10 - core/java/android/content/res/ColorStateList.java | 1 - core/java/android/gadget/GadgetHost.java | 249 -------------- core/java/android/gadget/GadgetHostView.java | 312 ----------------- core/java/android/gadget/GadgetManager.java | 320 ------------------ core/java/android/gadget/GadgetProvider.java | 154 --------- core/java/android/gadget/GadgetProviderInfo.aidl | 19 -- core/java/android/gadget/GadgetProviderInfo.java | 168 --------- core/java/android/gadget/package.html | 136 -------- .../android/inputmethodservice/ExtractButton.java | 30 ++ .../inputmethodservice/InputMethodService.java | 11 +- core/java/android/provider/Contacts.java | 37 +- core/java/android/provider/Gmail.java | 10 +- core/java/android/server/BluetoothA2dpService.java | 9 + .../android/server/BluetoothDeviceService.java | 1 + core/java/android/server/BluetoothEventLoop.java | 12 + .../android/text/method/QwertyKeyListener.java | 6 +- core/java/android/view/View.java | 19 +- core/java/android/view/ViewDebug.java | 376 +++++++++++++++++---- core/java/android/view/WindowManagerPolicy.java | 10 +- .../android/view/inputmethod/InputMethodInfo.java | 8 + .../view/inputmethod/InputMethodManager.java | 1 + core/java/android/webkit/LoadListener.java | 9 + core/java/android/webkit/TextDialog.java | 4 +- core/java/android/webkit/WebSettings.java | 8 +- core/java/android/webkit/WebView.java | 329 +++++++----------- core/java/android/webkit/WebViewCore.java | 6 - core/java/android/widget/AbsListView.java | 17 +- core/java/android/widget/ArrayAdapter.java | 13 + core/java/android/widget/AutoCompleteTextView.java | 14 +- core/java/android/widget/ListView.java | 24 +- core/java/android/widget/PopupWindow.java | 2 +- core/java/android/widget/RelativeLayout.java | 21 ++ core/java/android/widget/SlidingDrawer.java | 14 +- core/java/android/widget/TextView.java | 3 + core/java/android/widget/VideoView.java | 16 +- .../java/android/widget/ZoomButtonsController.java | 71 ++-- .../android/internal/appwidget/IAppWidgetHost.aidl | 28 ++ .../internal/appwidget/IAppWidgetService.aidl | 50 +++ .../com/android/internal/appwidget/package.html | 3 + .../com/android/internal/gadget/IGadgetHost.aidl | 28 -- .../android/internal/gadget/IGadgetService.aidl | 50 --- core/java/com/android/internal/gadget/package.html | 3 - .../com/android/internal/widget/NumberPicker.java | 37 +- 53 files changed, 2177 insertions(+), 1859 deletions(-) create mode 100644 core/java/android/appwidget/AppWidgetHost.java create mode 100644 core/java/android/appwidget/AppWidgetHostView.java create mode 100644 core/java/android/appwidget/AppWidgetManager.java create mode 100755 core/java/android/appwidget/AppWidgetProvider.java create mode 100644 core/java/android/appwidget/AppWidgetProviderInfo.aidl create mode 100644 core/java/android/appwidget/AppWidgetProviderInfo.java create mode 100644 core/java/android/appwidget/package.html delete mode 100644 core/java/android/gadget/GadgetHost.java delete mode 100644 core/java/android/gadget/GadgetHostView.java delete mode 100644 core/java/android/gadget/GadgetManager.java delete mode 100755 core/java/android/gadget/GadgetProvider.java delete mode 100644 core/java/android/gadget/GadgetProviderInfo.aidl delete mode 100644 core/java/android/gadget/GadgetProviderInfo.java delete mode 100644 core/java/android/gadget/package.html create mode 100644 core/java/android/inputmethodservice/ExtractButton.java create mode 100644 core/java/com/android/internal/appwidget/IAppWidgetHost.aidl create mode 100644 core/java/com/android/internal/appwidget/IAppWidgetService.aidl create mode 100644 core/java/com/android/internal/appwidget/package.html delete mode 100644 core/java/com/android/internal/gadget/IGadgetHost.aidl delete mode 100644 core/java/com/android/internal/gadget/IGadgetService.aidl delete mode 100644 core/java/com/android/internal/gadget/package.html (limited to 'core/java') 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 mRecentCursor = new WeakReference(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/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java new file mode 100644 index 0000000..10c2b02 --- /dev/null +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -0,0 +1,248 @@ +/* + * + * 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.Context; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.widget.RemoteViews; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import com.android.internal.appwidget.IAppWidgetHost; +import com.android.internal.appwidget.IAppWidgetService; + +/** + * AppWidgetHost provides the interaction with the AppWidget service for apps, + * like the home screen, that want to embed AppWidgets in their UI. + */ +public class AppWidgetHost { + + static final int HANDLE_UPDATE = 1; + static final int HANDLE_PROVIDER_CHANGED = 2; + + static Object sServiceLock = new Object(); + static IAppWidgetService sService; + + Context mContext; + String mPackageName; + + class Callbacks extends IAppWidgetHost.Stub { + public void updateAppWidget(int appWidgetId, RemoteViews views) { + Message msg = mHandler.obtainMessage(HANDLE_UPDATE); + msg.arg1 = appWidgetId; + msg.obj = views; + msg.sendToTarget(); + } + + public void providerChanged(int appWidgetId, AppWidgetProviderInfo info) { + Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED); + msg.arg1 = appWidgetId; + msg.obj = info; + msg.sendToTarget(); + } + } + + class UpdateHandler extends Handler { + public UpdateHandler(Looper looper) { + super(looper); + } + + public void handleMessage(Message msg) { + switch (msg.what) { + case HANDLE_UPDATE: { + updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj); + break; + } + case HANDLE_PROVIDER_CHANGED: { + onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj); + break; + } + } + } + } + + Handler mHandler; + + int mHostId; + Callbacks mCallbacks = new Callbacks(); + HashMap mViews = new HashMap(); + + 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.APPWIDGET_SERVICE); + sService = IAppWidgetService.Stub.asInterface(b); + } + } + } + + /** + * Start receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity + * becomes visible, i.e. from onStart() in your Activity. + */ + public void startListening() { + int[] updatedIds = null; + ArrayList updatedViews = new ArrayList(); + + try { + if (mPackageName == null) { + mPackageName = mContext.getPackageName(); + } + updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId, updatedViews); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + + final int N = updatedIds.length; + for (int i=0; i + *
  • Call this when initializing your database, as it might be because of a data wipe.
  • + *
  • 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.
  • + * + */ + public void deleteHost() { + try { + sService.deleteHost(mHostId); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Remove all records about all hosts for your package. + *
      + *
    • Call this when initializing your database, as it might be because of a data wipe.
    • + *
    • 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.
    • + *
    + */ + public static void deleteAllHosts() { + try { + sService.deleteAllHosts(); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + public final AppWidgetHostView createView(Context context, int appWidgetId, + AppWidgetProviderInfo appWidget) { + AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget); + view.setAppWidget(appWidgetId, appWidget); + synchronized (mViews) { + mViews.put(appWidgetId, view); + } + RemoteViews views = null; + try { + views = sService.getAppWidgetViews(appWidgetId); + } catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + view.updateAppWidget(views); + return view; + } + + /** + * Called to create the AppWidgetHostView. Override to return a custom subclass if you + * need it. {@more} + */ + protected AppWidgetHostView onCreateView(Context context, int appWidgetId, + AppWidgetProviderInfo appWidget) { + return new AppWidgetHostView(context); + } + + /** + * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk. + */ + protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) { + } + + void updateAppWidgetView(int appWidgetId, RemoteViews views) { + AppWidgetHostView v; + synchronized (mViews) { + v = mViews.get(appWidgetId); + } + if (v != null) { + v.updateAppWidget(views); + } + } +} + + diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java new file mode 100644 index 0000000..be0f96e --- /dev/null +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.appwidget; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.util.Config; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.widget.FrameLayout; +import android.widget.RemoteViews; +import android.widget.TextView; + +/** + * 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 AppWidgetHostView extends FrameLayout { + static final String TAG = "AppWidgetHostView"; + static final boolean LOGD = false; + static final boolean CROSSFADE = false; + + static final int VIEW_MODE_NOINIT = 0; + static final int VIEW_MODE_CONTENT = 1; + static final int VIEW_MODE_ERROR = 2; + static final int VIEW_MODE_DEFAULT = 3; + + static final int FADE_DURATION = 1000; + + // 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) { + return clazz.isAnnotationPresent(RemoteViews.RemoteView.class); + } + }; + + Context mContext; + + int mAppWidgetId; + AppWidgetProviderInfo mInfo; + View mView; + int mViewMode = VIEW_MODE_NOINIT; + int mLayoutId = -1; + long mFadeStartTime = -1; + Bitmap mOld; + Paint mOldPaint = new Paint(); + + /** + * Create a host view. Uses default fade animations. + */ + 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 #updateAppWidget(RemoteViews)}. + * + * @param animationIn Resource ID of in animation to use + * @param animationOut Resource ID of out animation to use + */ + public AppWidgetHostView(Context context, int animationIn, int animationOut) { + super(context); + mContext = context; + } + + /** + * Set the AppWidget that will be displayed by this view. + */ + public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) { + mAppWidgetId = appWidgetId; + mInfo = info; + } + + public int getAppWidgetId() { + return mAppWidgetId; + } + + public AppWidgetProviderInfo getAppWidgetInfo() { + return mInfo; + } + + /** + * Process a set of {@link RemoteViews} coming in as an update from the + * AppWidget provider. Will animate into these new views as needed. + */ + public void updateAppWidget(RemoteViews remoteViews) { + if (LOGD) Log.d(TAG, "updateAppWidget called mOld=" + mOld); + + boolean recycled = false; + View content = null; + Exception exception = null; + + // Capture the old view into a bitmap so we can do the crossfade. + if (CROSSFADE) { + if (mFadeStartTime < 0) { + if (mView != null) { + final int width = mView.getWidth(); + final int height = mView.getHeight(); + try { + mOld = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + } catch (OutOfMemoryError e) { + // we just won't do the fade + mOld = null; + } + if (mOld != null) { + //mView.drawIntoBitmap(mOld); + } + } + } + } + + if (remoteViews == null) { + if (mViewMode == VIEW_MODE_DEFAULT) { + // We've already done this -- nothing to do. + return; + } + content = getDefaultView(); + mLayoutId = -1; + mViewMode = VIEW_MODE_DEFAULT; + } else { + int layoutId = remoteViews.getLayoutId(); + + // If our stale view has been prepared to match active, and the new + // layout matches, try recycling it + if (content == null && layoutId == mLayoutId) { + try { + remoteViews.reapply(mContext, mView); + content = mView; + recycled = true; + if (LOGD) Log.d(TAG, "was able to recycled existing layout"); + } catch (RuntimeException e) { + exception = e; + } + } + + // Try normal RemoteView inflation + if (content == null) { + try { + content = remoteViews.apply(mContext, this); + if (LOGD) Log.d(TAG, "had to inflate new layout"); + } catch (RuntimeException e) { + exception = e; + } + } + + mLayoutId = layoutId; + mViewMode = VIEW_MODE_CONTENT; + } + + if (content == null) { + if (mViewMode == VIEW_MODE_ERROR) { + // We've already done this -- nothing to do. + return ; + } + Log.w(TAG, "updateAppWidget couldn't find any view, using error view", exception); + content = getErrorView(); + mViewMode = VIEW_MODE_ERROR; + } + + if (!recycled) { + prepareView(content); + addView(content); + } + + if (mView != content) { + removeView(mView); + mView = content; + } + + if (CROSSFADE) { + if (mFadeStartTime < 0) { + // if there is already an animation in progress, don't do anything -- + // the new view will pop in on top of the old one during the cross fade, + // and that looks okay. + mFadeStartTime = SystemClock.uptimeMillis(); + invalidate(); + } + } + } + + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (CROSSFADE) { + int alpha; + int l = child.getLeft(); + int t = child.getTop(); + if (mFadeStartTime > 0) { + alpha = (int)(((drawingTime-mFadeStartTime)*255)/FADE_DURATION); + if (alpha > 255) { + alpha = 255; + } + Log.d(TAG, "drawChild alpha=" + alpha + " l=" + l + " t=" + t + + " w=" + child.getWidth()); + if (alpha != 255 && mOld != null) { + mOldPaint.setAlpha(255-alpha); + //canvas.drawBitmap(mOld, l, t, mOldPaint); + } + } else { + alpha = 255; + } + int restoreTo = canvas.saveLayerAlpha(l, t, child.getWidth(), child.getHeight(), alpha, + Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); + boolean rv = super.drawChild(canvas, child, drawingTime); + canvas.restoreToCount(restoreTo); + if (alpha < 255) { + invalidate(); + } else { + mFadeStartTime = -1; + if (mOld != null) { + mOld.recycle(); + mOld = null; + } + } + return rv; + } else { + return super.drawChild(canvas, child, drawingTime); + } + } + + /** + * Prepare the given view to be shown. This might include adjusting + * {@link FrameLayout.LayoutParams} before inserting. + */ + protected void prepareView(View view) { + // Take requested dimensions from parent, but apply default gravity. + ViewGroup.LayoutParams requested = view.getLayoutParams(); + if (requested == null) { + requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.FILL_PARENT); + } + + FrameLayout.LayoutParams params = + new FrameLayout.LayoutParams(requested.width, requested.height); + params.gravity = Gravity.CENTER; + view.setLayoutParams(params); + } + + /** + * Inflate and return the default layout requested by AppWidget provider. + */ + protected View getDefaultView() { + View defaultView = null; + Exception exception = null; + + try { + if (mInfo != null) { + Context theirContext = mContext.createPackageContext( + mInfo.provider.getPackageName(), 0 /* no flags */); + LayoutInflater inflater = (LayoutInflater) + theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater = inflater.cloneInContext(theirContext); + inflater.setFilter(sInflaterFilter); + defaultView = inflater.inflate(mInfo.initialLayout, this, false); + } else { + Log.w(TAG, "can't inflate defaultView because mInfo is missing"); + } + } catch (PackageManager.NameNotFoundException e) { + exception = e; + } catch (RuntimeException e) { + exception = e; + } + + if (exception != null && LOGD) { + Log.w(TAG, "Error inflating AppWidget " + mInfo, exception); + } + + if (defaultView == null) { + if (LOGD) Log.d(TAG, "getDefaultView couldn't find any view, so inflating error"); + defaultView = getErrorView(); + } + + return defaultView; + } + + /** + * Inflate and return a view that represents an error state. + */ + protected View getErrorView() { + TextView tv = new TextView(mContext); + tv.setText(com.android.internal.R.string.gadget_host_error_inflating); + // TODO: get this color from somewhere. + tv.setBackgroundColor(Color.argb(127, 0, 0, 0)); + return tv; + } +} 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. + *

    + * You must supply the following extras: + * + * + * + * + * + *
    {@link #EXTRA_APPWIDGET_ID}A newly allocated appWidgetId, which will be bound to the AppWidget provider + * once the user has selected one.
    + * + *

    + * The system will respond with an onActivityResult call with the following extras in + * the intent: + * + * + * + * + * + *
    {@link #EXTRA_APPWIDGET_ID}The appWidgetId that you supplied in the original intent.
    + *

    + * 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}. + * + *

    + * The intent will contain the following extras: + * + * + * + * + * + *
    {@link #EXTRA_APPWIDGET_ID}The appWidgetId to configure.
    + * + *

    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. + *

    + * 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. + *

    + * 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. + * + *

    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. + * + *

    + * The intent will contain the following extras: + * + * + * + * + * + *
    {@link #EXTRA_APPWIDGET_IDS}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.
    + * + * @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> 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 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. + * + *

    + * 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. + * + *

    + * 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. + * + *

    + * 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 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. + * + *

    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. + * + *

    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. + * + *

    Sample Code

    + * For an example of how to write a AppWidget provider, see the + * android.appwidget + * package overview. + */ +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/appwidget/AppWidgetProviderInfo.aidl b/core/java/android/appwidget/AppWidgetProviderInfo.aidl new file mode 100644 index 0000000..82b3ada --- /dev/null +++ b/core/java/android/appwidget/AppWidgetProviderInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.appwidget; + +parcelable AppWidgetProviderInfo; diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java new file mode 100644 index 0000000..8530c35 --- /dev/null +++ b/core/java/android/appwidget/AppWidgetProviderInfo.java @@ -0,0 +1,168 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; +import android.content.ComponentName; + +/** + * Describes the meta data for an installed AppWidget provider. The fields in this class + * correspond to the fields in the <appwidget-provider> xml tag. + */ +public class AppWidgetProviderInfo implements Parcelable { + /** + * 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}. + * + *

    This field corresponds to the android:name attribute in + * the <receiver> element in the AndroidManifest.xml file. + */ + public ComponentName provider; + + /** + * Minimum width of the AppWidget, in dp. + * + *

    This field corresponds to the android:minWidth attribute in + * the AppWidget meta-data file. + */ + public int minWidth; + + /** + * Minimum height of the AppWidget, in dp. + * + *

    This field corresponds to the android:minHeight attribute in + * the AppWidget meta-data file. + */ + public int minHeight; + + /** + * 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. + * + *

    This field corresponds to the android:updatePeriodMillis attribute in + * the AppWidget meta-data file. + */ + public int updatePeriodMillis; + + /** + * The resource id of the initial layout for this AppWidget. This should be + * displayed until the RemoteViews for the AppWidget is available. + * + *

    This field corresponds to the android:initialLayout attribute in + * the AppWidget meta-data file. + */ + public int initialLayout; + + /** + * The activity to launch that will configure the AppWidget. + * + *

    This class name of field corresponds to the android:configure attribute in + * 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 AppWidget picker. If not supplied in the + * xml, the application label will be used. + * + *

    This field corresponds to the android:label attribute in + * the <receiver> element in the AndroidManifest.xml file. + */ + public String label; + + /** + * The icon to display for this AppWidget in the AppWidget picker. If not supplied in the + * xml, the application icon will be used. + * + *

    This field corresponds to the android:icon attribute in + * the <receiver> element in the AndroidManifest.xml file. + */ + public int icon; + + public AppWidgetProviderInfo() { + } + + /** + * Unflatten the AppWidgetProviderInfo from a parcel. + */ + public AppWidgetProviderInfo(Parcel in) { + if (0 != in.readInt()) { + this.provider = new ComponentName(in); + } + this.minWidth = in.readInt(); + this.minHeight = in.readInt(); + this.updatePeriodMillis = in.readInt(); + this.initialLayout = in.readInt(); + if (0 != in.readInt()) { + this.configure = new ComponentName(in); + } + this.label = in.readString(); + this.icon = in.readInt(); + } + + + public void writeToParcel(android.os.Parcel out, int flags) { + if (this.provider != null) { + out.writeInt(1); + this.provider.writeToParcel(out, flags); + } else { + out.writeInt(0); + } + out.writeInt(this.minWidth); + out.writeInt(this.minHeight); + out.writeInt(this.updatePeriodMillis); + out.writeInt(this.initialLayout); + if (this.configure != null) { + out.writeInt(1); + this.configure.writeToParcel(out, flags); + } else { + out.writeInt(0); + } + out.writeString(this.label); + out.writeInt(this.icon); + } + + public int describeContents() { + return 0; + } + + /** + * Parcelable.Creator that instantiates AppWidgetProviderInfo objects + */ + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() + { + public AppWidgetProviderInfo createFromParcel(Parcel parcel) + { + return new AppWidgetProviderInfo(parcel); + } + + public AppWidgetProviderInfo[] newArray(int size) + { + return new AppWidgetProviderInfo[size]; + } + }; + + public String toString() { + 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 @@ + +

    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." +

    +

    AppWidget Providers

    + +

    AppWidget Hosts

    + + +{@more} + + +

    AppWidget Providers

    +

    +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. + +

    Declaring a widget in the AndroidManifest

    + +

    +First, declare the {@link android.content.BroadcastReceiver} in your application's +AndroidManifest.xml file. + +{@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/AndroidManifest.xml AppWidgetProvider} + +

    +The <receiver> element has the following attributes: +

      +
    • android:name - which specifies the + {@link android.content.BroadcastReceiver} or {@link android.appwidget.AppWidgetProvider} + class.
    • +
    • android:label - which specifies the string resource that + will be shown by the widget picker as the label.
    • +
    • android:icon - which specifies the drawable resource that + will be shown by the widget picker as the icon.
    • +
    + +

    +The <intent-filter> 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. + +

    +The <meta-data> 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: +

      +
    • android:name="android.appwidget.provider" - identifies this meta-data + as the {@link android.appwidget.AppWidgetProviderInfo} descriptor.
    • +
    • android:resource - is the xml resource to use as that descriptor.
    • +
    + + +

    Adding the {@link android.appwidget.AppWidgetProviderInfo AppWidgetProviderInfo} meta-data

    + +

    +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 +android:resource="@xml/appwidget_info". That XML file would go in your application's +directory at res/xml/appwidget_info.xml. Here is a simple example. + +{@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/res/xml/appwidget_info.xml AppWidgetProviderInfo} + +

    +The attributes are as documented in the {@link android.appwidget.AppWidgetProviderInfo GagetInfo} class. (86400000 milliseconds means once per day) + + +

    Using the {@link android.appwidget.AppWidgetProvider AppWidgetProvider} class

    + +

    The AppWidgetProvider class is the easiest way to handle the widget provider intent broadcasts. +See the src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.java +sample class in ApiDemos for an example. + +

    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 +Application Fundamentals > +Broadcast Receiver Lifecycle for more information. + + + +

    AppWidget Configuration UI

    + +

    +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 android:configure attribute. + +

    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. + +

    See the src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java +sample class in ApiDemos for an example. + + + +

    AppWidget Broadcast Intents

    + +

    {@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: +

      +
    • {@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE}
    • +
    • {@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_DELETED}
    • +
    • {@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_ENABLED}
    • +
    • {@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_DISABLED}
    • +
    + +

    By way of example, the implementation of +{@link android.appwidget.AppWidgetProvider#onReceive} is quite simple:

    + +{@sample frameworks/base/core/java/android/appwidget/AppWidgetProvider.java onReceive} + + +

    AppWidget Hosts

    +

    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.

    +

    For more information on implementing your own widget host, see the +{@link android.appwidget.AppWidgetHost AppWidgetHost} class.

    + + 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; *
  • {@link #CATEGORY_INFO} *
  • {@link #CATEGORY_HOME} *
  • {@link #CATEGORY_PREFERENCE} - *
  • {@link #CATEGORY_GADGET} *
  • {@link #CATEGORY_TEST} * * @@ -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. - * - *

    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 mViews = new HashMap(); - - public GadgetHost(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); - } - } - } - - /** - * Start receiving onGadgetChanged calls for your gadgets. Call this when your activity - * becomes visible, i.e. from onStart() in your Activity. - */ - public void startListening() { - int[] updatedIds = null; - ArrayList updatedViews = new ArrayList(); - - try { - if (mPackageName == null) { - mPackageName = mContext.getPackageName(); - } - updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId, updatedViews); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - - final int N = updatedIds.length; - for (int i=0; i - *

  • Call this when initializing your database, as it might be because of a data wipe.
  • - *
  • Call this to have the gadget manager release all resources associated with your - * host. Any future calls about this host will cause the records to be re-allocated.
  • - * - */ - public void deleteHost() { - try { - sService.deleteHost(mHostId); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } - - /** - * Remove all records about all hosts for your package. - *
      - *
    • Call this when initializing your database, as it might be because of a data wipe.
    • - *
    • Call this to have the gadget manager release all resources associated with your - * host. Any future calls about this host will cause the records to be re-allocated.
    • - *
    - */ - public static void deleteAllHosts() { - try { - sService.deleteAllHosts(); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } - - public final GadgetHostView createView(Context context, int gadgetId, - GadgetProviderInfo gadget) { - GadgetHostView view = onCreateView(context, gadgetId, gadget); - view.setGadget(gadgetId, gadget); - synchronized (mViews) { - mViews.put(gadgetId, view); - } - RemoteViews views = null; - try { - views = sService.getGadgetViews(gadgetId); - } catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - view.updateGadget(views); - return view; - } - - /** - * Called to create the GadgetHostView. Override to return a custom subclass if you - * need it. {@more} - */ - protected GadgetHostView onCreateView(Context context, int gadgetId, - GadgetProviderInfo gadget) { - return new GadgetHostView(context); - } - - /** - * Called when the gadget provider for a gadget has been upgraded to a new apk. - */ - protected void onProviderChanged(int gadgetId, GadgetProviderInfo gadget) { - } - - void updateGadgetView(int gadgetId, RemoteViews views) { - GadgetHostView v; - synchronized (mViews) { - v = mViews.get(gadgetId); - } - if (v != null) { - v.updateGadget(views); - } - } -} - - diff --git a/core/java/android/gadget/GadgetHostView.java b/core/java/android/gadget/GadgetHostView.java deleted file mode 100644 index 5cbd988..0000000 --- a/core/java/android/gadget/GadgetHostView.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.gadget; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.os.Handler; -import android.os.Message; -import android.os.SystemClock; -import android.util.Config; -import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.Animation; -import android.widget.FrameLayout; -import android.widget.RemoteViews; -import android.widget.TextView; - -/** - * Provides the glue to show gadget 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"; - static final boolean LOGD = false; - static final boolean CROSSFADE = false; - - static final int VIEW_MODE_NOINIT = 0; - static final int VIEW_MODE_CONTENT = 1; - static final int VIEW_MODE_ERROR = 2; - static final int VIEW_MODE_DEFAULT = 3; - - static final int FADE_DURATION = 1000; - - // When we're inflating the initialLayout for a gadget, we only allow - // views that are allowed in RemoteViews. - static final LayoutInflater.Filter sInflaterFilter = new LayoutInflater.Filter() { - public boolean onLoadClass(Class clazz) { - return clazz.isAnnotationPresent(RemoteViews.RemoteView.class); - } - }; - - Context mContext; - - int mGadgetId; - GadgetProviderInfo mInfo; - View mView; - int mViewMode = VIEW_MODE_NOINIT; - int mLayoutId = -1; - long mFadeStartTime = -1; - Bitmap mOld; - Paint mOldPaint = new Paint(); - - /** - * Create a host view. Uses default fade animations. - */ - public GadgetHostView(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)}. - * - * @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) { - super(context); - mContext = context; - } - - /** - * Set the gadget that will be displayed by this view. - */ - public void setGadget(int gadgetId, GadgetProviderInfo info) { - mGadgetId = gadgetId; - mInfo = info; - } - - public int getGadgetId() { - return mGadgetId; - } - - public GadgetProviderInfo getGadgetInfo() { - 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. - */ - public void updateGadget(RemoteViews remoteViews) { - if (LOGD) Log.d(TAG, "updateGadget called mOld=" + mOld); - - boolean recycled = false; - View content = null; - Exception exception = null; - - // Capture the old view into a bitmap so we can do the crossfade. - if (CROSSFADE) { - if (mFadeStartTime < 0) { - if (mView != null) { - final int width = mView.getWidth(); - final int height = mView.getHeight(); - try { - mOld = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - } catch (OutOfMemoryError e) { - // we just won't do the fade - mOld = null; - } - if (mOld != null) { - //mView.drawIntoBitmap(mOld); - } - } - } - } - - if (remoteViews == null) { - if (mViewMode == VIEW_MODE_DEFAULT) { - // We've already done this -- nothing to do. - return; - } - content = getDefaultView(); - mLayoutId = -1; - mViewMode = VIEW_MODE_DEFAULT; - } else { - int layoutId = remoteViews.getLayoutId(); - - // If our stale view has been prepared to match active, and the new - // layout matches, try recycling it - if (content == null && layoutId == mLayoutId) { - try { - remoteViews.reapply(mContext, mView); - content = mView; - recycled = true; - if (LOGD) Log.d(TAG, "was able to recycled existing layout"); - } catch (RuntimeException e) { - exception = e; - } - } - - // Try normal RemoteView inflation - if (content == null) { - try { - content = remoteViews.apply(mContext, this); - if (LOGD) Log.d(TAG, "had to inflate new layout"); - } catch (RuntimeException e) { - exception = e; - } - } - - mLayoutId = layoutId; - mViewMode = VIEW_MODE_CONTENT; - } - - if (content == null) { - if (mViewMode == VIEW_MODE_ERROR) { - // We've already done this -- nothing to do. - return ; - } - Log.w(TAG, "updateGadget couldn't find any view, using error view", exception); - content = getErrorView(); - mViewMode = VIEW_MODE_ERROR; - } - - if (!recycled) { - prepareView(content); - addView(content); - } - - if (mView != content) { - removeView(mView); - mView = content; - } - - if (CROSSFADE) { - if (mFadeStartTime < 0) { - // if there is already an animation in progress, don't do anything -- - // the new view will pop in on top of the old one during the cross fade, - // and that looks okay. - mFadeStartTime = SystemClock.uptimeMillis(); - invalidate(); - } - } - } - - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - if (CROSSFADE) { - int alpha; - int l = child.getLeft(); - int t = child.getTop(); - if (mFadeStartTime > 0) { - alpha = (int)(((drawingTime-mFadeStartTime)*255)/FADE_DURATION); - if (alpha > 255) { - alpha = 255; - } - Log.d(TAG, "drawChild alpha=" + alpha + " l=" + l + " t=" + t - + " w=" + child.getWidth()); - if (alpha != 255 && mOld != null) { - mOldPaint.setAlpha(255-alpha); - //canvas.drawBitmap(mOld, l, t, mOldPaint); - } - } else { - alpha = 255; - } - int restoreTo = canvas.saveLayerAlpha(l, t, child.getWidth(), child.getHeight(), alpha, - Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); - boolean rv = super.drawChild(canvas, child, drawingTime); - canvas.restoreToCount(restoreTo); - if (alpha < 255) { - invalidate(); - } else { - mFadeStartTime = -1; - if (mOld != null) { - mOld.recycle(); - mOld = null; - } - } - return rv; - } else { - return super.drawChild(canvas, child, drawingTime); - } - } - - /** - * Prepare the given view to be shown. This might include adjusting - * {@link FrameLayout.LayoutParams} before inserting. - */ - protected void prepareView(View view) { - // Take requested dimensions from parent, but apply default gravity. - ViewGroup.LayoutParams requested = view.getLayoutParams(); - if (requested == null) { - requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, - LayoutParams.FILL_PARENT); - } - - FrameLayout.LayoutParams params = - new FrameLayout.LayoutParams(requested.width, requested.height); - params.gravity = Gravity.CENTER; - view.setLayoutParams(params); - } - - /** - * Inflate and return the default layout requested by gadget provider. - */ - protected View getDefaultView() { - View defaultView = null; - Exception exception = null; - - try { - if (mInfo != null) { - Context theirContext = mContext.createPackageContext( - mInfo.provider.getPackageName(), 0 /* no flags */); - LayoutInflater inflater = (LayoutInflater) - theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater = inflater.cloneInContext(theirContext); - inflater.setFilter(sInflaterFilter); - defaultView = inflater.inflate(mInfo.initialLayout, this, false); - } else { - Log.w(TAG, "can't inflate defaultView because mInfo is missing"); - } - } catch (PackageManager.NameNotFoundException e) { - exception = e; - } catch (RuntimeException e) { - exception = e; - } - - if (exception != null && LOGD) { - Log.w(TAG, "Error inflating gadget " + mInfo, exception); - } - - if (defaultView == null) { - if (LOGD) Log.d(TAG, "getDefaultView couldn't find any view, so inflating error"); - defaultView = getErrorView(); - } - - return defaultView; - } - - /** - * Inflate and return a view that represents an error state. - */ - protected View getErrorView() { - TextView tv = new TextView(mContext); - tv.setText(com.android.internal.R.string.gadget_host_error_inflating); - // TODO: get this color from somewhere. - tv.setBackgroundColor(Color.argb(127, 0, 0, 0)); - return tv; - } -} 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. - *

    - * You must supply the following extras: - * - * - * - * - * - *
    {@link #EXTRA_GADGET_ID}A newly allocated gadgetId, which will be bound to the gadget provider - * once the user has selected one.
    - * - *

    - * The system will respond with an onActivityResult call with the following extras in - * the intent: - * - * - * - * - * - *
    {@link #EXTRA_GADGET_ID}The gadgetId that you supplied in the original intent.
    - *

    - * 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}. - * - *

    - * The intent will contain the following extras: - * - * - * - * - * - *
    {@link #EXTRA_GADGET_ID}The gadgetId to configure.
    - * - *

    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. - *

    - * 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. - *

    - * 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. - * - *

    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. - * - *

    - * The intent will contain the following extras: - * - * - * - * - * - *
    {@link #EXTRA_GADGET_IDS}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.
    - * - * @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> 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 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. - * - *

    - * 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. - * - *

    - * 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. - * - *

    - * 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 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. - * - *

    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. - * - *

    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. - * - *

    Sample Code

    - * For an example of how to write a gadget provider, see the - * android.gadget - * package overview. - */ -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/GadgetProviderInfo.aidl b/core/java/android/gadget/GadgetProviderInfo.aidl deleted file mode 100644 index 589f886..0000000 --- a/core/java/android/gadget/GadgetProviderInfo.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2007, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.gadget; - -parcelable GadgetProviderInfo; diff --git a/core/java/android/gadget/GadgetProviderInfo.java b/core/java/android/gadget/GadgetProviderInfo.java deleted file mode 100644 index 95c0432..0000000 --- a/core/java/android/gadget/GadgetProviderInfo.java +++ /dev/null @@ -1,168 +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.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 <gadget-provider> xml tag. - */ -public class GadgetProviderInfo 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}. - * - *

    This field corresponds to the android:name attribute in - * the <receiver> element in the AndroidManifest.xml file. - */ - public ComponentName provider; - - /** - * Minimum width of the gadget, in dp. - * - *

    This field corresponds to the android:minWidth attribute in - * the gadget meta-data file. - */ - public int minWidth; - - /** - * Minimum height of the gadget, in dp. - * - *

    This field corresponds to the android:minHeight attribute in - * the gadget 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. - * - *

    This field corresponds to the android:updatePeriodMillis attribute in - * the gadget 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. - * - *

    This field corresponds to the android:initialLayout attribute in - * the gadget meta-data file. - */ - public int initialLayout; - - /** - * The activity to launch that will configure the gadget. - * - *

    This class name of field corresponds to the android:configure attribute in - * the gadget meta-data file. The package name always corresponds to the package containing - * the gadget provider. - */ - public ComponentName configure; - - /** - * The label to display to the user in the gadget picker. If not supplied in the - * xml, the application label will be used. - * - *

    This field corresponds to the android:label attribute in - * the <receiver> element in the AndroidManifest.xml file. - */ - public String label; - - /** - * The icon to display for this gadget in the gadget picker. If not supplied in the - * xml, the application icon will be used. - * - *

    This field corresponds to the android:icon attribute in - * the <receiver> element in the AndroidManifest.xml file. - */ - public int icon; - - public GadgetProviderInfo() { - } - - /** - * Unflatten the GadgetProviderInfo from a parcel. - */ - public GadgetProviderInfo(Parcel in) { - if (0 != in.readInt()) { - this.provider = new ComponentName(in); - } - this.minWidth = in.readInt(); - this.minHeight = in.readInt(); - this.updatePeriodMillis = in.readInt(); - this.initialLayout = in.readInt(); - if (0 != in.readInt()) { - this.configure = new ComponentName(in); - } - this.label = in.readString(); - this.icon = in.readInt(); - } - - - public void writeToParcel(android.os.Parcel out, int flags) { - if (this.provider != null) { - out.writeInt(1); - this.provider.writeToParcel(out, flags); - } else { - out.writeInt(0); - } - out.writeInt(this.minWidth); - out.writeInt(this.minHeight); - out.writeInt(this.updatePeriodMillis); - out.writeInt(this.initialLayout); - if (this.configure != null) { - out.writeInt(1); - this.configure.writeToParcel(out, flags); - } else { - out.writeInt(0); - } - out.writeString(this.label); - out.writeInt(this.icon); - } - - public int describeContents() { - return 0; - } - - /** - * Parcelable.Creator that instantiates GadgetProviderInfo objects - */ - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() - { - public GadgetProviderInfo createFromParcel(Parcel parcel) - { - return new GadgetProviderInfo(parcel); - } - - public GadgetProviderInfo[] newArray(int size) - { - return new GadgetProviderInfo[size]; - } - }; - - public String toString() { - return "GadgetProviderInfo(provider=" + this.provider + ")"; - } -} - - 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 @@ - -

    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." -

    -

    Gadget Providers

    - -

    Gadget Hosts

    - - -{@more} - - -

    Gadget Providers

    -

    -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. - -

    Declaring a gadget in the AndroidManifest

    - -

    -First, declare the {@link android.content.BroadcastReceiver} in your application's -AndroidManifest.xml file. - -{@sample frameworks/base/tests/gadgets/GadgetHostTest/AndroidManifest.xml GadgetProvider} - -

    -The <receiver> element has the following attributes: -

      -
    • android:name - which specifies the - {@link android.content.BroadcastReceiver} or {@link android.gadget.GadgetProvider} - class.
    • -
    • android:label - which specifies the string resource that - will be shown by the gadget picker as the label.
    • -
    • android:icon - which specifies the drawable resource that - will be shown by the gadget picker as the icon.
    • -
    - -

    -The <intent-filter> 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. - -

    -The <meta-data> 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: -

      -
    • android:name="android.gadget.provider" - identifies this meta-data - as the {@link android.gadget.GadgetProviderInfo} descriptor.
    • -
    • android:resource - is the xml resource to use as that descriptor.
    • -
    - - -

    Adding the {@link android.gadget.GadgetProviderInfo GadgetProviderInfo} meta-data

    - -

    -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 -android:resource="@xml/gadget_info". That XML file would go in your application's -directory at res/xml/gadget_info.xml. Here is a simple example. - -{@sample frameworks/base/tests/gadgets/GadgetHostTest/res/xml/gadget_info.xml GadgetProviderInfo} - -

    -The attributes are as documented in the {@link android.gadget.GadgetProviderInfo GagetInfo} class. (86400000 milliseconds means once per day) - - -

    Using the {@link android.gadget.GadgetProvider GadgetProvider} class

    - -

    The GadgetProvider class is the easiest way to handle the gadget provider intent broadcasts. -See the src/com/example/android/apis/gadget/ExampleGadgetProvider.java -sample class in ApiDemos for an example. - -

    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 -Application Fundamentals > -Broadcast Receiver Lifecycle for more information. - - - -

    Gadget Configuration UI

    - -

    -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 android:configure attribute. - -

    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. - -

    See the src/com/example/android/apis/gadget/ExampleGadgetConfigure.java -sample class in ApiDemos for an example. - - - -

    Gadget Broadcast Intents

    - -

    {@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: -

      -
    • {@link android.gadget.GadgetManager#ACTION_GADGET_UPDATE}
    • -
    • {@link android.gadget.GadgetManager#ACTION_GADGET_DELETED}
    • -
    • {@link android.gadget.GadgetManager#ACTION_GADGET_ENABLED}
    • -
    • {@link android.gadget.GadgetManager#ACTION_GADGET_DISABLED}
    • -
    - -

    By way of example, the implementation of -{@link android.gadget.GadgetProvider#onReceive} is quite simple:

    - -{@sample frameworks/base/core/java/android/gadget/GadgetProvider.java onReceive} - - -

    Gadget Hosts

    -

    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.

    -

    For more information on implementing your own gadget host, see the -{@link android.gadget.GadgetHost GadgetHost} class.

    - - 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. + *

    + * For mailto: URIs, the scheme specific portion must be a + * raw email address, such as one built using + * {@link Uri#fromParts(String, String, String)}. + *

    + * For tel: 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)}. + *

    + * Any extras from the {@link Insert} class will be passed along to the + * create activity if there are no contacts to show. + *

    + * 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. + * + *

    Type: BOOLEAN

    + */ + 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: + * + *
    +         * @ViewDebug.ExportedProperty(mapping = {
    +         *     @ViewDebug.IntToString(from = 0, to = "INVALID"),
    +         *     @ViewDebug.IntToString(from = 1, to = "FIRST"),
    +         *     @ViewDebug.IntToString(from = 2, to = "SECOND")
    +         * })
    +         * private int[] mElements;
    +         * 
    +         *
    +         * @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, Field[]> sFieldsForClasses;
         private static HashMap, Method[]> sMethodsForClasses;
    -        
    +    private static HashMap 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() {
    +                    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() {
    +                    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() {
    +                    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[] pre();
    +        void run(T... data);
    +        void post(T... data);
    +    }
    +
    +    private static  long profileViewOperation(View view, final ViewOperation 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, Field[]>();
             }
    +        if (sAnnotations == null) {
    +            sAnnotations = new HashMap(512);
    +        }
    +
             final HashMap, Field[]> map = sFieldsForClasses;
    +        final HashMap 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, Method[]>();
    +            sMethodsForClasses = new HashMap, Method[]>(100);
    +        }
    +        if (sAnnotations == null) {
    +            sAnnotations = new HashMap(512);
             }
    +
             final HashMap, Method[]> map = sMethodsForClasses;
    +        final HashMap 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.
      * 
    Li
    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. + *

    + * 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 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 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 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 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 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/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl new file mode 100644 index 0000000..2ed4773 --- /dev/null +++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.appwidget; + +import android.content.ComponentName; +import android.appwidget.AppWidgetProviderInfo; +import android.widget.RemoteViews; + +/** {@hide} */ +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/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl new file mode 100644 index 0000000..496aa1a --- /dev/null +++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.appwidget; + +import android.content.ComponentName; +import android.appwidget.AppWidgetProviderInfo; +import com.android.internal.appwidget.IAppWidgetHost; +import android.widget.RemoteViews; + +/** {@hide} */ +interface IAppWidgetService { + + // + // for AppWidgetHost + // + int[] startListening(IAppWidgetHost host, String packageName, int hostId, + out List updatedViews); + void stopListening(int hostId); + int allocateAppWidgetId(String packageName, int hostId); + void deleteAppWidgetId(int appWidgetId); + void deleteHost(int hostId); + void deleteAllHosts(); + RemoteViews getAppWidgetViews(int appWidgetId); + + // + // for AppWidgetManager + // + void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views); + void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views); + List 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/appwidget/package.html b/core/java/com/android/internal/appwidget/package.html new file mode 100644 index 0000000..db6f78b --- /dev/null +++ b/core/java/com/android/internal/appwidget/package.html @@ -0,0 +1,3 @@ + +{@hide} + \ No newline at end of file diff --git a/core/java/com/android/internal/gadget/IGadgetHost.aidl b/core/java/com/android/internal/gadget/IGadgetHost.aidl deleted file mode 100644 index e7b5a1e..0000000 --- a/core/java/com/android/internal/gadget/IGadgetHost.aidl +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.gadget; - -import android.content.ComponentName; -import android.gadget.GadgetProviderInfo; -import android.widget.RemoteViews; - -/** {@hide} */ -oneway interface IGadgetHost { - void updateGadget(int gadgetId, in RemoteViews views); - void providerChanged(int gadgetId, in GadgetProviderInfo info); -} - diff --git a/core/java/com/android/internal/gadget/IGadgetService.aidl b/core/java/com/android/internal/gadget/IGadgetService.aidl deleted file mode 100644 index 9c66b95..0000000 --- a/core/java/com/android/internal/gadget/IGadgetService.aidl +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.gadget; - -import android.content.ComponentName; -import android.gadget.GadgetProviderInfo; -import com.android.internal.gadget.IGadgetHost; -import android.widget.RemoteViews; - -/** {@hide} */ -interface IGadgetService { - - // - // for GadgetHost - // - int[] startListening(IGadgetHost host, String packageName, int hostId, - out List updatedViews); - void stopListening(int hostId); - int allocateGadgetId(String packageName, int hostId); - void deleteGadgetId(int gadgetId); - void deleteHost(int hostId); - void deleteAllHosts(); - RemoteViews getGadgetViews(int gadgetId); - - // - // for GadgetManager - // - void updateGadgetIds(in int[] gadgetIds, in RemoteViews views); - void updateGadgetProvider(in ComponentName provider, in RemoteViews views); - List getInstalledProviders(); - GadgetProviderInfo getGadgetInfo(int gadgetId); - void bindGadgetId(int gadgetId, in ComponentName provider); - int[] getGadgetIds(in ComponentName provider); - -} - diff --git a/core/java/com/android/internal/gadget/package.html b/core/java/com/android/internal/gadget/package.html deleted file mode 100644 index db6f78b..0000000 --- a/core/java/com/android/internal/gadget/package.html +++ /dev/null @@ -1,3 +0,0 @@ - -{@hide} - \ No newline at end of file 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); } } -- cgit v1.1