summaryrefslogtreecommitdiffstats
path: root/core/java/android/view
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/view')
-rw-r--r--core/java/android/view/FocusFinder.java2
-rw-r--r--core/java/android/view/GestureDetector.java189
-rw-r--r--core/java/android/view/IWindowManager.aidl3
-rw-r--r--core/java/android/view/KeyCharacterMap.java24
-rw-r--r--core/java/android/view/KeyEvent.java70
-rw-r--r--core/java/android/view/TouchDelegate.java12
-rw-r--r--core/java/android/view/View.java205
-rw-r--r--core/java/android/view/ViewConfiguration.java198
-rw-r--r--core/java/android/view/ViewGroup.java50
-rw-r--r--core/java/android/view/ViewRoot.java68
-rw-r--r--core/java/android/view/VolumePanel.java122
-rw-r--r--core/java/android/view/Window.java15
-rw-r--r--core/java/android/view/WindowManager.java25
-rw-r--r--core/java/android/view/WindowManagerPolicy.java17
-rw-r--r--core/java/android/view/animation/Animation.java79
-rw-r--r--core/java/android/view/animation/AnimationSet.java117
-rw-r--r--core/java/android/view/animation/LayoutAnimationController.java156
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java480
-rw-r--r--core/java/android/view/inputmethod/DefaultInputMethod.java239
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java47
-rw-r--r--core/java/android/view/inputmethod/ExtractedText.java20
-rw-r--r--core/java/android/view/inputmethod/ExtractedTextRequest.java9
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java45
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java107
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java19
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java300
-rw-r--r--core/java/android/view/inputmethod/MutableInputConnectionWrapper.java38
27 files changed, 1747 insertions, 909 deletions
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 4048763..15fb839 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -397,7 +397,7 @@ public class FocusFinder {
int numTouchables = touchables.size();
- int edgeSlop = ViewConfiguration.getEdgeSlop();
+ int edgeSlop = ViewConfiguration.get(root.mContext).getScaledEdgeSlop();
Rect closestBounds = new Rect();
Rect touchableBounds = mOtherRect;
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index fc9af05..a472689 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -18,6 +18,7 @@ package android.view;
import android.os.Handler;
import android.os.Message;
+import android.content.Context;
/**
* Detects various gestures and events using the supplied {@link MotionEvent}s.
@@ -34,7 +35,6 @@ import android.os.Message;
* </ul>
*/
public class GestureDetector {
-
/**
* The listener that is used to notify when gestures occur.
* If you want to listen for all the different gestures then implement
@@ -113,6 +113,14 @@ public class GestureDetector {
}
/**
+ * @hide pending API council
+ */
+ public interface OnDoubleTapListener {
+ boolean onSingleTapConfirmed(MotionEvent e);
+ boolean onDoubleTapEvent(MotionEvent e);
+ }
+
+ /**
* A convenience class to extend when you only want to listen for a
* subset of all the gestures. This implements all methods in the
* {@link OnGestureListener} but does nothing and return {@code false}
@@ -144,22 +152,40 @@ public class GestureDetector {
}
}
- private static final int TOUCH_SLOP_SQUARE = ViewConfiguration.getTouchSlop()
- * ViewConfiguration.getTouchSlop();
-
+ // TODO: ViewConfiguration
+ private int mBiggerTouchSlopSquare = 20 * 20;
+
+ private int mTouchSlopSquare;
+ private int mDoubleTapSlopSquare;
+ private int mMinimumFlingVelocity;
+
+ private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
+ private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
+ // TODO make new double-tap timeout, and define its events (i.e. either time
+ // between down-down or time between up-down)
+ private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getJumpTapTimeout();
+
// constants for Message.what used by GestureHandler below
private static final int SHOW_PRESS = 1;
private static final int LONG_PRESS = 2;
+ private static final int TAP = 3;
private final Handler mHandler;
private final OnGestureListener mListener;
+ private OnDoubleTapListener mDoubleTapListener;
private boolean mInLongPress;
private boolean mAlwaysInTapRegion;
+ private boolean mAlwaysInBiggerTapRegion;
private MotionEvent mCurrentDownEvent;
- private MotionEvent mCurrentUpEvent;
-
+
+ /**
+ * True when the user is still touching for the second tap (down, move, and
+ * up events). Can only be true if there is a double tap listener attached.
+ */
+ private boolean mIsDoubleTapping;
+
private float mLastMotionY;
private float mLastMotionX;
@@ -189,6 +215,12 @@ public class GestureDetector {
case LONG_PRESS:
dispatchLongPress();
break;
+
+ case TAP:
+ if (mDoubleTapListener != null) {
+ mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
+ }
+ break;
default:
throw new RuntimeException("Unknown message " + msg); //never
@@ -203,16 +235,17 @@ public class GestureDetector {
*
* @param listener the listener invoked for all the callbacks, this must
* not be null.
- * @param handler the handler to use, this must
- * not be null.
+ * @param handler the handler to use
*
* @throws NullPointerException if either {@code listener} or
* {@code handler} is null.
+ *
+ * @deprecated Use {@link #GestureDetector(android.content.Context,
+ * android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.
*/
+ @Deprecated
public GestureDetector(OnGestureListener listener, Handler handler) {
- mHandler = new GestureHandler(handler);
- mListener = listener;
- init();
+ this(null, listener, handler);
}
/**
@@ -222,19 +255,84 @@ public class GestureDetector {
*
* @param listener the listener invoked for all the callbacks, this must
* not be null.
+ *
* @throws NullPointerException if {@code listener} is null.
+ *
+ * @deprecated Use {@link #GestureDetector(android.content.Context,
+ * android.view.GestureDetector.OnGestureListener)} instead.
*/
+ @Deprecated
public GestureDetector(OnGestureListener listener) {
- mHandler = new GestureHandler();
+ this(null, listener, null);
+ }
+
+ /**
+ * Creates a GestureDetector with the supplied listener.
+ * You may only use this constructor from a UI thread (this is the usual situation).
+ * @see android.os.Handler#Handler()
+ *
+ * @param context the application's context
+ * @param listener the listener invoked for all the callbacks, this must
+ * not be null.
+ *
+ * @throws NullPointerException if {@code listener} is null.
+ */
+ public GestureDetector(Context context, OnGestureListener listener) {
+ this(context, listener, null);
+ }
+
+ /**
+ * Creates a GestureDetector with the supplied listener.
+ * You may only use this constructor from a UI thread (this is the usual situation).
+ * @see android.os.Handler#Handler()
+ *
+ * @param context the application's context
+ * @param listener the listener invoked for all the callbacks, this must
+ * not be null.
+ * @param handler the handler to use
+ *
+ * @throws NullPointerException if {@code listener} is null.
+ */
+ public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
+ if (handler != null) {
+ mHandler = new GestureHandler(handler);
+ } else {
+ mHandler = new GestureHandler();
+ }
mListener = listener;
- init();
+ init(context);
}
- private void init() {
+ private void init(Context context) {
if (mListener == null) {
throw new NullPointerException("OnGestureListener must not be null");
}
mIsLongpressEnabled = true;
+
+ // Fallback to support pre-donuts releases
+ int touchSlop, doubleTapSlop;
+ if (context == null) {
+ //noinspection deprecation
+ touchSlop = ViewConfiguration.getTouchSlop();
+ doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
+ //noinspection deprecation
+ mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
+ } else {
+ final ViewConfiguration configuration = ViewConfiguration.get(context);
+ touchSlop = configuration.getScaledTouchSlop();
+ doubleTapSlop = configuration.getScaledDoubleTapSlop();
+ mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+ }
+ mTouchSlopSquare = touchSlop * touchSlop;
+ mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
+ }
+
+ /**
+ * @hide pending API council
+ * @param onDoubleTapListener
+ */
+ public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
+ mDoubleTapListener = onDoubleTapListener;
}
/**
@@ -266,9 +364,6 @@ public class GestureDetector {
* else false.
*/
public boolean onTouchEvent(MotionEvent ev) {
- final long tapTime = ViewConfiguration.getTapTimeout();
- final long longpressTime = ViewConfiguration.getLongPressTimeout();
- final int touchSlop = ViewConfiguration.getTouchSlop();
final int action = ev.getAction();
final float y = ev.getY();
final float x = ev.getX();
@@ -282,19 +377,32 @@ public class GestureDetector {
switch (action) {
case MotionEvent.ACTION_DOWN:
+ if (mDoubleTapListener != null) {
+ mHandler.removeMessages(TAP);
+ if (mCurrentDownEvent != null && isConsideredDoubleTap(mCurrentDownEvent, ev)) {
+ // This is a second tap
+ mIsDoubleTapping = true;
+ handled = mDoubleTapListener.onDoubleTapEvent(ev);
+ } else {
+ // This is a first tap
+ mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
+ }
+ }
+
mLastMotionX = x;
mLastMotionY = y;
mCurrentDownEvent = MotionEvent.obtain(ev);
mAlwaysInTapRegion = true;
+ mAlwaysInBiggerTapRegion = true;
mInLongPress = false;
-
+
if (mIsLongpressEnabled) {
mHandler.removeMessages(LONG_PRESS);
mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
- + tapTime + longpressTime);
+ + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
}
- mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + tapTime);
- handled = mListener.onDown(ev);
+ mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
+ handled |= mListener.onDown(ev);
break;
case MotionEvent.ACTION_MOVE:
@@ -303,11 +411,13 @@ public class GestureDetector {
}
final float scrollX = mLastMotionX - x;
final float scrollY = mLastMotionY - y;
- if (mAlwaysInTapRegion) {
+ if (mIsDoubleTapping) {
+ handled = mDoubleTapListener.onDoubleTapEvent(ev);
+ } else if (mAlwaysInTapRegion) {
final int deltaX = (int) (x - mCurrentDownEvent.getX());
final int deltaY = (int) (y - mCurrentDownEvent.getY());
int distance = (deltaX * deltaX) + (deltaY * deltaY);
- if (distance > TOUCH_SLOP_SQUARE) {
+ if (distance > mTouchSlopSquare) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastMotionX = x;
mLastMotionY = y;
@@ -315,6 +425,9 @@ public class GestureDetector {
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
}
+ if (distance > mBiggerTouchSlopSquare) {
+ mAlwaysInBiggerTapRegion = false;
+ }
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastMotionX = x;
@@ -323,8 +436,13 @@ public class GestureDetector {
break;
case MotionEvent.ACTION_UP:
- mCurrentUpEvent = MotionEvent.obtain(ev);
- if (mInLongPress) {
+ MotionEvent currentUpEvent = MotionEvent.obtain(ev);
+ if (mIsDoubleTapping) {
+ handled = mDoubleTapListener.onDoubleTapEvent(ev);
+ mIsDoubleTapping = false;
+ break;
+ } else if (mInLongPress) {
+ mHandler.removeMessages(TAP);
mInLongPress = false;
break;
}
@@ -338,9 +456,9 @@ public class GestureDetector {
final float velocityY = velocityTracker.getYVelocity();
final float velocityX = velocityTracker.getXVelocity();
- if ((Math.abs(velocityY) > ViewConfiguration.getMinimumFlingVelocity())
- || (Math.abs(velocityX) > ViewConfiguration.getMinimumFlingVelocity())){
- handled = mListener.onFling(mCurrentDownEvent, mCurrentUpEvent, velocityX, velocityY);
+ if ((Math.abs(velocityY) > mMinimumFlingVelocity)
+ || (Math.abs(velocityX) > mMinimumFlingVelocity)){
+ handled = mListener.onFling(mCurrentDownEvent, currentUpEvent, velocityX, velocityY);
}
}
mVelocityTracker.recycle();
@@ -351,6 +469,7 @@ public class GestureDetector {
case MotionEvent.ACTION_CANCEL:
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
+ mHandler.removeMessages(TAP);
mVelocityTracker.recycle();
mVelocityTracker = null;
if (mInLongPress) {
@@ -361,6 +480,20 @@ public class GestureDetector {
return handled;
}
+ private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent secondDown) {
+ if (!mAlwaysInBiggerTapRegion) {
+ return false;
+ }
+
+ if (secondDown.getEventTime() - firstDown.getEventTime() > DOUBLE_TAP_TIMEOUT) {
+ return false;
+ }
+
+ int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
+ int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
+ return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
+ }
+
private void dispatchLongPress() {
mInLongPress = true;
mListener.onLongPress(mCurrentDownEvent);
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 40251db..a856b24 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -104,6 +104,9 @@ interface IWindowManager
int getKeycodeState(int sw);
int getKeycodeStateForDevice(int devid, int sw);
+ // Report whether the hardware supports the given keys; returns true if successful
+ boolean hasKeys(in int[] keycodes, inout boolean[] keyExists);
+
// For testing
void setInTouchMode(boolean showFocus);
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 0347d50..25958aa 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -18,6 +18,8 @@ package android.view;
import android.text.method.MetaKeyKeyListener;
import android.util.SparseIntArray;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.util.SparseArray;
@@ -350,6 +352,28 @@ public class KeyCharacterMap
return getKeyboardType_native(mPointer);
}
+ /**
+ * Queries the framework about whether any physical keys exist on the
+ * device that are capable of producing the given key codes.
+ */
+ public static boolean deviceHasKey(int keyCode) {
+ int[] codeArray = new int[1];
+ codeArray[0] = keyCode;
+ boolean[] ret = deviceHasKeys(codeArray);
+ return ret[0];
+ }
+
+ public static boolean[] deviceHasKeys(int[] keyCodes) {
+ boolean[] ret = new boolean[keyCodes.length];
+ IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+ try {
+ wm.hasKeys(keyCodes, ret);
+ } catch (RemoteException e) {
+ // no fallback; just return the empty array
+ }
+ return ret;
+ }
+
private int mPointer;
private int mKeyboardDevice;
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 1575aad..d5434b6 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -92,7 +92,7 @@ public class KeyEvent implements Parcelable {
public static final int KEYCODE_SYM = 63;
public static final int KEYCODE_EXPLORER = 64;
public static final int KEYCODE_ENVELOPE = 65;
- public static final int KEYCODE_ENTER = 66;
+ public static final int KEYCODE_ENTER = 66;
public static final int KEYCODE_DEL = 67;
public static final int KEYCODE_GRAVE = 68;
public static final int KEYCODE_MINUS = 69;
@@ -144,8 +144,12 @@ public class KeyEvent implements Parcelable {
public static final int ACTION_UP = 1;
/**
* {@link #getAction} value: multiple duplicate key events have
- * occurred in a row. The {#link {@link #getRepeatCount()} method returns
- * the number of duplicates.
+ * occurred in a row, or a complex string is being delivered. If the
+ * key code is not {#link {@link #KEYCODE_UNKNOWN} then the
+ * {#link {@link #getRepeatCount()} method returns the number of times
+ * the given key code should be executed.
+ * Otherwise, if the key code {@link #KEYCODE_UNKNOWN}, then
+ * this is a sequence of characters as returned by {@link #getCharacters}.
*/
public static final int ACTION_MULTIPLE = 2;
@@ -248,6 +252,7 @@ public class KeyEvent implements Parcelable {
private int mFlags;
private long mDownTime;
private long mEventTime;
+ private String mCharacters;
public interface Callback {
/**
@@ -406,6 +411,28 @@ public class KeyEvent implements Parcelable {
}
/**
+ * Create a new key event for a string of characters. The key code,
+ * action, and repeat could will automatically be set to
+ * {@link #KEYCODE_UNKNOWN}, {@link #ACTION_MULTIPLE}, and 0 for you.
+ *
+ * @param time The time (in {@link android.os.SystemClock#uptimeMillis})
+ * at which this event occured.
+ * @param characters The string of characters.
+ * @param device The device ID that generated the key event.
+ * @param flags The flags for this key event
+ */
+ public KeyEvent(long time, String characters, int device, int flags) {
+ mDownTime = time;
+ mEventTime = time;
+ mCharacters = characters;
+ mAction = ACTION_MULTIPLE;
+ mKeyCode = KEYCODE_UNKNOWN;
+ mRepeatCount = 0;
+ mDeviceId = device;
+ mFlags = flags;
+ }
+
+ /**
* Copy an existing key event, modifying its time and repeat count.
*
* @param origEvent The existing event to be copied.
@@ -423,6 +450,7 @@ public class KeyEvent implements Parcelable {
mDeviceId = origEvent.mDeviceId;
mScancode = origEvent.mScancode;
mFlags = origEvent.mFlags;
+ mCharacters = origEvent.mCharacters;
}
/**
@@ -441,6 +469,8 @@ public class KeyEvent implements Parcelable {
mDeviceId = origEvent.mDeviceId;
mScancode = origEvent.mScancode;
mFlags = origEvent.mFlags;
+ // Don't copy mCharacters, since one way or the other we'll lose it
+ // when changing the action.
}
/**
@@ -580,7 +610,7 @@ public class KeyEvent implements Parcelable {
/**
* Retrieve the key code of the key event. This is the physical key that
- * was pressed -- not the Unicode character.
+ * was pressed, <em>not</em> the Unicode character.
*
* @return The key code of the event.
*/
@@ -589,6 +619,18 @@ public class KeyEvent implements Parcelable {
}
/**
+ * For the special case of a {@link #ACTION_MULTIPLE} event with key
+ * code of {@link #KEYCODE_UNKNOWN}, this is a raw string of characters
+ * associated with the event. In all other cases it is null.
+ *
+ * @return Returns a String of 1 or more characters associated with
+ * the event.
+ */
+ public final String getCharacters() {
+ return mCharacters;
+ }
+
+ /**
* Retrieve the hardware key id of this key event. These values are not
* reliable and vary from device to device.
*
@@ -772,16 +814,18 @@ public class KeyEvent implements Parcelable {
if (receiver.onKeyMultiple(code, count, this)) {
return true;
}
- mAction = ACTION_DOWN;
- mRepeatCount = 0;
- boolean handled = receiver.onKeyDown(code, this);
- if (handled) {
- mAction = ACTION_UP;
- receiver.onKeyUp(code, this);
+ if (code != KeyEvent.KEYCODE_UNKNOWN) {
+ mAction = ACTION_DOWN;
+ mRepeatCount = 0;
+ boolean handled = receiver.onKeyDown(code, this);
+ if (handled) {
+ mAction = ACTION_UP;
+ receiver.onKeyUp(code, this);
+ }
+ mAction = ACTION_MULTIPLE;
+ mRepeatCount = count;
+ return handled;
}
- mAction = ACTION_MULTIPLE;
- mRepeatCount = count;
- return handled;
}
return false;
}
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
index 057df92..27b49db 100644
--- a/core/java/android/view/TouchDelegate.java
+++ b/core/java/android/view/TouchDelegate.java
@@ -77,7 +77,9 @@ public class TouchDelegate {
* actual extent.
*/
public static final int TO_RIGHT = 8;
-
+
+ private int mSlop;
+
/**
* Constructor
*
@@ -87,10 +89,10 @@ public class TouchDelegate {
*/
public TouchDelegate(Rect bounds, View delegateView) {
mBounds = bounds;
-
- int slop = ViewConfiguration.getTouchSlop();
+
+ mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();
mSlopBounds = new Rect(bounds);
- mSlopBounds.inset(-slop, -slop);
+ mSlopBounds.inset(-mSlop, -mSlop);
mDelegateView = delegateView;
}
@@ -141,7 +143,7 @@ public class TouchDelegate {
} else {
// Offset event coordinates to be outside the target view (in case it does
// something like tracking pressed state)
- int slop = ViewConfiguration.getTouchSlop();
+ int slop = mSlop;
event.setLocation(-(slop * 2), -(slop * 2));
}
handled = delegateView.dispatchTouchEvent(event);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 85f482c..a51b564 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -60,12 +60,31 @@ import java.util.Arrays;
/**
* <p>
- * The <code>View</code> class represents the basic UI building block. A view
+ * This class represents the basic building block for user interface components. A View
* occupies a rectangular area on the screen and is responsible for drawing and
- * event handling. <code>View</code> is the base class for <em>widgets</em>,
- * used to create interactive graphical user interfaces.
+ * event handling. View is the base class for <em>widgets</em>, which are
+ * used to create interactive UI components (buttons, text fields, etc.). The
+ * {@link android.view.ViewGroup} subclass is the base class for <em>layouts</em>, which
+ * are invisible containers that hold other Views (or other ViewGroups) and define
+ * their layout properties.
* </p>
*
+ * <div class="special">
+ * <p>For an introduction to using this class to develop your
+ * application's user interface, read the Developer Guide documentation on
+ * <strong><a href="{@docRoot}guide/topics/ui/index.html">User Interface</a></strong>. Special topics
+ * include:
+ * <br/><a href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/layout-objects.html">Common Layout Objects</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/binding.html">Binding to Data with AdapterView</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/ui-events.html">Handling UI Events</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/themes.html">Applying Styles and Themes</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/custom-components.html">Building Custom Components</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/how-android-draws.html">How Android Draws Views</a>.
+ * </p>
+ * </div>
+ *
* <a name="Using"></a>
* <h3>Using Views</h3>
* <p>
@@ -1308,6 +1327,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
static final int HAS_BOUNDS = 0x00000010;
/** {@hide} */
static final int DRAWN = 0x00000020;
+ /**
+ * When this flag is set, this view is running an animation on behalf of its
+ * children and should therefore not cancel invalidate requests, even if they
+ * lie outside of this view's bounds.
+ *
+ * {@hide}
+ */
+ static final int DRAW_ANIMATION = 0x00000040;
/** {@hide} */
static final int SKIP_DRAW = 0x00000080;
/** {@hide} */
@@ -1353,8 +1380,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
*/
static final int SCROLL_CONTAINER_ADDED = 0x00100000;
- // Note: flag 0x00000040 is available
-
/**
* The parent this view is attached to.
* {@hide}
@@ -1559,6 +1584,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
private int mNextFocusDownId = View.NO_ID;
private CheckForLongPress mPendingCheckForLongPress;
+ private UnsetPressedState mUnsetPressedState;
/**
* Whether the long press's action has been invoked. The tap's action is invoked on the
@@ -1898,7 +1924,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
initScrollCache();
mScrollCache.fadingEdgeLength = a.getDimensionPixelSize(
- R.styleable.View_fadingEdgeLength, ViewConfiguration.getFadingEdgeLength());
+ R.styleable.View_fadingEdgeLength,
+ ViewConfiguration.get(mContext).getScaledFadingEdgeLength());
}
/**
@@ -2013,36 +2040,38 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
mScrollCache.scrollBar = new ScrollBarDrawable();
}
- mScrollCache.scrollBarSize = a.getDimensionPixelSize(
+ final ScrollabilityCache scrollabilityCache = mScrollCache;
+
+ scrollabilityCache.scrollBarSize = a.getDimensionPixelSize(
com.android.internal.R.styleable.View_scrollbarSize,
- ViewConfiguration.getScrollBarSize());
+ ViewConfiguration.get(mContext).getScaledScrollBarSize());
Drawable track = a.getDrawable(R.styleable.View_scrollbarTrackHorizontal);
- mScrollCache.scrollBar.setHorizontalTrackDrawable(track);
+ scrollabilityCache.scrollBar.setHorizontalTrackDrawable(track);
Drawable thumb = a.getDrawable(R.styleable.View_scrollbarThumbHorizontal);
if (thumb != null) {
- mScrollCache.scrollBar.setHorizontalThumbDrawable(thumb);
+ scrollabilityCache.scrollBar.setHorizontalThumbDrawable(thumb);
}
boolean alwaysDraw = a.getBoolean(R.styleable.View_scrollbarAlwaysDrawHorizontalTrack,
false);
if (alwaysDraw) {
- mScrollCache.scrollBar.setAlwaysDrawHorizontalTrack(true);
+ scrollabilityCache.scrollBar.setAlwaysDrawHorizontalTrack(true);
}
track = a.getDrawable(R.styleable.View_scrollbarTrackVertical);
- mScrollCache.scrollBar.setVerticalTrackDrawable(track);
+ scrollabilityCache.scrollBar.setVerticalTrackDrawable(track);
thumb = a.getDrawable(R.styleable.View_scrollbarThumbVertical);
if (thumb != null) {
- mScrollCache.scrollBar.setVerticalThumbDrawable(thumb);
+ scrollabilityCache.scrollBar.setVerticalThumbDrawable(thumb);
}
alwaysDraw = a.getBoolean(R.styleable.View_scrollbarAlwaysDrawVerticalTrack,
false);
if (alwaysDraw) {
- mScrollCache.scrollBar.setAlwaysDrawVerticalTrack(true);
+ scrollabilityCache.scrollBar.setAlwaysDrawVerticalTrack(true);
}
// Re-apply user/background padding so that scrollbar(s) get added
@@ -2056,7 +2085,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
*/
private void initScrollCache() {
if (mScrollCache == null) {
- mScrollCache = new ScrollabilityCache();
+ mScrollCache = new ScrollabilityCache(ViewConfiguration.get(mContext));
}
}
@@ -2635,6 +2664,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
*/
public void setVisibility(int visibility) {
setFlags(visibility, VISIBILITY_MASK);
+ if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false);
}
/**
@@ -3410,6 +3440,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
}
void performCollectViewAttributes(int visibility) {
+ //noinspection PointlessBitwiseExpression
if (((visibility | mViewFlags) & (VISIBILITY_MASK | KEEP_SCREEN_ON))
== (VISIBLE | KEEP_SCREEN_ON)) {
mAttachInfo.mKeepScreenOn = true;
@@ -3708,10 +3739,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
}
}
- final UnsetPressedState unsetPressedState = new UnsetPressedState();
- if (!post(unsetPressedState)) {
+ if (mUnsetPressedState == null) {
+ mUnsetPressedState = new UnsetPressedState();
+ }
+
+ if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
- unsetPressedState.run();
+ mUnsetPressedState.run();
}
}
break;
@@ -3734,7 +3768,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
final int y = (int) event.getY();
// Be lenient about moving outside of buttons
- int slop = ViewConfiguration.getTouchSlop();
+ int slop = ViewConfiguration.get(mContext).getScaledTouchSlop();
if ((x < 0 - slop) || (x >= getWidth() + slop) ||
(y < 0 - slop) || (y >= getHeight() + slop)) {
// Outside button
@@ -4413,14 +4447,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
* @see #invalidate()
*/
public void postInvalidate() {
- // We try only with the AttachInfo because there's no point in invalidating
- // if we are not attached to our window
- if (mAttachInfo != null) {
- Message msg = Message.obtain();
- msg.what = AttachInfo.INVALIDATE_MSG;
- msg.obj = this;
- mAttachInfo.mHandler.sendMessage(msg);
- }
+ postInvalidateDelayed(0);
}
/**
@@ -4436,16 +4463,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
* @see #invalidate(Rect)
*/
public void postInvalidate(int left, int top, int right, int bottom) {
- // We try only with the AttachInfo because there's no point in invalidating
- // if we are not attached to our window
- if (mAttachInfo != null) {
- Message msg = Message.obtain();
- msg.what = AttachInfo.INVALIDATE_RECT_MSG;
- msg.obj = this;
- msg.arg1 = (left << 16) | (top & 0xFFFF);
- msg.arg2 = (right << 16) | (bottom & 0xFFFF);
- mAttachInfo.mHandler.sendMessage(msg);
- }
+ postInvalidateDelayed(0, left, top, right, bottom);
}
/**
@@ -4477,16 +4495,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
* @param right The right coordinate of the rectangle to invalidate.
* @param bottom The bottom coordinate of the rectangle to invalidate.
*/
- public void postInvalidateDelayed(long delayMilliseconds, int left, int top
- , int right, int bottom) {
+ public void postInvalidateDelayed(long delayMilliseconds, int left, int top,
+ int right, int bottom) {
+
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
if (mAttachInfo != null) {
- Message msg = Message.obtain();
+ final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire();
+ info.target = this;
+ info.left = left;
+ info.top = top;
+ info.right = right;
+ info.bottom = bottom;
+
+ final Message msg = Message.obtain();
msg.what = AttachInfo.INVALIDATE_RECT_MSG;
- msg.obj = this;
- msg.arg1 = (left << 16) | (top & 0xFFFF);
- msg.arg2 = (right << 16) | (bottom & 0xFFFF);
+ msg.obj = info;
mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
}
@@ -4865,7 +4889,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
final boolean drawHorizontalScrollBar =
(viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL;
final boolean drawVerticalScrollBar =
- (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL;
+ (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL
+ && !isVerticalScrollBarHidden();
if (drawVerticalScrollBar || drawHorizontalScrollBar) {
final int width = mRight - mLeft;
@@ -4887,6 +4912,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
}
}
}
+
+ /**
+ * Override this if the vertical scrollbar needs to be hidden in a subclass, like when
+ * FastScroller is visible.
+ * @return whether to temporarily hide the vertical scrollbar
+ * @hide
+ */
+ protected boolean isVerticalScrollBarHidden() {
+ return false;
+ }
/**
* <p>Draw the horizontal scrollbar if
@@ -5022,6 +5057,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
+ destroyDrawingCache();
}
/**
@@ -5408,7 +5444,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
if (width <= 0 || height <= 0 ||
(width * height * (opaque ? 2 : 4) >= // Projected bitmap size in bytes
- ViewConfiguration.getMaximumDrawingCacheSize())) {
+ ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
if (mDrawingCache != null) {
mDrawingCache.recycle();
}
@@ -5485,9 +5521,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
final int restoreCount = canvas.save();
canvas.translate(-mScrollX, -mScrollY);
+ mPrivateFlags |= DRAWN;
+
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
- mPrivateFlags |= DRAWN;
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
}
@@ -5616,6 +5653,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
}
+ mPrivateFlags |= DRAWN;
+
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
@@ -5656,7 +5695,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
- mPrivateFlags |= DRAWN;
onDraw(canvas);
// Step 4, draw the children
@@ -5760,7 +5798,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
}
// Step 3, draw the content
- mPrivateFlags |= DRAWN;
onDraw(canvas);
// Step 4, draw the children
@@ -7671,6 +7708,67 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
void playSoundEffect(int effectId);
}
+ /**
+ * InvalidateInfo is used to post invalidate(int, int, int, int) messages
+ * to a Handler. This class contains the target (View) to invalidate and
+ * the coordinates of the dirty rectangle.
+ *
+ * For performance purposes, this class also implements a pool of up to
+ * POOL_LIMIT objects that get reused. This reduces memory allocations
+ * whenever possible.
+ *
+ * The pool is implemented as a linked list of InvalidateInfo object with
+ * the root pointing to the next available InvalidateInfo. If the root
+ * is null (i.e. when all instances from the pool have been acquired),
+ * then a new InvalidateInfo is created and returned to the caller.
+ *
+ * An InvalidateInfo is sent back to the pool by calling its release()
+ * method. If the pool is full the object is simply discarded.
+ *
+ * This implementation follows the object pool pattern used in the
+ * MotionEvent class.
+ */
+ static class InvalidateInfo {
+ private static final int POOL_LIMIT = 10;
+ private static final Object sLock = new Object();
+
+ private static int sAcquiredCount = 0;
+ private static InvalidateInfo sRoot;
+
+ private InvalidateInfo next;
+
+ View target;
+
+ int left;
+ int top;
+ int right;
+ int bottom;
+
+ static InvalidateInfo acquire() {
+ synchronized (sLock) {
+ if (sRoot == null) {
+ return new InvalidateInfo();
+ }
+
+ InvalidateInfo info = sRoot;
+ sRoot = info.next;
+ sAcquiredCount--;
+
+ return info;
+ }
+ }
+
+ void release() {
+ synchronized (sLock) {
+ if (sAcquiredCount < POOL_LIMIT) {
+ sAcquiredCount++;
+ next = sRoot;
+ sRoot = this;
+ }
+ }
+ }
+ }
+
final IWindowSession mSession;
final IWindow mWindow;
@@ -7839,18 +7937,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback {
* instances of View.</p>
*/
private static class ScrollabilityCache {
- public int fadingEdgeLength = ViewConfiguration.getFadingEdgeLength();
+ public int fadingEdgeLength;
- public int scrollBarSize = ViewConfiguration.getScrollBarSize();
+ public int scrollBarSize;
public ScrollBarDrawable scrollBar;
public final Paint paint;
public final Matrix matrix;
public Shader shader;
- private int mLastColor = 0;
+ private int mLastColor;
+
+ public ScrollabilityCache(ViewConfiguration configuration) {
+ fadingEdgeLength = configuration.getScaledFadingEdgeLength();
+ scrollBarSize = configuration.getScaledScrollBarSize();
- public ScrollabilityCache() {
paint = new Paint();
matrix = new Matrix();
// use use a height of 1, and then wack the matrix each time we
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index b7110ce..7153ea1 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -16,17 +16,19 @@
package android.view;
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.util.SparseArray;
+
/**
* Contains methods to standard constants used in the UI for timeouts, sizes, and distances.
- *
*/
public class ViewConfiguration {
-
/**
* Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in
* pixels
*/
- private static final int SCROLL_BAR_SIZE = 6;
+ private static final int SCROLL_BAR_SIZE = 10;
/**
* Defines the length of the fading edges in pixels
@@ -83,6 +85,11 @@ public class ViewConfiguration {
private static final int TOUCH_SLOP = 12;
/**
+ * Distance between the first touch and second touch to still be considered a double tap
+ */
+ private static final int DOUBLE_TAP_SLOP = 100;
+
+ /**
* Distance a touch needs to be outside of a window's bounds for it to
* count as outside for purposes of dismissing the window.
*/
@@ -97,28 +104,124 @@ public class ViewConfiguration {
* The maximum size of View's drawing cache, expressed in bytes. This size
* should be at least equal to the size of the screen in ARGB888 format.
*/
- private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // One HVGA screen, ARGB8888
+ @Deprecated
+ private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // HVGA screen, ARGB8888
/**
* The coefficient of friction applied to flings/scrolls.
*/
private static float SCROLL_FRICTION = 0.015f;
+ private final int mEdgeSlop;
+ private final int mFadingEdgeLength;
+ private final int mMinimumFlingVelocity;
+ private final int mScrollbarSize;
+ private final int mTouchSlop;
+ private final int mDoubleTapSlop;
+ private final int mWindowTouchSlop;
+ private final int mMaximumDrawingCacheSize;
+
+ private static final SparseArray<ViewConfiguration> sConfigurations =
+ new SparseArray<ViewConfiguration>(2);
+
+ /**
+ * @deprecated Use {@link android.view.ViewConfiguration#get(android.content.Context)} instead.
+ */
+ @Deprecated
+ public ViewConfiguration() {
+ mEdgeSlop = EDGE_SLOP;
+ mFadingEdgeLength = FADING_EDGE_LENGTH;
+ mMinimumFlingVelocity = MINIMUM_FLING_VELOCITY;
+ mScrollbarSize = SCROLL_BAR_SIZE;
+ mTouchSlop = TOUCH_SLOP;
+ mDoubleTapSlop = DOUBLE_TAP_SLOP;
+ mWindowTouchSlop = WINDOW_TOUCH_SLOP;
+ //noinspection deprecation
+ mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
+ }
+
+ /**
+ * Creates a new configuration for the specified context. The configuration depends on
+ * various parameters of the context, like the dimension of the display or the density
+ * of the display.
+ *
+ * @param context The application context used to initialize this view configuration.
+ *
+ * @see #get(android.content.Context)
+ * @see android.util.DisplayMetrics
+ */
+ private ViewConfiguration(Context context) {
+ final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ final float density = metrics.density;
+
+ mEdgeSlop = (int) (density * EDGE_SLOP + 0.5f);
+ mFadingEdgeLength = (int) (density * FADING_EDGE_LENGTH + 0.5f);
+ mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f);
+ mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
+ mTouchSlop = (int) (density * TOUCH_SLOP + 0.5f);
+ mDoubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f);
+ mWindowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f);
+
+ // Size of the screen in bytes, in ARGB_8888 format
+ mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels;
+ }
+
+ /**
+ * Returns a configuration for the specified context. The configuration depends on
+ * various parameters of the context, like the dimension of the display or the
+ * density of the display.
+ *
+ * @param context The application context used to initialize the view configuration.
+ */
+ public static ViewConfiguration get(Context context) {
+ final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ final int density = (int) (100.0f * metrics.density);
+
+ ViewConfiguration configuration = sConfigurations.get(density);
+ if (configuration == null) {
+ configuration = new ViewConfiguration(context);
+ sConfigurations.put(density, configuration);
+ }
+
+ return configuration;
+ }
+
/**
* @return The width of the horizontal scrollbar and the height of the vertical
* scrollbar in pixels
+ *
+ * @deprecated Use {@link #getScaledScrollBarSize()} instead.
*/
+ @Deprecated
public static int getScrollBarSize() {
return SCROLL_BAR_SIZE;
}
/**
+ * @return The width of the horizontal scrollbar and the height of the vertical
+ * scrollbar in pixels
+ */
+ public int getScaledScrollBarSize() {
+ return mScrollbarSize;
+ }
+
+ /**
* @return Defines the length of the fading edges in pixels
+ *
+ * @deprecated Use {@link #getScaledFadingEdgeLength()} instead.
*/
+ @Deprecated
public static int getFadingEdgeLength() {
return FADING_EDGE_LENGTH;
}
-
+
+ /**
+ * @return Defines the length of the fading edges in pixels
+ */
+ public int getScaledFadingEdgeLength() {
+ return mFadingEdgeLength;
+ }
+
/**
* @return Defines the duration in milliseconds of the pressed state in child
* components.
@@ -156,44 +259,121 @@ public class ViewConfiguration {
/**
* @return Inset in pixels to look for touchable content when the user touches the edge of the
* screen
+ *
+ * @deprecated Use {@link #getScaledEdgeSlop()} instead.
*/
+ @Deprecated
public static int getEdgeSlop() {
return EDGE_SLOP;
}
-
+
+ /**
+ * @return Inset in pixels to look for touchable content when the user touches the edge of the
+ * screen
+ */
+ public int getScaledEdgeSlop() {
+ return mEdgeSlop;
+ }
+
/**
* @return Distance a touch can wander before we think the user is scrolling in pixels
+ *
+ * @deprecated Use {@link #getScaledTouchSlop()} instead.
*/
+ @Deprecated
public static int getTouchSlop() {
return TOUCH_SLOP;
}
+
+ /**
+ * @return Distance a touch can wander before we think the user is scrolling in pixels
+ */
+ public int getScaledTouchSlop() {
+ return mTouchSlop;
+ }
+
+ /**
+ * @return Distance between the first touch and second touch to still be
+ * considered a double tap
+ * @deprecated Use {@link #getScaledDoubleTapSlop()} instead.
+ * @hide The only client of this should be GestureDetector, which needs this
+ * for clients that still use its deprecated constructor.
+ */
+ @Deprecated
+ public static int getDoubleTapSlop() {
+ return DOUBLE_TAP_SLOP;
+ }
/**
+ * @return Distance between the first touch and second touch to still be
+ * considered a double tap
+ * @hide pending API council
+ */
+ public int getScaledDoubleTapSlop() {
+ return mDoubleTapSlop;
+ }
+
+ /**
* @return Distance a touch must be outside the bounds of a window for it
* to be counted as outside the window for purposes of dismissing that
* window.
+ *
+ * @deprecated Use {@link #getScaledWindowTouchSlop()} instead.
*/
+ @Deprecated
public static int getWindowTouchSlop() {
return WINDOW_TOUCH_SLOP;
}
+
+ /**
+ * @return Distance a touch must be outside the bounds of a window for it
+ * to be counted as outside the window for purposes of dismissing that
+ * window.
+ */
+ public int getScaledWindowTouchSlop() {
+ return mWindowTouchSlop;
+ }
/**
- * Minimum velocity to initiate a fling, as measured in pixels per second
+ * @return Minimum velocity to initiate a fling, as measured in pixels per second.
+ *
+ * @deprecated Use {@link #getScaledMinimumFlingVelocity()} instead.
*/
- public static int getMinimumFlingVelocity() {
- return MINIMUM_FLING_VELOCITY;
+ @Deprecated
+ public static int getMinimumFlingVelocity() {
+ return MINIMUM_FLING_VELOCITY;
+ }
+
+ /**
+ * @return Minimum velocity to initiate a fling, as measured in pixels per second.
+ */
+ public int getScaledMinimumFlingVelocity() {
+ return mMinimumFlingVelocity;
}
/**
* The maximum drawing cache size expressed in bytes.
*
* @return the maximum size of View's drawing cache expressed in bytes
+ *
+ * @deprecated Use {@link #getScaledMaximumDrawingCacheSize()} instead.
*/
+ @Deprecated
public static int getMaximumDrawingCacheSize() {
+ //noinspection deprecation
return MAXIMUM_DRAWING_CACHE_SIZE;
}
/**
+ * The maximum drawing cache size expressed in bytes.
+ *
+ * @return the maximum size of View's drawing cache expressed in bytes
+ */
+ public int getScaledMaximumDrawingCacheSize() {
+ return mMaximumDrawingCacheSize;
+ }
+
+ /**
* The amount of time that the zoom controls should be
* displayed on the screen expressed in milliseconds.
*
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index e26a19e..c758662 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -25,6 +25,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.RectF;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.EventLog;
@@ -74,6 +75,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// The current transformation to apply on the child being drawn
private Transformation mChildTransformation;
+ private RectF mInvalidateRegion;
// Target of Motion events
private View mMotionTarget;
@@ -1199,6 +1201,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
+ // We will draw our child's animation, let's reset the flag
+ mPrivateFlags &= ~DRAW_ANIMATION;
mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
boolean more = false;
@@ -1328,8 +1332,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
boolean concatMatrix = false;
if (a != null) {
- if (!a.isInitialized()) {
+ if (mInvalidateRegion == null) {
+ mInvalidateRegion = new RectF();
+ }
+ final RectF region = mInvalidateRegion;
+
+ final boolean initialized = a.isInitialized();
+ if (!initialized) {
a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
+ a.initializeInvalidateRegion(cl, ct, cr, cb);
child.onAnimationStart();
}
@@ -1347,10 +1358,22 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
FLAG_OPTIMIZE_INVALIDATE) {
mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
} else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
+ // The child need to draw an animation, potentially offscreen, so
+ // make sure we do not cancel invalidate requests
+ mPrivateFlags |= DRAW_ANIMATION;
invalidate(cl, ct, cr, cb);
}
} else {
- mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
+ a.getInvalidateRegion(cl, ct, cr, cb, region, transformToApply);
+
+ // The child need to draw an animation, potentially offscreen, so
+ // make sure we do not cancel invalidate requests
+ mPrivateFlags |= DRAW_ANIMATION;
+ // Enlarge the invalidate region to account for rounding errors
+ // in Animation#getInvalidateRegion(); Using 0.5f is unfortunately
+ // not enough for some types of animations (e.g. scale down.)
+ invalidate((int) (region.left - 1.0f), (int) (region.top - 1.0f),
+ (int) (region.right + 1.0f), (int) (region.bottom + 1.0f));
}
}
} else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
@@ -1367,7 +1390,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
- if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW)) {
+ if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
+ (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
return more;
}
@@ -1435,10 +1459,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
+ // Clear the flag as early as possible to allow draw() implementations
+ // to call invalidate() successfully when doing animations
+ child.mPrivateFlags |= DRAWN;
+
if (hasNoCache) {
// Fast path for layouts with no backgrounds
if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
- child.mPrivateFlags |= DRAWN;
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
}
@@ -1455,7 +1482,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
cachePaint.setAlpha(255);
mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
}
- child.mPrivateFlags |= DRAWN;
if (ViewRoot.PROFILE_DRAWING) {
EventLog.writeEvent(60003, hashCode());
}
@@ -1922,8 +1948,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
LayoutAnimationController.AnimationParameters animationParams =
params.layoutAnimationParameters;
if (animationParams == null) {
- animationParams =
- new LayoutAnimationController.AnimationParameters();
+ animationParams = new LayoutAnimationController.AnimationParameters();
params.layoutAnimationParameters = animationParams;
}
@@ -2278,8 +2303,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final int[] location = attachInfo.mInvalidateChildLocation;
location[CHILD_LEFT_INDEX] = child.mLeft;
location[CHILD_TOP_INDEX] = child.mTop;
+
+ // If the child is drawing an animation, we want to copy this flag onto
+ // ourselves and the parent to make sure the invalidate request goes
+ // through
+ final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
do {
+ if (drawAnimation && parent instanceof View) {
+ ((View) parent).mPrivateFlags |= DRAW_ANIMATION;
+ }
parent = parent.invalidateChildInParent(location, dirty);
} while (parent != null);
}
@@ -2307,7 +2340,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final int left = mLeft;
final int top = mTop;
- if (dirty.intersect(0, 0, mRight - left, mBottom - top)) {
+ if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
+ (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
mPrivateFlags &= ~DRAWING_CACHE_VALID;
location[CHILD_LEFT_INDEX] = left;
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 9e0289a..4e46397 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -177,13 +177,14 @@ public final class ViewRoot extends Handler implements ViewParent,
boolean mUseGL;
boolean mGlWanted;
+ final ViewConfiguration mViewConfiguration;
+
/**
* see {@link #playSoundEffect(int)}
*/
AudioManager mAudioManager;
-
public ViewRoot(Context context) {
super();
@@ -224,6 +225,7 @@ public final class ViewRoot extends Handler implements ViewParent,
mSurface = new Surface();
mAdded = false;
mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
+ mViewConfiguration = ViewConfiguration.get(context);
}
@Override
@@ -1101,6 +1103,7 @@ public final class ViewRoot extends Handler implements ViewParent,
mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
canvas.translate(0, -yoff);
+ mView.mPrivateFlags |= View.DRAWN;
mView.draw(canvas);
canvas.translate(0, yoff);
@@ -1139,6 +1142,8 @@ public final class ViewRoot extends Handler implements ViewParent,
Canvas canvas;
try {
canvas = surface.lockCanvas(dirty);
+ // TODO: Do this in native
+ canvas.setDensityScale(mView.getResources().getDisplayMetrics().density);
} catch (Surface.OutOfResourcesException e) {
Log.e("ViewRoot", "OutOfResourcesException locking surface", e);
// TODO: we should ask the window manager to do something!
@@ -1175,6 +1180,7 @@ public final class ViewRoot extends Handler implements ViewParent,
dirty.setEmpty();
mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
canvas.translate(0, -yoff);
+ mView.mPrivateFlags |= View.DRAWN;
mView.draw(canvas);
canvas.translate(0, yoff);
@@ -1197,6 +1203,23 @@ public final class ViewRoot extends Handler implements ViewParent,
if (LOCAL_LOGV) {
Log.v("ViewRoot", "Surface " + surface + " unlockCanvasAndPost");
}
+
+ } else if (mWidth == 0 || mHeight == 0) {
+ // This is a special case where a window dimension is 0 -- we
+ // normally wouldn't draw anything because we have an empty
+ // dirty rect, but the surface flinger may be waiting for us to
+ // draw the window before it stops freezing the screen, so we
+ // need to diddle it like this to keep it from getting stuck.
+ Canvas canvas;
+ try {
+ canvas = surface.lockCanvas(dirty);
+ } catch (Surface.OutOfResourcesException e) {
+ Log.e("ViewRoot", "OutOfResourcesException locking surface", e);
+ // TODO: we should ask the window manager to do something!
+ // for now we just do nothing
+ return;
+ }
+ surface.unlockCanvasAndPost(canvas);
}
if (scrolling) {
@@ -1414,6 +1437,7 @@ public final class ViewRoot extends Handler implements ViewParent,
public final static int FINISHED_EVENT = 1010;
public final static int DISPATCH_KEY_FROM_IME = 1011;
public final static int FINISH_INPUT_CONNECTION = 1012;
+ public final static int CHECK_FOCUS = 1013;
@Override
public void handleMessage(Message msg) {
@@ -1422,11 +1446,9 @@ public final class ViewRoot extends Handler implements ViewParent,
((View) msg.obj).invalidate();
break;
case View.AttachInfo.INVALIDATE_RECT_MSG:
- int left = msg.arg1 >>> 16;
- int top = msg.arg1 & 0xFFFF;
- int right = msg.arg2 >>> 16;
- int bottom = msg.arg2 & 0xFFFF;
- ((View) msg.obj).invalidate(left, top, right, bottom);
+ final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
+ info.target.invalidate(info.left, info.top, info.right, info.bottom);
+ info.release();
break;
case DO_TRAVERSAL:
if (mProfile) {
@@ -1478,7 +1500,7 @@ public final class ViewRoot extends Handler implements ViewParent,
event.offsetLocation(0, mCurScrollY);
handled = mView.dispatchTouchEvent(event);
if (!handled && isDown) {
- int edgeSlop = ViewConfiguration.getEdgeSlop();
+ int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
final int edgeFlags = event.getEdgeFlags();
int direction = View.FOCUS_UP;
@@ -1615,7 +1637,7 @@ public final class ViewRoot extends Handler implements ViewParent,
dispatchDetachedFromWindow();
break;
case DISPATCH_KEY_FROM_IME:
- if (LOCAL_LOGV) Log.v(
+ if (true) Log.v(
"ViewRoot", "Dispatching key "
+ msg.obj + " from IME to " + mView);
deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false);
@@ -1626,6 +1648,12 @@ public final class ViewRoot extends Handler implements ViewParent,
imm.reportFinishInputConnection((InputConnection)msg.obj);
}
} break;
+ case CHECK_FOCUS: {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ imm.checkFocus((View)msg.obj);
+ }
+ } break;
}
}
@@ -2042,8 +2070,10 @@ public final class ViewRoot extends Handler implements ViewParent,
}
private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
- boolean handled = false;
- handled = mView.dispatchKeyEventPreIme(event);
+ // If mView is null, we just consume the key event because it doesn't
+ // make sense to do anything else with it.
+ boolean handled = mView != null
+ ? mView.dispatchKeyEventPreIme(event) : true;
if (handled) {
if (sendDone) {
if (LOCAL_LOGV) Log.v(
@@ -2061,7 +2091,7 @@ public final class ViewRoot extends Handler implements ViewParent,
if (WindowManager.LayoutParams.mayUseInputMethod(
mWindowAttributes.flags)) {
InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null && mView != null && imm.isActive()) {
+ if (imm != null && mView != null) {
int seq = enqueuePendingEvent(event, sendDone);
if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
+ seq + " event=" + event);
@@ -2748,7 +2778,6 @@ public final class ViewRoot extends Handler implements ViewParent,
synchronized (mActions) {
final ArrayList<HandlerAction> actions = mActions;
- final int count = actions.size();
while (actions.remove(handlerAction)) {
// Keep going
@@ -2776,7 +2805,20 @@ public final class ViewRoot extends Handler implements ViewParent,
@Override
public boolean equals(Object o) {
- return action.equals(o);
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ HandlerAction that = (HandlerAction) o;
+
+ return !(action != null ? !action.equals(that.action) : that.action != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = action != null ? action.hashCode() : 0;
+ result = 31 * result + (int) (delay ^ (delay >>> 32));
+ return result;
}
}
}
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index f4d0fde..a573983 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -16,18 +16,17 @@
package android.view;
-import android.media.ToneGenerator;
-import android.media.AudioManager;
-import android.media.AudioService;
-import android.media.AudioSystem;
+import android.bluetooth.HeadsetBase;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Resources;
+import android.media.AudioManager;
+import android.media.AudioService;
+import android.media.AudioSystem;
+import android.media.ToneGenerator;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
-import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
import android.widget.ImageView;
@@ -39,7 +38,7 @@ import android.widget.Toast;
* Handle the volume up and down keys.
*
* This code really should be moved elsewhere.
- *
+ *
* @hide
*/
public class VolumePanel extends Handler
@@ -54,7 +53,7 @@ public class VolumePanel extends Handler
* PhoneWindow will implement this part.
*/
public static final int PLAY_SOUND_DELAY = 300;
-
+
/**
* The delay before vibrating. This small period exists so if the user is
* moving to silent mode, it will not emit a short vibrate (it normally
@@ -64,28 +63,30 @@ public class VolumePanel extends Handler
public static final int VIBRATE_DELAY = 300;
private static final int VIBRATE_DURATION = 300;
- private static final int BEEP_DURATION = 150;
+ private static final int BEEP_DURATION = 150;
private static final int MAX_VOLUME = 100;
private static final int FREE_DELAY = 10000;
-
+
private static final int MSG_VOLUME_CHANGED = 0;
private static final int MSG_FREE_RESOURCES = 1;
private static final int MSG_PLAY_SOUND = 2;
private static final int MSG_STOP_SOUNDS = 3;
private static final int MSG_VIBRATE = 4;
-
+
private static final int RINGTONE_VOLUME_TEXT = com.android.internal.R.string.volume_ringtone;
private static final int MUSIC_VOLUME_TEXT = com.android.internal.R.string.volume_music;
private static final int INCALL_VOLUME_TEXT = com.android.internal.R.string.volume_call;
private static final int ALARM_VOLUME_TEXT = com.android.internal.R.string.volume_alarm;
private static final int UNKNOWN_VOLUME_TEXT = com.android.internal.R.string.volume_unknown;
private static final int NOTIFICATION_VOLUME_TEXT =
- com.android.internal.R.string.volume_notification;
-
+ com.android.internal.R.string.volume_notification;
+ private static final int BLUETOOTH_INCALL_VOLUME_TEXT =
+ com.android.internal.R.string.volume_bluetooth_call;
+
protected Context mContext;
private AudioManager mAudioManager;
protected AudioService mAudioService;
-
+
private final Toast mToast;
private final View mView;
private final TextView mMessage;
@@ -117,13 +118,13 @@ public class VolumePanel extends Handler
mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
mVibrator = new Vibrator();
}
-
+
public void postVolumeChanged(int streamType, int flags) {
if (hasMessages(MSG_VOLUME_CHANGED)) return;
removeMessages(MSG_FREE_RESOURCES);
obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
}
-
+
/**
* Override this if you have other work to do when the volume changes (for
* example, vibrating, playing a sound, etc.). Make sure to call through to
@@ -132,31 +133,31 @@ public class VolumePanel extends Handler
protected void onVolumeChanged(int streamType, int flags) {
if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
-
+
if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
onShowVolumeChanged(streamType, flags);
}
-
+
if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
removeMessages(MSG_PLAY_SOUND);
sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
}
-
+
if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
removeMessages(MSG_PLAY_SOUND);
removeMessages(MSG_VIBRATE);
onStopSounds();
}
-
+
removeMessages(MSG_FREE_RESOURCES);
- sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
+ sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
}
protected void onShowVolumeChanged(int streamType, int flags) {
int index = mAudioService.getStreamVolume(streamType);
int message = UNKNOWN_VOLUME_TEXT;
int additionalMessage = 0;
-
+
if (LOGD) {
Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
+ ", flags: " + flags + "), index: " + index);
@@ -166,13 +167,13 @@ public class VolumePanel extends Handler
int max = mAudioService.getStreamMaxVolume(streamType);
switch (streamType) {
-
+
case AudioManager.STREAM_RING: {
message = RINGTONE_VOLUME_TEXT;
setRingerIcon(index);
break;
}
-
+
case AudioManager.STREAM_MUSIC: {
message = MUSIC_VOLUME_TEXT;
if (mAudioManager.isBluetoothA2dpOn()) {
@@ -184,7 +185,7 @@ public class VolumePanel extends Handler
}
break;
}
-
+
case AudioManager.STREAM_VOICE_CALL: {
/*
* For in-call voice call volume, there is no inaudible volume.
@@ -194,13 +195,7 @@ public class VolumePanel extends Handler
index++;
max++;
message = INCALL_VOLUME_TEXT;
- if (mAudioManager.isBluetoothScoOn()) {
- additionalMessage =
- com.android.internal.R.string.volume_call_hint_playing_through_bluetooth;
- setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_in_call);
- } else {
- setSmallIcon(index);
- }
+ setSmallIcon(index);
break;
}
@@ -209,12 +204,25 @@ public class VolumePanel extends Handler
setSmallIcon(index);
break;
}
-
+
case AudioManager.STREAM_NOTIFICATION: {
message = NOTIFICATION_VOLUME_TEXT;
setSmallIcon(index);
break;
}
+
+ case AudioManager.STREAM_BLUETOOTH_SCO: {
+ /*
+ * For in-call voice call volume, there is no inaudible volume.
+ * Rescale the UI control so the progress bar doesn't go all
+ * the way to zero and don't show the mute icon.
+ */
+ index++;
+ max++;
+ message = BLUETOOTH_INCALL_VOLUME_TEXT;
+ setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_in_call);
+ break;
+ }
}
String messageString = Resources.getSystem().getString(message);
@@ -228,25 +236,25 @@ public class VolumePanel extends Handler
mAdditionalMessage.setVisibility(View.VISIBLE);
mAdditionalMessage.setText(Resources.getSystem().getString(additionalMessage));
}
-
+
if (max != mLevel.getMax()) {
mLevel.setMax(max);
}
mLevel.setProgress(index);
-
+
mToast.setView(mView);
mToast.setDuration(Toast.LENGTH_SHORT);
mToast.setGravity(Gravity.TOP, 0, 0);
mToast.show();
-
+
// Do a little vibrate if applicable (only when going into vibrate mode)
- if ((flags & AudioManager.FLAG_VIBRATE) != 0 &&
+ if ((flags & AudioManager.FLAG_VIBRATE) != 0 &&
mAudioService.isStreamAffectedByRingerMode(streamType) &&
mAudioService.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE &&
mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) {
sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
}
-
+
}
protected void onPlaySound(int streamType, int flags) {
@@ -256,7 +264,7 @@ public class VolumePanel extends Handler
// Force stop right now
onStopSounds();
}
-
+
synchronized (this) {
ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
@@ -266,7 +274,7 @@ public class VolumePanel extends Handler
}
protected void onStopSounds() {
-
+
synchronized (this) {
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int i = numStreamTypes - 1; i >= 0; i--) {
@@ -277,17 +285,17 @@ public class VolumePanel extends Handler
}
}
}
-
+
protected void onVibrate() {
-
+
// Make sure we ended up in vibrate ringer mode
if (mAudioService.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
return;
}
-
+
mVibrator.vibrate(VIBRATE_DURATION);
}
-
+
/**
* Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
*/
@@ -303,13 +311,13 @@ public class VolumePanel extends Handler
/**
* Makes the small icon visible, and hides the large icon.
- *
+ *
* @param index The volume index, where 0 means muted.
*/
private void setSmallIcon(int index) {
mLargeStreamIcon.setVisibility(View.GONE);
mSmallStreamIcon.setVisibility(View.VISIBLE);
-
+
mSmallStreamIcon.setImageResource(index == 0
? com.android.internal.R.drawable.ic_volume_off_small
: com.android.internal.R.drawable.ic_volume_small);
@@ -317,7 +325,7 @@ public class VolumePanel extends Handler
/**
* Makes the large image view visible with the given icon.
- *
+ *
* @param resId The icon to display.
*/
private void setLargeIcon(int resId) {
@@ -329,7 +337,7 @@ public class VolumePanel extends Handler
/**
* Makes the ringer icon visible with an icon that is chosen
* based on the current ringer mode.
- *
+ *
* @param index
*/
private void setRingerIcon(int index) {
@@ -350,13 +358,13 @@ public class VolumePanel extends Handler
}
mLargeStreamIcon.setImageResource(icon);
}
-
+
protected void onFreeResources() {
// We'll keep the views, just ditch the cached drawable and hence
// bitmaps
mSmallStreamIcon.setImageDrawable(null);
mLargeStreamIcon.setImageDrawable(null);
-
+
synchronized (this) {
for (int i = mToneGenerators.length - 1; i >= 0; i--) {
if (mToneGenerators[i] != null) {
@@ -366,26 +374,26 @@ public class VolumePanel extends Handler
}
}
}
-
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
-
+
case MSG_VOLUME_CHANGED: {
onVolumeChanged(msg.arg1, msg.arg2);
break;
}
-
+
case MSG_FREE_RESOURCES: {
onFreeResources();
break;
}
-
+
case MSG_STOP_SOUNDS: {
onStopSounds();
break;
}
-
+
case MSG_PLAY_SOUND: {
onPlaySound(msg.arg1, msg.arg2);
break;
@@ -395,8 +403,8 @@ public class VolumePanel extends Handler
onVibrate();
break;
}
-
+
}
}
-
+
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index a68436b..428de67 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -525,6 +525,21 @@ public abstract class Window {
}
/**
+ * Specify custom animations to use for the window, as per
+ * {@link WindowManager.LayoutParams#windowAnimations
+ * WindowManager.LayoutParams.windowAnimations}. Providing anything besides
+ * 0 here will override the animations the window would
+ * normally retrieve from its theme.
+ */
+ public void setWindowAnimations(int resId) {
+ final WindowManager.LayoutParams attrs = getAttributes();
+ attrs.windowAnimations = resId;
+ if (mCallback != null) {
+ mCallback.onWindowAttributesChanged(attrs);
+ }
+ }
+
+ /**
* Specify an explicit soft input mode to use for the window, as per
* {@link WindowManager.LayoutParams#softInputMode
* WindowManager.LayoutParams.softInputMode}. Providing anything besides
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 7e47ad1..d08a6fa 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -16,6 +16,7 @@
package android.view;
+import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.os.Parcel;
@@ -126,8 +127,6 @@ public interface WindowManager extends ViewManager {
* @see #TYPE_APPLICATION_MEDIA
* @see #TYPE_APPLICATION_SUB_PANEL
* @see #TYPE_APPLICATION_ATTACHED_DIALOG
- * @see #TYPE_INPUT_METHOD
- * @see #TYPE_INPUT_METHOD_DIALOG
* @see #TYPE_STATUS_BAR
* @see #TYPE_SEARCH_BAR
* @see #TYPE_PHONE
@@ -645,6 +644,17 @@ public interface WindowManager extends ViewManager {
*/
public String packageName = null;
+ /**
+ * Specific orientation value for a window.
+ * May be any of the same values allowed
+ * for {@link android.content.pm.ActivityInfo#screenOrientation}.
+ * If not set, a default value of
+ * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED}
+ * will be used.
+ */
+ public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+
public LayoutParams() {
super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
type = TYPE_APPLICATION;
@@ -722,6 +732,7 @@ public interface WindowManager extends ViewManager {
out.writeStrongBinder(token);
out.writeString(packageName);
TextUtils.writeToParcel(mTitle, out, parcelableFlags);
+ out.writeInt(screenOrientation);
}
public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -755,6 +766,7 @@ public interface WindowManager extends ViewManager {
token = in.readStrongBinder();
packageName = in.readString();
mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ screenOrientation = in.readInt();
}
public static final int LAYOUT_CHANGED = 1<<0;
@@ -767,6 +779,7 @@ public interface WindowManager extends ViewManager {
public static final int ALPHA_CHANGED = 1<<7;
public static final int MEMORY_TYPE_CHANGED = 1<<8;
public static final int SOFT_INPUT_MODE_CHANGED = 1<<9;
+ public static final int SCREEN_ORIENTATION_CHANGED = 1<<10;
public final int copyFrom(LayoutParams o) {
int changes = 0;
@@ -862,6 +875,10 @@ public interface WindowManager extends ViewManager {
changes |= DIM_AMOUNT_CHANGED;
}
+ if (screenOrientation != o.screenOrientation) {
+ screenOrientation = o.screenOrientation;
+ changes |= SCREEN_ORIENTATION_CHANGED;
+ }
return changes;
}
@@ -907,6 +924,10 @@ public interface WindowManager extends ViewManager {
sb.append(" wanim=0x");
sb.append(Integer.toHexString(windowAnimations));
}
+ if (screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
+ sb.append("or=");
+ sb.append(screenOrientation);
+ }
sb.append('}');
return sb.toString();
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 6af4915..542b35f 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -289,16 +289,18 @@ public interface WindowManagerPolicy {
* Can be called by the policy to force a window to be hidden,
* regardless of whether the client or window manager would like
* it shown. Must be called with the window manager lock held.
+ * Returns true if {@link #showLw} was last called for the window.
*/
- public void hideLw(boolean doAnimation);
+ public boolean hideLw(boolean doAnimation);
/**
* Can be called to undo the effect of {@link #hideLw}, allowing a
* window to be shown as long as the window manager and client would
* also like it to be shown. Must be called with the window manager
* lock held.
+ * Returns true if {@link #hideLw} was last called for the window.
*/
- public void showLw(boolean doAnimation);
+ public boolean showLw(boolean doAnimation);
}
/** No transition happening. */
@@ -735,10 +737,17 @@ public interface WindowManagerPolicy {
* ActivityInfo.SCREEN_ORIENTATION_PORTRAIT}), return a surface
* rotation.
*/
- public int rotationForOrientation(int orientation);
+ public int rotationForOrientation(int orientation, int lastRotation,
+ boolean displayEnabled);
/**
- * Called when the system is mostly done booting
+ * Called when the system is mostly done booting to dentermine whether
+ * the system should go into safe mode.
+ */
+ public boolean detectSafeMode();
+
+ /**
+ * Called when the system is mostly done booting.
*/
public void systemReady();
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 9264398..c96b3e5 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -20,13 +20,14 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.TypedValue;
+import android.graphics.RectF;
/**
* Abstraction for an Animation that can be applied to Views, Surfaces, or
* other objects. See the {@link android.view.animation animation package
* description file}.
*/
-public abstract class Animation {
+public abstract class Animation implements Cloneable {
/**
* Repeat the animation indefinitely.
*/
@@ -174,8 +175,12 @@ public abstract class Animation {
*/
private int mZAdjustment;
- // Indicates what was the last value returned by getTransformation()
private boolean mMore = true;
+ private boolean mOneMoreTime = true;
+
+ RectF mPreviousRegion = new RectF();
+ Transformation mTransformation = new Transformation();
+ Transformation mPreviousTransformation = new Transformation();
/**
* Creates a new animation with a duration of 0ms, the default interpolator, with
@@ -217,16 +222,28 @@ public abstract class Animation {
a.recycle();
}
+ @Override
+ protected Animation clone() throws CloneNotSupportedException {
+ final Animation animation = (Animation) super.clone();
+ animation.mPreviousRegion = new RectF();
+ animation.mTransformation = new Transformation();
+ animation.mPreviousTransformation = new Transformation();
+ return animation;
+ }
+
/**
* Reset the initialization state of this animation.
*
* @see #initialize(int, int, int, int)
*/
public void reset() {
+ mPreviousRegion.setEmpty();
+ mPreviousTransformation.clear();
mInitialized = false;
mCycleFlip = false;
mRepeated = 0;
mMore = true;
+ mOneMoreTime = true;
}
/**
@@ -255,10 +272,8 @@ public abstract class Animation {
* @param parentHeight Height of the animated object's parent
*/
public void initialize(int width, int height, int parentWidth, int parentHeight) {
+ reset();
mInitialized = true;
- mCycleFlip = false;
- mRepeated = 0;
- mMore = true;
}
/**
@@ -707,6 +722,11 @@ public abstract class Animation {
}
}
+ if (!mMore && mOneMoreTime) {
+ mOneMoreTime = false;
+ return true;
+ }
+
return mMore;
}
@@ -765,7 +785,54 @@ public abstract class Animation {
return value;
}
}
-
+
+ /**
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ * @param invalidate
+ * @param transformation
+ *
+ * @hide
+ */
+ public void getInvalidateRegion(int left, int top, int right, int bottom,
+ RectF invalidate, Transformation transformation) {
+
+ final RectF previousRegion = mPreviousRegion;
+
+ invalidate.set(left, top, right, bottom);
+ transformation.getMatrix().mapRect(invalidate);
+ invalidate.union(previousRegion);
+
+ previousRegion.set(left, top, right, bottom);
+ transformation.getMatrix().mapRect(previousRegion);
+
+ final Transformation tempTransformation = mTransformation;
+ final Transformation previousTransformation = mPreviousTransformation;
+
+ tempTransformation.set(transformation);
+ transformation.set(previousTransformation);
+ previousTransformation.set(tempTransformation);
+ }
+
+ /**
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ *
+ * @hide
+ */
+ public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
+ final RectF region = mPreviousRegion;
+ region.set(left, top, right, bottom);
+ if (mFillBefore) {
+ final Transformation previousTransformation = mPreviousTransformation;
+ applyTransformation(0.0f, previousTransformation);
+ }
+ }
+
/**
* Utility class to parse a string description of a size.
*/
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 688da70..7b56f00 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -19,6 +19,7 @@ package android.view.animation;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
+import android.graphics.RectF;
import java.util.ArrayList;
import java.util.List;
@@ -39,6 +40,7 @@ public class AnimationSet extends Animation {
private static final int PROPERTY_SHARE_INTERPOLATOR_MASK = 0x10;
private static final int PROPERTY_DURATION_MASK = 0x20;
private static final int PROPERTY_MORPH_MATRIX_MASK = 0x40;
+ private static final int PROPERTY_CHANGE_BOUNDS_MASK = 0x80;
private int mFlags = 0;
@@ -82,6 +84,22 @@ public class AnimationSet extends Animation {
init();
}
+ @Override
+ protected AnimationSet clone() throws CloneNotSupportedException {
+ final AnimationSet animation = (AnimationSet) super.clone();
+ animation.mTempTransformation = new Transformation();
+ animation.mAnimations = new ArrayList<Animation>();
+
+ final int count = mAnimations.size();
+ final ArrayList<Animation> animations = mAnimations;
+
+ for (int i = 0; i < count; i++) {
+ animation.mAnimations.add(animations.get(i).clone());
+ }
+
+ return animation;
+ }
+
private void setFlag(int mask, boolean value) {
if (value) {
mFlags |= mask;
@@ -145,6 +163,11 @@ public class AnimationSet extends Animation {
mFlags |= PROPERTY_MORPH_MATRIX_MASK;
}
+ boolean changeBounds = (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == 0;
+ if (changeBounds && a.willChangeTransformationMatrix()) {
+ mFlags |= PROPERTY_CHANGE_BOUNDS_MASK;
+ }
+
if (mAnimations.size() == 1) {
mDuration = a.getStartOffset() + a.getDuration();
mLastEnd = mStartOffset + mDuration;
@@ -239,7 +262,54 @@ public class AnimationSet extends Animation {
}
return duration;
}
-
+
+ /**
+ * @hide
+ */
+ public void getInvalidateRegion(int left, int top, int right, int bottom,
+ RectF invalidate, Transformation transformation) {
+
+ final RectF previousRegion = mPreviousRegion;
+
+ invalidate.set(left, top, right, bottom);
+ transformation.getMatrix().mapRect(invalidate);
+ invalidate.union(previousRegion);
+
+ previousRegion.set(left, top, right, bottom);
+ transformation.getMatrix().mapRect(previousRegion);
+
+ final Transformation tempTransformation = mTransformation;
+ final Transformation previousTransformation = mPreviousTransformation;
+
+ tempTransformation.set(transformation);
+ transformation.set(previousTransformation);
+ previousTransformation.set(tempTransformation);
+ }
+
+ /**
+ * @hide
+ */
+ public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
+ final RectF region = mPreviousRegion;
+ region.set(left, top, right, bottom);
+
+ if (mFillBefore) {
+ final int count = mAnimations.size();
+ final ArrayList<Animation> animations = mAnimations;
+ final Transformation temp = mTempTransformation;
+
+ final Transformation previousTransformation = mPreviousTransformation;
+
+ for (int i = count - 1; i >= 0; --i) {
+ final Animation a = animations.get(i);
+
+ temp.clear();
+ a.applyTransformation(0.0f, temp);
+ previousTransformation.compose(temp);
+ }
+ }
+ }
+
/**
* The transformation of an animation set is the concatenation of all of its
* component animations.
@@ -313,7 +383,7 @@ public class AnimationSet extends Animation {
== PROPERTY_SHARE_INTERPOLATOR_MASK;
boolean startOffsetSet = (mFlags & PROPERTY_START_OFFSET_MASK)
== PROPERTY_START_OFFSET_MASK;
-
+
if (shareInterpolator) {
ensureInterpolator();
}
@@ -327,7 +397,13 @@ public class AnimationSet extends Animation {
final int repeatMode = mRepeatMode;
final Interpolator interpolator = mInterpolator;
final long startOffset = mStartOffset;
-
+
+
+ long[] storedOffsets = mStoredOffsets;
+ if (storedOffsets == null || storedOffsets.length != count) {
+ storedOffsets = mStoredOffsets = new long[count];
+ }
+
for (int i = 0; i < count; i++) {
Animation a = children.get(i);
if (durationSet) {
@@ -346,42 +422,36 @@ public class AnimationSet extends Animation {
a.setInterpolator(interpolator);
}
if (startOffsetSet) {
- a.setStartOffset(startOffset);
+ long offset = a.getStartOffset();
+ a.setStartOffset(offset + startOffset);
+ storedOffsets[i] = offset;
}
a.initialize(width, height, parentWidth, parentHeight);
}
}
- /**
- * @hide
- * @param startOffset the startOffset to add to the children's startOffset
- */
- void saveChildrenStartOffset(long startOffset) {
- final ArrayList<Animation> children = mAnimations;
- final int count = children.size();
- long[] storedOffsets = mStoredOffsets = new long[count];
-
- for (int i = 0; i < count; i++) {
- Animation animation = children.get(i);
- long offset = animation.getStartOffset();
- animation.setStartOffset(offset + startOffset);
- storedOffsets[i] = offset;
- }
+ @Override
+ public void reset() {
+ super.reset();
+ restoreChildrenStartOffset();
}
/**
* @hide
*/
void restoreChildrenStartOffset() {
+ final long[] offsets = mStoredOffsets;
+ if (offsets == null) return;
+
final ArrayList<Animation> children = mAnimations;
final int count = children.size();
- final long[] offsets = mStoredOffsets;
+
for (int i = 0; i < count; i++) {
children.get(i).setStartOffset(offsets[i]);
}
}
-
+
/**
* @return All the child animations in this AnimationSet. Note that
* this may include other AnimationSets, which are not expanded.
@@ -394,4 +464,9 @@ public class AnimationSet extends Animation {
public boolean willChangeTransformationMatrix() {
return (mFlags & PROPERTY_MORPH_MATRIX_MASK) == PROPERTY_MORPH_MATRIX_MASK;
}
+
+ @Override
+ public boolean willChangeBounds() {
+ return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK;
+ }
}
diff --git a/core/java/android/view/animation/LayoutAnimationController.java b/core/java/android/view/animation/LayoutAnimationController.java
index 9cfa8d7..882e738 100644
--- a/core/java/android/view/animation/LayoutAnimationController.java
+++ b/core/java/android/view/animation/LayoutAnimationController.java
@@ -318,9 +318,16 @@ public class LayoutAnimationController {
* @see #getDelayForView(android.view.View)
*/
public final Animation getAnimationForView(View view) {
- final long delay = getDelayForView(view);
+ final long delay = getDelayForView(view) + mAnimation.getStartOffset();
mMaxDelay = Math.max(mMaxDelay, delay);
- return new DelayedAnimation(delay, mAnimation);
+
+ try {
+ final Animation animation = mAnimation.clone();
+ animation.setStartOffset(delay);
+ return animation;
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
}
/**
@@ -425,149 +432,4 @@ public class LayoutAnimationController {
*/
public int index;
}
-
- /**
- * Encapsulates an animation and delays its start offset by a specified
- * amount. This allows to reuse the same base animation for various views
- * and get the effect of running multiple instances of the animation at
- * different times.
- */
- private static class DelayedAnimation extends Animation {
- private final long mDelay;
- private final Animation mAnimation;
-
- /**
- * Creates a new delayed animation that will delay the controller's
- * animation by the specified delay in milliseconds.
- *
- * @param delay the delay in milliseconds by which to offset the
- * @param animation the animation to delay
- */
- private DelayedAnimation(long delay, Animation animation) {
- mDelay = delay;
- mAnimation = animation;
- }
-
- @Override
- public boolean isInitialized() {
- return mAnimation.isInitialized();
- }
-
- @Override
- public void initialize(int width, int height, int parentWidth, int parentHeight) {
- mAnimation.initialize(width, height, parentWidth, parentHeight);
- }
-
- @Override
- public void reset() {
- mAnimation.reset();
- }
-
- @Override
- public boolean getTransformation(long currentTime, Transformation outTransformation) {
- final long oldOffset = mAnimation.getStartOffset();
- final boolean isSet = mAnimation instanceof AnimationSet;
- if (isSet) {
- AnimationSet set = ((AnimationSet) mAnimation);
- set.saveChildrenStartOffset(mDelay);
- }
- mAnimation.setStartOffset(oldOffset + mDelay);
-
- boolean result = mAnimation.getTransformation(currentTime,
- outTransformation);
-
- if (isSet) {
- AnimationSet set = ((AnimationSet) mAnimation);
- set.restoreChildrenStartOffset();
- }
- mAnimation.setStartOffset(oldOffset);
-
- return result;
- }
-
- @Override
- public void setStartTime(long startTimeMillis) {
- mAnimation.setStartTime(startTimeMillis);
- }
-
- @Override
- public long getStartTime() {
- return mAnimation.getStartTime();
- }
-
- @Override
- public void setInterpolator(Interpolator i) {
- mAnimation.setInterpolator(i);
- }
-
- @Override
- public void setStartOffset(long startOffset) {
- mAnimation.setStartOffset(startOffset);
- }
-
- @Override
- public void setDuration(long durationMillis) {
- mAnimation.setDuration(durationMillis);
- }
-
- @Override
- public void scaleCurrentDuration(float scale) {
- mAnimation.scaleCurrentDuration(scale);
- }
-
- @Override
- public void setRepeatMode(int repeatMode) {
- mAnimation.setRepeatMode(repeatMode);
- }
-
- @Override
- public void setFillBefore(boolean fillBefore) {
- mAnimation.setFillBefore(fillBefore);
- }
-
- @Override
- public void setFillAfter(boolean fillAfter) {
- mAnimation.setFillAfter(fillAfter);
- }
-
- @Override
- public Interpolator getInterpolator() {
- return mAnimation.getInterpolator();
- }
-
- @Override
- public long getDuration() {
- return mAnimation.getDuration();
- }
-
- @Override
- public long getStartOffset() {
- return mAnimation.getStartOffset() + mDelay;
- }
-
- @Override
- public int getRepeatMode() {
- return mAnimation.getRepeatMode();
- }
-
- @Override
- public boolean getFillBefore() {
- return mAnimation.getFillBefore();
- }
-
- @Override
- public boolean getFillAfter() {
- return mAnimation.getFillAfter();
- }
-
- @Override
- public boolean willChangeTransformationMatrix() {
- return mAnimation.willChangeTransformationMatrix();
- }
-
- @Override
- public boolean willChangeBounds() {
- return mAnimation.willChangeBounds();
- }
- }
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index a6ce293..56c6c92 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -17,35 +17,365 @@
package android.view.inputmethod;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
import android.os.Handler;
-import android.os.Message;
+import android.os.SystemClock;
+import android.text.Editable;
+import android.text.NoCopySpan;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.method.MetaKeyKeyListener;
+import android.util.Log;
+import android.util.LogPrinter;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewRoot;
+class ComposingText implements NoCopySpan {
+}
+
/**
* Base class for implementors of the InputConnection interface, taking care
- * of implementing common system-oriented parts of the functionality.
+ * of most of the common behavior for providing a connection to an Editable.
+ * Implementors of this class will want to be sure to implement
+ * {@link #getEditable} to provide access to their own editable object.
*/
-public abstract class BaseInputConnection implements InputConnection {
+public class BaseInputConnection implements InputConnection {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "BaseInputConnection";
+ static final Object COMPOSING = new ComposingText();
+
final InputMethodManager mIMM;
final Handler mH;
final View mTargetView;
+ final boolean mDummyMode;
+
+ private Object[] mDefaultComposingSpans;
+
+ Editable mEditable;
+ KeyCharacterMap mKeyCharacterMap;
- BaseInputConnection(InputMethodManager mgr) {
+ BaseInputConnection(InputMethodManager mgr, boolean dummyMode) {
mIMM = mgr;
mTargetView = null;
mH = null;
+ mDummyMode = dummyMode;
}
- public BaseInputConnection(View targetView) {
+ public BaseInputConnection(View targetView, boolean dummyMode) {
mIMM = (InputMethodManager)targetView.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
mH = targetView.getHandler();
mTargetView = targetView;
+ mDummyMode = dummyMode;
+ }
+
+ public static final void removeComposingSpans(Spannable text) {
+ text.removeSpan(COMPOSING);
+ Object[] sps = text.getSpans(0, text.length(), Object.class);
+ if (sps != null) {
+ for (int i=sps.length-1; i>=0; i--) {
+ Object o = sps[i];
+ if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) {
+ text.removeSpan(o);
+ }
+ }
+ }
+ }
+
+ public static void setComposingSpans(Spannable text) {
+ final Object[] sps = text.getSpans(0, text.length(), Object.class);
+ if (sps != null) {
+ for (int i=sps.length-1; i>=0; i--) {
+ final Object o = sps[i];
+ if (o == COMPOSING) {
+ text.removeSpan(o);
+ continue;
+ }
+ final int fl = text.getSpanFlags(o);
+ if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK))
+ != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
+ text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
+ (fl&Spanned.SPAN_POINT_MARK_MASK)
+ | Spanned.SPAN_COMPOSING
+ | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ }
+
+ text.setSpan(COMPOSING, 0, text.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
+ }
+
+ public static int getComposingSpanStart(Spannable text) {
+ return text.getSpanStart(COMPOSING);
+ }
+
+ public static int getComposingSpanEnd(Spannable text) {
+ return text.getSpanEnd(COMPOSING);
+ }
+
+ /**
+ * Return the target of edit operations. The default implementation
+ * returns its own fake editable that is just used for composing text;
+ * subclasses that are real text editors should override this and
+ * supply their own.
+ */
+ public Editable getEditable() {
+ if (mEditable == null) {
+ mEditable = Editable.Factory.getInstance().newEditable("");
+ Selection.setSelection(mEditable, 0);
+ }
+ return mEditable;
}
/**
+ * Default implementation does nothing.
+ */
+ public boolean beginBatchEdit() {
+ return false;
+ }
+
+ /**
+ * Default implementation does nothing.
+ */
+ public boolean endBatchEdit() {
+ return false;
+ }
+
+ /**
+ * Default implementation uses
+ * {@link MetaKeyKeyListener#clearMetaKeyState(long, int)
+ * MetaKeyKeyListener.clearMetaKeyState(long, int)} to clear the state.
+ */
+ public boolean clearMetaKeyStates(int states) {
+ final Editable content = getEditable();
+ if (content == null) return false;
+ MetaKeyKeyListener.clearMetaKeyState(content, states);
+ return true;
+ }
+
+ /**
+ * Default implementation does nothing.
+ */
+ public boolean commitCompletion(CompletionInfo text) {
+ return false;
+ }
+
+ /**
+ * Default implementation replaces any existing composing text with
+ * the given text. In addition, only if dummy mode, a key event is
+ * sent for the new text and the current editable buffer cleared.
+ */
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ if (DEBUG) Log.v(TAG, "commitText " + text);
+ replaceText(text, newCursorPosition, false);
+ sendCurrentText();
+ return true;
+ }
+
+ /**
+ * The default implementation performs the deletion around the current
+ * selection position of the editable text.
+ */
+ public boolean deleteSurroundingText(int leftLength, int rightLength) {
+ if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength
+ + " / " + rightLength);
+ final Editable content = getEditable();
+ if (content == null) return false;
+
+ beginBatchEdit();
+
+ int a = Selection.getSelectionStart(content);
+ int b = Selection.getSelectionEnd(content);
+
+ if (a > b) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ // ignore the composing text.
+ int ca = getComposingSpanStart(content);
+ int cb = getComposingSpanEnd(content);
+ if (cb < ca) {
+ int tmp = ca;
+ ca = cb;
+ cb = tmp;
+ }
+ if (ca != -1 && cb != -1) {
+ if (ca < a) a = ca;
+ if (cb > b) b = cb;
+ }
+
+ int deleted = 0;
+
+ if (leftLength > 0) {
+ int start = a - leftLength;
+ if (start < 0) start = 0;
+ content.delete(start, a);
+ deleted = a - start;
+ }
+
+ if (rightLength > 0) {
+ b = b - deleted;
+
+ int end = b + rightLength;
+ if (end > content.length()) end = content.length();
+
+ content.delete(b, end);
+ }
+
+ endBatchEdit();
+
+ return true;
+ }
+
+ /**
+ * The default implementation removes the composing state from the
+ * current editable text. In addition, only if dummy mode, a key event is
+ * sent for the new text and the current editable buffer cleared.
+ */
+ public boolean finishComposingText() {
+ if (DEBUG) Log.v(TAG, "finishComposingText");
+ final Editable content = getEditable();
+ if (content != null) {
+ beginBatchEdit();
+ removeComposingSpans(content);
+ endBatchEdit();
+ sendCurrentText();
+ }
+ return true;
+ }
+
+ /**
+ * The default implementation uses TextUtils.getCapsMode to get the
+ * cursor caps mode for the current selection position in the editable
+ * text, unless in dummy mode in which case 0 is always returned.
+ */
+ public int getCursorCapsMode(int reqModes) {
+ if (mDummyMode) return 0;
+
+ final Editable content = getEditable();
+ if (content == null) return 0;
+
+ int a = Selection.getSelectionStart(content);
+ int b = Selection.getSelectionEnd(content);
+
+ if (a > b) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ return TextUtils.getCapsMode(content, a, reqModes);
+ }
+
+ /**
+ * The default implementation always returns null.
+ */
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ return null;
+ }
+
+ /**
+ * The default implementation returns the given amount of text from the
+ * current cursor position in the buffer.
+ */
+ public CharSequence getTextBeforeCursor(int length, int flags) {
+ final Editable content = getEditable();
+ if (content == null) return null;
+
+ int a = Selection.getSelectionStart(content);
+ int b = Selection.getSelectionEnd(content);
+
+ if (a > b) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ if (length > a) {
+ length = a;
+ }
+
+ if ((flags&GET_TEXT_WITH_STYLES) != 0) {
+ return content.subSequence(a - length, a);
+ }
+ return TextUtils.substring(content, a - length, a);
+ }
+
+ /**
+ * The default implementation returns the given amount of text from the
+ * current cursor position in the buffer.
+ */
+ public CharSequence getTextAfterCursor(int length, int flags) {
+ final Editable content = getEditable();
+ if (content == null) return null;
+
+ int a = Selection.getSelectionStart(content);
+ int b = Selection.getSelectionEnd(content);
+
+ if (a > b) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ if (b + length > content.length()) {
+ length = content.length() - b;
+ }
+
+
+ if ((flags&GET_TEXT_WITH_STYLES) != 0) {
+ return content.subSequence(b, b + length);
+ }
+ return TextUtils.substring(content, b, b + length);
+ }
+
+ /**
+ * The default implementation does nothing.
+ */
+ public boolean performContextMenuAction(int id) {
+ return false;
+ }
+
+ /**
+ * The default implementation does nothing.
+ */
+ public boolean performPrivateCommand(String action, Bundle data) {
+ return false;
+ }
+
+ /**
+ * The default implementation places the given text into the editable,
+ * replacing any existing composing text. The new text is marked as
+ * in a composing state with the composing style.
+ */
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ if (DEBUG) Log.v(TAG, "setComposingText " + text);
+ replaceText(text, newCursorPosition, true);
+ return true;
+ }
+
+ /**
+ * The default implementation changes the selection position in the
+ * current editable text.
+ */
+ public boolean setSelection(int start, int end) {
+ if (DEBUG) Log.v(TAG, "setSelection " + start + ", " + end);
+ final Editable content = getEditable();
+ if (content == null) return false;
+ Selection.setSelection(content, start, end);
+ return true;
+ }
+
+ /**
* Provides standard implementation for sending a key event to the window
* attached to the input connection's view.
*/
@@ -82,4 +412,144 @@ public abstract class BaseInputConnection implements InputConnection {
mIMM.updateStatusIcon(resId, packageName);
return true;
}
+
+ private void sendCurrentText() {
+ if (!mDummyMode) {
+ return;
+ }
+
+ Editable content = getEditable();
+ if (content != null) {
+ if (content.length() == 1) {
+ // If it's 1 character, we have a chance of being
+ // able to generate normal key events...
+ if (mKeyCharacterMap == null) {
+ mKeyCharacterMap = KeyCharacterMap.load(
+ KeyCharacterMap.BUILT_IN_KEYBOARD);
+ }
+ char[] chars = new char[1];
+ content.getChars(0, 1, chars, 0);
+ KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
+ if (events != null) {
+ for (int i=0; i<events.length; i++) {
+ if (DEBUG) Log.v(TAG, "Sending: " + events[i]);
+ sendKeyEvent(events[i]);
+ }
+ content.clear();
+ return;
+ }
+ }
+
+ // Otherwise, revert to the special key event containing
+ // the actual characters.
+ KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(),
+ content.toString(), KeyCharacterMap.BUILT_IN_KEYBOARD, 0);
+ sendKeyEvent(event);
+ content.clear();
+ }
+ }
+
+ private void replaceText(CharSequence text, int newCursorPosition,
+ boolean composing) {
+ final Editable content = getEditable();
+ if (content == null) {
+ return;
+ }
+
+ beginBatchEdit();
+
+ // delete composing text set previously.
+ int a = getComposingSpanStart(content);
+ int b = getComposingSpanEnd(content);
+
+ if (DEBUG) Log.v(TAG, "Composing span: " + a + " to " + b);
+
+ if (b < a) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ if (a != -1 && b != -1) {
+ removeComposingSpans(content);
+ } else {
+ a = Selection.getSelectionStart(content);
+ b = Selection.getSelectionEnd(content);
+ if (a >=0 && b>= 0 && a != b) {
+ if (b < a) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+ }
+ }
+
+ if (composing) {
+ Spannable sp = null;
+ if (!(text instanceof Spannable)) {
+ sp = new SpannableStringBuilder(text);
+ text = sp;
+ if (mDefaultComposingSpans == null) {
+ Context context;
+ if (mTargetView != null) {
+ context = mTargetView.getContext();
+ } else if (mIMM.mServedView != null) {
+ context = mIMM.mServedView.getContext();
+ } else {
+ context = null;
+ }
+ if (context != null) {
+ TypedArray ta = context.getTheme()
+ .obtainStyledAttributes(new int[] {
+ com.android.internal.R.attr.candidatesTextStyleSpans
+ });
+ CharSequence style = ta.getText(0);
+ ta.recycle();
+ if (style != null && style instanceof Spanned) {
+ mDefaultComposingSpans = ((Spanned)style).getSpans(
+ 0, style.length(), Object.class);
+ }
+ }
+ }
+ if (mDefaultComposingSpans != null) {
+ for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
+ sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ } else {
+ sp = (Spannable)text;
+ }
+ setComposingSpans(sp);
+ }
+
+ // Adjust newCursorPosition to be relative the start of the text.
+ newCursorPosition += a;
+
+ if (DEBUG) Log.v(TAG, "Replacing from " + a + " to " + b + " with \""
+ + text + "\", composing=" + composing
+ + ", type=" + text.getClass().getCanonicalName());
+
+ if (DEBUG) {
+ LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG);
+ lp.println("Current text:");
+ TextUtils.dumpSpans(content, lp, " ");
+ lp.println("Composing text:");
+ TextUtils.dumpSpans(text, lp, " ");
+ }
+
+ content.replace(a, b, text);
+ if (newCursorPosition < 0) newCursorPosition = 0;
+ if (newCursorPosition > content.length())
+ newCursorPosition = content.length();
+ Selection.setSelection(content, newCursorPosition);
+
+ if (DEBUG) {
+ LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG);
+ lp.println("Final text:");
+ TextUtils.dumpSpans(content, lp, " ");
+ }
+
+ endBatchEdit();
+ }
}
diff --git a/core/java/android/view/inputmethod/DefaultInputMethod.java b/core/java/android/view/inputmethod/DefaultInputMethod.java
deleted file mode 100644
index 073b01c..0000000
--- a/core/java/android/view/inputmethod/DefaultInputMethod.java
+++ /dev/null
@@ -1,239 +0,0 @@
-package android.view.inputmethod;
-
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethod;
-import com.android.internal.view.IInputMethodCallback;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.InputConnectionWrapper;
-
-/**
- * This is the default input method that runs in the same context of the
- * application that requests text input. It does nothing but returns false for
- * any key events, so that all key events will be processed by the key listener
- * of the focused text box.
- * {@hide}
- */
-public class DefaultInputMethod implements InputMethod, InputMethodSession {
- private static IInputMethod sInstance = new SimpleInputMethod(
- new DefaultInputMethod());
-
- private static InputMethodInfo sProperty = new InputMethodInfo(
- "android.text.inputmethod", DefaultInputMethod.class.getName(),
- "Default", "android.text.inputmethod.defaultImeSettings");
-
- private InputConnection mInputConnection;
-
- public static IInputMethod getInstance() {
- return sInstance;
- }
-
- public static InputMethodInfo getMetaInfo() {
- return sProperty;
- }
-
- public void bindInput(InputBinding binding) {
- mInputConnection = binding.getConnection();
- }
-
- public void unbindInput() {
- }
-
- public void createSession(SessionCallback callback) {
- callback.sessionCreated(this);
- }
-
- public void setSessionEnabled(InputMethodSession session, boolean enabled) {
- }
-
- public void revokeSession(InputMethodSession session) {
- }
-
- public void finishInput() {
- mInputConnection.hideStatusIcon();
- }
-
- public void displayCompletions(CompletionInfo[] completions) {
- }
-
- public void updateExtractedText(int token, ExtractedText text) {
- }
-
- public void updateSelection(int oldSelStart, int oldSelEnd,
- int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
- }
-
- public void updateCursor(Rect newCursor) {
- }
-
- public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
- callback.finishedEvent(seq, false);
- }
-
- public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) {
- callback.finishedEvent(seq, false);
- }
-
- public void restartInput(EditorInfo attribute) {
- }
-
- public void attachToken(IBinder token) {
- }
-
- public void startInput(EditorInfo attribute) {
- mInputConnection
- .showStatusIcon("android", com.android.internal.R.drawable.ime_qwerty);
- }
-
- public void appPrivateCommand(String action, Bundle data) {
- }
-
- public void hideSoftInput() {
- }
-
- public void showSoftInput(int flags) {
- }
-}
-
-// ----------------------------------------------------------------------
-
-class SimpleInputMethod extends IInputMethod.Stub {
- final InputMethod mInputMethod;
-
- static class Session extends IInputMethodSession.Stub {
- final InputMethodSession mSession;
-
- Session(InputMethodSession session) {
- mSession = session;
- }
-
- public void finishInput() {
- mSession.finishInput();
- }
-
- public void updateSelection(int oldSelStart, int oldSelEnd,
- int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
- mSession.updateSelection(oldSelStart, oldSelEnd,
- newSelStart, newSelEnd, candidatesStart, candidatesEnd);
- }
-
- public void updateCursor(Rect newCursor) {
- mSession.updateCursor(newCursor);
- }
-
- static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback {
- final IInputMethodCallback mCb;
- InputMethodEventCallbackWrapper(IInputMethodCallback cb) {
- mCb = cb;
- }
- public void finishedEvent(int seq, boolean handled) {
- try {
- mCb.finishedEvent(seq, handled);
- } catch (RemoteException e) {
- }
- }
- }
-
- public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) {
- mSession.dispatchKeyEvent(seq, event,
- new InputMethodEventCallbackWrapper(callback));
- }
-
- public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallback callback) {
- mSession.dispatchTrackballEvent(seq, event,
- new InputMethodEventCallbackWrapper(callback));
- }
-
- public void displayCompletions(CompletionInfo[] completions) {
- mSession.displayCompletions(completions);
- }
-
- public void updateExtractedText(int token, ExtractedText text) {
- mSession.updateExtractedText(token, text);
- }
-
- public void appPrivateCommand(String action, Bundle data) {
- mSession.appPrivateCommand(action, data);
- }
- }
-
- public SimpleInputMethod(InputMethod inputMethod) {
- mInputMethod = inputMethod;
- }
-
- public InputMethod getInternalInputMethod() {
- return mInputMethod;
- }
-
- public void attachToken(IBinder token) {
- mInputMethod.attachToken(token);
- }
-
- public void bindInput(InputBinding binding) {
- InputConnectionWrapper ic = new InputConnectionWrapper(
- IInputContext.Stub.asInterface(binding.getConnectionToken()));
- InputBinding nu = new InputBinding(ic, binding);
- mInputMethod.bindInput(nu);
- }
-
- public void unbindInput() {
- mInputMethod.unbindInput();
- }
-
- public void restartInput(EditorInfo attribute) {
- mInputMethod.restartInput(attribute);
- }
-
- public void startInput(EditorInfo attribute) {
- mInputMethod.startInput(attribute);
- }
-
- static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
- final IInputMethodCallback mCb;
- InputMethodSessionCallbackWrapper(IInputMethodCallback cb) {
- mCb = cb;
- }
-
- public void sessionCreated(InputMethodSession session) {
- try {
- mCb.sessionCreated(new Session(session));
- } catch (RemoteException e) {
- }
- }
- }
-
- public void createSession(IInputMethodCallback callback) throws RemoteException {
- mInputMethod.createSession(new InputMethodSessionCallbackWrapper(callback));
- }
-
- public void setSessionEnabled(IInputMethodSession session, boolean enabled) throws RemoteException {
- try {
- InputMethodSession ls = ((Session)session).mSession;
- mInputMethod.setSessionEnabled(ls, enabled);
- } catch (ClassCastException e) {
- Log.w("SimpleInputMethod", "Incoming session not of correct type: " + session, e);
- }
- }
-
- public void revokeSession(IInputMethodSession session) throws RemoteException {
- try {
- InputMethodSession ls = ((Session)session).mSession;
- mInputMethod.revokeSession(ls);
- } catch (ClassCastException e) {
- Log.w("SimpleInputMethod", "Incoming session not of correct type: " + session, e);
- }
- }
-
- public void showSoftInput(boolean blah) {
- }
-
- public void hideSoftInput() {
- }
-}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index c050691..b2f26d7 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -5,6 +5,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.InputType;
import android.text.TextUtils;
+import android.util.Printer;
/**
* An EditorInfo describes several attributes of a text editing object
@@ -21,7 +22,7 @@ public class EditorInfo implements InputType, Parcelable {
* @see #TYPE_MASK_VARIATION
* @see #TYPE_MASK_FLAGS
*/
- public int inputType = TYPE_CLASS_TEXT;
+ public int inputType = TYPE_NULL;
/**
* A string supplying additional information about the content type that
@@ -71,6 +72,26 @@ public class EditorInfo implements InputType, Parcelable {
public CharSequence label;
/**
+ * Name of the package that owns this editor.
+ */
+ public String packageName;
+
+ /**
+ * Identifier for the editor's field. This is optional, and may be
+ * 0. By default it is filled in with the result of
+ * {@link android.view.View#getId() View.getId()} on the View that
+ * is being edited.
+ */
+ public int fieldId;
+
+ /**
+ * Additional name for the editor's field. This can supply additional
+ * name information for the field. By default it is null. The actual
+ * contents have no meaning.
+ */
+ public String fieldName;
+
+ /**
* Any extra data to supply to the input method. This is for extended
* communication with specific input methods; the name fields in the
* bundle should be scoped (such as "com.mydomain.im.SOME_FIELD") so
@@ -81,6 +102,24 @@ public class EditorInfo implements InputType, Parcelable {
public Bundle extras;
/**
+ * Write debug output of this object.
+ */
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "inputType=0x" + Integer.toHexString(inputType)
+ + " privateContentType=" + privateContentType);
+ pw.println(prefix + "initialSelStart=" + initialSelStart
+ + " initialSelEnd=" + initialSelEnd
+ + " initialCapsMode=0x"
+ + Integer.toHexString(initialCapsMode));
+ pw.println(prefix + "hintText=" + hintText
+ + " label=" + label);
+ pw.println(prefix + "packageName=" + packageName
+ + " fieldId=" + fieldId
+ + " fieldName=" + fieldName);
+ pw.println(prefix + "extras=" + extras);
+ }
+
+ /**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
@@ -94,6 +133,9 @@ public class EditorInfo implements InputType, Parcelable {
dest.writeInt(initialCapsMode);
TextUtils.writeToParcel(hintText, dest, flags);
TextUtils.writeToParcel(label, dest, flags);
+ dest.writeString(packageName);
+ dest.writeInt(fieldId);
+ dest.writeString(fieldName);
dest.writeBundle(extras);
}
@@ -110,6 +152,9 @@ public class EditorInfo implements InputType, Parcelable {
res.initialCapsMode = source.readInt();
res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ res.packageName = source.readString();
+ res.fieldId = source.readInt();
+ res.fieldName = source.readString();
res.extras = source.readBundle();
return res;
}
diff --git a/core/java/android/view/inputmethod/ExtractedText.java b/core/java/android/view/inputmethod/ExtractedText.java
index 0ca3c79..e5d3cae 100644
--- a/core/java/android/view/inputmethod/ExtractedText.java
+++ b/core/java/android/view/inputmethod/ExtractedText.java
@@ -19,6 +19,22 @@ public class ExtractedText implements Parcelable {
public int startOffset;
/**
+ * If the content is a report of a partial text change, this is the
+ * offset where the change starts and it runs until
+ * {@link #partialEndOffset}. If the content is the full text, this
+ * field is -1.
+ */
+ public int partialStartOffset;
+
+ /**
+ * If the content is a report of a partial text change, this is the offset
+ * where the change ends. Note that the actual text may be larger or
+ * smaller than the difference between this and {@link #partialEndOffset},
+ * meaning a reduction or increase, respectively, in the total text.
+ */
+ public int partialEndOffset;
+
+ /**
* The offset where the selection currently starts within the extracted
* text. The real selection start position is at
* <var>startOffset</var>+<var>selectionStart</var>.
@@ -52,6 +68,8 @@ public class ExtractedText implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
TextUtils.writeToParcel(text, dest, flags);
dest.writeInt(startOffset);
+ dest.writeInt(partialStartOffset);
+ dest.writeInt(partialEndOffset);
dest.writeInt(selectionStart);
dest.writeInt(selectionEnd);
dest.writeInt(flags);
@@ -65,6 +83,8 @@ public class ExtractedText implements Parcelable {
ExtractedText res = new ExtractedText();
res.text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.startOffset = source.readInt();
+ res.partialStartOffset = source.readInt();
+ res.partialEndOffset = source.readInt();
res.selectionStart = source.readInt();
res.selectionEnd = source.readInt();
res.flags = source.readInt();
diff --git a/core/java/android/view/inputmethod/ExtractedTextRequest.java b/core/java/android/view/inputmethod/ExtractedTextRequest.java
index d962329..e84b094 100644
--- a/core/java/android/view/inputmethod/ExtractedTextRequest.java
+++ b/core/java/android/view/inputmethod/ExtractedTextRequest.java
@@ -16,6 +16,13 @@ public class ExtractedTextRequest implements Parcelable {
public int token;
/**
+ * Additional request flags, having the same possible values as the
+ * flags parameter of {@link InputConnection#getTextBeforeCursor
+ * InputConnection.getTextBeforeCursor()}.
+ */
+ public int flags;
+
+ /**
* Hint for the maximum number of lines to return.
*/
public int hintMaxLines;
@@ -33,6 +40,7 @@ public class ExtractedTextRequest implements Parcelable {
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(token);
+ dest.writeInt(this.flags);
dest.writeInt(hintMaxLines);
dest.writeInt(hintMaxChars);
}
@@ -45,6 +53,7 @@ public class ExtractedTextRequest implements Parcelable {
public ExtractedTextRequest createFromParcel(Parcel source) {
ExtractedTextRequest res = new ExtractedTextRequest();
res.token = source.readInt();
+ res.flags = source.readInt();
res.hintMaxLines = source.readInt();
res.hintMaxChars = source.readInt();
return res;
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index bd7b050..8c30d3f 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -32,6 +32,21 @@ import android.view.KeyEvent;
*/
public interface InputConnection {
/**
+ * Flag for use with {@link #getTextAfterCursor} and
+ * {@link #getTextBeforeCursor} to have style information returned along
+ * with the text. If not set, you will receive only the raw text. If
+ * set, you may receive a complex CharSequence of both text and style
+ * spans.
+ */
+ static final int GET_TEXT_WITH_STYLES = 0x0001;
+
+ /**
+ * Flag for use with {@link #getExtractedText} to indicate you would
+ * like to receive updates when the extracted text changes.
+ */
+ public static final int GET_EXTRACTED_TEXT_MONITOR = 0x0001;
+
+ /**
* Get <var>n</var> characters of text before the current cursor position.
*
* <p>This method may fail either if the input connection has become invalid
@@ -40,11 +55,13 @@ public interface InputConnection {
* In either case, a null is returned.
*
* @param n The expected length of the text.
+ * @param flags Supplies additional options controlling how the text is
+ * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
*
* @return Returns the text before the cursor position; the length of the
* returned text might be less than <var>n</var>.
*/
- public CharSequence getTextBeforeCursor(int n);
+ public CharSequence getTextBeforeCursor(int n, int flags);
/**
* Get <var>n</var> characters of text after the current cursor position.
@@ -55,11 +72,13 @@ public interface InputConnection {
* In either case, a null is returned.
*
* @param n The expected length of the text.
+ * @param flags Supplies additional options controlling how the text is
+ * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
*
* @return Returns the text after the cursor position; the length of the
* returned text might be less than <var>n</var>.
*/
- public CharSequence getTextAfterCursor(int n);
+ public CharSequence getTextAfterCursor(int n, int flags);
/**
* Retrieve the current capitalization mode in effect at the current
@@ -82,8 +101,6 @@ public interface InputConnection {
*/
public int getCursorCapsMode(int reqModes);
- public static final int EXTRACTED_TEXT_MONITOR = 0x0001;
-
/**
* Retrieve the current text in the input connection's editor, and monitor
* for any changes to it. This function returns with the current text,
@@ -97,7 +114,7 @@ public interface InputConnection {
*
* @param request Description of how the text should be returned.
* @param flags Additional options to control the client, either 0 or
- * {@link #EXTRACTED_TEXT_MONITOR}.
+ * {@link #GET_EXTRACTED_TEXT_MONITOR}.
*
* @return Returns an ExtractedText object describing the state of the
* text view and containing the extracted text itself.
@@ -141,7 +158,7 @@ public interface InputConnection {
/**
* Have the text editor finish whatever composing text is currently
- * active. This simple leaves the text as-is, removing any special
+ * active. This simply leaves the text as-is, removing any special
* composing styling or other state that was around it. The cursor
* position remains unchanged.
*/
@@ -177,6 +194,22 @@ public interface InputConnection {
public boolean commitCompletion(CompletionInfo text);
/**
+ * Set the selection of the text editor. To set the cursor position,
+ * start and end should have the same value.
+ */
+ public boolean setSelection(int start, int end);
+
+ /**
+ * Perform a context menu action on the field. The given id may be one of:
+ * {@link android.R.id#selectAll},
+ * {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText},
+ * {@link android.R.id#cut}, {@link android.R.id#copy},
+ * {@link android.R.id#paste}, {@link android.R.id#copyUrl},
+ * or {@link android.R.id#switchInputMethod}
+ */
+ public boolean performContextMenuAction(int id);
+
+ /**
* Tell the editor that you are starting a batch of editor operations.
* The editor will try to avoid sending you updates about its state
* until {@link #endBatchEdit} is called.
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
deleted file mode 100644
index f65b2a1..0000000
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2007-2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package android.view.inputmethod;
-
-import android.os.Bundle;
-import android.view.KeyEvent;
-
-/**
- * Wrapper around InputConnection interface, calling through to another
- * implementation of it.
- */
-public class InputConnectionWrapper implements InputConnection {
- InputConnection mBase;
-
- /**
- * Create a new wrapper around an existing InputConnection implementation.
- */
- public InputConnectionWrapper(InputConnection base) {
- mBase = base;
- }
-
- /**
- * Return the base InputConnection that this class is wrapping.
- */
- InputConnection getBase() {
- return mBase;
- }
-
- public CharSequence getTextBeforeCursor(int n) {
- return mBase.getTextBeforeCursor(n);
- }
-
- public CharSequence getTextAfterCursor(int n) {
- return mBase.getTextAfterCursor(n);
- }
-
- public int getCursorCapsMode(int reqModes) {
- return mBase.getCursorCapsMode(reqModes);
- }
-
- public ExtractedText getExtractedText(ExtractedTextRequest request,
- int flags) {
- return mBase.getExtractedText(request, flags);
- }
-
- public boolean deleteSurroundingText(int leftLength, int rightLength) {
- return mBase.deleteSurroundingText(leftLength, rightLength);
- }
-
- public boolean setComposingText(CharSequence text, int newCursorPosition) {
- return mBase.setComposingText(text, newCursorPosition);
- }
-
- public boolean finishComposingText() {
- return mBase.finishComposingText();
- }
-
- public boolean commitText(CharSequence text, int newCursorPosition) {
- return mBase.commitText(text, newCursorPosition);
- }
-
- public boolean commitCompletion(CompletionInfo text) {
- return mBase.commitCompletion(text);
- }
-
- public boolean beginBatchEdit() {
- return mBase.beginBatchEdit();
- }
-
- public boolean endBatchEdit() {
- return mBase.endBatchEdit();
- }
-
- public boolean sendKeyEvent(KeyEvent event) {
- return mBase.sendKeyEvent(event);
- }
-
- public boolean clearMetaKeyStates(int states) {
- return mBase.clearMetaKeyStates(states);
- }
-
- public boolean performPrivateCommand(String action, Bundle data) {
- return mBase.performPrivateCommand(action, data);
- }
-
- public boolean showStatusIcon(String packageName, int resId) {
- return mBase.showStatusIcon(packageName, resId);
- }
-
- public boolean hideStatusIcon() {
- return mBase.hideStatusIcon();
- }
-}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index c0e6590..740dca8 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -113,12 +113,15 @@ public interface InputMethod {
* is ready for this input method to process received events and send result
* text back to the application.
*
- * @param attribute The attribute of the text box (typically, a EditText)
+ * @param inputConnection Optional specific input connection for
+ * communicating with the text box; if null, you should use the generic
+ * bound input connection.
+ * @param info Information about the text box (typically, an EditText)
* that requests input.
*
* @see EditorInfo
*/
- public void startInput(EditorInfo attribute);
+ public void startInput(InputConnection inputConnection, EditorInfo info);
/**
* This method is called when the state of this input method needs to be
@@ -128,12 +131,15 @@ public interface InputMethod {
* Typically, this method is called when the input focus is moved from one
* text box to another.
*
+ * @param inputConnection Optional specific input connection for
+ * communicating with the text box; if null, you should use the generic
+ * bound input connection.
* @param attribute The attribute of the text box (typically, a EditText)
* that requests input.
*
* @see EditorInfo
*/
- public void restartInput(EditorInfo attribute);
+ public void restartInput(InputConnection inputConnection, EditorInfo attribute);
/**
* Create a new {@link InputMethodSession} that can be handed to client
@@ -173,6 +179,13 @@ public interface InputMethod {
public static final int SHOW_EXPLICIT = 0x00001;
/**
+ * Flag for {@link #showSoftInput(int)}: this show has been forced to
+ * happen by the user. If set, the input method should remain visible
+ * until deliberated dismissed by the user in its UI.
+ */
+ public static final int SHOW_FORCED = 0x00002;
+
+ /**
* Request that any soft input part of the input method be shown to the user.
*
* @param flags Provide additional information about the show request.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a9a9594..99d5aa5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -26,11 +26,14 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewRoot;
+import com.android.internal.os.HandlerCaller;
import com.android.internal.view.IInputConnectionWrapper;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodCallback;
@@ -39,7 +42,11 @@ import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Central system API to the overall input method framework (IMF) architecture,
@@ -199,8 +206,7 @@ public final class InputMethodManager {
// global lock.
final H mH;
- // The currently active input connection.
- final MutableInputConnectionWrapper mInputConnectionWrapper;
+ // Our generic input connection if the current target does not have its own.
final IInputContext mIInputContext;
/**
@@ -208,11 +214,6 @@ public final class InputMethodManager {
*/
boolean mActive = false;
- /**
- * The current base input connection, used when mActive is true.
- */
- InputConnection mCurrentInputConnection;
-
// -----------------------------------------------------------
/**
@@ -270,7 +271,7 @@ public final class InputMethodManager {
// -----------------------------------------------------------
- static final int MSG_CHECK_FOCUS = 1;
+ static final int MSG_DUMP = 1;
class H extends Handler {
H(Looper looper) {
@@ -280,85 +281,55 @@ public final class InputMethodManager {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_CHECK_FOCUS:
- checkFocus();
+ case MSG_DUMP: {
+ HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
+ try {
+ doDump((FileDescriptor)args.arg1,
+ (PrintWriter)args.arg2, (String[])args.arg3);
+ } catch (RuntimeException e) {
+ ((PrintWriter)args.arg2).println("Exception: " + e);
+ }
+ synchronized (args.arg4) {
+ ((CountDownLatch)args.arg4).countDown();
+ }
return;
+ }
}
}
}
- static class NoOpInputConnection implements InputConnection {
-
- public boolean clearMetaKeyStates(int states) {
- return false;
- }
-
- public boolean beginBatchEdit() {
- return false;
- }
-
- public boolean endBatchEdit() {
- return false;
- }
-
- public boolean commitCompletion(CompletionInfo text) {
- return false;
- }
-
- public boolean commitText(CharSequence text, int newCursorPosition) {
- return false;
- }
-
- public boolean deleteSurroundingText(int leftLength, int rightLength) {
- return false;
- }
-
- public int getCursorCapsMode(int reqModes) {
- return 0;
- }
-
- public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
- return null;
- }
-
- public CharSequence getTextAfterCursor(int n) {
- return null;
+ class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
+ public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
+ super(mainLooper, conn);
}
- public CharSequence getTextBeforeCursor(int n) {
- return null;
- }
-
- public boolean hideStatusIcon() {
- return false;
- }
-
- public boolean performPrivateCommand(String action, Bundle data) {
- return false;
- }
-
- public boolean sendKeyEvent(KeyEvent event) {
- return false;
- }
-
- public boolean setComposingText(CharSequence text, int newCursorPosition) {
- return false;
- }
-
- public boolean finishComposingText() {
- return false;
- }
-
- public boolean showStatusIcon(String packageName, int resId) {
- return false;
+ public boolean isActive() {
+ return mActive;
}
}
- final NoOpInputConnection mNoOpInputConnection = new NoOpInputConnection();
-
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
- public void setUsingInputMethod(boolean state) {
+ @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ // No need to check for dump permission, since we only give this
+ // interface to the system.
+ CountDownLatch latch = new CountDownLatch(1);
+ HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs();
+ sargs.arg1 = fd;
+ sargs.arg2 = fout;
+ sargs.arg3 = args;
+ sargs.arg4 = latch;
+ mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
+ try {
+ if (!latch.await(5, TimeUnit.SECONDS)) {
+ fout.println("Timeout waiting for dump");
+ }
+ } catch (InterruptedException e) {
+ fout.println("Interrupted waiting for dump");
+ }
+ }
+
+ public void setUsingInputMethod(boolean state) {
}
public void onBindMethod(InputBindResult res) {
@@ -403,62 +374,17 @@ public final class InputMethodManager {
public void setActive(boolean active) {
mActive = active;
- mInputConnectionWrapper.setBaseInputConnection(active
- ? mCurrentInputConnection : mNoOpInputConnection);
}
};
- final InputConnection mDummyInputConnection = new BaseInputConnection(this) {
- public boolean beginBatchEdit() {
- return false;
- }
- public boolean endBatchEdit() {
- return false;
- }
- public boolean commitText(CharSequence text, int newCursorPosition) {
- return false;
- }
- public boolean commitCompletion(CompletionInfo text) {
- return false;
- }
- public boolean deleteSurroundingText(int leftLength, int rightLength) {
- return false;
- }
- public ExtractedText getExtractedText(ExtractedTextRequest request,
- int flags) {
- return null;
- }
- public CharSequence getTextAfterCursor(int n) {
- return null;
- }
- public CharSequence getTextBeforeCursor(int n) {
- return null;
- }
- public int getCursorCapsMode(int reqModes) {
- return 0;
- }
- public boolean clearMetaKeyStates(int states) {
- return false;
- }
- public boolean performPrivateCommand(String action, Bundle data) {
- return false;
- }
- public boolean setComposingText(CharSequence text, int newCursorPosition) {
- return false;
- }
- public boolean finishComposingText() {
- return false;
- }
- };
+ final InputConnection mDummyInputConnection = new BaseInputConnection(this, true);
InputMethodManager(IInputMethodManager service, Looper looper) {
mService = service;
mMainLooper = looper;
mH = new H(looper);
- mInputConnectionWrapper = new MutableInputConnectionWrapper(mNoOpInputConnection);
- mIInputContext = new IInputConnectionWrapper(looper,
- mInputConnectionWrapper);
- setCurrentInputConnection(mDummyInputConnection);
+ mIInputContext = new ControlledInputConnectionWrapper(looper,
+ mDummyInputConnection);
if (mInstance == null) {
mInstance = this;
@@ -563,22 +489,12 @@ public final class InputMethodManager {
}
/**
- * Record the desired input connection, but only set it if mActive is true.
- */
- void setCurrentInputConnection(InputConnection connection) {
- mCurrentInputConnection = connection;
- mInputConnectionWrapper.setBaseInputConnection(mActive
- ? connection : mNoOpInputConnection);
- }
-
- /**
* Reset all of the state associated with a served view being connected
* to an input method
*/
void clearConnectionLocked() {
mCurrentTextBoxAttribute = null;
mServedInputConnection = null;
- setCurrentInputConnection(mDummyInputConnection);
}
/**
@@ -659,13 +575,20 @@ public final class InputMethodManager {
}
/**
- * Flag for {@link #showSoftInput} to indicate that the this is an implicit
+ * Flag for {@link #showSoftInput} to indicate that this is an implicit
* request to show the input window, not as the result of a direct request
* by the user. The window may not be shown in this case.
*/
public static final int SHOW_IMPLICIT = 0x0001;
/**
+ * Flag for {@link #showSoftInput} to indicate that the user has forced
+ * the input method open (such as by long-pressing menu) so it should
+ * not be closed until they explicitly do so.
+ */
+ public static final int SHOW_FORCED = 0x0002;
+
+ /**
* Explicitly request that the current input method's soft input area be
* shown to the user, if needed. Call this if the user interacts with
* your view in such a way that they have expressed they would like to
@@ -689,6 +612,14 @@ public final class InputMethodManager {
}
}
+ /** @hide */
+ public void showSoftInputUnchecked(int flags) {
+ try {
+ mService.showSoftInput(mClient, flags);
+ } catch (RemoteException e) {
+ }
+ }
+
/**
* Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
* input window should only be hidden if it was not explicitly shown
@@ -697,6 +628,13 @@ public final class InputMethodManager {
public static final int HIDE_IMPLICIT_ONLY = 0x0001;
/**
+ * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
+ * input window should normally be hidden, unless it was originally
+ * shown with {@link #SHOW_FORCED}.
+ */
+ public static final int HIDE_NOT_ALWAYS = 0x0002;
+
+ /**
* Request to hide the soft input window from the context of the window
* that is currently accepting input. This should be called as a result
* of the user doing some actually than fairly explicitly requests to
@@ -779,6 +717,8 @@ public final class InputMethodManager {
// do its stuff.
// Life is good: let's hook everything up!
EditorInfo tba = new EditorInfo();
+ tba.packageName = view.getContext().getPackageName();
+ tba.fieldId = view.getId();
InputConnection ic = view.onCreateInputConnection(tba);
if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
@@ -801,22 +741,23 @@ public final class InputMethodManager {
mCurrentTextBoxAttribute = tba;
mServedConnecting = false;
mServedInputConnection = ic;
+ IInputContext servedContext;
if (ic != null) {
mCursorSelStart = tba.initialSelStart;
mCursorSelEnd = tba.initialSelEnd;
mCursorCandStart = -1;
mCursorCandEnd = -1;
mCursorRect.setEmpty();
- setCurrentInputConnection(ic);
+ servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
} else {
- setCurrentInputConnection(mDummyInputConnection);
+ servedContext = null;
}
try {
if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
+ ic + " tba=" + tba + " initial=" + initial);
- InputBindResult res = mService.startInput(mClient, tba, initial,
- mCurMethod == null);
+ InputBindResult res = mService.startInput(mClient,
+ servedContext, tba, initial, mCurMethod == null);
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res != null) {
if (res.id != null) {
@@ -885,10 +826,20 @@ public final class InputMethodManager {
+ " winFocus=" + view.hasWindowFocus());
if (mServedView == view) {
ic = mServedInputConnection;
- if (view.hasWindowFocus()) {
+ // The following code would auto-hide the IME if we end up
+ // with no more views with focus. This can happen, however,
+ // whenever we go into touch mode, so it ends up hiding
+ // at times when we don't really want it to. For now it
+ // seems better to just turn it all off.
+ if (false && view.hasWindowFocus()) {
mLastServedView = view;
- mH.removeMessages(MSG_CHECK_FOCUS);
- mH.sendEmptyMessage(MSG_CHECK_FOCUS);
+ Handler vh = view.getHandler();
+ if (vh != null) {
+ // This will result in a call to checkFocus() below.
+ vh.removeMessages(ViewRoot.CHECK_FOCUS);
+ vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS,
+ view));
+ }
}
}
}
@@ -898,8 +849,14 @@ public final class InputMethodManager {
}
}
- void checkFocus() {
+ /**
+ * @hide
+ */
+ public void checkFocus(View view) {
synchronized (mH) {
+ if (view != mLastServedView) {
+ return;
+ }
if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
+ " last=" + mLastServedView);
if (mServedView == mLastServedView) {
@@ -915,7 +872,7 @@ public final class InputMethodManager {
void closeCurrentInput() {
try {
- mService.hideSoftInput(mClient, 0);
+ mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS);
} catch (RemoteException e) {
}
}
@@ -1006,6 +963,32 @@ public final class InputMethodManager {
}
/**
+ * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
+ * InputMethodSession.appPrivateCommand()} on the current Input Method.
+ * @param view Optional View that is sending the command, or null if
+ * you want to send the command regardless of the view that is attached
+ * to the input method.
+ * @param action Name of the command to be performed. This <em>must</em>
+ * be a scoped name, i.e. prefixed with a package name you own, so that
+ * different developers will not create conflicting commands.
+ * @param data Any data to include with the command.
+ */
+ public void sendAppPrivateCommand(View view, String action, Bundle data) {
+ synchronized (mH) {
+ if ((view != null && mServedView != view)
+ || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+ return;
+ }
+ try {
+ if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
+ mCurMethod.appPrivateCommand(action, data);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId, e);
+ }
+ }
+ }
+
+ /**
* Force switch to a new input method component. This can only be called
* from the currently active input method, as validated by the given token.
* @param token Supplies the identifying token given to an input method
@@ -1048,7 +1031,7 @@ public final class InputMethodManager {
synchronized (mH) {
if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
- if (mCurMethod == null || mCurrentTextBoxAttribute == null) {
+ if (mCurMethod == null) {
try {
callback.finishedEvent(seq, false);
} catch (RemoteException e) {
@@ -1116,4 +1099,33 @@ public final class InputMethodManager {
}
}
}
+
+ void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ final Printer p = new PrintWriterPrinter(fout);
+ p.println("Input method client state for " + this + ":");
+
+ p.println(" mService=" + mService);
+ p.println(" mMainLooper=" + mMainLooper);
+ p.println(" mIInputContext=" + mIInputContext);
+ p.println(" mActive=" + mActive
+ + " mBindSequence=" + mBindSequence
+ + " mCurId=" + mCurId);
+ p.println(" mCurMethod=" + mCurMethod);
+ p.println(" mServedView=" + mServedView);
+ p.println(" mLastServedView=" + mLastServedView);
+ p.println(" mServedConnecting=" + mServedConnecting);
+ if (mCurrentTextBoxAttribute != null) {
+ p.println(" mCurrentTextBoxAttribute:");
+ mCurrentTextBoxAttribute.dump(p, " ");
+ } else {
+ p.println(" mCurrentTextBoxAttribute: null");
+ }
+ p.println(" mServedInputConnection=" + mServedInputConnection);
+ p.println(" mCompletions=" + mCompletions);
+ p.println(" mCursorRect=" + mCursorRect);
+ p.println(" mCursorSelStart=" + mCursorSelStart
+ + " mCursorSelEnd=" + mCursorSelEnd
+ + " mCursorCandStart=" + mCursorCandStart
+ + " mCursorCandEnd=" + mCursorCandEnd);
+ }
}
diff --git a/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java b/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java
deleted file mode 100644
index 025a059..0000000
--- a/core/java/android/view/inputmethod/MutableInputConnectionWrapper.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2007-2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package android.view.inputmethod;
-
-
-/**
- * Special version of {@link InputConnectionWrapper} that allows the base
- * input connection to be modified after it is initially set.
- */
-public class MutableInputConnectionWrapper extends InputConnectionWrapper {
- public MutableInputConnectionWrapper(InputConnection base) {
- super(base);
- }
-
- /**
- * Change the base InputConnection for this wrapper. All calls will then be
- * delegated to the base input connection.
- *
- * @param base The new base InputConnection for this wrapper.
- */
- public void setBaseInputConnection(InputConnection base) {
- mBase = base;
- }
-}