summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/LauncherActivity.java17
-rw-r--r--core/java/android/app/SearchDialog.java8
-rw-r--r--core/java/android/content/Intent.java2
-rw-r--r--core/java/android/gadget/GadgetHost.java24
-rw-r--r--core/java/android/gadget/GadgetHostView.java206
-rw-r--r--core/java/android/gadget/GadgetManager.java170
-rwxr-xr-xcore/java/android/gadget/GadgetProvider.java53
-rw-r--r--core/java/android/gadget/GadgetProviderInfo.aidl (renamed from core/java/android/gadget/GadgetInfo.aidl)2
-rw-r--r--core/java/android/gadget/GadgetProviderInfo.java (renamed from core/java/android/gadget/GadgetInfo.java)58
-rw-r--r--core/java/android/gadget/package.html123
-rw-r--r--core/java/android/hardware/Camera.java17
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java85
-rwxr-xr-xcore/java/android/inputmethodservice/KeyboardView.java4
-rw-r--r--core/java/android/net/UrlQuerySanitizer.java126
-rw-r--r--core/java/android/pim/RecurrenceSet.java1
-rw-r--r--core/java/android/preference/PreferenceGroupAdapter.java3
-rw-r--r--core/java/android/provider/Settings.java22
-rw-r--r--core/java/android/server/BluetoothDeviceService.java61
-rw-r--r--core/java/android/server/BluetoothEventLoop.java83
-rw-r--r--core/java/android/text/method/ArrowKeyMovementMethod.java9
-rw-r--r--core/java/android/text/method/MetaKeyKeyListener.java8
-rw-r--r--core/java/android/text/method/PasswordTransformationMethod.java6
-rw-r--r--core/java/android/view/HapticFeedbackConstants.java45
-rw-r--r--core/java/android/view/IWindowSession.aidl3
-rw-r--r--core/java/android/view/View.java105
-rw-r--r--core/java/android/view/ViewRoot.java15
-rw-r--r--core/java/android/view/WindowManager.java2
-rw-r--r--core/java/android/view/WindowManagerPolicy.java11
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java30
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java26
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java33
-rw-r--r--core/java/android/webkit/CookieManager.java9
-rw-r--r--core/java/android/webkit/WebView.java126
-rw-r--r--core/java/android/webkit/WebViewCore.java6
-rw-r--r--core/java/android/webkit/WebViewDatabase.java47
-rw-r--r--core/java/android/widget/AbsListView.java4
-rw-r--r--core/java/android/widget/CursorFilter.java5
-rw-r--r--core/java/android/widget/DatePicker.java27
-rw-r--r--core/java/android/widget/Filter.java6
-rw-r--r--core/java/android/widget/Gallery.java6
-rw-r--r--core/java/android/widget/ImageView.java1
-rw-r--r--core/java/android/widget/RemoteViews.java69
-rw-r--r--core/java/android/widget/TextView.java2
-rw-r--r--core/java/android/widget/ViewAnimator.java3
-rw-r--r--core/java/android/widget/ViewFlipper.java4
-rw-r--r--core/java/android/widget/ZoomButton.java2
-rw-r--r--core/java/android/widget/ZoomRing.java271
-rw-r--r--core/java/android/widget/ZoomRingController.java64
-rw-r--r--core/java/com/android/internal/gadget/IGadgetHost.aidl3
-rw-r--r--core/java/com/android/internal/gadget/IGadgetService.aidl6
-rw-r--r--core/java/com/android/internal/view/IInputConnectionWrapper.java24
-rw-r--r--core/java/com/android/internal/view/IInputContext.aidl6
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl2
-rw-r--r--core/java/com/android/internal/view/InputConnectionWrapper.java13
-rw-r--r--core/java/com/android/internal/widget/NumberPicker.java62
55 files changed, 1430 insertions, 696 deletions
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index c363f04..d6fcbb1 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -18,7 +18,6 @@ package android.app;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
@@ -31,9 +30,7 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -145,17 +142,6 @@ public abstract class LauncherActivity extends ListActivity {
return view;
}
- private char getCandidateLetter(ResolveInfo info) {
- PackageManager pm = LauncherActivity.this.getPackageManager();
- CharSequence label = info.loadLabel(pm);
-
- if (label == null) {
- label = info.activityInfo.name;
- }
-
- return Character.toLowerCase(label.charAt(0));
- }
-
private void bindView(View view, ListItem item) {
TextView text = (TextView) view;
text.setText(item.label);
@@ -191,7 +177,6 @@ public abstract class LauncherActivity extends ListActivity {
results.count = list.size();
}
} else {
- final PackageManager pm = LauncherActivity.this.getPackageManager();
final String prefixString = prefix.toString().toLowerCase();
ArrayList<ListItem> values = mOriginalValues;
@@ -243,8 +228,6 @@ public abstract class LauncherActivity extends ListActivity {
private int mIconWidth = -1;
private int mIconHeight = -1;
- private final Paint mPaint = new Paint();
- private final Rect mBounds = new Rect();
private final Rect mOldBounds = new Rect();
private Canvas mCanvas = new Canvas();
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 64f1ba2..5744ddc 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -835,9 +835,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// also guard against possible race conditions (late arrival after dismiss)
if (mSearchable != null) {
handled = doSuggestionsKey(v, keyCode, event);
- if (!handled) {
- handled = refocusingKeyListener(v, keyCode, event);
- }
}
return handled;
}
@@ -1024,6 +1021,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* @param jamQuery True means to set the query, false means to reset it to the user's choice
*/
private void jamSuggestionQuery(boolean jamQuery, AdapterView<?> parent, int position) {
+ // quick check against race conditions
+ if (mSearchable == null) {
+ return;
+ }
+
mSuggestionsAdapter.setNonUserQuery(true); // disables any suggestions processing
if (jamQuery) {
CursorAdapter ca = getSuggestionsAdapter(parent);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 52aae0d..c4d3f9d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1112,6 +1112,8 @@ public class Intent implements Parcelable {
* <p>My include the following extras:
* <ul>
* <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package.
+ * <li> {@link #EXTRA_REPLACING} is set to true if this is following
+ * an {@link #ACTION_PACKAGE_REMOVED} broadcast for the same package.
* </ul>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
diff --git a/core/java/android/gadget/GadgetHost.java b/core/java/android/gadget/GadgetHost.java
index 9176d18..31aed32 100644
--- a/core/java/android/gadget/GadgetHost.java
+++ b/core/java/android/gadget/GadgetHost.java
@@ -38,6 +38,7 @@ import com.android.internal.gadget.IGadgetService;
public class GadgetHost {
static final int HANDLE_UPDATE = 1;
+ static final int HANDLE_PROVIDER_CHANGED = 2;
static Object sServiceLock = new Object();
static IGadgetService sService;
@@ -52,6 +53,13 @@ public class GadgetHost {
msg.obj = views;
msg.sendToTarget();
}
+
+ public void providerChanged(int gadgetId, GadgetProviderInfo info) {
+ Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED);
+ msg.arg1 = gadgetId;
+ msg.obj = info;
+ msg.sendToTarget();
+ }
}
Handler mHandler = new Handler() {
@@ -61,6 +69,10 @@ public class GadgetHost {
updateGadgetView(msg.arg1, (RemoteViews)msg.obj);
break;
}
+ case HANDLE_PROVIDER_CHANGED: {
+ onProviderChanged(msg.arg1, (GadgetProviderInfo)msg.obj);
+ break;
+ }
}
}
};
@@ -183,7 +195,8 @@ public class GadgetHost {
}
}
- public final GadgetHostView createView(Context context, int gadgetId, GadgetInfo gadget) {
+ public final GadgetHostView createView(Context context, int gadgetId,
+ GadgetProviderInfo gadget) {
GadgetHostView view = onCreateView(context, gadgetId, gadget);
view.setGadget(gadgetId, gadget);
synchronized (mViews) {
@@ -203,9 +216,16 @@ public class GadgetHost {
* Called to create the GadgetHostView. Override to return a custom subclass if you
* need it. {@more}
*/
- protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetInfo gadget) {
+ 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;
diff --git a/core/java/android/gadget/GadgetHostView.java b/core/java/android/gadget/GadgetHostView.java
index d92c123..a985bd4 100644
--- a/core/java/android/gadget/GadgetHostView.java
+++ b/core/java/android/gadget/GadgetHostView.java
@@ -18,7 +18,6 @@ package android.gadget;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.gadget.GadgetInfo;
import android.graphics.Color;
import android.util.Config;
import android.util.Log;
@@ -26,12 +25,18 @@ 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;
import android.widget.ViewAnimator;
-public class GadgetHostView extends ViewAnimator {
+/**
+ * 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 ViewAnimator implements Animation.AnimationListener {
static final String TAG = "GadgetHostView";
static final boolean LOGD = Config.LOGD || true;
@@ -42,57 +47,93 @@ public class GadgetHostView extends ViewAnimator {
return clazz.isAnnotationPresent(RemoteViews.RemoteView.class);
}
};
-
+
+ Context mLocalContext;
+
int mGadgetId;
- GadgetInfo mInfo;
- View mActiveView;
- View mStaleView;
+ GadgetProviderInfo mInfo;
- protected int mDefaultGravity = Gravity.CENTER;
-
+ View mActiveView = null;
+ View mStaleView = null;
+
+ int mActiveLayoutId = -1;
+ int mStaleLayoutId = -1;
+
+ /**
+ * Last set of {@link RemoteViews} applied to {@link #mActiveView}
+ */
+ RemoteViews mActiveActions = null;
+
+ /**
+ * Flag indicating that {@link #mActiveActions} has been applied to
+ * {@link #mStaleView}, meaning it's readyto recycle.
+ */
+ boolean mStalePrepared = false;
+
+ /**
+ * Create a host view. Uses default fade animations.
+ */
public GadgetHostView(Context context) {
- super(context);
+ this(context, android.R.anim.fade_in, android.R.anim.fade_out);
}
- public void setGadget(int gadgetId, GadgetInfo info) {
- if (LOGD) Log.d(TAG, "setGadget is incoming with info=" + info);
+ /**
+ * 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);
+ mLocalContext = context;
+
+ // Prepare our default transition animations
+ setAnimateFirstView(true);
+ setInAnimation(context, animationIn);
+ setOutAnimation(context, animationOut);
+
+ // Watch for animation events to prepare recycling
+ Animation inAnimation = getInAnimation();
+ if (inAnimation != null) {
+ inAnimation.setAnimationListener(this);
+ }
+ }
+
+ /**
+ * Set the gadget that will be displayed by this view.
+ */
+ public void setGadget(int gadgetId, GadgetProviderInfo info) {
if (mInfo != null) {
// TODO: remove the old view, or whatever
}
mGadgetId = gadgetId;
mInfo = info;
-
- View defaultView = getDefaultView();
- flipUpdate(defaultView);
}
- /**
- * Trigger actual animation between current and new content in the
- * {@link ViewAnimator}.
- */
- protected void flipUpdate(View newContent) {
- if (LOGD) Log.d(TAG, "pushing an update to surface");
-
- // Take requested dimensions from parent, but apply default gravity.
- ViewGroup.LayoutParams requested = newContent.getLayoutParams();
- if (requested == null) {
- requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT,
- LayoutParams.FILL_PARENT);
+ public int getGadgetId() {
+ return mGadgetId;
+ }
+
+ public GadgetProviderInfo getGadgetInfo() {
+ return mInfo;
+ }
+
+ public void onAnimationEnd(Animation animation) {
+ // When our transition animation finishes, we should try bringing our
+ // newly-stale view up to the current view.
+ if (mActiveActions != null &&
+ mStaleLayoutId == mActiveActions.getLayoutId()) {
+ if (LOGD) Log.d(TAG, "after animation, layoutId matched so we're recycling old view");
+ mActiveActions.reapply(mLocalContext, mStaleView);
+ mStalePrepared = true;
}
-
- FrameLayout.LayoutParams params =
- new FrameLayout.LayoutParams(requested.width, requested.height);
- params.gravity = mDefaultGravity;
- newContent.setLayoutParams(params);
-
- // Add new content and animate to it
- addView(newContent);
- showNext();
-
- // Dispose old stale view
- removeView(mStaleView);
- mStaleView = mActiveView;
- mActiveView = newContent;
+ }
+
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ public void onAnimationStart(Animation animation) {
}
/**
@@ -100,26 +141,42 @@ public class GadgetHostView extends ViewAnimator {
* gadget provider. Will animate into these new views as needed.
*/
public void updateGadget(RemoteViews remoteViews) {
- if (LOGD) Log.d(TAG, "updateGadget() with remoteViews = " + remoteViews);
+ if (LOGD) Log.d(TAG, "updateGadget called");
+ boolean recycled = false;
View newContent = null;
Exception exception = null;
- try {
- if (remoteViews == null) {
- // there is no remoteViews (yet), so use the initial layout
- newContent = getDefaultView();
- } else {
- // use the RemoteViews
- // TODO: try applying RemoteViews to existing staleView if available
- newContent = remoteViews.apply(mContext, this);
+ if (remoteViews == null) {
+ newContent = getDefaultView();
+ }
+
+ // If our stale view has been prepared to match active, and the new
+ // layout matches, try recycling it
+ if (newContent == null && mStalePrepared &&
+ remoteViews.getLayoutId() == mStaleLayoutId) {
+ try {
+ remoteViews.reapply(mLocalContext, mStaleView);
+ newContent = mStaleView;
+ recycled = true;
+ if (LOGD) Log.d(TAG, "was able to recycled existing layout");
+ } catch (RuntimeException e) {
+ exception = e;
+ }
+ }
+
+ // Try normal RemoteView inflation
+ if (newContent == null) {
+ try {
+ newContent = remoteViews.apply(mLocalContext, this);
+ if (LOGD) Log.d(TAG, "had to inflate new layout");
+ } catch (RuntimeException e) {
+ exception = e;
}
- } catch (RuntimeException e) {
- exception = e;
}
if (exception != null && LOGD) {
- Log.w(TAG, "Error inflating gadget " + mInfo, exception);
+ Log.w(TAG, "Error inflating gadget " + getGadgetInfo(), exception);
}
if (newContent == null) {
@@ -128,8 +185,44 @@ public class GadgetHostView extends ViewAnimator {
if (LOGD) Log.d(TAG, "updateGadget couldn't find any view, so inflating error");
newContent = getErrorView();
}
-
- flipUpdate(newContent);
+
+ if (!recycled) {
+ prepareView(newContent);
+ addView(newContent);
+ }
+
+ showNext();
+
+ if (!recycled) {
+ removeView(mStaleView);
+ }
+
+ mStalePrepared = false;
+ mActiveActions = remoteViews;
+
+ mStaleView = mActiveView;
+ mActiveView = newContent;
+
+ mStaleLayoutId = mActiveLayoutId;
+ mActiveLayoutId = (remoteViews == null) ? -1 : remoteViews.getLayoutId();
+ }
+
+ /**
+ * 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);
}
/**
@@ -141,7 +234,7 @@ public class GadgetHostView extends ViewAnimator {
try {
if (mInfo != null) {
- Context theirContext = mContext.createPackageContext(
+ Context theirContext = mLocalContext.createPackageContext(
mInfo.provider.getPackageName(), 0 /* no flags */);
LayoutInflater inflater = (LayoutInflater)
theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -173,11 +266,10 @@ public class GadgetHostView extends ViewAnimator {
* Inflate and return a view that represents an error state.
*/
protected View getErrorView() {
- TextView tv = new TextView(mContext);
+ TextView tv = new TextView(mLocalContext);
// TODO: move this error string and background color into resources
tv.setText("Error inflating gadget");
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
index 20f4014..a9a2c80 100644
--- a/core/java/android/gadget/GadgetManager.java
+++ b/core/java/android/gadget/GadgetManager.java
@@ -38,68 +38,142 @@ public class GadgetManager {
static final String TAG = "GadgetManager";
/**
- * Send this when you want to pick a gadget to display.
+ * Send this from your gadget host activity when you want to pick a gadget to display.
+ * The gadget picker activity will be launched.
+ * <p>
+ * You must supply the following extras:
+ * <table>
+ * <tr>
+ * <td>{@link #EXTRA_GADGET_ID}</td>
+ * <td>A newly allocated gadgetId, which will be bound to the gadget provider
+ * once the user has selected one.</td>
+ * </tr>
+ * </table>
*
* <p>
* The system will respond with an onActivityResult call with the following extras in
* the intent:
- * <ul>
- * <li><b>gadgetIds</b></li>
- * <li><b>hostId</b></li>
- * </ul>
- * TODO: Add constants for these.
- * TODO: Where does this go?
+ * <table>
+ * <tr>
+ * <td>{@link #EXTRA_GADGET_ID}</td>
+ * <td>The gadgetId that you supplied in the original intent.</td>
+ * </tr>
+ * </table>
+ * <p>
+ * When you receive the result from the gadget pick activity, if the resultCode is
+ * {@link android.app.Activity#RESULT_OK}, a gadget has been selected. You should then
+ * check the GadgetProviderInfo for the returned gadget, and if it has one, launch its configuration
+ * activity. If {@link android.app.Activity#RESULT_CANCELED} is returned, you should delete
+ * the gadgetId.
+ *
+ * @see #ACTION_GADGET_CONFIGURE
+ */
+ public static final String ACTION_GADGET_PICK = "android.gadget.action.GADGET_PICK";
+
+ /**
+ * Sent when it is time to configure your gadget while it is being added to a host.
+ * This action is not sent as a broadcast to the gadget provider, but as a startActivity
+ * to the activity specified in the {@link GadgetProviderInfo GadgetProviderInfo meta-data}.
+ *
+ * <p>
+ * The intent will contain the following extras:
+ * <table>
+ * <tr>
+ * <td>{@link #EXTRA_GADGET_ID}</td>
+ * <td>The gadgetId to configure.</td>
+ * </tr>
+ * </table>
+ *
+ * <p>If you return {@link android.app.Activity#RESULT_OK} using
+ * {@link android.app.Activity#setResult Activity.setResult()}, the gadget will be added,
+ * and you will receive an {@link #ACTION_GADGET_UPDATE} broadcast for this gadget.
+ * If you return {@link android.app.Activity#RESULT_CANCELED}, the host will cancel the add
+ * and not display this gadget, and you will receive a {@link #ACTION_GADGET_DELETED} broadcast.
*/
- public static final String GADGET_PICK_ACTION = "android.gadget.action.PICK_GADGET";
+ public static final String ACTION_GADGET_CONFIGURE = "android.gadget.action.GADGET_CONFIGURE";
+ /**
+ * An intent extra that contains one gadgetId.
+ * <p>
+ * The value will be an int that can be retrieved like this:
+ * {@sample frameworks/base/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/GadgetHostActivity.java getExtra_EXTRA_GADGET_ID}
+ */
public static final String EXTRA_GADGET_ID = "gadgetId";
+
+ /**
+ * An intent extra that contains multiple gadgetIds.
+ * <p>
+ * The value will be an int array that can be retrieved like this:
+ * {@sample frameworks/base/tests/gadgets/GadgetHostTest/src/com/android/tests/gadgethost/TestGadgetProvider.java getExtra_EXTRA_GADGET_IDS}
+ */
public static final String EXTRA_GADGET_IDS = "gadgetIds";
- public static final String EXTRA_HOST_ID = "hostId";
+
+ /**
+ * A sentiel value that the gadget manager will never return as a gadgetId.
+ */
+ public static final int INVALID_GADGET_ID = 0;
/**
* Sent when it is time to update your gadget.
*
* <p>This may be sent in response to a new instance for this gadget provider having
- * been instantiated, the requested {@link GadgetInfo#updatePeriodMillis update interval}
+ * been instantiated, the requested {@link GadgetProviderInfo#updatePeriodMillis update interval}
* having lapsed, or the system booting.
- */
- public static final String GADGET_UPDATE_ACTION = "android.gadget.action.GADGET_UPDATE";
-
- /**
- * Sent when it is time to configure your gadget. This action is not sent as a broadcast
- * to the gadget provider, but as a startActivity to the activity specified in the
- * {@link GadgetInfo GadgetInfo meta-data}.
*
- * <p>The {@link #EXTRA_GADGET_ID} extra contains the gadget ID.
+ * <p>
+ * The intent will contain the following extras:
+ * <table>
+ * <tr>
+ * <td>{@link #EXTRA_GADGET_IDS}</td>
+ * <td>The gadgetIds to update. This may be all of the gadgets created for this
+ * provider, or just a subset. The system tries to send updates for as few gadget
+ * instances as possible.</td>
+ * </tr>
+ * </table>
+ *
+ * @see GadgetProvider#onUpdate GadgetProvider.onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds)
*/
- public static final String GADGET_CONFIGURE_ACTION = "android.gadget.action.GADGET_CONFIGURE";
+ public static final String ACTION_GADGET_UPDATE = "android.gadget.action.GADGET_UPDATE";
/**
- * Sent when the 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.
+ * 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 GADGET_ENABLED_ACTION = "android.gadget.action.GADGET_ENABLED";
+ public static final String ACTION_GADGET_DELETED = "android.gadget.action.GADGET_DELETED";
/**
- * Sent when an instances of a gadget is deleted from the host.
+ * Sent when an instance of a gadget is removed from the last host.
+ *
+ * @see GadgetProvider#onEnabled GadgetProvider.onEnabled(Context context)
*/
- public static final String GADGET_DELETED_ACTION = "android.gadget.action.GADGET_DELETED";
+ public static final String ACTION_GADGET_DISABLED = "android.gadget.action.GADGET_DISABLED";
/**
- * Sent when the gadget is removed from the last host.
+ * 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 GADGET_DISABLED_ACTION = "android.gadget.action.GADGET_DISABLED";
+ 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 GADGET_PROVIDER_META_DATA = "android.gadget.provider";
+ public static final String META_DATA_GADGET_PROVIDER = "android.gadget.provider";
static WeakHashMap<Context, WeakReference<GadgetManager>> sManagerCache = new WeakHashMap();
static IGadgetService sService;
Context mContext;
+ /**
+ * Get the GadgetManager instance to use for the supplied {@link android.content.Context
+ * Context} object.
+ */
public static GadgetManager getInstance(Context context) {
synchronized (sManagerCache) {
if (sService == null) {
@@ -125,9 +199,11 @@ public class GadgetManager {
}
/**
- * Call this with the new RemoteViews for your gadget whenever you need to.
+ * Set the RemoteViews to use for the specified gadgetIds.
*
* <p>
+ * It is okay to call this method both inside an {@link #ACTION_GADGET_UPDATE} broadcast,
+ * and outside of the handler.
* This method will only work when called from the uid that owns the gadget provider.
*
* @param gadgetIds The gadget instances for which to set the RemoteViews.
@@ -143,9 +219,26 @@ public class GadgetManager {
}
/**
- * Call this with the new RemoteViews for your gadget whenever you need to.
+ * Set the RemoteViews to use for the specified gadgetId.
*
* <p>
+ * It is okay to call this method both inside an {@link #ACTION_GADGET_UPDATE} broadcast,
+ * and outside of the handler.
+ * This method will only work when called from the uid that owns the gadget provider.
+ *
+ * @param gadgetId The gadget instance for which to set the RemoteViews.
+ * @param views The RemoteViews object to show.
+ */
+ public void updateGadget(int gadgetId, RemoteViews views) {
+ updateGadget(new int[] { gadgetId }, views);
+ }
+
+ /**
+ * Set the RemoteViews to use for all gadget instances for the supplied gadget provider.
+ *
+ * <p>
+ * It is okay to call this method both inside an {@link #ACTION_GADGET_UPDATE} broadcast,
+ * and outside of the handler.
* This method will only work when called from the uid that owns the gadget provider.
*
* @param provider The {@link ComponentName} for the {@link
@@ -165,7 +258,7 @@ public class GadgetManager {
/**
* Return a list of the gadget providers that are currently installed.
*/
- public List<GadgetInfo> getInstalledProviders() {
+ public List<GadgetProviderInfo> getInstalledProviders() {
try {
return sService.getInstalledProviders();
}
@@ -175,12 +268,12 @@ public class GadgetManager {
}
/**
- * Get the available info about the gadget. If the gadgetId has not been bound yet,
- * this method will return null.
+ * Get the available info about the gadget.
*
- * TODO: throws GadgetNotFoundException ??? if not valid
+ * @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 GadgetInfo getGadgetInfo(int gadgetId) {
+ public GadgetProviderInfo getGadgetInfo(int gadgetId) {
try {
return sService.getGadgetInfo(gadgetId);
}
@@ -190,7 +283,14 @@ public class GadgetManager {
}
/**
- * Set the component for a given gadgetId. You need the GADGET_LIST permission.
+ * Set the component for a given gadgetId.
+ *
+ * <p class="note">You need the GADGET_LIST permission. This method is to be used by the
+ * gadget picker.
+ *
+ * @param gadgetId The gadget instance for which to set the RemoteViews.
+ * @param provider The {@link android.content.BroadcastReceiver} that will be the gadget
+ * provider for this gadget.
*/
public void bindGadgetId(int gadgetId, ComponentName provider) {
try {
diff --git a/core/java/android/gadget/GadgetProvider.java b/core/java/android/gadget/GadgetProvider.java
index 1ddfe3f..7e10e78 100755
--- a/core/java/android/gadget/GadgetProvider.java
+++ b/core/java/android/gadget/GadgetProvider.java
@@ -55,7 +55,7 @@ public class GadgetProvider extends BroadcastReceiver {
// 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.GADGET_UPDATE_ACTION.equals(action)) {
+ if (GadgetManager.ACTION_GADGET_UPDATE.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS);
@@ -64,7 +64,7 @@ public class GadgetProvider extends BroadcastReceiver {
}
}
}
- else if (GadgetManager.GADGET_DELETED_ACTION.equals(action)) {
+ else if (GadgetManager.ACTION_GADGET_DELETED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS);
@@ -73,102 +73,81 @@ public class GadgetProvider extends BroadcastReceiver {
}
}
}
- else if (GadgetManager.GADGET_ENABLED_ACTION.equals(action)) {
+ else if (GadgetManager.ACTION_GADGET_ENABLED.equals(action)) {
this.onEnabled(context);
}
- else if (GadgetManager.GADGET_DISABLED_ACTION.equals(action)) {
+ else if (GadgetManager.ACTION_GADGET_DISABLED.equals(action)) {
this.onDisabled(context);
}
}
// END_INCLUDE(onReceive)
/**
- * Called in response to the {@link GadgetManager#GADGET_UPDATE_ACTION} broadcast when
+ * 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}
- * <p class="note">If you want this method called, you must declare in an intent-filter in
- * your AndroidManifest.xml file that you accept the GADGET_UPDATE_ACTION intent action.
- * For example:
- * <font color=red>TODO: SAMPLE CODE GOES HERE</font>
- * </p>
*
* @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#updateGadgets} on.
+ * 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#GADGET_UPDATE_ACTION
+ * @see GadgetManager#ACTION_GADGET_UPDATE
*/
public void onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds) {
}
/**
- * Called in response to the {@link GadgetManager#GADGET_DELETED_ACTION} broadcast when
+ * 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}
- * <p class="note">If you want this method called, you must declare in an intent-filter in
- * your AndroidManifest.xml file that you accept the GADGET_DELETED_ACTION intent action.
- * For example:
- * <font color=red>TODO: SAMPLE CODE GOES HERE</font>
- * </p>
*
* @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#GADGET_DELETED_ACTION
+ * @see GadgetManager#ACTION_GADGET_DELETED
*/
public void onDeleted(Context context, int[] gadgetIds) {
}
/**
- * Called in response to the {@link GadgetManager#GADGET_ENABLED_ACTION} broadcast when
+ * 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#GADGET_DISABLED_ACTION} is sent and {@link #onDisabled}
- * is called. If after that, a gadget for this provider is created again, onEnabled() will
- * be called again.
+ * {@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.
*
- * <p class="note">If you want this method called, you must declare in an intent-filter in
- * your AndroidManifest.xml file that you accept the GADGET_ENABLED_ACTION intent action.
- * For example:
- * <font color=red>TODO: SAMPLE CODE GOES HERE</font>
- * </p>
- *
* @param context The {@link android.content.Context Context} in which this receiver is
* running.
*
- * @see GadgetManager#GADGET_ENABLED_ACTION
+ * @see GadgetManager#ACTION_GADGET_ENABLED
*/
public void onEnabled(Context context) {
}
/**
- * Called in response to the {@link GadgetManager#GADGET_DISABLED_ACTION} broadcast, which
+ * 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}
- * <p class="note">If you want this method called, you must declare in an intent-filter in
- * your AndroidManifest.xml file that you accept the GADGET_DISABLED_ACTION intent action.
- * For example:
- * <font color=red>TODO: SAMPLE CODE GOES HERE</font>
- * </p>
*
* @param context The {@link android.content.Context Context} in which this receiver is
* running.
*
- * @see GadgetManager#GADGET_DISABLED_ACTION
+ * @see GadgetManager#ACTION_GADGET_DISABLED
*/
public void onDisabled(Context context) {
}
diff --git a/core/java/android/gadget/GadgetInfo.aidl b/core/java/android/gadget/GadgetProviderInfo.aidl
index 7231545..589f886 100644
--- a/core/java/android/gadget/GadgetInfo.aidl
+++ b/core/java/android/gadget/GadgetProviderInfo.aidl
@@ -16,4 +16,4 @@
package android.gadget;
-parcelable GadgetInfo;
+parcelable GadgetProviderInfo;
diff --git a/core/java/android/gadget/GadgetInfo.java b/core/java/android/gadget/GadgetProviderInfo.java
index 5ac3da9..95c0432 100644
--- a/core/java/android/gadget/GadgetInfo.java
+++ b/core/java/android/gadget/GadgetProviderInfo.java
@@ -21,60 +21,88 @@ import android.os.Parcelable;
import android.content.ComponentName;
/**
- * Describes the meta data for an installed gadget.
+ * Describes the meta data for an installed gadget provider. The fields in this class
+ * correspond to the fields in the <code>&lt;gadget-provider&gt;</code> xml tag.
*/
-public class GadgetInfo implements Parcelable {
+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}.
+ *
+ * <p>This field corresponds to the <code>android:name</code> attribute in
+ * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
*/
public ComponentName provider;
/**
* Minimum width of the gadget, in dp.
+ *
+ * <p>This field corresponds to the <code>android:minWidth</code> attribute in
+ * the gadget meta-data file.
*/
public int minWidth;
/**
* Minimum height of the gadget, in dp.
+ *
+ * <p>This field corresponds to the <code>android:minHeight</code> 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.
+ *
+ * <p>This field corresponds to the <code>android:updatePeriodMillis</code> 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.
+ *
+ * <p>This field corresponds to the <code>android:initialLayout</code> attribute in
+ * the gadget meta-data file.
*/
public int initialLayout;
/**
* The activity to launch that will configure the gadget.
+ *
+ * <p>This class name of field corresponds to the <code>android:configure</code> attribute in
+ * the gadget meta-data file. The package name always corresponds to the package containing
+ * the gadget provider.
*/
public ComponentName configure;
/**
- * The label to display to the user.
+ * The label to display to the user in the gadget picker. If not supplied in the
+ * xml, the application label will be used.
+ *
+ * <p>This field corresponds to the <code>android:label</code> attribute in
+ * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
*/
public String label;
/**
- * The icon to display for this gadget in the picker list.
+ * The icon to display for this gadget in the gadget picker. If not supplied in the
+ * xml, the application icon will be used.
+ *
+ * <p>This field corresponds to the <code>android:icon</code> attribute in
+ * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
*/
public int icon;
- public GadgetInfo() {
+ public GadgetProviderInfo() {
}
/**
- * Unflatten the GadgetInfo from a parcel.
+ * Unflatten the GadgetProviderInfo from a parcel.
*/
- public GadgetInfo(Parcel in) {
+ public GadgetProviderInfo(Parcel in) {
if (0 != in.readInt()) {
this.provider = new ComponentName(in);
}
@@ -116,24 +144,24 @@ public class GadgetInfo implements Parcelable {
}
/**
- * Parcelable.Creator that instantiates GadgetInfo objects
+ * Parcelable.Creator that instantiates GadgetProviderInfo objects
*/
- public static final Parcelable.Creator<GadgetInfo> CREATOR
- = new Parcelable.Creator<GadgetInfo>()
+ public static final Parcelable.Creator<GadgetProviderInfo> CREATOR
+ = new Parcelable.Creator<GadgetProviderInfo>()
{
- public GadgetInfo createFromParcel(Parcel parcel)
+ public GadgetProviderInfo createFromParcel(Parcel parcel)
{
- return new GadgetInfo(parcel);
+ return new GadgetProviderInfo(parcel);
}
- public GadgetInfo[] newArray(int size)
+ public GadgetProviderInfo[] newArray(int size)
{
- return new GadgetInfo[size];
+ return new GadgetProviderInfo[size];
}
};
public String toString() {
- return "GadgetInfo(provider=" + this.provider + ")";
+ return "GadgetProviderInfo(provider=" + this.provider + ")";
}
}
diff --git a/core/java/android/gadget/package.html b/core/java/android/gadget/package.html
index 4b8b9d9..4c04396 100644
--- a/core/java/android/gadget/package.html
+++ b/core/java/android/gadget/package.html
@@ -1,41 +1,126 @@
<body>
-{@hide}
<p>Android allows applications to publish views to be embedded in other applications. These
views are called gadgets, and are published by "gadget providers." The component that can
-contain gadgets is called a "gadget host." See the links below for more information.
+contain gadgets is called a "gadget host."
</p>
-<h3><a href="{@toroot}reference/android/gadget/package-descr.html#providers">Gadget Providers</a></h3>
+<h3><a href="package-descr.html#providers">Gadget Providers</a></h3>
<ul>
- <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_manifest">Declaring a gadget in the AndroidManifest</a></li>
- <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_meta_data">Adding the {@link android.gadget.GadgetInfo GadgetInfo} meta-data</a></li>
- <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_GadgetProvider">Using the {@link android.gadget.GadgetProvider GadgetProvider} class</a></li>
- <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_configuration">Gadget Configuration UI</a></li>
- <li><a href="{@toroot}reference/android/gadget/package-descr.html#provider_broadcasts">Gadget Broadcast Intents</a></li>
-</ul>
-<h3><a href="{@toroot}reference/android/gadget/package-descr.html#">Gadget Hosts</a></h3>
-<ul>
- <li><a href="{@toroot}reference/android/gadget/package-descr.html#">asdf</a></li>
+ <li><a href="package-descr.html#provider_manifest">Declaring a gadget in the AndroidManifest</a></li>
+ <li><a href="package-descr.html#provider_meta_data">Adding the GadgetProviderInfo meta-data</a></li>
+ <li><a href="package-descr.html#provider_GadgetProvider">Using the GadgetProvider class</a></li>
+ <li><a href="package-descr.html#provider_configuration">Gadget Configuration UI</a></li>
+ <li><a href="package-descr.html#provider_broadcasts">Gadget Broadcast Intents</a></li>
</ul>
+<h3><a href="package-descr.html#">Gadget Hosts</a></h3>
+
+
{@more}
+
+
<h2><a name="providers"></a>Gadget Providers</h2>
-<p>Any application can publish gadgets. All an application needs to do to publish a gadget is
+<p>
+Any application can publish gadgets. All an application needs to do to publish a gadget is
to have a {@link android.content.BroadcastReceiver} that receives the {@link
-android.gadget.GadgetManager#GADGET_UPDATE_ACTION GadgetManager.GADGET_UPDATE_ACTION} intent,
-and provide some meta-data about the gadget.
+android.gadget.GadgetManager#ACTION_GADGET_UPDATE GadgetManager.ACTION_GADGET_UPDATE} intent,
+and provide some meta-data about the gadget. Android provides the
+{@link android.gadget.GadgetProvider} class, which extends BroadcastReceiver, as a convenience
+class to aid in handling the broadcasts.
<h3><a name="provider_manifest"></a>Declaring a gadget in the AndroidManifest</h3>
-<h3><a name="provider_meta_data"></a>Adding the {@link android.gadget.GadgetInfo GadgetInfo} meta-data</h3>
+<p>
+First, declare the {@link android.content.BroadcastReceiver} in your application's
+<code>AndroidManifest.xml</code> file.
+
+{@sample frameworks/base/tests/gadgets/GadgetHostTest/AndroidManifest.xml GadgetProvider}
+
+<p>
+The <b><code>&lt;receiver&gt;</b> element has the following attributes:
+<ul>
+ <li><b><code>android:name</code> -</b> which specifies the
+ {@link android.content.BroadcastReceiver} or {@link android.gadget.GadgetProvider}
+ class.</li>
+ <li><b><code>android:label</code> -</b> which specifies the string resource that
+ will be shown by the gadget picker as the label.</li>
+ <li><b><code>android:icon</code> -</b> which specifies the drawable resource that
+ will be shown by the gadget picker as the icon.</li>
+</ul>
+
+<p>
+The <b><code>&lt;intent-filter&gt;</b> element tells the {@link android.content.pm.PackageManager}
+that this {@link android.content.BroadcastReceiver} receives the {@link
+android.gadget.GadgetManager#ACTION_GADGET_UPDATE GadgetManager.ACTION_GADGET_UPDATE} broadcast.
+The gadget manager will send other broadcasts directly to your gadget provider as required.
+It is only necessary to explicitly declare that you accept the {@link
+android.gadget.GadgetManager#ACTION_GADGET_UPDATE GadgetManager.ACTION_GADGET_UPDATE} broadcast.
+
+<p>
+The <b><code>&lt;meta-data&gt;</code></b> element tells the gadget manager which xml resource to
+read to find the {@link android.gadget.GadgetProviderInfo} for your gadget provider. It has the following
+attributes:
+<ul>
+ <li><b><code>android:name="android.gadget.provider"</code> -</b> identifies this meta-data
+ as the {@link android.gadget.GadgetProviderInfo} descriptor.</li>
+ <li><b><code>android:resource</code> -</b> is the xml resource to use as that descriptor.</li>
+</ul>
+
+
+<h3><a name="provider_meta_data"></a>Adding the {@link android.gadget.GadgetProviderInfo GadgetProviderInfo} meta-data</h3>
+
+<p>
+For a gadget, the values in the {@link android.gadget.GadgetProviderInfo} structure are supplied
+in an XML resource. In the example above, the xml resource is referenced with
+<code>android:resource="@xml/gadget_info"</code>. That XML file would go in your application's
+directory at <code>res/xml/gadget_info.xml</code>. Here is a simple example.
+
+{@sample frameworks/base/tests/gadgets/GadgetHostTest/res/xml/gadget_info.xml GadgetProviderInfo}
+
+<p>
+The attributes are as documented in the {@link android.gadget.GadgetProviderInfo GagetInfo} class. (86400000 milliseconds means once per day)
+
<h3><a name="provider_GadgetProvider"></a>Using the {@link android.gadget.GadgetProvider GadgetProvider} class</h3>
+<p>The GadgetProvider class is the easiest way to handle the gadget provider intent broadcasts.
+See the <code>src/com/example/android/apis/gadget/ExampleGadgetProvider.java</code>
+sample class in ApiDemos for an example.
+
+<p class="note">Keep in mind that since the the GadgetProvider is a BroadcastReceiver,
+your process is not guaranteed to keep running after the callback methods return. See
+<a href="../../../guide/topics/fundamentals.html#broadlife">Application Fundamentals &gt;
+Broadcast Receiver Lifecycle</a> for more information.
+
+
+
<h3><a name="provider_configuration"></a>Gadget Configuration UI</h3>
+<p>
+Gadget hosts have the ability to start a configuration activity when a gadget is instantiated.
+The activity should be declared as normal in AndroidManifest.xml, and it should be listed in
+the GadgetProviderInfo XML file in the <code>android:configure</code> attribute.
+
+<p>The activity you specified will be launched with the {@link
+android.gadget.GadgetManager#ACTION_GADGET_CONFIGURE} action. See the documentation for that
+action for more info.
+
+<p>See the <code>src/com/example/android/apis/gadget/ExampleGadgetConfigure.java</code>
+sample class in ApiDemos for an example.
+
+
+
<h3><a name="providers_broadcasts"></a>Gadget Broadcast Intents</h3>
-<p>{@link GadgetProvider} is just a convenience class. If you would like to receive the
-gadget broadcasts directly, you can. By way of example, the implementation of
-{@link GadgetProvider.onReceive} is quite simple:</p>
+<p>{@link android.gadget.GadgetProvider} is just a convenience class. If you would like
+to receive the gadget broadcasts directly, you can. The four intents you need to care about are:
+<ul>
+ <li>{@link android.gadget.GadgetManager#ACTION_GADGET_UPDATE}</li>
+ <li>{@link android.gadget.GadgetManager#ACTION_GADGET_DELETED}</li>
+ <li>{@link android.gadget.GadgetManager#ACTION_GADGET_ENABLED}</li>
+ <li>{@link android.gadget.GadgetManager#ACTION_GADGET_DISABLED}</li>
+</ul>
+
+<p>By way of example, the implementation of
+{@link android.gadget.GadgetProvider#onReceive} is quite simple:</p>
{@sample frameworks/base/core/java/android/gadget/GadgetProvider.java onReceive}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index c09567c..40a5b47 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -18,6 +18,7 @@ package android.hardware;
import java.lang.ref.WeakReference;
import java.util.HashMap;
+import java.util.StringTokenizer;
import java.io.IOException;
import android.util.Log;
@@ -494,11 +495,17 @@ public class Camera {
*/
public void unflatten(String flattened) {
mMap.clear();
- String[] pairs = flattened.split(";");
- for (String p : pairs) {
- String[] kv = p.split("=");
- if (kv.length == 2)
- mMap.put(kv[0], kv[1]);
+
+ StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
+ while (tokenizer.hasMoreElements()) {
+ String kv = tokenizer.nextToken();
+ int pos = kv.indexOf('=');
+ if (pos == -1) {
+ continue;
+ }
+ String k = kv.substring(0, pos);
+ String v = kv.substring(pos + 1);
+ mMap.put(k, v);
}
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index ea5f741..c884120 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -206,6 +206,8 @@ public class InputMethodService extends AbstractInputMethodService {
static final String TAG = "InputMethodService";
static final boolean DEBUG = false;
+ InputMethodManager mImm;
+
LayoutInflater mInflater;
View mRootView;
SoftInputWindow mWindow;
@@ -293,6 +295,8 @@ public class InputMethodService extends AbstractInputMethodService {
mInputConnection = binding.getConnection();
if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
+ " ic=" + mInputConnection);
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) ic.reportFullscreenMode(mIsFullscreen);
initialize();
onBindInput();
}
@@ -423,7 +427,7 @@ public class InputMethodService extends AbstractInputMethodService {
* of the application behind. This value is relative to the top edge
* of the input method window.
*/
- int contentTopInsets;
+ public int contentTopInsets;
/**
* This is the top part of the UI that is visibly covering the
@@ -436,7 +440,7 @@ public class InputMethodService extends AbstractInputMethodService {
* needed to make the focus visible. This value is relative to the top edge
* of the input method window.
*/
- int visibleTopInsets;
+ public int visibleTopInsets;
/**
* Option for {@link #touchableInsets}: the entire window frame
@@ -469,6 +473,7 @@ public class InputMethodService extends AbstractInputMethodService {
@Override public void onCreate() {
super.onCreate();
+ mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
mInflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this);
@@ -554,7 +559,6 @@ public class InputMethodService extends AbstractInputMethodService {
boolean visible = mWindowVisible;
boolean showingInput = mShowInputRequested;
boolean showingForced = mShowInputForced;
- boolean showingCandidates = mCandidatesVisibility == View.VISIBLE;
initViews();
mInputViewStarted = false;
mCandidatesViewStarted = false;
@@ -577,9 +581,6 @@ public class InputMethodService extends AbstractInputMethodService {
// Otherwise just put it back for its candidates.
showWindow(false);
}
- if (showingCandidates) {
- setCandidatesViewShown(true);
- }
}
}
@@ -670,6 +671,8 @@ public class InputMethodService extends AbstractInputMethodService {
if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
changed = true;
mIsFullscreen = isFullscreen;
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) ic.reportFullscreenMode(isFullscreen);
mFullscreenApplied = true;
initialize();
Drawable bg = onCreateBackgroundDrawable();
@@ -860,12 +863,14 @@ public class InputMethodService extends AbstractInputMethodService {
return isFullscreenMode() ? View.GONE : View.INVISIBLE;
}
- public void setStatusIcon(int iconResId) {
+ public void showStatusIcon(int iconResId) {
mStatusIcon = iconResId;
- InputConnection ic = getCurrentInputConnection();
- if (ic != null && mWindowVisible) {
- ic.showStatusIcon(getPackageName(), iconResId);
- }
+ mImm.showStatusIcon(mToken, getPackageName(), iconResId);
+ }
+
+ public void hideStatusIcon() {
+ mStatusIcon = 0;
+ mImm.hideStatusIcon(mToken);
}
/**
@@ -876,8 +881,7 @@ public class InputMethodService extends AbstractInputMethodService {
* @param id Unique identifier of the new input method ot start.
*/
public void switchInputMethod(String id) {
- ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE))
- .setInputMethod(mToken, id);
+ mImm.setInputMethod(mToken, id);
}
public void setExtractView(View view) {
@@ -1149,15 +1153,9 @@ public class InputMethodService extends AbstractInputMethodService {
if (!wasVisible) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
+ onWindowShown();
mWindow.show();
}
-
- if (!wasVisible || !wasCreated) {
- InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- ic.showStatusIcon(getPackageName(), mStatusIcon);
- }
- }
}
public void hideWindow() {
@@ -1173,14 +1171,26 @@ public class InputMethodService extends AbstractInputMethodService {
if (mWindowVisible) {
mWindow.hide();
mWindowVisible = false;
- InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- ic.hideStatusIcon();
- }
+ onWindowHidden();
}
}
/**
+ * Called when the input method window has been shown to the user, after
+ * previously not being visible. This is done after all of the UI setup
+ * for the window has occurred (creating its views etc).
+ */
+ public void onWindowShown() {
+ }
+
+ /**
+ * Called when the input method window has been hidden from the user,
+ * after previously being visible.
+ */
+ public void onWindowHidden() {
+ }
+
+ /**
* Called when a new client has bound to the input method. This
* may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
* and {@link #onFinishInput()} calls as the user navigates through its
@@ -1341,8 +1351,7 @@ public class InputMethodService extends AbstractInputMethodService {
* InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
*/
public void dismissSoftInput(int flags) {
- ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE))
- .hideSoftInputFromInputMethod(mToken, flags);
+ mImm.hideSoftInputFromInputMethod(mToken, flags);
}
/**
@@ -1447,17 +1456,19 @@ public class InputMethodService extends AbstractInputMethodService {
return true;
}
} else {
- KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN);
- if (movement.onKeyDown(eet,
- (Spannable)eet.getText(), keyCode, down)) {
- KeyEvent up = new KeyEvent(event, KeyEvent.ACTION_UP);
- movement.onKeyUp(eet,
- (Spannable)eet.getText(), keyCode, up);
- while (--count > 0) {
- movement.onKeyDown(eet,
- (Spannable)eet.getText(), keyCode, down);
+ if (!movement.onKeyOther(eet, (Spannable)eet.getText(), event)) {
+ KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN);
+ if (movement.onKeyDown(eet,
+ (Spannable)eet.getText(), keyCode, down)) {
+ KeyEvent up = new KeyEvent(event, KeyEvent.ACTION_UP);
movement.onKeyUp(eet,
(Spannable)eet.getText(), keyCode, up);
+ while (--count > 0) {
+ movement.onKeyDown(eet,
+ (Spannable)eet.getText(), keyCode, down);
+ movement.onKeyUp(eet,
+ (Spannable)eet.getText(), keyCode, up);
+ }
}
}
}
@@ -1593,5 +1604,9 @@ public class InputMethodService extends AbstractInputMethodService {
p.println(" mExtractedToken=" + mExtractedToken);
p.println(" mIsInputViewShown=" + mIsInputViewShown
+ " mStatusIcon=" + mStatusIcon);
+ p.println("Last computed insets:");
+ p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets
+ + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
+ + " touchableInsets=" + mTmpInsets.touchableInsets);
}
}
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index b2c74f2..b8bd10d 100755
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -1084,6 +1084,10 @@ public class KeyboardView extends View implements View.OnClickListener {
if (mPreviewPopup.isShowing()) {
mPreviewPopup.dismiss();
}
+ mHandler.removeMessages(MSG_REPEAT);
+ mHandler.removeMessages(MSG_LONGPRESS);
+ mHandler.removeMessages(MSG_SHOW_PREVIEW);
+
dismissPopupKeyboard();
}
diff --git a/core/java/android/net/UrlQuerySanitizer.java b/core/java/android/net/UrlQuerySanitizer.java
index 70e50b7..a6efcdd 100644
--- a/core/java/android/net/UrlQuerySanitizer.java
+++ b/core/java/android/net/UrlQuerySanitizer.java
@@ -23,7 +23,7 @@ import java.util.Set;
import java.util.StringTokenizer;
/**
- *
+ *
* Sanitizes the Query portion of a URL. Simple example:
* <code>
* UrlQuerySanitizer sanitizer = new UrlQuerySanitizer();
@@ -32,7 +32,7 @@ import java.util.StringTokenizer;
* String name = sanitizer.getValue("name"));
* // name now contains "Joe_User"
* </code>
- *
+ *
* Register ValueSanitizers to customize the way individual
* parameters are sanitized:
* <code>
@@ -46,7 +46,7 @@ import java.util.StringTokenizer;
* unregistered parameter sanitizer does not allow any special characters,
* and ' ' is a special character.)
* </code>
- *
+ *
* There are several ways to create ValueSanitizers. In order of increasing
* sophistication:
* <ol>
@@ -56,7 +56,7 @@ import java.util.StringTokenizer;
* <li>Subclass UrlQuerySanitizer.ValueSanitizer to define your own value
* sanitizer.
* </ol>
- *
+ *
*/
public class UrlQuerySanitizer {
@@ -84,7 +84,7 @@ public class UrlQuerySanitizer {
*/
public String mValue;
}
-
+
final private HashMap<String, ValueSanitizer> mSanitizers =
new HashMap<String, ValueSanitizer>();
final private HashMap<String, String> mEntries =
@@ -95,9 +95,9 @@ public class UrlQuerySanitizer {
private boolean mPreferFirstRepeatedParameter;
private ValueSanitizer mUnregisteredParameterValueSanitizer =
getAllIllegal();
-
+
/**
- * A functor used to sanitize a single query value.
+ * A functor used to sanitize a single query value.
*
*/
public static interface ValueSanitizer {
@@ -108,7 +108,7 @@ public class UrlQuerySanitizer {
*/
public String sanitize(String value);
}
-
+
/**
* Sanitize values based on which characters they contain. Illegal
* characters are replaced with either space or '_', depending upon
@@ -117,7 +117,7 @@ public class UrlQuerySanitizer {
public static class IllegalCharacterValueSanitizer implements
ValueSanitizer {
private int mFlags;
-
+
/**
* Allow space (' ') characters.
*/
@@ -165,21 +165,21 @@ public class UrlQuerySanitizer {
* such as "javascript:" or "vbscript:"
*/
public final static int SCRIPT_URL_OK = 1 << 10;
-
+
/**
* Mask with all fields set to OK
*/
public final static int ALL_OK = 0x7ff;
-
+
/**
* Mask with both regular space and other whitespace OK
*/
public final static int ALL_WHITESPACE_OK =
SPACE_OK | OTHER_WHITESPACE_OK;
-
+
// Common flag combinations:
-
+
/**
* <ul>
* <li>Deny all special characters.
@@ -262,18 +262,18 @@ public class UrlQuerySanitizer {
*/
public final static int ALL_BUT_NUL_AND_ANGLE_BRACKETS_LEGAL =
ALL_OK & ~(NUL_OK | LT_OK | GT_OK);
-
+
/**
* Script URL definitions
*/
-
+
private final static String JAVASCRIPT_PREFIX = "javascript:";
-
+
private final static String VBSCRIPT_PREFIX = "vbscript:";
-
+
private final static int MIN_SCRIPT_PREFIX_LENGTH = Math.min(
JAVASCRIPT_PREFIX.length(), VBSCRIPT_PREFIX.length());
-
+
/**
* Construct a sanitizer. The parameters set the behavior of the
* sanitizer.
@@ -312,7 +312,7 @@ public class UrlQuerySanitizer {
}
}
}
-
+
// If whitespace isn't OK, get rid of whitespace at beginning
// and end of value.
if ( (mFlags & ALL_WHITESPACE_OK) == 0) {
@@ -337,7 +337,7 @@ public class UrlQuerySanitizer {
}
return stringBuilder.toString();
}
-
+
/**
* Trim whitespace from the beginning and end of a string.
* <p>
@@ -361,7 +361,7 @@ public class UrlQuerySanitizer {
}
return value.substring(start, end + 1);
}
-
+
/**
* Check if c is whitespace.
* @param c character to test
@@ -380,7 +380,7 @@ public class UrlQuerySanitizer {
return false;
}
}
-
+
/**
* Check whether an individual character is legal. Uses the
* flag bit-set passed into the constructor.
@@ -400,11 +400,11 @@ public class UrlQuerySanitizer {
case '%' : return (mFlags & PCT_OK) != 0;
case '\0': return (mFlags & NUL_OK) != 0;
default : return (c >= 32 && c < 127) ||
- (c >= 128 && c <= 255 && ((mFlags & NON_7_BIT_ASCII_OK) != 0));
- }
+ ((c >= 128) && ((mFlags & NON_7_BIT_ASCII_OK) != 0));
+ }
}
}
-
+
/**
* Get the current value sanitizer used when processing
* unregistered parameter values.
@@ -412,14 +412,14 @@ public class UrlQuerySanitizer {
* <b>Note:</b> The default unregistered parameter value sanitizer is
* one that doesn't allow any special characters, similar to what
* is returned by calling createAllIllegal.
- *
+ *
* @return the current ValueSanitizer used to sanitize unregistered
* parameter values.
*/
public ValueSanitizer getUnregisteredParameterValueSanitizer() {
return mUnregisteredParameterValueSanitizer;
}
-
+
/**
* Set the value sanitizer used when processing unregistered
* parameter values.
@@ -430,46 +430,46 @@ public class UrlQuerySanitizer {
ValueSanitizer sanitizer) {
mUnregisteredParameterValueSanitizer = sanitizer;
}
-
-
+
+
// Private fields for singleton sanitizers:
-
+
private static final ValueSanitizer sAllIllegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.ALL_ILLEGAL);
-
+
private static final ValueSanitizer sAllButNulLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.ALL_BUT_NUL_LEGAL);
-
+
private static final ValueSanitizer sAllButWhitespaceLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.ALL_BUT_WHITESPACE_LEGAL);
-
+
private static final ValueSanitizer sURLLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.URL_LEGAL);
-
+
private static final ValueSanitizer sUrlAndSpaceLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.URL_AND_SPACE_LEGAL);
-
+
private static final ValueSanitizer sAmpLegal =
new IllegalCharacterValueSanitizer(
- IllegalCharacterValueSanitizer.AMP_LEGAL);
-
+ IllegalCharacterValueSanitizer.AMP_LEGAL);
+
private static final ValueSanitizer sAmpAndSpaceLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.AMP_AND_SPACE_LEGAL);
-
+
private static final ValueSanitizer sSpaceLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.SPACE_LEGAL);
-
+
private static final ValueSanitizer sAllButNulAndAngleBracketsLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.ALL_BUT_NUL_AND_ANGLE_BRACKETS_LEGAL);
-
+
/**
* Return a value sanitizer that does not allow any special characters,
* and also does not allow script URLs.
@@ -478,7 +478,7 @@ public class UrlQuerySanitizer {
public static final ValueSanitizer getAllIllegal() {
return sAllIllegal;
}
-
+
/**
* Return a value sanitizer that allows everything except Nul ('\0')
* characters. Script URLs are allowed.
@@ -547,7 +547,7 @@ public class UrlQuerySanitizer {
public static final ValueSanitizer getAllButNulAndAngleBracketsLegal() {
return sAllButNulAndAngleBracketsLegal;
}
-
+
/**
* Constructs a UrlQuerySanitizer.
* <p>
@@ -560,7 +560,7 @@ public class UrlQuerySanitizer {
*/
public UrlQuerySanitizer() {
}
-
+
/**
* Constructs a UrlQuerySanitizer and parse a URL.
* This constructor is provided for convenience when the
@@ -585,7 +585,7 @@ public class UrlQuerySanitizer {
setAllowUnregisteredParamaters(true);
parseUrl(url);
}
-
+
/**
* Parse the query parameters out of an encoded URL.
* Works by extracting the query portion from the URL and then
@@ -604,7 +604,7 @@ public class UrlQuerySanitizer {
}
parseQuery(query);
}
-
+
/**
* Parse a query. A query string is any number of parameter-value clauses
* separated by any non-zero number of ampersands. A parameter-value clause
@@ -631,7 +631,7 @@ public class UrlQuerySanitizer {
}
}
}
-
+
/**
* Get a set of all of the parameters found in the sanitized query.
* <p>
@@ -641,7 +641,7 @@ public class UrlQuerySanitizer {
public Set<String> getParameterSet() {
return mEntries.keySet();
}
-
+
/**
* An array list of all of the parameter value pairs in the sanitized
* query, in the order they appeared in the query. May contain duplicate
@@ -691,7 +691,7 @@ public class UrlQuerySanitizer {
}
mSanitizers.put(parameter, valueSanitizer);
}
-
+
/**
* Register a value sanitizer for an array of parameters.
* @param parameters An array of unencoded parameter names.
@@ -705,7 +705,7 @@ public class UrlQuerySanitizer {
mSanitizers.put(parameters[i], valueSanitizer);
}
}
-
+
/**
* Set whether or not unregistered parameters are allowed. If they
* are not allowed, then they will be dropped when a query is sanitized.
@@ -718,7 +718,7 @@ public class UrlQuerySanitizer {
boolean allowUnregisteredParamaters) {
mAllowUnregisteredParamaters = allowUnregisteredParamaters;
}
-
+
/**
* Get whether or not unregistered parameters are allowed. If not
* allowed, they will be dropped when a query is parsed.
@@ -728,10 +728,10 @@ public class UrlQuerySanitizer {
public boolean getAllowUnregisteredParamaters() {
return mAllowUnregisteredParamaters;
}
-
+
/**
* Set whether or not the first occurrence of a repeated parameter is
- * preferred. True means the first repeated parameter is preferred.
+ * preferred. True means the first repeated parameter is preferred.
* False means that the last repeated parameter is preferred.
* <p>
* The preferred parameter is the one that is returned when getParameter
@@ -746,7 +746,7 @@ public class UrlQuerySanitizer {
boolean preferFirstRepeatedParameter) {
mPreferFirstRepeatedParameter = preferFirstRepeatedParameter;
}
-
+
/**
* Get whether or not the first occurrence of a repeated parameter is
* preferred.
@@ -757,10 +757,10 @@ public class UrlQuerySanitizer {
public boolean getPreferFirstRepeatedParameter() {
return mPreferFirstRepeatedParameter;
}
-
+
/**
* Parse an escaped parameter-value pair. The default implementation
- * unescapes both the parameter and the value, then looks up the
+ * unescapes both the parameter and the value, then looks up the
* effective value sanitizer for the parameter and uses it to sanitize
* the value. If all goes well then addSanitizedValue is called with
* the unescaped parameter and the sanitized unescaped value.
@@ -779,7 +779,7 @@ public class UrlQuerySanitizer {
String sanitizedValue = valueSanitizer.sanitize(unescapedValue);
addSanitizedEntry(unescapedParameter, sanitizedValue);
}
-
+
/**
* Record a sanitized parameter-value pair. Override if you want to
* do additional filtering or validation.
@@ -796,7 +796,7 @@ public class UrlQuerySanitizer {
}
mEntries.put(parameter, value);
}
-
+
/**
* Get the value sanitizer for a parameter. Returns null if there
* is no value sanitizer registered for the parameter.
@@ -807,7 +807,7 @@ public class UrlQuerySanitizer {
public ValueSanitizer getValueSanitizer(String parameter) {
return mSanitizers.get(parameter);
}
-
+
/**
* Get the effective value sanitizer for a parameter. Like getValueSanitizer,
* except if there is no value sanitizer registered for a parameter, and
@@ -823,7 +823,7 @@ public class UrlQuerySanitizer {
}
return sanitizer;
}
-
+
/**
* Unescape an escaped string.
* <ul>
@@ -867,7 +867,7 @@ public class UrlQuerySanitizer {
}
return stringBuilder.toString();
}
-
+
/**
* Test if a character is a hexidecimal digit. Both upper case and lower
* case hex digits are allowed.
@@ -877,7 +877,7 @@ public class UrlQuerySanitizer {
protected boolean isHexDigit(char c) {
return decodeHexDigit(c) >= 0;
}
-
+
/**
* Convert a character that represents a hexidecimal digit into an integer.
* If the character is not a hexidecimal digit, then -1 is returned.
@@ -885,7 +885,7 @@ public class UrlQuerySanitizer {
* @param c the hexidecimal digit.
* @return the integer value of the hexidecimal digit.
*/
-
+
protected int decodeHexDigit(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
@@ -900,7 +900,7 @@ public class UrlQuerySanitizer {
return -1;
}
}
-
+
/**
* Clear the existing entries. Called to get ready to parse a new
* query string.
diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java
index c6615da..1a287c8 100644
--- a/core/java/android/pim/RecurrenceSet.java
+++ b/core/java/android/pim/RecurrenceSet.java
@@ -140,7 +140,6 @@ public class RecurrenceSet {
recurrence = recurrence.substring(tzidx + 1);
}
Time time = new Time(tz);
- boolean rdateNotInUtc = !tz.equals(Time.TIMEZONE_UTC);
String[] rawDates = recurrence.split(",");
int n = rawDates.length;
long[] dates = new long[n];
diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java
index 05c2952..02ab1da 100644
--- a/core/java/android/preference/PreferenceGroupAdapter.java
+++ b/core/java/android/preference/PreferenceGroupAdapter.java
@@ -88,6 +88,9 @@ class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeIn
public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
mPreferenceGroup = preferenceGroup;
+ // If this group gets or loses any children, let us know
+ mPreferenceGroup.setOnPreferenceChangeInternalListener(this);
+
mPreferenceList = new ArrayList<Preference>();
mPreferenceClassNames = new ArrayList<String>();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 054da1d..c6a7b40 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -879,15 +879,6 @@ public final class Settings {
public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
/**
- * The interval in milliseconds after which Wi-Fi is considered idle.
- * When idle, it is possible for the device to be switched from Wi-Fi to
- * the mobile data network.
- *
- * @hide pending API Council approval
- */
- public static final String WIFI_IDLE_MS = "wifi_idle_ms";
-
- /**
* The policy for deciding when Wi-Fi should go to sleep (which will in
* turn switch to using the mobile data as an Internet connection).
* <p>
@@ -1288,6 +1279,12 @@ public final class Settings {
*/
public static final String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
+ /**
+ * Whether the haptic feedback (long presses, ...) are enabled. The value is
+ * boolean (1 or 0).
+ */
+ public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
+
// Settings moved to Settings.Secure
/**
@@ -2732,6 +2729,13 @@ public final class Settings {
"gprs_register_check_period_ms";
/**
+ * The interval in milliseconds after which Wi-Fi is considered idle.
+ * When idle, it is possible for the device to be switched from Wi-Fi to
+ * the mobile data network.
+ */
+ public static final String WIFI_IDLE_MS = "wifi_idle_ms";
+
+ /**
* Screen timeout in milliseconds corresponding to the
* PowerManager's POKE_LOCK_SHORT_TIMEOUT flag (i.e. the fastest
* possible screen timeout behavior.)
diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java
index d149761..7c15045 100644
--- a/core/java/android/server/BluetoothDeviceService.java
+++ b/core/java/android/server/BluetoothDeviceService.java
@@ -25,8 +25,8 @@
package android.server;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset; // just for dump()
import android.bluetooth.BluetoothError;
+import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothIntent;
import android.bluetooth.IBluetoothDevice;
import android.bluetooth.IBluetoothDeviceCallback;
@@ -35,23 +35,20 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.util.Log;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
+import android.os.RemoteException;
import android.os.SystemService;
+import android.provider.Settings;
+import android.util.Log;
-import java.io.IOException;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
public class BluetoothDeviceService extends IBluetoothDevice.Stub {
@@ -119,7 +116,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
public synchronized boolean disable() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
-
+
if (mEnableThread != null && mEnableThread.isAlive()) {
return false;
}
@@ -229,9 +226,9 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
long origCallerIdentityToken = Binder.clearCallingIdentity();
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
bluetoothOn ? 1 : 0);
- Binder.restoreCallingIdentity(origCallerIdentityToken);
+ Binder.restoreCallingIdentity(origCallerIdentityToken);
}
-
+
private native int enableNative();
private native int disableNative();
@@ -247,6 +244,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
public class BondState {
private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
+ private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>();
public synchronized void loadBondState() {
if (!mIsEnabled) {
@@ -281,8 +279,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
intent.putExtra(BluetoothIntent.BOND_PREVIOUS_STATE, oldState);
if (state == BluetoothDevice.BOND_NOT_BONDED) {
if (reason <= 0) {
- Log.w(TAG, "setBondState() called to unbond device with invalid reason code " +
- "Setting reason = BOND_RESULT_REMOVED");
+ Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
+ "invalid. Overriding reason code with BOND_RESULT_REMOVED");
reason = BluetoothDevice.UNBOND_REASON_REMOVED;
}
intent.putExtra(BluetoothIntent.REASON, reason);
@@ -290,11 +288,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
} else {
mState.put(address, state);
}
- if (state == BluetoothDevice.BOND_BONDING) {
- mPinAttempt.put(address, Integer.valueOf(0));
- } else {
- mPinAttempt.remove(address);
- }
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
@@ -316,6 +310,24 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
return result.toArray(new String[result.size()]);
}
+ public synchronized void addAutoPairingFailure(String address) {
+ if (!mAutoPairingFailures.contains(address)) {
+ mAutoPairingFailures.add(address);
+ }
+ }
+
+ public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
+ return getAttempt(address) != 0;
+ }
+
+ public synchronized void clearPinAttempts(String address) {
+ mPinAttempt.remove(address);
+ }
+
+ public synchronized boolean hasAutoPairingFailed(String address) {
+ return mAutoPairingFailures.contains(address);
+ }
+
public synchronized int getAttempt(String address) {
Integer attempt = mPinAttempt.get(address);
if (attempt == null) {
@@ -326,10 +338,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
public synchronized void attempt(String address) {
Integer attempt = mPinAttempt.get(address);
+ int newAttempt;
if (attempt == null) {
- return;
+ newAttempt = 1;
+ } else {
+ newAttempt = attempt.intValue() + 1;
}
- mPinAttempt.put(address, new Integer(attempt.intValue() + 1));
+ mPinAttempt.put(address, new Integer(newAttempt));
}
}
@@ -508,7 +523,11 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
return false;
}
address = address.toUpperCase();
- if (mBondState.getBondState(address) != BluetoothDevice.BOND_NOT_BONDED) {
+
+ // Check for bond state only if we are not performing auto
+ // pairing exponential back-off attempts.
+ if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
+ mBondState.getBondState(address) != BluetoothDevice.BOND_NOT_BONDED) {
return false;
}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 0f60fae..b5e4090 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -24,6 +24,8 @@ import android.bluetooth.BluetoothIntent;
import android.bluetooth.IBluetoothDeviceCallback;
import android.content.Context;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
@@ -48,9 +50,33 @@ class BluetoothEventLoop {
private BluetoothDeviceService mBluetoothService;
private Context mContext;
+ private static final int EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 1;
+
+ // The time (in millisecs) to delay the pairing attempt after the first
+ // auto pairing attempt fails. We use an exponential delay with
+ // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
+ // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
+ private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
+ private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
+
private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
+ String address = (String)msg.obj;
+ if (address != null) {
+ mBluetoothService.createBond(address);
+ return;
+ }
+ break;
+ }
+ }
+ };
+
static { classInitNative(); }
private static native void classInitNative();
@@ -149,16 +175,6 @@ class BluetoothEventLoop {
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
- private void onPairingRequest() {
- Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
- mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
- }
-
- private void onPairingCancel() {
- Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION);
- mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
- }
-
private void onRemoteDeviceFound(String address, int deviceClass, short rssi) {
Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
@@ -214,12 +230,55 @@ class BluetoothEventLoop {
address = address.toUpperCase();
if (result == BluetoothError.SUCCESS) {
mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
+ if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
+ mBluetoothService.getBondState().clearPinAttempts(address);
+ }
+ } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
+ mBluetoothService.getBondState().getAttempt(address) == 1) {
+ mBluetoothService.getBondState().addAutoPairingFailure(address);
+ pairingAttempt(address, result);
+ } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
+ mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
+ pairingAttempt(address, result);
} else {
mBluetoothService.getBondState().setBondState(address,
BluetoothDevice.BOND_NOT_BONDED, result);
+ if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
+ mBluetoothService.getBondState().clearPinAttempts(address);
+ }
}
}
+ private void pairingAttempt(String address, int result) {
+ // This happens when our initial guess of "0000" as the pass key
+ // fails. Try to create the bond again and display the pin dialog
+ // to the user. Use back-off while posting the delayed
+ // message. The initial value is
+ // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
+ // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
+ // reached, display an error to the user.
+ int attempt = mBluetoothService.getBondState().getAttempt(address);
+ if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
+ MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
+ mBluetoothService.getBondState().clearPinAttempts(address);
+ mBluetoothService.getBondState().setBondState(address,
+ BluetoothDevice.BOND_NOT_BONDED, result);
+ return;
+ }
+
+ Message message = mHandler.obtainMessage(EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
+ message.obj = address;
+ boolean postResult = mHandler.sendMessageDelayed(message,
+ attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
+ if (!postResult) {
+ mBluetoothService.getBondState().clearPinAttempts(address);
+ mBluetoothService.getBondState().setBondState(address,
+ BluetoothDevice.BOND_NOT_BONDED, result);
+ return;
+ }
+ mBluetoothService.getBondState().attempt(address);
+ }
+
private void onBondingCreated(String address) {
mBluetoothService.getBondState().setBondState(address.toUpperCase(),
BluetoothDevice.BOND_BONDED);
@@ -253,12 +312,12 @@ class BluetoothEventLoop {
case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
- if (mBluetoothService.getBondState().getAttempt(address) < 1) {
+ if (!mBluetoothService.getBondState().hasAutoPairingFailed(address)) {
mBluetoothService.getBondState().attempt(address);
mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes("0000"));
return;
}
- }
+ }
}
Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index a559b9d..6df0b35 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.util.Log;
import android.view.KeyEvent;
import android.text.*;
import android.widget.TextView;
@@ -185,15 +186,9 @@ implements MovementMethod
if (code != KeyEvent.KEYCODE_UNKNOWN
&& event.getAction() == KeyEvent.ACTION_MULTIPLE) {
int repeat = event.getRepeatCount();
- boolean first = true;
boolean handled = false;
while ((--repeat) > 0) {
- if (first && executeDown(view, text, code)) {
- handled = true;
- MetaKeyKeyListener.adjustMetaAfterKeypress(text);
- MetaKeyKeyListener.resetLockedMeta(text);
- }
- first = false;
+ handled |= executeDown(view, text, code);
}
return handled;
}
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index d89fbec..39ad976 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -287,10 +287,10 @@ public abstract class MetaKeyKeyListener {
}
public static void clearMetaKeyState(Editable content, int states) {
- if ((states&META_SHIFT_ON) != 0) resetLock(content, CAP);
- if ((states&META_ALT_ON) != 0) resetLock(content, ALT);
- if ((states&META_SYM_ON) != 0) resetLock(content, SYM);
- if ((states&META_SELECTING) != 0) resetLock(content, SELECTING);
+ if ((states&META_SHIFT_ON) != 0) content.removeSpan(CAP);
+ if ((states&META_ALT_ON) != 0) content.removeSpan(ALT);
+ if ((states&META_SYM_ON) != 0) content.removeSpan(SYM);
+ if ((states&META_SELECTING) != 0) content.removeSpan(SELECTING);
}
/**
diff --git a/core/java/android/text/method/PasswordTransformationMethod.java b/core/java/android/text/method/PasswordTransformationMethod.java
index 85adabd..fad4f64 100644
--- a/core/java/android/text/method/PasswordTransformationMethod.java
+++ b/core/java/android/text/method/PasswordTransformationMethod.java
@@ -105,8 +105,10 @@ implements TransformationMethod, TextWatcher
sp.removeSpan(old[i]);
}
- sp.setSpan(new Visible(sp, this), start, start + count,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (count == 1) {
+ sp.setSpan(new Visible(sp, this), start, start + count,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
}
}
}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
new file mode 100644
index 0000000..cc3563c
--- /dev/null
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 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.view;
+
+/**
+ * Constants to be used to perform haptic feedback effects via
+ * {@link View#performHapticFeedback(int)}
+ */
+public class HapticFeedbackConstants {
+
+ private HapticFeedbackConstants() {}
+
+ public static final int LONG_PRESS = 0;
+
+ /** @hide pending API council */
+ public static final int ZOOM_RING_TICK = 1;
+
+ /**
+ * Flag for {@link View#performHapticFeedback(int, int)
+ * View.performHapticFeedback(int, int)}: Ignore the setting in the
+ * view for whether to perform haptic feedback, do it always.
+ */
+ public static final int FLAG_IGNORE_VIEW_SETTING = 0x0001;
+
+ /**
+ * Flag for {@link View#performHapticFeedback(int, int)
+ * View.performHapticFeedback(int, int)}: Ignore the global setting
+ * for whether to perform haptic feedback, do it always.
+ */
+ public static final int FLAG_IGNORE_GLOBAL_SETTING = 0x0002;
+}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7276f17..1156856 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -106,5 +106,6 @@ interface IWindowSession {
void setInTouchMode(boolean showFocus);
boolean getInTouchMode();
+
+ boolean performHapticFeedback(IWindow window, int effectId, boolean always);
}
-
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a51b564..1d5e7cd 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -836,6 +836,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
public static final int SOUND_EFFECTS_ENABLED = 0x08000000;
/**
+ * View flag indicating whether this view should have haptic feedback
+ * enabled for events such as long presses.
+ */
+ public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000;
+
+ /**
* Use with {@link #focusSearch}. Move focus to the previous selectable
* item.
*/
@@ -1637,6 +1643,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
public View(Context context) {
mContext = context;
mResources = context != null ? context.getResources() : null;
+ mViewFlags = SOUND_EFFECTS_ENABLED|HAPTIC_FEEDBACK_ENABLED;
++sInstanceCount;
}
@@ -1703,9 +1710,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
- viewFlagValues |= SOUND_EFFECTS_ENABLED;
- viewFlagMasks |= SOUND_EFFECTS_ENABLED;
-
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
@@ -1801,6 +1805,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
viewFlagValues &= ~SOUND_EFFECTS_ENABLED;
viewFlagMasks |= SOUND_EFFECTS_ENABLED;
}
+ case com.android.internal.R.styleable.View_hapticFeedbackEnabled:
+ if (!a.getBoolean(attr, true)) {
+ viewFlagValues &= ~HAPTIC_FEEDBACK_ENABLED;
+ viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED;
+ }
case R.styleable.View_scrollbars:
final int scrollbars = a.getInt(attr, SCROLLBARS_NONE);
if (scrollbars != SCROLLBARS_NONE) {
@@ -2182,6 +2191,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
if (!handled) {
handled = showContextMenu();
}
+ if (handled) {
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ }
return handled;
}
@@ -2742,7 +2754,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
* Set whether this view should have sound effects enabled for events such as
* clicking and touching.
*
- * You may wish to disable sound effects for a view if you already play sounds,
+ * <p>You may wish to disable sound effects for a view if you already play sounds,
* for instance, a dial key that plays dtmf tones.
*
* @param soundEffectsEnabled whether sound effects are enabled for this view.
@@ -2768,6 +2780,35 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
}
/**
+ * Set whether this view should have haptic feedback for events such as
+ * long presses.
+ *
+ * <p>You may wish to disable haptic feedback if your view already controls
+ * its own haptic feedback.
+ *
+ * @param hapticFeedbackEnabled whether haptic feedback enabled for this view.
+ * @see #isHapticFeedbackEnabled()
+ * @see #performHapticFeedback(int)
+ * @attr ref android.R.styleable#View_hapticFeedbackEnabled
+ */
+ public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) {
+ setFlags(hapticFeedbackEnabled ? HAPTIC_FEEDBACK_ENABLED: 0, HAPTIC_FEEDBACK_ENABLED);
+ }
+
+ /**
+ * @return whether this view should have haptic feedback enabled for events
+ * long presses.
+ *
+ * @see #setHapticFeedbackEnabled(boolean)
+ * @see #performHapticFeedback(int)
+ * @attr ref android.R.styleable#View_hapticFeedbackEnabled
+ */
+ @ViewDebug.ExportedProperty
+ public boolean isHapticFeedbackEnabled() {
+ return HAPTIC_FEEDBACK_ENABLED == (mViewFlags & HAPTIC_FEEDBACK_ENABLED);
+ }
+
+ /**
* If this view doesn't do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
@@ -7312,20 +7353,57 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
/**
* Play a sound effect for this view.
*
- * The framework will play sound effects for some built in actions, such as
+ * <p>The framework will play sound effects for some built in actions, such as
* clicking, but you may wish to play these effects in your widget,
* for instance, for internal navigation.
*
- * The sound effect will only be played if sound effects are enabled by the user, and
+ * <p>The sound effect will only be played if sound effects are enabled by the user, and
* {@link #isSoundEffectsEnabled()} is true.
*
* @param soundConstant One of the constants defined in {@link SoundEffectConstants}
*/
- protected void playSoundEffect(int soundConstant) {
- if (mAttachInfo == null || mAttachInfo.mSoundEffectPlayer == null || !isSoundEffectsEnabled()) {
+ public void playSoundEffect(int soundConstant) {
+ if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {
return;
}
- mAttachInfo.mSoundEffectPlayer.playSoundEffect(soundConstant);
+ mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);
+ }
+
+ /**
+ * Provide haptic feedback to the user for this view.
+ *
+ * <p>The framework will provide haptic feedback for some built in actions,
+ * such as long presses, but you may wish to provide feedback for your
+ * own widget.
+ *
+ * <p>The feedback will only be performed if
+ * {@link #isHapticFeedbackEnabled()} is true.
+ *
+ * @param feedbackConstant One of the constants defined in
+ * {@link HapticFeedbackConstants}
+ */
+ public boolean performHapticFeedback(int feedbackConstant) {
+ return performHapticFeedback(feedbackConstant, 0);
+ }
+
+ /**
+ * Like {@link #performHapticFeedback(int)}, with additional options.
+ *
+ * @param feedbackConstant One of the constants defined in
+ * {@link HapticFeedbackConstants}
+ * @param flags Additional flags as per {@link HapticFeedbackConstants}.
+ */
+ public boolean performHapticFeedback(int feedbackConstant, int flags) {
+ if (mAttachInfo == null) {
+ return false;
+ }
+ if ((flags&HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING) == 0
+ && !isHapticFeedbackEnabled()) {
+ return false;
+ }
+ return mAttachInfo.mRootCallbacks.performHapticFeedback(
+ feedbackConstant,
+ (flags&HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0);
}
/**
@@ -7704,8 +7782,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
*/
static class AttachInfo {
- interface SoundEffectPlayer {
+ interface Callbacks {
void playSoundEffect(int effectId);
+ boolean performHapticFeedback(int effectId, boolean always);
}
/**
@@ -7775,7 +7854,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
final IBinder mWindowToken;
- final SoundEffectPlayer mSoundEffectPlayer;
+ final Callbacks mRootCallbacks;
/**
* The top view of the hierarchy.
@@ -7922,12 +8001,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
* @param handler the events handler the view must use
*/
AttachInfo(IWindowSession session, IWindow window,
- Handler handler, SoundEffectPlayer effectPlayer) {
+ Handler handler, Callbacks effectPlayer) {
mSession = session;
mWindow = window;
mWindowToken = window.asBinder();
mHandler = handler;
- mSoundEffectPlayer = effectPlayer;
+ mRootCallbacks = effectPlayer;
}
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 4e46397..ccfa6bf 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -61,7 +61,7 @@ import static javax.microedition.khronos.opengles.GL10.*;
*/
@SuppressWarnings({"EmptyCatchBlock"})
public final class ViewRoot extends Handler implements ViewParent,
- View.AttachInfo.SoundEffectPlayer {
+ View.AttachInfo.Callbacks {
private static final String TAG = "ViewRoot";
private static final boolean DBG = false;
@SuppressWarnings({"ConstantConditionalExpression"})
@@ -1637,7 +1637,7 @@ public final class ViewRoot extends Handler implements ViewParent,
dispatchDetachedFromWindow();
break;
case DISPATCH_KEY_FROM_IME:
- if (true) Log.v(
+ if (LOCAL_LOGV) Log.v(
"ViewRoot", "Dispatching key "
+ msg.obj + " from IME to " + mView);
deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false);
@@ -2238,6 +2238,17 @@ public final class ViewRoot extends Handler implements ViewParent,
/**
* {@inheritDoc}
*/
+ public boolean performHapticFeedback(int effectId, boolean always) {
+ try {
+ return sWindowSession.performHapticFeedback(mWindow, effectId, always);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public View focusSearch(View focused, int direction) {
checkThread();
if (!(mView instanceof ViewGroup)) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d08a6fa..406af3e3 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -925,7 +925,7 @@ public interface WindowManager extends ViewManager {
sb.append(Integer.toHexString(windowAnimations));
}
if (screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
- sb.append("or=");
+ sb.append(" or=");
sb.append(screenOrientation);
}
sb.append('}');
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 542b35f..051f823 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -771,4 +771,15 @@ public interface WindowManagerPolicy {
public boolean isCheekPressedAgainstScreen(MotionEvent ev);
public void setCurrentOrientation(int newOrientation);
+
+ /**
+ * Call from application to perform haptic feedback on its window.
+ */
+ public boolean performHapticFeedback(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();
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 56c6c92..9509b15 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -371,6 +371,14 @@ public class BaseInputConnection implements InputConnection {
if (DEBUG) Log.v(TAG, "setSelection " + start + ", " + end);
final Editable content = getEditable();
if (content == null) return false;
+ int len = content.length();
+ if (start > len || end > len) {
+ // If the given selection is out of bounds, just ignore it.
+ // Most likely the text was changed out from under the IME,
+ // the the IME is going to have to update all of its state
+ // anyway.
+ return true;
+ }
Selection.setSelection(content, start, end);
return true;
}
@@ -396,20 +404,10 @@ public class BaseInputConnection implements InputConnection {
}
/**
- * Provides standard implementation for hiding the status icon associated
- * with the current input method.
+ * Updates InputMethodManager with the current fullscreen mode.
*/
- public boolean hideStatusIcon() {
- mIMM.updateStatusIcon(0, null);
- return true;
- }
-
- /**
- * Provides standard implementation for showing the status icon associated
- * with the current input method.
- */
- public boolean showStatusIcon(String packageName, int resId) {
- mIMM.updateStatusIcon(resId, packageName);
+ public boolean reportFullscreenMode(boolean enabled) {
+ mIMM.setFullscreenMode(enabled);
return true;
}
@@ -420,7 +418,11 @@ public class BaseInputConnection implements InputConnection {
Editable content = getEditable();
if (content != null) {
- if (content.length() == 1) {
+ final int N = content.length();
+ if (N == 0) {
+ return;
+ }
+ if (N == 1) {
// If it's 1 character, we have a chance of being
// able to generate normal key events...
if (mKeyCharacterMap == null) {
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 8c30d3f..13173f6 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -267,6 +267,13 @@ public interface InputConnection {
public boolean clearMetaKeyStates(int states);
/**
+ * Called by the IME to tell the client when it switches between fullscreen
+ * and normal modes. This will normally be called for you by the standard
+ * implementation of {@link android.inputmethodservice.InputMethodService}.
+ */
+ public boolean reportFullscreenMode(boolean enabled);
+
+ /**
* API to send private commands from an input method to its connected
* editor. This can be used to provide domain-specific features that are
* only known between certain input methods and their clients. Note that
@@ -284,23 +291,4 @@ public interface InputConnection {
* valid.
*/
public boolean performPrivateCommand(String action, Bundle data);
-
- /**
- * Show an icon in the status bar.
- *
- * @param packageName The package holding the icon resource to be shown.
- * @param resId The resource id of the icon to show.
- *
- * @return Returns true on success, false if the input connection is no longer
- * valid.
- */
- public boolean showStatusIcon(String packageName, int resId);
-
- /**
- * Hide the icon shown in the status bar.
- *
- * @return Returns true on success, false if the input connection is no longer
- * valid.
- */
- public boolean hideStatusIcon();
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 99d5aa5..fe14166 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -214,6 +214,11 @@ public final class InputMethodManager {
*/
boolean mActive = false;
+ /**
+ * As reported by IME through InputConnection.
+ */
+ boolean mFullscreenMode;
+
// -----------------------------------------------------------
/**
@@ -374,6 +379,7 @@ public final class InputMethodManager {
public void setActive(boolean active) {
mActive = active;
+ mFullscreenMode = false;
}
};
@@ -443,14 +449,36 @@ public final class InputMethodManager {
}
}
- public void updateStatusIcon(int iconId, String iconPackage) {
+ public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
+ try {
+ mService.updateStatusIcon(imeToken, packageName, iconId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void hideStatusIcon(IBinder imeToken) {
try {
- mService.updateStatusIcon(iconId, iconPackage);
+ mService.updateStatusIcon(imeToken, null, 0);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
+ /** @hide */
+ public void setFullscreenMode(boolean enabled) {
+ mFullscreenMode = true;
+ }
+
+ /**
+ * Allows you to discover whether the attached input method is running
+ * in fullscreen mode. Return true if it is fullscreen, entirely covering
+ * your UI, else returns false.
+ */
+ public boolean isFullscreenMode() {
+ return mFullscreenMode;
+ }
+
/**
* Return true if the given view is the currently active view for the
* input method.
@@ -503,7 +531,6 @@ public final class InputMethodManager {
void finishInputLocked() {
if (mServedView != null) {
if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
- updateStatusIcon(0, null);
if (mCurrentTextBoxAttribute != null) {
try {
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 5a37f04..07c1a5d 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -171,6 +171,10 @@ public final class CookieManager {
boolean pathMatch(String urlPath) {
if (urlPath.startsWith(path)) {
int len = path.length();
+ if (len == 0) {
+ Log.w(LOGTAG, "Empty cookie path");
+ return false;
+ }
int urlLen = urlPath.length();
if (path.charAt(len-1) != PATH_DELIM && urlLen > len) {
// make sure /wee doesn't match /we
@@ -864,7 +868,10 @@ public final class CookieManager {
"illegal format for max-age: " + value);
}
} else if (name.equals(PATH)) {
- cookie.path = value;
+ // only allow non-empty path value
+ if (value.length() > 0) {
+ cookie.path = value;
+ }
} else if (name.equals(DOMAIN)) {
int lastPeriod = value.lastIndexOf(PERIOD);
if (lastPeriod == 0) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 3306700..bdbf38a 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -260,19 +260,8 @@ public class WebView extends AbsoluteLayout
// Whether we are in the drag tap mode, which exists starting at the second
// tap's down, through its move, and includes its up. These events should be
// given to the method on the zoom controller.
- private boolean mInZoomTapDragMode;
-
- // The event time of the previous touch up.
- private long mPreviousUpTime;
-
- private Runnable mRemoveReleaseSingleTap = new Runnable() {
- public void run() {
- mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
- mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
- mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
- }
- };
-
+ private boolean mInZoomTapDragMode = false;
+
// Whether to prevent drag during touch. The initial value depends on
// mForwardTouchEvents. If WebCore wants touch events, we assume it will
// take control of touch events unless it says no for touch down event.
@@ -517,6 +506,11 @@ public class WebView extends AbsoluteLayout
private ZoomRingController mZoomRingController;
+ // These keep track of the center point of the zoom ring. They are used to
+ // determine the point around which we should zoom.
+ private float mZoomCenterX;
+ private float mZoomCenterY;
+
private ZoomRingController.OnZoomListener mZoomListener =
new ZoomRingController.OnZoomListener() {
@@ -554,12 +548,9 @@ public class WebView extends AbsoluteLayout
deltaZoomLevel == 0) {
return false;
}
-
- int deltaX = centerX - getViewWidth() / 2;
- int deltaY = centerY - getViewHeight() / 2;
+ mZoomCenterX = (float) centerX;
+ mZoomCenterY = (float) centerY;
- pinScrollBy(deltaX, deltaY, false, 0);
-
while (deltaZoomLevel != 0) {
if (deltaZoomLevel > 0) {
if (!zoomIn()) return false;
@@ -569,15 +560,16 @@ public class WebView extends AbsoluteLayout
deltaZoomLevel++;
}
}
-
- pinScrollBy(-deltaX, -deltaY, false, 0);
-
+
return true;
}
public void onSimpleZoom(boolean zoomIn) {
- if (zoomIn) zoomIn();
- else zoomOut();
+ if (zoomIn) {
+ zoomIn();
+ } else {
+ zoomOut();
+ }
}
};
@@ -1586,8 +1578,8 @@ public class WebView extends AbsoluteLayout
int oldX = mScrollX;
int oldY = mScrollY;
float ratio = scale * mInvActualScale; // old inverse
- float sx = ratio * oldX + (ratio - 1) * getViewWidth() * 0.5f;
- float sy = ratio * oldY + (ratio - 1) * getViewHeight() * 0.5f;
+ float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
+ float sy = ratio * oldY + (ratio - 1) * mZoomCenterY;
// now update our new scale and inverse
if (scale != mActualScale && !mPreviewZoomOnly) {
@@ -2264,8 +2256,8 @@ public class WebView extends AbsoluteLayout
zoomScale = mZoomScale;
}
float scale = (mActualScale - zoomScale) * mInvActualScale;
- float tx = scale * ((getLeft() + getRight()) * 0.5f + mScrollX);
- float ty = scale * ((getTop() + getBottom()) * 0.5f + mScrollY);
+ float tx = scale * (mZoomCenterX + mScrollX);
+ float ty = scale * (mZoomCenterY + mScrollY);
// this block pins the translate to "legal" bounds. This makes the
// animation a bit non-obvious, but it means we won't pop when the
@@ -3025,8 +3017,8 @@ public class WebView extends AbsoluteLayout
(keyCode == KeyEvent.KEYCODE_7) ? 1 : 0, 0);
break;
case KeyEvent.KEYCODE_9:
- debugDump();
- break;
+ nativeInstrumentReport();
+ return true;
}
}
@@ -3161,6 +3153,7 @@ public class WebView extends AbsoluteLayout
* @hide
*/
public void emulateShiftHeld() {
+ mExtendSelection = false;
mShiftIsPressed = true;
}
@@ -3176,6 +3169,7 @@ public class WebView extends AbsoluteLayout
mWebViewCore.sendMessage(EventHub.GET_SELECTION, selection);
copiedSomething = true;
}
+ mExtendSelection = false;
}
mShiftIsPressed = false;
if (mTouchMode == TOUCH_SELECT_MODE) {
@@ -3218,6 +3212,11 @@ public class WebView extends AbsoluteLayout
}
}
+ /**
+ * @deprecated WebView should not have implemented
+ * ViewTreeObserver.OnGlobalFocusChangeListener. This method
+ * does nothing now.
+ */
@Deprecated
public void onGlobalFocusChanged(View oldFocus, View newFocus) {
}
@@ -3281,7 +3280,11 @@ public class WebView extends AbsoluteLayout
@Override
protected void onSizeChanged(int w, int h, int ow, int oh) {
super.onSizeChanged(w, h, ow, oh);
-
+ // Center zooming to the center of the screen. This is appropriate for
+ // this case of zooming, and it also sets us up properly if we remove
+ // the new zoom ring controller
+ mZoomCenterX = getViewWidth() * .5f;
+ mZoomCenterY = getViewHeight() * .5f;
// we always force, in case our height changed, in which case we still
// want to send the notification over to webkit
setNewZoomScale(mActualScale, true);
@@ -3342,25 +3345,12 @@ public class WebView extends AbsoluteLayout
+ mTouchMode);
}
- if (mZoomRingController.isVisible()) {
- if (mInZoomTapDragMode) {
- mZoomRingController.handleDoubleTapEvent(ev);
- if (ev.getAction() == MotionEvent.ACTION_UP) {
- // Just released the second tap, no longer in tap-drag mode
- mInZoomTapDragMode = false;
- }
- return true;
- } else {
- // TODO: properly do this.
- /*
- * When the zoom widget is showing, the user can tap outside of
- * it to dismiss it. Furthermore, he can drag outside of it to
- * pan the browser. However, we do not want a tap on a link to
- * open the link.
- */
- post(mRemoveReleaseSingleTap);
- // Continue through to normal processing
+ if (mZoomRingController.isVisible() && mInZoomTapDragMode) {
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ // Just released the second tap, no longer in tap-drag mode
+ mInZoomTapDragMode = false;
}
+ return mZoomRingController.handleDoubleTapEvent(ev);
}
int action = ev.getAction();
@@ -3418,21 +3408,19 @@ public class WebView extends AbsoluteLayout
, viewToContent(mSelectY), false);
mTouchSelection = mExtendSelection = true;
} else if (!ZoomRingController.useOldZoom(mContext) &&
- eventTime - mPreviousUpTime < DOUBLE_TAP_TIMEOUT &&
- getSettings().supportZoom() &&
- mMinZoomScale < mMaxZoomScale) {
+ mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
// Found doubletap, invoke the zoom controller
- mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
- mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
mZoomRingController.setVisible(true);
mInZoomTapDragMode = true;
- mZoomRingController.handleDoubleTapEvent(ev);
+ return mZoomRingController.handleDoubleTapEvent(ev);
} else {
mTouchMode = TOUCH_INIT_MODE;
mPreventDrag = mForwardTouchEvents;
}
- if (mTouchMode == TOUCH_INIT_MODE) {
+ // don't trigger the link if zoom ring is visible
+ if (mTouchMode == TOUCH_INIT_MODE
+ && !mZoomRingController.isVisible()) {
mPrivateHandler.sendMessageDelayed(mPrivateHandler
.obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
}
@@ -3485,9 +3473,6 @@ public class WebView extends AbsoluteLayout
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
}
- // Prevent double-tap from being invoked
- mPreviousUpTime = 0;
-
// if it starts nearly horizontal or vertical, enforce it
int ax = Math.abs(deltaX);
int ay = Math.abs(deltaY);
@@ -3597,6 +3582,10 @@ public class WebView extends AbsoluteLayout
case MotionEvent.ACTION_UP: {
switch (mTouchMode) {
case TOUCH_INIT_MODE: // tap
+ if (mZoomRingController.isVisible()) {
+ // don't trigger the link if zoom ring is visible
+ break;
+ }
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
if (getSettings().supportZoom()
&& (mMinZoomScale < mMaxZoomScale)) {
@@ -3611,7 +3600,7 @@ public class WebView extends AbsoluteLayout
break;
case TOUCH_SELECT_MODE:
commitCopy();
- mTouchSelection = mExtendSelection = false;
+ mTouchSelection = false;
break;
case SCROLL_ZOOM_ANIMATION_IN:
case SCROLL_ZOOM_ANIMATION_OUT:
@@ -3679,7 +3668,6 @@ public class WebView extends AbsoluteLayout
mVelocityTracker.recycle();
mVelocityTracker = null;
}
- mPreviousUpTime = eventTime;
break;
}
case MotionEvent.ACTION_CANCEL: {
@@ -4110,6 +4098,14 @@ public class WebView extends AbsoluteLayout
}
/**
+ * @hide pending API council? Assuming we make ZoomRingController itself
+ * public, which I think we will.
+ */
+ public ZoomRingController getZoomRingController() {
+ return mZoomRingController;
+ }
+
+ /**
* Perform zoom in in the webview
* @return TRUE if zoom in succeeds. FALSE if no zoom changes.
*/
@@ -4193,16 +4189,15 @@ public class WebView extends AbsoluteLayout
return;
}
switchOutDrawHistory();
- // FIXME: we don't know if the current (x,y) is on a focus node or
- // not -- so playing the sound effect here is premature
- if (nativeUpdateFocusNode()) {
- playSoundEffect(SoundEffectConstants.CLICK);
- }
// mLastTouchX and mLastTouchY are the point in the current viewport
int contentX = viewToContent((int) mLastTouchX + mScrollX);
int contentY = viewToContent((int) mLastTouchY + mScrollY);
int contentSize = ViewConfiguration.get(getContext()).getScaledTouchSlop();
nativeMotionUp(contentX, contentY, contentSize, true);
+ if (nativeUpdateFocusNode() && !mFocusNode.mIsTextField
+ && !mFocusNode.mIsTextArea) {
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
}
@Override
@@ -5013,6 +5008,7 @@ public class WebView extends AbsoluteLayout
private native boolean nativeUpdateFocusNode();
private native Rect nativeGetFocusRingBounds();
private native Rect nativeGetNavBounds();
+ private native void nativeInstrumentReport();
private native void nativeMarkNodeInvalid(int node);
private native void nativeMotionUp(int x, int y, int slop, boolean isClick);
// returns false if it handled the key
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 8f78887..b979032 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -330,8 +330,7 @@ final class WebViewCore {
String currentText, int keyCode, int keyValue, boolean down,
boolean cap, boolean fn, boolean sym);
- private native void nativeSaveDocumentState(int frame, int node, int x,
- int y);
+ private native void nativeSaveDocumentState(int frame);
private native void nativeSetFinalFocus(int framePtr, int nodePtr, int x,
int y, boolean block);
@@ -777,8 +776,7 @@ final class WebViewCore {
case SAVE_DOCUMENT_STATE: {
FocusData fDat = (FocusData) msg.obj;
- nativeSaveDocumentState(fDat.mFrame, fDat.mNode,
- fDat.mX, fDat.mY);
+ nativeSaveDocumentState(fDat.mFrame);
break;
}
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 96f3698..1004e30 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -531,33 +531,34 @@ public class WebViewDatabase {
* @param url The url
* @return CacheResult The CacheManager.CacheResult
*/
- @SuppressWarnings("deprecation")
CacheResult getCache(String url) {
if (url == null || mCacheDatabase == null) {
return null;
}
- CacheResult ret = null;
- final String s = "SELECT filepath, lastmodify, etag, expires, mimetype, encoding, httpstatus, location, contentlength FROM cache WHERE url = ";
- StringBuilder sb = new StringBuilder(256);
- sb.append(s);
- DatabaseUtils.appendEscapedSQLString(sb, url);
- Cursor cursor = mCacheDatabase.rawQuery(sb.toString(), null);
+ Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, "
+ + "mimetype, encoding, httpstatus, location, contentlength "
+ + "FROM cache WHERE url = ?",
+ new String[] { url });
- if (cursor.moveToFirst()) {
- ret = new CacheResult();
- ret.localPath = cursor.getString(0);
- ret.lastModified = cursor.getString(1);
- ret.etag = cursor.getString(2);
- ret.expires = cursor.getLong(3);
- ret.mimeType = cursor.getString(4);
- ret.encoding = cursor.getString(5);
- ret.httpStatusCode = cursor.getInt(6);
- ret.location = cursor.getString(7);
- ret.contentLength = cursor.getLong(8);
+ try {
+ if (cursor.moveToFirst()) {
+ CacheResult ret = new CacheResult();
+ ret.localPath = cursor.getString(0);
+ ret.lastModified = cursor.getString(1);
+ ret.etag = cursor.getString(2);
+ ret.expires = cursor.getLong(3);
+ ret.mimeType = cursor.getString(4);
+ ret.encoding = cursor.getString(5);
+ ret.httpStatusCode = cursor.getInt(6);
+ ret.location = cursor.getString(7);
+ ret.contentLength = cursor.getLong(8);
+ return ret;
+ }
+ } finally {
+ if (cursor != null) cursor.close();
}
- cursor.close();
- return ret;
+ return null;
}
/**
@@ -565,16 +566,12 @@ public class WebViewDatabase {
*
* @param url The url
*/
- @SuppressWarnings("deprecation")
void removeCache(String url) {
if (url == null || mCacheDatabase == null) {
return;
}
- StringBuilder sb = new StringBuilder(256);
- sb.append("DELETE FROM cache WHERE url = ");
- DatabaseUtils.appendEscapedSQLString(sb, url);
- mCacheDatabase.execSQL(sb.toString());
+ mCacheDatabase.execSQL("DELETE FROM cache WHERE url = ?", new String[] { url });
}
/**
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 378d218..c012e25 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -31,6 +31,7 @@ import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -1622,6 +1623,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
handled = super.showContextMenuForChild(AbsListView.this);
}
+ if (handled) {
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ }
return handled;
}
diff --git a/core/java/android/widget/CursorFilter.java b/core/java/android/widget/CursorFilter.java
index afd5b10..dbded69 100644
--- a/core/java/android/widget/CursorFilter.java
+++ b/core/java/android/widget/CursorFilter.java
@@ -60,11 +60,10 @@ class CursorFilter extends Filter {
}
@Override
- protected void publishResults(CharSequence constraint,
- FilterResults results) {
+ protected void publishResults(CharSequence constraint, FilterResults results) {
Cursor oldCursor = mClient.getCursor();
- if (results.values != oldCursor) {
+ if (results.values != null && results.values != oldCursor) {
mClient.changeCursor((Cursor) results.values);
}
}
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 67010b2..54f2707 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -47,11 +47,8 @@ public class DatePicker extends FrameLayout {
/* UI Components */
private final NumberPicker mDayPicker;
private final NumberPicker mMonthPicker;
- private final NumberPicker mYearPicker;
-
- private final int mStartYear;
- private final int mEndYear;
-
+ private final NumberPicker mYearPicker;
+
/**
* How we notify users the date has changed.
*/
@@ -87,12 +84,9 @@ public class DatePicker extends FrameLayout {
public DatePicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- LayoutInflater inflater =
- (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.date_picker,
- this, // we are the parent
- true);
-
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.date_picker, this, true);
+
mDayPicker = (NumberPicker) findViewById(R.id.day);
mDayPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
mDayPicker.setSpeed(100);
@@ -134,20 +128,17 @@ public class DatePicker extends FrameLayout {
});
// attributes
- TypedArray a = context
- .obtainStyledAttributes(attrs, R.styleable.DatePicker);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker);
- mStartYear = a.getInt(R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
- mEndYear = a.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
+ int mStartYear = a.getInt(R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
+ int mEndYear = a.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
mYearPicker.setRange(mStartYear, mEndYear);
a.recycle();
// initialize to current date
Calendar cal = Calendar.getInstance();
- init(cal.get(Calendar.YEAR),
- cal.get(Calendar.MONTH),
- cal.get(Calendar.DAY_OF_MONTH), null);
+ init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), null);
// re-order the number pickers to match the current date format
reorderPickers();
diff --git a/core/java/android/widget/Filter.java b/core/java/android/widget/Filter.java
index 7f1601e..a2316cf 100644
--- a/core/java/android/widget/Filter.java
+++ b/core/java/android/widget/Filter.java
@@ -20,6 +20,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.util.Log;
/**
* <p>A filter constrains data with a filtering pattern.</p>
@@ -36,6 +37,8 @@ import android.os.Message;
* @see android.widget.Filterable
*/
public abstract class Filter {
+ private static final String LOG_TAG = "Filter";
+
private static final String THREAD_NAME = "Filter";
private static final int FILTER_TOKEN = 0xD0D0F00D;
private static final int FINISH_TOKEN = 0xDEADBEEF;
@@ -221,6 +224,9 @@ public abstract class Filter {
RequestArguments args = (RequestArguments) msg.obj;
try {
args.results = performFiltering(args.constraint);
+ } catch (Exception e) {
+ args.results = new FilterResults();
+ Log.w(LOG_TAG, "An exception occured during performFiltering()!", e);
} finally {
message = mResultHandler.obtainMessage(what);
message.obj = args;
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index ffabb02..e7b303a 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -27,6 +27,7 @@ import android.util.Config;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -994,6 +995,7 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
return;
}
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
long id = getItemIdAtPosition(mDownTouchPosition);
dispatchLongPress(mDownTouchView, mDownTouchPosition, id);
}
@@ -1086,6 +1088,10 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
handled = super.showContextMenuForChild(this);
}
+ if (handled) {
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ }
+
return handled;
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index b5d4e2d..4ae322e 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -48,6 +48,7 @@ import android.widget.RemoteViews.RemoteView;
* @attr ref android.R.styleable#ImageView_maxHeight
* @attr ref android.R.styleable#ImageView_tint
* @attr ref android.R.styleable#ImageView_scaleType
+ * @attr ref android.R.styleable#ImageView_cropToPadding
*/
@RemoteView
public class ImageView extends View {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a1023bd..25afee8 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -35,6 +35,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater.Filter;
import android.view.View.OnClickListener;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -548,6 +550,54 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Equivalent to calling {@link android.widget.ViewFlipper#startFlipping()}
+ * or {@link android.widget.ViewFlipper#stopFlipping()} along with
+ * {@link android.widget.ViewFlipper#setFlipInterval(int)}.
+ */
+ private class SetFlipping extends Action {
+ public SetFlipping(int id, boolean flipping, int milliseconds) {
+ this.viewId = id;
+ this.flipping = flipping;
+ this.milliseconds = milliseconds;
+ }
+
+ public SetFlipping(Parcel parcel) {
+ viewId = parcel.readInt();
+ flipping = parcel.readInt() != 0;
+ milliseconds = parcel.readInt();
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(TAG);
+ dest.writeInt(viewId);
+ dest.writeInt(flipping ? 1 : 0);
+ dest.writeInt(milliseconds);
+ }
+
+ @Override
+ public void apply(View root) {
+ final View target = root.findViewById(viewId);
+ if (target instanceof ViewFlipper) {
+ final ViewFlipper flipper = (ViewFlipper) target;
+ if (milliseconds != -1) {
+ flipper.setFlipInterval(milliseconds);
+ }
+ if (flipping) {
+ flipper.startFlipping();
+ } else {
+ flipper.stopFlipping();
+ }
+ }
+ }
+
+ int viewId;
+ boolean flipping;
+ int milliseconds;
+
+ public final static int TAG = 10;
+ }
+
+ /**
* Create a new RemoteViews object that will display the views contained
* in the specified layout file.
*
@@ -603,6 +653,9 @@ public class RemoteViews implements Parcelable, Filter {
case SetTextColor.TAG:
mActions.add(new SetTextColor(parcel));
break;
+ case SetFlipping.TAG:
+ mActions.add(new SetFlipping(parcel));
+ break;
default:
throw new ActionException("Tag " + tag + "not found");
}
@@ -769,6 +822,22 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Equivalent to calling {@link android.widget.ViewFlipper#startFlipping()}
+ * or {@link android.widget.ViewFlipper#stopFlipping()} along with
+ * {@link android.widget.ViewFlipper#setFlipInterval(int)}.
+ *
+ * @param viewId The id of the view to apply changes to
+ * @param flipping True means we should
+ * {@link android.widget.ViewFlipper#startFlipping()}, otherwise
+ * {@link android.widget.ViewFlipper#stopFlipping()}.
+ * @param milliseconds How long to wait before flipping to the next view, or
+ * -1 to leave unchanged.
+ */
+ public void setFlipping(int viewId, boolean flipping, int milliseconds) {
+ addAction(new SetFlipping(viewId, flipping, milliseconds));
+ }
+
+ /**
* Inflates the view hierarchy represented by this object and applies
* all of the actions.
*
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d21c017..2ae5d4e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3844,7 +3844,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean doDown = true;
if (otherEvent != null) {
try {
- boolean handled = mMovement.onKeyOther(this, (Editable) mText,
+ boolean handled = mMovement.onKeyOther(this, (Spannable) mText,
otherEvent);
doDown = false;
if (handled) {
diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java
index 8c652e5..fa8935e 100644
--- a/core/java/android/widget/ViewAnimator.java
+++ b/core/java/android/widget/ViewAnimator.java
@@ -28,6 +28,9 @@ import android.view.animation.AnimationUtils;
/**
* Base class for a {@link FrameLayout} container that will perform animations
* when switching between its views.
+ *
+ * @attr ref android.R.styleable#ViewAnimator_inAnimation
+ * @attr ref android.R.styleable#ViewAnimator_outAnimation
*/
public class ViewAnimator extends FrameLayout {
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index a3c15d9..e20bfdf 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -22,12 +22,16 @@ import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
+import android.widget.RemoteViews.RemoteView;
/**
* Simple {@link ViewAnimator} that will animate between two or more views
* that have been added to it. Only one child is shown at a time. If
* requested, can automatically flip between each child at a regular interval.
+ *
+ * @attr ref android.R.styleable#ViewFlipper_flipInterval
*/
+@RemoteView
public class ViewFlipper extends ViewAnimator {
private int mFlipInterval = 3000;
private boolean mKeepFlipping = false;
diff --git a/core/java/android/widget/ZoomButton.java b/core/java/android/widget/ZoomButton.java
index df3f307..0df919d 100644
--- a/core/java/android/widget/ZoomButton.java
+++ b/core/java/android/widget/ZoomButton.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
+import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -57,6 +58,7 @@ public class ZoomButton extends ImageButton implements OnLongClickListener {
mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent e) {
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
onLongClick(ZoomButton.this);
}
});
diff --git a/core/java/android/widget/ZoomRing.java b/core/java/android/widget/ZoomRing.java
index 20d6056..be3b1fb 100644
--- a/core/java/android/widget/ZoomRing.java
+++ b/core/java/android/widget/ZoomRing.java
@@ -6,10 +6,9 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
+import android.graphics.drawable.RotateDrawable;
import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
+import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -18,17 +17,20 @@ import android.view.ViewConfiguration;
* @hide
*/
public class ZoomRing extends View {
-
+
// TODO: move to ViewConfiguration?
- private static final int DOUBLE_TAP_DISMISS_TIMEOUT = ViewConfiguration.getJumpTapTimeout();
+ static final int DOUBLE_TAP_DISMISS_TIMEOUT = ViewConfiguration.getJumpTapTimeout();
// TODO: get from theme
private static final int DISABLED_ALPHA = 160;
-
+
private static final String TAG = "ZoomRing";
+ // TODO: Temporary until the trail is done
+ private static final boolean DRAW_TRAIL = false;
+
// TODO: xml
- private static final int THUMB_DISTANCE = 63;
-
+ private static final int THUMB_DISTANCE = 63;
+
/** To avoid floating point calculations, we multiply radians by this value. */
public static final int RADIAN_INT_MULTIPLIER = 100000000;
/** PI using our multiplier. */
@@ -36,68 +38,81 @@ public class ZoomRing extends View {
/** PI/2 using our multiplier. */
private static final int HALF_PI_INT_MULTIPLIED = PI_INT_MULTIPLIED / 2;
+ private int mZeroAngle = HALF_PI_INT_MULTIPLIED * 3;
+
private static final int THUMB_GRAB_SLOP = PI_INT_MULTIPLIED / 4;
-
+
/** The cached X of our center. */
private int mCenterX;
- /** The cached Y of our center. */
+ /** The cached Y of our center. */
private int mCenterY;
/** The angle of the thumb (in int radians) */
private int mThumbAngle;
private boolean mIsThumbAngleValid;
- private int mThumbCenterX;
- private int mThumbCenterY;
private int mThumbHalfWidth;
private int mThumbHalfHeight;
-
- private int mCallbackThreshold = Integer.MAX_VALUE;
-
- /** The accumulated amount of drag for the thumb (in int radians). */
- private int mAcculumalatedThumbDrag = 0;
-
+
/** The inner radius of the track. */
private int mBoundInnerRadiusSquared = 0;
/** The outer radius of the track. */
private int mBoundOuterRadiusSquared = Integer.MAX_VALUE;
-
+
private int mPreviousWidgetDragX;
private int mPreviousWidgetDragY;
-
+
private boolean mDrawThumb = true;
private Drawable mThumbDrawable;
-
+
private static final int MODE_IDLE = 0;
private static final int MODE_DRAG_THUMB = 1;
+ /**
+ * User has his finger down, but we are waiting for him to pass the touch
+ * slop before going into the #MODE_MOVE_ZOOM_RING. This is a good time to
+ * show the movable hint.
+ */
+ private static final int MODE_WAITING_FOR_MOVE_ZOOM_RING = 4;
private static final int MODE_MOVE_ZOOM_RING = 2;
private static final int MODE_TAP_DRAG = 3;
private int mMode;
- private long mPreviousTapTime;
-
- private Handler mHandler = new Handler();
-
+ private long mPreviousDownTime;
+ private int mPreviousDownX;
+ private int mPreviousDownY;
+
private Disabler mDisabler = new Disabler();
-
+
private OnZoomRingCallback mCallback;
-
+ private int mPreviousCallbackAngle;
+ private int mCallbackThreshold = Integer.MAX_VALUE;
+
private boolean mResetThumbAutomatically = true;
private int mThumbDragStartAngle;
-
+ private final int mTouchSlop;
+ private Drawable mTrail;
+ private double mAcculumalatedTrailAngle;
+
public ZoomRing(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- // TODO get drawable from style instead
+
+ ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
+ mTouchSlop = viewConfiguration.getScaledTouchSlop();
+
+ // TODO get drawables from style instead
Resources res = context.getResources();
mThumbDrawable = res.getDrawable(R.drawable.zoom_ring_thumb);
-
+ if (DRAW_TRAIL) {
+ mTrail = res.getDrawable(R.drawable.zoom_ring_trail).mutate();
+ }
+
// TODO: add padding to drawable
setBackgroundResource(R.drawable.zoom_ring_track);
// TODO get from style
setBounds(30, Integer.MAX_VALUE);
-
+
mThumbHalfHeight = mThumbDrawable.getIntrinsicHeight() / 2;
mThumbHalfWidth = mThumbDrawable.getIntrinsicWidth() / 2;
-
+
mCallbackThreshold = PI_INT_MULTIPLIED / 6;
}
@@ -108,7 +123,7 @@ public class ZoomRing extends View {
public ZoomRing(Context context) {
this(context, null);
}
-
+
public void setCallback(OnZoomRingCallback callback) {
mCallback = callback;
}
@@ -132,26 +147,49 @@ public class ZoomRing extends View {
mBoundOuterRadiusSquared = Integer.MAX_VALUE;
}
}
-
+
public void setThumbAngle(int angle) {
mThumbAngle = angle;
- mThumbCenterX = (int) (Math.cos(1f * angle / RADIAN_INT_MULTIPLIER) * THUMB_DISTANCE)
- + mCenterX;
- mThumbCenterY = (int) (Math.sin(1f * angle / RADIAN_INT_MULTIPLIER) * THUMB_DISTANCE)
- * -1 + mCenterY;
+ int unoffsetAngle = angle + mZeroAngle;
+ int thumbCenterX = (int) (Math.cos(1f * unoffsetAngle / RADIAN_INT_MULTIPLIER) *
+ THUMB_DISTANCE) + mCenterX;
+ int thumbCenterY = (int) (Math.sin(1f * unoffsetAngle / RADIAN_INT_MULTIPLIER) *
+ THUMB_DISTANCE) * -1 + mCenterY;
+
+ mThumbDrawable.setBounds(thumbCenterX - mThumbHalfWidth,
+ thumbCenterY - mThumbHalfHeight,
+ thumbCenterX + mThumbHalfWidth,
+ thumbCenterY + mThumbHalfHeight);
+
+ if (DRAW_TRAIL) {
+ double degrees;
+ degrees = Math.min(359.0, Math.abs(mAcculumalatedTrailAngle));
+ int level = (int) (10000.0 * degrees / 360.0);
+
+ mTrail.setLevel((int) (10000.0 *
+ (-Math.toDegrees(angle / (double) RADIAN_INT_MULTIPLIER) -
+ degrees + 90) / 360.0));
+ ((RotateDrawable) mTrail).getDrawable().setLevel(level);
+ }
+
invalidate();
}
-
+
+ public void resetThumbAngle(int angle) {
+ mPreviousCallbackAngle = angle;
+ setThumbAngle(angle);
+ }
+
public void resetThumbAngle() {
if (mResetThumbAutomatically) {
- setThumbAngle(HALF_PI_INT_MULTIPLIED);
+ resetThumbAngle(0);
}
}
-
+
public void setResetThumbAutomatically(boolean resetThumbAutomatically) {
mResetThumbAutomatically = resetThumbAutomatically;
}
-
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
@@ -162,7 +200,7 @@ public class ZoomRing extends View {
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
-
+
// Cache the center point
mCenterX = (right - left) / 2;
mCenterY = (bottom - top) / 2;
@@ -172,8 +210,12 @@ public class ZoomRing extends View {
if (mThumbAngle == Integer.MIN_VALUE) {
resetThumbAngle();
}
+
+ if (DRAW_TRAIL) {
+ mTrail.setBounds(0, 0, right - left, bottom - top);
+ }
}
-
+
@Override
public boolean onTouchEvent(MotionEvent event) {
return handleTouch(event.getAction(), event.getEventTime(),
@@ -184,61 +226,66 @@ public class ZoomRing extends View {
private void resetState() {
mMode = MODE_IDLE;
mPreviousWidgetDragX = mPreviousWidgetDragY = Integer.MIN_VALUE;
- mAcculumalatedThumbDrag = 0;
+ mAcculumalatedTrailAngle = 0.0;
mIsThumbAngleValid = false;
}
-
+
public void setTapDragMode(boolean tapDragMode, int x, int y) {
resetState();
mMode = tapDragMode ? MODE_TAP_DRAG : MODE_IDLE;
mIsThumbAngleValid = false;
-
+
if (tapDragMode && mCallback != null) {
onThumbDragStarted(getAngle(x - mCenterX, y - mCenterY));
}
}
-
+
public boolean handleTouch(int action, long time, int x, int y, int rawX, int rawY) {
switch (action) {
-
+
case MotionEvent.ACTION_DOWN:
- if (mPreviousTapTime + DOUBLE_TAP_DISMISS_TIMEOUT >= time) {
+ if (mPreviousDownTime + DOUBLE_TAP_DISMISS_TIMEOUT >= time) {
if (mCallback != null) {
mCallback.onZoomRingDismissed();
}
} else {
- mPreviousTapTime = time;
+ mPreviousDownTime = time;
+ mPreviousDownX = x;
+ mPreviousDownY = y;
}
resetState();
return true;
-
+
case MotionEvent.ACTION_MOVE:
// Fall through to code below switch
break;
-
+
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (mCallback != null) {
- if (mMode == MODE_MOVE_ZOOM_RING) {
- mCallback.onZoomRingMovingStopped();
+ if (mMode == MODE_MOVE_ZOOM_RING || mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) {
+ mCallback.onZoomRingSetMovableHintVisible(false);
+ if (mMode == MODE_MOVE_ZOOM_RING) {
+ mCallback.onZoomRingMovingStopped();
+ }
} else if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) {
onThumbDragStopped(getAngle(x - mCenterX, y - mCenterY));
}
}
mDisabler.setEnabling(true);
return true;
-
+
default:
return false;
}
-
+
// local{X,Y} will be where the center of the widget is (0,0)
int localX = x - mCenterX;
int localY = y - mCenterY;
boolean isTouchingThumb = true;
boolean isInBounds = true;
int touchAngle = getAngle(localX, localY);
-
+
int radiusSquared = localX * localX + localY * localY;
if (radiusSquared < mBoundInnerRadiusSquared ||
radiusSquared > mBoundOuterRadiusSquared) {
@@ -246,7 +293,7 @@ public class ZoomRing extends View {
isTouchingThumb = false;
isInBounds = false;
}
-
+
int deltaThumbAndTouch = getDelta(touchAngle, mThumbAngle);
int absoluteDeltaThumbAndTouch = deltaThumbAndTouch >= 0 ?
deltaThumbAndTouch : -deltaThumbAndTouch;
@@ -255,19 +302,35 @@ public class ZoomRing extends View {
// Didn't grab close enough to the thumb
isTouchingThumb = false;
}
-
+
if (mMode == MODE_IDLE) {
- mMode = isTouchingThumb ? MODE_DRAG_THUMB : MODE_MOVE_ZOOM_RING;
-
+ if (isTouchingThumb) {
+ mMode = MODE_DRAG_THUMB;
+ } else {
+ mMode = MODE_WAITING_FOR_MOVE_ZOOM_RING;
+ }
+
if (mCallback != null) {
if (mMode == MODE_DRAG_THUMB) {
onThumbDragStarted(touchAngle);
- } else if (mMode == MODE_MOVE_ZOOM_RING) {
+ } else if (mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) {
+ mCallback.onZoomRingSetMovableHintVisible(true);
+ }
+ }
+
+ } else if (mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) {
+ if (Math.abs(x - mPreviousDownX) > mTouchSlop ||
+ Math.abs(y - mPreviousDownY) > mTouchSlop) {
+ /* Make sure the user has moved the slop amount before going into that mode. */
+ mMode = MODE_MOVE_ZOOM_RING;
+
+ if (mCallback != null) {
mCallback.onZoomRingMovingStarted();
}
}
}
-
+
+ // Purposefully not an "else if"
if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) {
if (isInBounds) {
onThumbDragged(touchAngle, mIsThumbAngleValid ? deltaThumbAndTouch : 0);
@@ -277,13 +340,13 @@ public class ZoomRing extends View {
} else if (mMode == MODE_MOVE_ZOOM_RING) {
onZoomRingMoved(rawX, rawY);
}
-
+
return true;
}
-
+
private int getDelta(int angle1, int angle2) {
int delta = angle1 - angle2;
-
+
// Assume this is a result of crossing over the discontinuous 0 -> 2pi
if (delta > PI_INT_MULTIPLIED || delta < -PI_INT_MULTIPLIED) {
// Bring both the radians and previous angle onto a continuous range
@@ -295,7 +358,7 @@ public class ZoomRing extends View {
delta -= PI_INT_MULTIPLIED * 2;
}
}
-
+
return delta;
}
@@ -303,46 +366,69 @@ public class ZoomRing extends View {
mThumbDragStartAngle = startAngle;
mCallback.onZoomRingThumbDraggingStarted(startAngle);
}
-
+
private void onThumbDragged(int touchAngle, int deltaAngle) {
- mAcculumalatedThumbDrag += deltaAngle;
- if (mAcculumalatedThumbDrag > mCallbackThreshold
- || mAcculumalatedThumbDrag < -mCallbackThreshold) {
+ mAcculumalatedTrailAngle += Math.toDegrees(deltaAngle / (double) RADIAN_INT_MULTIPLIER);
+ int totalDeltaAngle = getDelta(touchAngle, mPreviousCallbackAngle);
+ if (totalDeltaAngle > mCallbackThreshold
+ || totalDeltaAngle < -mCallbackThreshold) {
if (mCallback != null) {
boolean canStillZoom = mCallback.onZoomRingThumbDragged(
- mAcculumalatedThumbDrag / mCallbackThreshold,
- mAcculumalatedThumbDrag, mThumbDragStartAngle, touchAngle);
+ totalDeltaAngle / mCallbackThreshold,
+ mThumbDragStartAngle, touchAngle);
mDisabler.setEnabling(canStillZoom);
+
+ if (canStillZoom) {
+ // TODO: we're trying the haptics to see how it goes with
+ // users, so we're ignoring the settings (for now)
+ performHapticFeedback(HapticFeedbackConstants.ZOOM_RING_TICK,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING |
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
}
- mAcculumalatedThumbDrag = 0;
+
+ // Get the closest tick and lock on there
+ mPreviousCallbackAngle = getClosestTickAngle(touchAngle);
}
-
+
setThumbAngle(touchAngle);
mIsThumbAngleValid = true;
}
-
+
+ private int getClosestTickAngle(int angle) {
+ int smallerAngleDistance = angle % mCallbackThreshold;
+ int smallerAngle = angle - smallerAngleDistance;
+ if (smallerAngleDistance < mCallbackThreshold / 2) {
+ // Closer to the smaller angle
+ return smallerAngle;
+ } else {
+ // Closer to the bigger angle (premodding)
+ return (smallerAngle + mCallbackThreshold) % (PI_INT_MULTIPLIED * 2);
+ }
+ }
+
private void onThumbDragStopped(int stopAngle) {
mCallback.onZoomRingThumbDraggingStopped(stopAngle);
}
-
+
private void onZoomRingMoved(int x, int y) {
if (mPreviousWidgetDragX != Integer.MIN_VALUE) {
int deltaX = x - mPreviousWidgetDragX;
int deltaY = y - mPreviousWidgetDragY;
-
+
if (mCallback != null) {
mCallback.onZoomRingMoved(deltaX, deltaY);
}
}
-
+
mPreviousWidgetDragX = x;
mPreviousWidgetDragY = y;
}
-
+
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
-
+
if (!hasWindowFocus && mCallback != null) {
mCallback.onZoomRingDismissed();
}
@@ -353,22 +439,25 @@ public class ZoomRing extends View {
// Convert from [-pi,pi] to {0,2pi]
if (radians < 0) {
- return -radians;
+ radians = -radians;
} else if (radians > 0) {
- return 2 * PI_INT_MULTIPLIED - radians;
+ radians = 2 * PI_INT_MULTIPLIED - radians;
} else {
- return 0;
+ radians = 0;
}
+
+ radians = radians - mZeroAngle;
+ return radians >= 0 ? radians : radians + 2 * PI_INT_MULTIPLIED;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
-
+
if (mDrawThumb) {
- mThumbDrawable.setBounds(mThumbCenterX - mThumbHalfWidth, mThumbCenterY
- - mThumbHalfHeight, mThumbCenterX + mThumbHalfWidth, mThumbCenterY
- + mThumbHalfHeight);
+ if (DRAW_TRAIL) {
+ mTrail.draw(canvas);
+ }
mThumbDrawable.draw(canvas);
}
}
@@ -409,12 +498,14 @@ public class ZoomRing extends View {
}
public interface OnZoomRingCallback {
+ void onZoomRingSetMovableHintVisible(boolean visible);
+
void onZoomRingMovingStarted();
boolean onZoomRingMoved(int deltaX, int deltaY);
void onZoomRingMovingStopped();
void onZoomRingThumbDraggingStarted(int startAngle);
- boolean onZoomRingThumbDragged(int numLevels, int dragAmount, int startAngle, int curAngle);
+ boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle);
void onZoomRingThumbDraggingStopped(int endAngle);
void onZoomRingDismissed();
diff --git a/core/java/android/widget/ZoomRingController.java b/core/java/android/widget/ZoomRingController.java
index 2ca0374..eb28767 100644
--- a/core/java/android/widget/ZoomRingController.java
+++ b/core/java/android/widget/ZoomRingController.java
@@ -17,14 +17,17 @@
package android.widget;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
+import android.os.Vibrator;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
@@ -42,6 +45,7 @@ import android.view.animation.DecelerateInterpolator;
/**
* TODO: Docs
+ *
* @hide
*/
public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
@@ -222,7 +226,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
public ZoomRingController(Context context, View ownerView) {
mContext = context;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-
+
mOwnerView = ownerView;
mZoomRing = new ZoomRing(context);
@@ -437,7 +441,15 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
case MotionEvent.ACTION_UP:
mTouchMode = TOUCH_MODE_IDLE;
+
+ /*
+ * This is a power-user feature that only shows the
+ * zoom while the user is performing the tap-drag.
+ * That means once it is released, the zoom ring
+ * should disappear.
+ */
mZoomRing.setTapDragMode(false, (int) event.getX(), (int) event.getY());
+ dismissZoomRingDelayed(0);
break;
}
break;
@@ -560,10 +572,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
mZoomRing.handleTouch(event.getAction(), event.getEventTime(), x, y, rawX, rawY);
}
+ public void onZoomRingSetMovableHintVisible(boolean visible) {
+ setPanningArrowsVisible(visible);
+ }
+
public void onZoomRingMovingStarted() {
mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
mScroller.abortAnimation();
- setPanningArrowsVisible(true);
}
private void setPanningArrowsVisible(boolean visible) {
@@ -641,8 +656,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
}
}
- public boolean onZoomRingThumbDragged(int numLevels, int dragAmount, int startAngle,
- int curAngle) {
+ public boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle) {
if (mCallback != null) {
int deltaZoomLevel = -numLevels;
int globalZoomCenterX = mContainerLayoutParams.x + mZoomRing.getLeft() +
@@ -650,7 +664,8 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
int globalZoomCenterY = mContainerLayoutParams.y + mZoomRing.getTop() +
mZoomRingHeight / 2;
- return mCallback.onDragZoom(deltaZoomLevel, globalZoomCenterX - mOwnerViewBounds.left,
+ return mCallback.onDragZoom(deltaZoomLevel,
+ globalZoomCenterX - mOwnerViewBounds.left,
globalZoomCenterY - mOwnerViewBounds.top,
(float) startAngle / ZoomRing.RADIAN_INT_MULTIPLIER,
(float) curAngle / ZoomRing.RADIAN_INT_MULTIPLIER);
@@ -719,6 +734,45 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
ensureZoomRingIsCentered();
}
+ /**
+ * Shows a "tutorial" (some text) to the user teaching her the new zoom
+ * invocation method.
+ * <p>
+ * It checks the global system setting to ensure this has not been seen
+ * before. Furthermore, if the application does not have privilege to write
+ * to the system settings, it will store this bit locally in a shared
+ * preference.
+ *
+ * @hide This should only be used by our main apps--browser, maps, and
+ * gallery
+ */
+ public static void showZoomTutorialOnce(Context context) {
+ ContentResolver cr = context.getContentResolver();
+ if (Settings.System.getInt(cr, SETTING_NAME_SHOWN_TOAST, 0) == 1) {
+ return;
+ }
+
+ SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE);
+ if (sp.getInt(SETTING_NAME_SHOWN_TOAST, 0) == 1) {
+ return;
+ }
+
+ try {
+ Settings.System.putInt(cr, SETTING_NAME_SHOWN_TOAST, 1);
+ } catch (SecurityException e) {
+ /*
+ * The app does not have permission to clear this global flag, make
+ * sure the user does not see the message when he comes back to this
+ * same app at least.
+ */
+ sp.edit().putInt(SETTING_NAME_SHOWN_TOAST, 1).commit();
+ }
+
+ Toast.makeText(context,
+ com.android.internal.R.string.tutorial_double_tap_to_zoom_message_short,
+ Toast.LENGTH_LONG).show();
+ }
+
private class Panner implements Runnable {
private static final int RUN_DELAY = 15;
private static final float STOP_SLOWDOWN = 0.8f;
diff --git a/core/java/com/android/internal/gadget/IGadgetHost.aidl b/core/java/com/android/internal/gadget/IGadgetHost.aidl
index a5b8654..e7b5a1e 100644
--- a/core/java/com/android/internal/gadget/IGadgetHost.aidl
+++ b/core/java/com/android/internal/gadget/IGadgetHost.aidl
@@ -17,11 +17,12 @@
package com.android.internal.gadget;
import android.content.ComponentName;
-import android.gadget.GadgetInfo;
+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
index 1b3946f..a22f3f3 100644
--- a/core/java/com/android/internal/gadget/IGadgetService.aidl
+++ b/core/java/com/android/internal/gadget/IGadgetService.aidl
@@ -17,7 +17,7 @@
package com.android.internal.gadget;
import android.content.ComponentName;
-import android.gadget.GadgetInfo;
+import android.gadget.GadgetProviderInfo;
import com.android.internal.gadget.IGadgetHost;
import android.widget.RemoteViews;
@@ -41,8 +41,8 @@ interface IGadgetService {
//
void updateGadgetIds(in int[] gadgetIds, in RemoteViews views);
void updateGadgetProvider(in ComponentName provider, in RemoteViews views);
- List<GadgetInfo> getInstalledProviders();
- GadgetInfo getGadgetInfo(int gadgetId);
+ List<GadgetProviderInfo> getInstalledProviders();
+ GadgetProviderInfo getGadgetInfo(int gadgetId);
void bindGadgetId(int gadgetId, in ComponentName provider);
}
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index b0b00b2..ac72a20 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -32,8 +32,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
private static final int DO_DELETE_SURROUNDING_TEXT = 80;
private static final int DO_BEGIN_BATCH_EDIT = 90;
private static final int DO_END_BATCH_EDIT = 95;
- private static final int DO_HIDE_STATUS_ICON = 100;
- private static final int DO_SHOW_STATUS_ICON = 110;
+ private static final int DO_REPORT_FULLSCREEN_MODE = 100;
private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
private static final int DO_CLEAR_META_KEY_STATES = 130;
@@ -133,12 +132,8 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
dispatchMessage(obtainMessage(DO_END_BATCH_EDIT));
}
- public void hideStatusIcon() {
- dispatchMessage(obtainMessage(DO_HIDE_STATUS_ICON));
- }
-
- public void showStatusIcon(String packageName, int resId) {
- dispatchMessage(obtainMessageIO(DO_SHOW_STATUS_ICON, resId, packageName));
+ public void reportFullscreenMode(boolean enabled) {
+ dispatchMessage(obtainMessageII(DO_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0));
}
public void performPrivateCommand(String action, Bundle data) {
@@ -323,22 +318,13 @@ public class IInputConnectionWrapper extends IInputContext.Stub {
ic.endBatchEdit();
return;
}
- case DO_HIDE_STATUS_ICON: {
- InputConnection ic = mInputConnection.get();
- if (ic == null || !isActive()) {
- Log.w(TAG, "hideStatusIcon on inactive InputConnection");
- return;
- }
- ic.hideStatusIcon();
- return;
- }
- case DO_SHOW_STATUS_ICON: {
+ case DO_REPORT_FULLSCREEN_MODE: {
InputConnection ic = mInputConnection.get();
if (ic == null || !isActive()) {
Log.w(TAG, "showStatusIcon on inactive InputConnection");
return;
}
- ic.showStatusIcon((String)msg.obj, msg.arg1);
+ ic.reportFullscreenMode(msg.arg1 != 1);
return;
}
case DO_PERFORM_PRIVATE_COMMAND: {
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 7cc8ada..02b6044 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -56,13 +56,11 @@ import com.android.internal.view.IInputContextCallback;
void endBatchEdit();
+ void reportFullscreenMode(boolean enabled);
+
void sendKeyEvent(in KeyEvent event);
void clearMetaKeyStates(int states);
void performPrivateCommand(String action, in Bundle data);
-
- void showStatusIcon(String packageName, int resId);
-
- void hideStatusIcon();
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 2f5cd14..1b1c7f7 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -47,7 +47,7 @@ interface IInputMethodManager {
void showInputMethodPickerFromClient(in IInputMethodClient client);
void setInputMethod(in IBinder token, String id);
void hideMySoftInput(in IBinder token, int flags);
- void updateStatusIcon(int iconId, String iconPackage);
+ void updateStatusIcon(in IBinder token, String packageName, int iconId);
boolean setInputMethodEnabled(String id, boolean enabled);
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index af4ad25..32d9f3d 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -322,18 +322,9 @@ public class InputConnectionWrapper implements InputConnection {
}
}
- public boolean hideStatusIcon() {
+ public boolean reportFullscreenMode(boolean enabled) {
try {
- mIInputContext.showStatusIcon(null, 0);
- return true;
- } catch (RemoteException e) {
- return false;
- }
- }
-
- public boolean showStatusIcon(String packageName, int resId) {
- try {
- mIInputContext.showStatusIcon(packageName, resId);
+ mIInputContext.reportFullscreenMode(enabled);
return true;
} catch (RemoteException e) {
return false;
diff --git a/core/java/com/android/internal/widget/NumberPicker.java b/core/java/com/android/internal/widget/NumberPicker.java
index 20ea6a6..1647c20 100644
--- a/core/java/com/android/internal/widget/NumberPicker.java
+++ b/core/java/com/android/internal/widget/NumberPicker.java
@@ -28,12 +28,8 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnLongClickListener;
-import android.view.animation.Animation;
-import android.view.animation.TranslateAnimation;
-import android.widget.EditText;
-import android.widget.LinearLayout;
import android.widget.TextView;
-import android.widget.ViewSwitcher;
+import android.widget.LinearLayout;
import com.android.internal.R;
@@ -71,25 +67,18 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
private final Runnable mRunnable = new Runnable() {
public void run() {
if (mIncrement) {
- changeCurrent(mCurrent + 1, mSlideUpInAnimation, mSlideUpOutAnimation);
+ changeCurrent(mCurrent + 1);
mHandler.postDelayed(this, mSpeed);
} else if (mDecrement) {
- changeCurrent(mCurrent - 1, mSlideDownInAnimation, mSlideDownOutAnimation);
+ changeCurrent(mCurrent - 1);
mHandler.postDelayed(this, mSpeed);
}
}
};
-
- private final LayoutInflater mInflater;
+
private final TextView mText;
- private final InputFilter mInputFilter;
private final InputFilter mNumberInputFilter;
-
- private final Animation mSlideUpOutAnimation;
- private final Animation mSlideUpInAnimation;
- private final Animation mSlideDownOutAnimation;
- private final Animation mSlideDownInAnimation;
-
+
private String[] mDisplayedValues;
private int mStart;
private int mEnd;
@@ -110,14 +99,14 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
this(context, attrs, 0);
}
- public NumberPicker(Context context, AttributeSet attrs,
- int defStyle) {
+ @SuppressWarnings({"UnusedDeclaration"})
+ public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
setOrientation(VERTICAL);
- mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mInflater.inflate(R.layout.number_picker, this, true);
+ LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.number_picker, this, true);
mHandler = new Handler();
- mInputFilter = new NumberPickerInputFilter();
+ InputFilter inputFilter = new NumberPickerInputFilter();
mNumberInputFilter = new NumberRangeKeyListener();
mIncrementButton = (NumberPickerButton) findViewById(R.id.increment);
mIncrementButton.setOnClickListener(this);
@@ -130,30 +119,9 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
mText = (TextView) findViewById(R.id.timepicker_input);
mText.setOnFocusChangeListener(this);
- mText.setFilters(new InputFilter[] { mInputFilter });
+ mText.setFilters(new InputFilter[] {inputFilter});
mText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
-
- mSlideUpOutAnimation = new TranslateAnimation(
- Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF,
- 0, Animation.RELATIVE_TO_SELF, 0,
- Animation.RELATIVE_TO_SELF, -100);
- mSlideUpOutAnimation.setDuration(200);
- mSlideUpInAnimation = new TranslateAnimation(
- Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF,
- 0, Animation.RELATIVE_TO_SELF, 100,
- Animation.RELATIVE_TO_SELF, 0);
- mSlideUpInAnimation.setDuration(200);
- mSlideDownOutAnimation = new TranslateAnimation(
- Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF,
- 0, Animation.RELATIVE_TO_SELF, 0,
- Animation.RELATIVE_TO_SELF, 100);
- mSlideDownOutAnimation.setDuration(200);
- mSlideDownInAnimation = new TranslateAnimation(
- Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF,
- 0, Animation.RELATIVE_TO_SELF, -100,
- Animation.RELATIVE_TO_SELF, 0);
- mSlideDownInAnimation.setDuration(200);
-
+
if (!isEnabled()) {
setEnabled(false);
}
@@ -228,9 +196,9 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
// now perform the increment/decrement
if (R.id.increment == v.getId()) {
- changeCurrent(mCurrent + 1, mSlideUpInAnimation, mSlideUpOutAnimation);
+ changeCurrent(mCurrent + 1);
} else if (R.id.decrement == v.getId()) {
- changeCurrent(mCurrent - 1, mSlideDownInAnimation, mSlideDownOutAnimation);
+ changeCurrent(mCurrent - 1);
}
}
@@ -240,7 +208,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
: String.valueOf(value);
}
- private void changeCurrent(int current, Animation in, Animation out) {
+ private void changeCurrent(int current) {
// Wrap around the values if we go past the start or end
if (current > mEnd) {