diff options
Diffstat (limited to 'core/java/android/view')
23 files changed, 2016 insertions, 398 deletions
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java index 841066c..e1f2823 100644 --- a/core/java/android/view/HapticFeedbackConstants.java +++ b/core/java/android/view/HapticFeedbackConstants.java @@ -24,9 +24,30 @@ public class HapticFeedbackConstants { private HapticFeedbackConstants() {} + /** + * The user has performed a long press on an object that is resulting + * in an action being performed. + */ public static final int LONG_PRESS = 0; /** + * The user has pressed on a virtual on-screen key. + */ + public static final int VIRTUAL_KEY = 1; + + /** + * This is a private constant. Feel free to renumber as desired. + * @hide + */ + public static final int SAFE_MODE_DISABLED = 10000; + + /** + * This is a private constant. Feel free to renumber as desired. + * @hide + */ + public static final int SAFE_MODE_ENABLED = 10001; + + /** * Flag for {@link View#performHapticFeedback(int, int) * View.performHapticFeedback(int, int)}: Ignore the setting in the * view for whether to perform haptic feedback, do it always. diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 99d5c0c..71302cb 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -18,11 +18,11 @@ package android.view; import android.graphics.Rect; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; import android.view.KeyEvent; import android.view.MotionEvent; -import android.os.ParcelFileDescriptor; - /** * API back to a client window that the Window Manager uses to inform it of * interesting things happening. @@ -46,8 +46,8 @@ oneway interface IWindow { void resized(int w, int h, in Rect coveredInsets, in Rect visibleInsets, boolean reportDraw); void dispatchKey(in KeyEvent event); - void dispatchPointer(in MotionEvent event, long eventTime); - void dispatchTrackball(in MotionEvent event, long eventTime); + void dispatchPointer(in MotionEvent event, long eventTime, boolean callWhenDone); + void dispatchTrackball(in MotionEvent event, long eventTime, boolean callWhenDone); void dispatchAppVisibility(boolean visible); void dispatchGetNewSurface(); @@ -56,4 +56,14 @@ oneway interface IWindow { * to date on the current state showing navigational focus (touch mode) too. */ void windowFocusChanged(boolean hasFocus, boolean inTouchMode); + + void closeSystemDialogs(String reason); + + /** + * Called for wallpaper windows when their offsets change. + */ + void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync); + + void dispatchWallpaperCommand(String action, int x, int y, + int z, in Bundle extras, boolean sync); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 5607d4b..23e7fb7 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -71,6 +71,7 @@ interface IWindowManager void setFocusedApp(IBinder token, boolean moveFocusNow); void prepareAppTransition(int transit); int getPendingAppTransition(); + void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim); void executeAppTransition(); void setAppStartingWindow(IBinder token, String pkg, int theme, CharSequence nonLocalizedLabel, int labelRes, @@ -90,6 +91,7 @@ interface IWindowManager void exitKeyguardSecurely(IOnKeyguardExitResult callback); boolean inKeyguardRestrictedInputMode(); + void closeSystemDialogs(String reason); // These can only be called with the SET_ANIMATON_SCALE permission. float getAnimationScale(int which); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 1156856..b6b009b 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -19,6 +19,7 @@ package android.view; import android.graphics.Rect; import android.graphics.Region; +import android.os.Bundle; import android.view.IWindow; import android.view.MotionEvent; import android.view.WindowManager; @@ -108,4 +109,19 @@ interface IWindowSession { boolean getInTouchMode(); boolean performHapticFeedback(IWindow window, int effectId, boolean always); + + /** + * For windows with the wallpaper behind them, and the wallpaper is + * larger than the screen, set the offset within the screen. + * For multi screen launcher type applications, xstep and ystep indicate + * how big the increment is from one screen to another. + */ + void setWallpaperPosition(IBinder windowToken, float x, float y, float xstep, float ystep); + + void wallpaperOffsetsComplete(IBinder window); + + Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, + int z, in Bundle extras, boolean sync); + + void wallpaperCommandComplete(IBinder window, in Bundle result); } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 6349288..d4f9787 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -18,6 +18,8 @@ package android.view; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; +import android.util.SparseIntArray; import android.view.KeyCharacterMap; import android.view.KeyCharacterMap.KeyData; @@ -258,6 +260,51 @@ public class KeyEvent implements Parcelable { public static final int FLAG_EDITOR_ACTION = 0x10; /** + * When associated with up key events, this indicates that the key press + * has been canceled. Typically this is used with virtual touch screen + * keys, where the user can slide from the virtual key area on to the + * display: in that case, the application will receive a canceled up + * event and should not perform the action normally associated with the + * key. Note that for this to work, the application can not perform an + * action for a key until it receives an up or the long press timeout has + * expired. + */ + public static final int FLAG_CANCELED = 0x20; + + /** + * This key event was generated by a virtual (on-screen) hard key area. + * Typically this is an area of the touchscreen, outside of the regular + * display, dedicated to "hardware" buttons. + */ + public static final int FLAG_VIRTUAL_HARD_KEY = 0x40; + + /** + * This flag is set for the first key repeat that occurs after the + * long press timeout. + */ + public static final int FLAG_LONG_PRESS = 0x80; + + /** + * Set when a key event has {@link #FLAG_CANCELED} set because a long + * press action was executed while it was down. + */ + public static final int FLAG_CANCELED_LONG_PRESS = 0x100; + + /** + * Set for {@link #ACTION_UP} when this event's key code is still being + * tracked from its initial down. That is, somebody requested that tracking + * started on the key down and a long press has not caused + * the tracking to be canceled. + */ + public static final int FLAG_TRACKING = 0x200; + + /** + * Private control to determine when an app is tracking a key sequence. + * @hide + */ + public static final int FLAG_START_TRACKING = 0x40000000; + + /** * Returns the maximum keycode. */ public static int getMaxKeyCode() { @@ -273,6 +320,9 @@ public class KeyEvent implements Parcelable { return KeyCharacterMap.getDeadChar(accent, c); } + static final boolean DEBUG = false; + static final String TAG = "KeyEvent"; + private int mMetaState; private int mAction; private int mKeyCode; @@ -286,7 +336,11 @@ public class KeyEvent implements Parcelable { public interface Callback { /** - * Called when a key down event has occurred. + * Called when a key down event has occurred. If you return true, + * you can first call {@link KeyEvent#startTracking() + * KeyEvent.startTracking()} to have the framework track the event + * through its {@link #onKeyUp(int, KeyEvent)} and also call your + * {@link #onKeyLongPress(int, KeyEvent)} if it occurs. * * @param keyCode The value in event.getKeyCode(). * @param event Description of the key event. @@ -297,6 +351,22 @@ public class KeyEvent implements Parcelable { boolean onKeyDown(int keyCode, KeyEvent event); /** + * Called when a long press has occurred. If you return true, + * the final key up will have {@link KeyEvent#FLAG_CANCELED} and + * {@link KeyEvent#FLAG_CANCELED_LONG_PRESS} set. Note that in + * order to receive this callback, someone in the event change + * <em>must</em> return true from {@link #onKeyDown} <em>and</em> + * call {@link KeyEvent#startTracking()} on the event. + * + * @param keyCode The value in event.getKeyCode(). + * @param event Description of the key event. + * + * @return If you handled the event, return true. If you want to allow + * the event to be handled by the next receiver, return false. + */ + boolean onKeyLongPress(int keyCode, KeyEvent event); + + /** * Called when a key up event has occurred. * * @param keyCode The value in event.getKeyCode(). @@ -481,11 +551,15 @@ public class KeyEvent implements Parcelable { /** * Copy an existing key event, modifying its time and repeat count. * + * @deprecated Use {@link #changeTimeRepeat(KeyEvent, long, int)} + * instead. + * * @param origEvent The existing event to be copied. * @param eventTime The new event time * (in {@link android.os.SystemClock#uptimeMillis}) of the event. * @param newRepeat The new repeat count of the event. */ + @Deprecated public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) { mDownTime = origEvent.mDownTime; mEventTime = eventTime; @@ -514,6 +588,26 @@ public class KeyEvent implements Parcelable { } /** + * Create a new key event that is the same as the given one, but whose + * event time and repeat count are replaced with the given value. + * + * @param event The existing event to be copied. This is not modified. + * @param eventTime The new event time + * (in {@link android.os.SystemClock#uptimeMillis}) of the event. + * @param newRepeat The new repeat count of the event. + * @param newFlags New flags for the event, replacing the entire value + * in the original event. + */ + public static KeyEvent changeTimeRepeat(KeyEvent event, long eventTime, + int newRepeat, int newFlags) { + KeyEvent ret = new KeyEvent(event); + ret.mEventTime = eventTime; + ret.mRepeatCount = newRepeat; + ret.mFlags = newFlags; + return ret; + } + + /** * Copy an existing key event, modifying its action. * * @param origEvent The existing event to be copied. @@ -694,6 +788,42 @@ public class KeyEvent implements Parcelable { } /** + * For {@link #ACTION_UP} events, indicates that the event has been + * canceled as per {@link #FLAG_CANCELED}. + */ + public final boolean isCanceled() { + return (mFlags&FLAG_CANCELED) != 0; + } + + /** + * Call this during {@link Callback#onKeyDown} to have the system track + * the key through its final up (possibly including a long press). Note + * that only one key can be tracked at a time -- if another key down + * event is received while a previous one is being tracked, tracking is + * stopped on the previous event. + */ + public final void startTracking() { + mFlags |= FLAG_START_TRACKING; + } + + /** + * For {@link #ACTION_UP} events, indicates that the event is still being + * tracked from its initial down event as per + * {@link #FLAG_TRACKING}. + */ + public final boolean isTracking() { + return (mFlags&FLAG_TRACKING) != 0; + } + + /** + * For {@link #ACTION_DOWN} events, indicates that the event has been + * canceled as per {@link #FLAG_LONG_PRESS}. + */ + public final boolean isLongPress() { + return (mFlags&FLAG_LONG_PRESS) != 0; + } + + /** * Retrieve the key code of the key event. This is the physical key that * was pressed, <em>not</em> the Unicode character. * @@ -879,19 +1009,55 @@ public class KeyEvent implements Parcelable { } /** + * @deprecated Use {@link #dispatch(Callback, DispatcherState, Object)} instead. + */ + @Deprecated + public final boolean dispatch(Callback receiver) { + return dispatch(receiver, null, null); + } + + /** * Deliver this key event to a {@link Callback} interface. If this is * an ACTION_MULTIPLE event and it is not handled, then an attempt will * be made to deliver a single normal event. * * @param receiver The Callback that will be given the event. + * @param state State information retained across events. + * @param target The target of the dispatch, for use in tracking. * * @return The return value from the Callback method that was called. */ - public final boolean dispatch(Callback receiver) { + public final boolean dispatch(Callback receiver, DispatcherState state, + Object target) { switch (mAction) { - case ACTION_DOWN: - return receiver.onKeyDown(mKeyCode, this); + case ACTION_DOWN: { + mFlags &= ~FLAG_START_TRACKING; + if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state + + ": " + this); + boolean res = receiver.onKeyDown(mKeyCode, this); + if (state != null) { + if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) { + if (DEBUG) Log.v(TAG, " Start tracking!"); + state.startTracking(this, target); + } else if (isLongPress() && state.isTracking(this)) { + try { + if (receiver.onKeyLongPress(mKeyCode, this)) { + if (DEBUG) Log.v(TAG, " Clear from long press!"); + state.performedLongPress(this); + res = true; + } + } catch (AbstractMethodError e) { + } + } + } + return res; + } case ACTION_UP: + if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state + + ": " + this); + if (state != null) { + state.handleUpEvent(this); + } return receiver.onKeyUp(mKeyCode, this); case ACTION_MULTIPLE: final int count = mRepeatCount; @@ -911,10 +1077,103 @@ public class KeyEvent implements Parcelable { mRepeatCount = count; return handled; } + return false; } return false; } + /** + * Use with {@link KeyEvent#dispatch(Callback, DispatcherState, Object)} + * for more advanced key dispatching, such as long presses. + */ + public static class DispatcherState { + int mDownKeyCode; + Object mDownTarget; + SparseIntArray mActiveLongPresses = new SparseIntArray(); + + /** + * Reset back to initial state. + */ + public void reset() { + if (DEBUG) Log.v(TAG, "Reset: " + this); + mDownKeyCode = 0; + mDownTarget = null; + mActiveLongPresses.clear(); + } + + /** + * Stop any tracking associated with this target. + */ + public void reset(Object target) { + if (mDownTarget == target) { + if (DEBUG) Log.v(TAG, "Reset in " + target + ": " + this); + mDownKeyCode = 0; + mDownTarget = null; + } + } + + /** + * Start tracking the key code associated with the given event. This + * can only be called on a key down. It will allow you to see any + * long press associated with the key, and will result in + * {@link KeyEvent#isTracking} return true on the long press and up + * events. + * + * <p>This is only needed if you are directly dispatching events, rather + * than handling them in {@link Callback#onKeyDown}. + */ + public void startTracking(KeyEvent event, Object target) { + if (event.getAction() != ACTION_DOWN) { + throw new IllegalArgumentException( + "Can only start tracking on a down event"); + } + if (DEBUG) Log.v(TAG, "Start trackingt in " + target + ": " + this); + mDownKeyCode = event.getKeyCode(); + mDownTarget = target; + } + + /** + * Return true if the key event is for a key code that is currently + * being tracked by the dispatcher. + */ + public boolean isTracking(KeyEvent event) { + return mDownKeyCode == event.getKeyCode(); + } + + /** + * Keep track of the given event's key code as having performed an + * action with a long press, so no action should occur on the up. + * <p>This is only needed if you are directly dispatching events, rather + * than handling them in {@link Callback#onKeyLongPress}. + */ + public void performedLongPress(KeyEvent event) { + mActiveLongPresses.put(event.getKeyCode(), 1); + } + + /** + * Handle key up event to stop tracking. This resets the dispatcher state, + * and updates the key event state based on it. + * <p>This is only needed if you are directly dispatching events, rather + * than handling them in {@link Callback#onKeyUp}. + */ + public void handleUpEvent(KeyEvent event) { + final int keyCode = event.getKeyCode(); + if (DEBUG) Log.v(TAG, "Handle key up " + event + ": " + this); + int index = mActiveLongPresses.indexOfKey(keyCode); + if (index >= 0) { + if (DEBUG) Log.v(TAG, " Index: " + index); + event.mFlags |= FLAG_CANCELED | FLAG_CANCELED_LONG_PRESS; + mActiveLongPresses.removeAt(index); + } + if (mDownKeyCode == keyCode) { + if (DEBUG) Log.v(TAG, " Tracking!"); + event.mFlags |= FLAG_TRACKING; + mDownKeyCode = 0; + mDownTarget = null; + } + } + } + public String toString() { return "KeyEvent{action=" + mAction + " code=" + mKeyCode + " repeat=" + mRepeatCount diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 94acd3f..e5985c1 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -458,11 +458,12 @@ public abstract class LayoutInflater { public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { Constructor constructor = sConstructorMap.get(name); + Class clazz = null; try { if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it - Class clazz = mContext.getClassLoader().loadClass( + clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name); if (mFilter != null && clazz != null) { @@ -480,7 +481,7 @@ public abstract class LayoutInflater { Boolean allowedState = mFilterMap.get(name); if (allowedState == null) { // New class -- remember whether it is allowed - Class clazz = mContext.getClassLoader().loadClass( + clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name); boolean allowed = clazz != null && mFilter.onLoadClass(clazz); @@ -511,7 +512,7 @@ public abstract class LayoutInflater { } catch (Exception e) { InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " - + (constructor == null ? "<unknown>" : constructor.getClass().getName())); + + (clazz == null ? "<unknown>" : clazz.getName())); ie.initCause(e); throw ie; } diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index a224ed3..ca907af 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -19,7 +19,7 @@ package android.view; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; -import android.util.Config; +import android.util.Log; /** * Object used to report movement (mouse, pen, finger, trackball) events. This @@ -27,17 +27,26 @@ import android.util.Config; * it is being used for. */ public final class MotionEvent implements Parcelable { + static final boolean DEBUG_POINTERS = false; + + /** + * Bit mask of the parts of the action code that are the action itself. + */ + public static final int ACTION_MASK = 0xff; + /** * Constant for {@link #getAction}: A pressed gesture has started, the * motion contains the initial starting location. */ public static final int ACTION_DOWN = 0; + /** * Constant for {@link #getAction}: A pressed gesture has finished, the * motion contains the final release location as well as any intermediate * points since the last down or move event. */ public static final int ACTION_UP = 1; + /** * Constant for {@link #getAction}: A change has happened during a * press gesture (between {@link #ACTION_DOWN} and {@link #ACTION_UP}). @@ -45,12 +54,14 @@ public final class MotionEvent implements Parcelable { * points since the last down or move event. */ public static final int ACTION_MOVE = 2; + /** * Constant for {@link #getAction}: The current gesture has been aborted. * You will not receive any more points in it. You should treat this as * an up event, but not perform any action that you normally would. */ public static final int ACTION_CANCEL = 3; + /** * Constant for {@link #getAction}: A movement has happened outside of the * normal bounds of the UI element. This does not provide a full gesture, @@ -58,6 +69,70 @@ public final class MotionEvent implements Parcelable { */ public static final int ACTION_OUTSIDE = 4; + /** + * A non-primary pointer has gone down. The bits in + * {@link #ACTION_POINTER_ID_MASK} indicate which pointer changed. + */ + public static final int ACTION_POINTER_DOWN = 5; + + /** + * Synonym for {@link #ACTION_POINTER_DOWN} with + * {@link #ACTION_POINTER_ID_MASK} of 0: the primary pointer has gone done. + */ + public static final int ACTION_POINTER_1_DOWN = ACTION_POINTER_DOWN | 0x0000; + + /** + * Synonym for {@link #ACTION_POINTER_DOWN} with + * {@link #ACTION_POINTER_ID_MASK} of 1: the secondary pointer has gone done. + */ + public static final int ACTION_POINTER_2_DOWN = ACTION_POINTER_DOWN | 0x0100; + + /** + * Synonym for {@link #ACTION_POINTER_DOWN} with + * {@link #ACTION_POINTER_ID_MASK} of 2: the tertiary pointer has gone done. + */ + public static final int ACTION_POINTER_3_DOWN = ACTION_POINTER_DOWN | 0x0200; + + /** + * A non-primary pointer has gone up. The bits in + * {@link #ACTION_POINTER_ID_MASK} indicate which pointer changed. + */ + public static final int ACTION_POINTER_UP = 6; + + /** + * Synonym for {@link #ACTION_POINTER_UP} with + * {@link #ACTION_POINTER_ID_MASK} of 0: the primary pointer has gone up. + */ + public static final int ACTION_POINTER_1_UP = ACTION_POINTER_UP | 0x0000; + + /** + * Synonym for {@link #ACTION_POINTER_UP} with + * {@link #ACTION_POINTER_ID_MASK} of 1: the secondary pointer has gone up. + */ + public static final int ACTION_POINTER_2_UP = ACTION_POINTER_UP | 0x0100; + + /** + * Synonym for {@link #ACTION_POINTER_UP} with + * {@link #ACTION_POINTER_ID_MASK} of 2: the tertiary pointer has gone up. + */ + public static final int ACTION_POINTER_3_UP = ACTION_POINTER_UP | 0x0200; + + /** + * Bits in the action code that represent a pointer ID, used with + * {@link #ACTION_POINTER_DOWN} and {@link #ACTION_POINTER_UP}. Pointer IDs + * start at 0, with 0 being the primary (first) pointer in the motion. Note + * that this not <em>not</em> an index into the array of pointer values, + * which is compacted to only contain pointers that are down; the pointer + * ID for a particular index can be found with {@link #findPointerIndex}. + */ + public static final int ACTION_POINTER_ID_MASK = 0xff00; + + /** + * Bit shift for the action bits holding the pointer identifier as + * defined by {@link #ACTION_POINTER_ID_MASK}. + */ + public static final int ACTION_POINTER_ID_SHIFT = 8; + private static final boolean TRACK_RECYCLED_LOCATION = false; /** @@ -80,34 +155,83 @@ public final class MotionEvent implements Parcelable { */ public static final int EDGE_RIGHT = 0x00000008; + /** + * Offset for the sample's X coordinate. + * @hide + */ + static public final int SAMPLE_X = 0; + + /** + * Offset for the sample's Y coordinate. + * @hide + */ + static public final int SAMPLE_Y = 1; + + /** + * Offset for the sample's X coordinate. + * @hide + */ + static public final int SAMPLE_PRESSURE = 2; + + /** + * Offset for the sample's X coordinate. + * @hide + */ + static public final int SAMPLE_SIZE = 3; + + /** + * Number of data items for each sample. + * @hide + */ + static public final int NUM_SAMPLE_DATA = 4; + + /** + * Number of possible pointers. + * @hide + */ + static public final int BASE_AVAIL_POINTERS = 5; + + static private final int BASE_AVAIL_SAMPLES = 8; + static private final int MAX_RECYCLED = 10; static private Object gRecyclerLock = new Object(); static private int gRecyclerUsed = 0; static private MotionEvent gRecyclerTop = null; private long mDownTime; - private long mEventTime; + private long mEventTimeNano; private int mAction; - private float mX; - private float mY; private float mRawX; private float mRawY; - private float mPressure; - private float mSize; - private int mMetaState; - private int mNumHistory; - private float[] mHistory; - private long[] mHistoryTimes; private float mXPrecision; private float mYPrecision; private int mDeviceId; private int mEdgeFlags; + private int mMetaState; + + // Here is the actual event data. Note that the order of the array + // is a little odd: the first entry is the most recent, and the ones + // following it are the historical data from oldest to newest. This + // allows us to easily retrieve the most recent data, without having + // to copy the arrays every time a new sample is added. + + private int mNumPointers; + private int mNumSamples; + // Array of mNumPointers size of identifiers for each pointer of data. + private int[] mPointerIdentifiers; + // Array of (mNumSamples * mNumPointers * NUM_SAMPLE_DATA) size of event data. + private float[] mDataSamples; + // Array of mNumSamples size of time stamps. + private long[] mTimeSamples; private MotionEvent mNext; private RuntimeException mRecycledLocation; private boolean mRecycled; private MotionEvent() { + mPointerIdentifiers = new int[BASE_AVAIL_POINTERS]; + mDataSamples = new float[BASE_AVAIL_POINTERS*BASE_AVAIL_SAMPLES*NUM_SAMPLE_DATA]; + mTimeSamples = new long[BASE_AVAIL_SAMPLES]; } static private MotionEvent obtain() { @@ -127,6 +251,86 @@ public final class MotionEvent implements Parcelable { /** * Create a new MotionEvent, filling in all of the basic values that * define the motion. + * + * @param downTime The time (in ms) when the user originally pressed down to start + * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}. + * @param eventTime The the time (in ms) when this specific event was generated. This + * must be obtained from {@link SystemClock#uptimeMillis()}. + * @param eventTimeNano The the time (in ns) when this specific event was generated. This + * must be obtained from {@link System#nanoTime()}. + * @param action The kind of action being performed -- one of either + * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or + * {@link #ACTION_CANCEL}. + * @param pointers The number of points that will be in this event. + * @param inPointerIds An array of <em>pointers</em> values providing + * an identifier for each pointer. + * @param inData An array of <em>pointers*NUM_SAMPLE_DATA</em> of initial + * data samples for the event. + * @param metaState The state of any meta / modifier keys that were in effect when + * the event was generated. + * @param xPrecision The precision of the X coordinate being reported. + * @param yPrecision The precision of the Y coordinate being reported. + * @param deviceId The id for the device that this event came from. An id of + * zero indicates that the event didn't come from a physical device; other + * numbers are arbitrary and you shouldn't depend on the values. + * @param edgeFlags A bitfield indicating which edges, if any, where touched by this + * MotionEvent. + * + * @hide + */ + static public MotionEvent obtainNano(long downTime, long eventTime, long eventTimeNano, + int action, int pointers, int[] inPointerIds, float[] inData, int metaState, + float xPrecision, float yPrecision, int deviceId, int edgeFlags) { + MotionEvent ev = obtain(); + ev.mDeviceId = deviceId; + ev.mEdgeFlags = edgeFlags; + ev.mDownTime = downTime; + ev.mEventTimeNano = eventTimeNano; + ev.mAction = action; + ev.mMetaState = metaState; + ev.mRawX = inData[SAMPLE_X]; + ev.mRawY = inData[SAMPLE_Y]; + ev.mXPrecision = xPrecision; + ev.mYPrecision = yPrecision; + ev.mNumPointers = pointers; + ev.mNumSamples = 1; + + int[] pointerIdentifiers = ev.mPointerIdentifiers; + if (pointerIdentifiers.length < pointers) { + ev.mPointerIdentifiers = pointerIdentifiers = new int[pointers]; + } + System.arraycopy(inPointerIds, 0, pointerIdentifiers, 0, pointers); + + final int ND = pointers * NUM_SAMPLE_DATA; + float[] dataSamples = ev.mDataSamples; + if (dataSamples.length < ND) { + ev.mDataSamples = dataSamples = new float[ND]; + } + System.arraycopy(inData, 0, dataSamples, 0, ND); + + ev.mTimeSamples[0] = eventTime; + + if (DEBUG_POINTERS) { + StringBuilder sb = new StringBuilder(128); + sb.append("New:"); + for (int i=0; i<pointers; i++) { + sb.append(" #"); + sb.append(ev.mPointerIdentifiers[i]); + sb.append("("); + sb.append(ev.mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_X]); + sb.append(","); + sb.append(ev.mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_Y]); + sb.append(")"); + } + Log.v("MotionEvent", sb.toString()); + } + + return ev; + } + + /** + * Create a new MotionEvent, filling in all of the basic values that + * define the motion. * * @param downTime The time (in ms) when the user originally pressed down to start * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}. @@ -162,16 +366,83 @@ public final class MotionEvent implements Parcelable { ev.mDeviceId = deviceId; ev.mEdgeFlags = edgeFlags; ev.mDownTime = downTime; - ev.mEventTime = eventTime; + ev.mEventTimeNano = eventTime * 1000000; ev.mAction = action; - ev.mX = ev.mRawX = x; - ev.mY = ev.mRawY = y; - ev.mPressure = pressure; - ev.mSize = size; ev.mMetaState = metaState; ev.mXPrecision = xPrecision; ev.mYPrecision = yPrecision; + ev.mNumPointers = 1; + ev.mNumSamples = 1; + int[] pointerIds = ev.mPointerIdentifiers; + pointerIds[0] = 0; + float[] data = ev.mDataSamples; + data[SAMPLE_X] = ev.mRawX = x; + data[SAMPLE_Y] = ev.mRawY = y; + data[SAMPLE_PRESSURE] = pressure; + data[SAMPLE_SIZE] = size; + ev.mTimeSamples[0] = eventTime; + + return ev; + } + + /** + * Create a new MotionEvent, filling in all of the basic values that + * define the motion. + * + * @param downTime The time (in ms) when the user originally pressed down to start + * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}. + * @param eventTime The the time (in ms) when this specific event was generated. This + * must be obtained from {@link SystemClock#uptimeMillis()}. + * @param action The kind of action being performed -- one of either + * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or + * {@link #ACTION_CANCEL}. + * @param pointers The number of pointers that are active in this event. + * @param x The X coordinate of this event. + * @param y The Y coordinate of this event. + * @param pressure The current pressure of this event. The pressure generally + * ranges from 0 (no pressure at all) to 1 (normal pressure), however + * values higher than 1 may be generated depending on the calibration of + * the input device. + * @param size A scaled value of the approximate size of the area being pressed when + * touched with the finger. The actual value in pixels corresponding to the finger + * touch is normalized with a device specific range of values + * and scaled to a value between 0 and 1. + * @param metaState The state of any meta / modifier keys that were in effect when + * the event was generated. + * @param xPrecision The precision of the X coordinate being reported. + * @param yPrecision The precision of the Y coordinate being reported. + * @param deviceId The id for the device that this event came from. An id of + * zero indicates that the event didn't come from a physical device; other + * numbers are arbitrary and you shouldn't depend on the values. + * @param edgeFlags A bitfield indicating which edges, if any, where touched by this + * MotionEvent. + */ + static public MotionEvent obtain(long downTime, long eventTime, int action, + int pointers, float x, float y, float pressure, float size, int metaState, + float xPrecision, float yPrecision, int deviceId, int edgeFlags) { + MotionEvent ev = obtain(); + ev.mDeviceId = deviceId; + ev.mEdgeFlags = edgeFlags; + ev.mDownTime = downTime; + ev.mEventTimeNano = eventTime * 1000000; + ev.mAction = action; + ev.mNumPointers = pointers; + ev.mMetaState = metaState; + ev.mXPrecision = xPrecision; + ev.mYPrecision = yPrecision; + + ev.mNumPointers = 1; + ev.mNumSamples = 1; + int[] pointerIds = ev.mPointerIdentifiers; + pointerIds[0] = 0; + float[] data = ev.mDataSamples; + data[SAMPLE_X] = ev.mRawX = x; + data[SAMPLE_Y] = ev.mRawY = y; + data[SAMPLE_PRESSURE] = pressure; + data[SAMPLE_SIZE] = size; + ev.mTimeSamples[0] = eventTime; + return ev; } @@ -198,16 +469,24 @@ public final class MotionEvent implements Parcelable { ev.mDeviceId = 0; ev.mEdgeFlags = 0; ev.mDownTime = downTime; - ev.mEventTime = eventTime; + ev.mEventTimeNano = eventTime * 1000000; ev.mAction = action; - ev.mX = ev.mRawX = x; - ev.mY = ev.mRawY = y; - ev.mPressure = 1.0f; - ev.mSize = 1.0f; + ev.mNumPointers = 1; ev.mMetaState = metaState; ev.mXPrecision = 1.0f; ev.mYPrecision = 1.0f; + ev.mNumPointers = 1; + ev.mNumSamples = 1; + int[] pointerIds = ev.mPointerIdentifiers; + pointerIds[0] = 0; + float[] data = ev.mDataSamples; + data[SAMPLE_X] = ev.mRawX = x; + data[SAMPLE_Y] = ev.mRawY = y; + data[SAMPLE_PRESSURE] = 1.0f; + data[SAMPLE_SIZE] = 1.0f; + ev.mTimeSamples[0] = eventTime; + return ev; } @@ -217,72 +496,96 @@ public final class MotionEvent implements Parcelable { * @hide */ public void scale(float scale) { - mX *= scale; - mY *= scale; mRawX *= scale; mRawY *= scale; - mSize *= scale; mXPrecision *= scale; mYPrecision *= scale; - if (mHistory != null) { - float[] history = mHistory; - int length = history.length; - for (int i = 0; i < length; i += 4) { - history[i] *= scale; // X - history[i + 1] *= scale; // Y - // no need to scale pressure ([i+2]) - history[i + 3] *= scale; // Size, TODO: square this? - } + float[] history = mDataSamples; + final int length = mNumPointers * mNumSamples * NUM_SAMPLE_DATA; + for (int i = 0; i < length; i += NUM_SAMPLE_DATA) { + history[i + SAMPLE_X] *= scale; + history[i + SAMPLE_Y] *= scale; + // no need to scale pressure + history[i + SAMPLE_SIZE] *= scale; // TODO: square this? } } /** - * Translate the coordination of the event by given x and y. - * - * @hide + * Create a new MotionEvent, copying from an existing one. */ - public void translate(float dx, float dy) { - mX += dx; - mY += dy; - mRawX += dx; - mRawY += dx; - if (mHistory != null) { - float[] history = mHistory; - int length = history.length; - for (int i = 0; i < length; i += 4) { - history[i] += dx; // X - history[i + 1] += dy; // Y - // no need to translate pressure (i+2) and size (i+3) - } + static public MotionEvent obtain(MotionEvent o) { + MotionEvent ev = obtain(); + ev.mDeviceId = o.mDeviceId; + ev.mEdgeFlags = o.mEdgeFlags; + ev.mDownTime = o.mDownTime; + ev.mEventTimeNano = o.mEventTimeNano; + ev.mAction = o.mAction; + ev.mNumPointers = o.mNumPointers; + ev.mRawX = o.mRawX; + ev.mRawY = o.mRawY; + ev.mMetaState = o.mMetaState; + ev.mXPrecision = o.mXPrecision; + ev.mYPrecision = o.mYPrecision; + + final int NS = ev.mNumSamples = o.mNumSamples; + if (ev.mTimeSamples.length >= NS) { + System.arraycopy(o.mTimeSamples, 0, ev.mTimeSamples, 0, NS); + } else { + ev.mTimeSamples = (long[])o.mTimeSamples.clone(); } + + final int NP = (ev.mNumPointers=o.mNumPointers); + if (ev.mPointerIdentifiers.length >= NP) { + System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, NP); + } else { + ev.mPointerIdentifiers = (int[])o.mPointerIdentifiers.clone(); + } + + final int ND = NP * NS * NUM_SAMPLE_DATA; + if (ev.mDataSamples.length >= ND) { + System.arraycopy(o.mDataSamples, 0, ev.mDataSamples, 0, ND); + } else { + ev.mDataSamples = (float[])o.mDataSamples.clone(); + } + + return ev; } /** - * Create a new MotionEvent, copying from an existing one. + * Create a new MotionEvent, copying from an existing one, but not including + * any historical point information. */ - static public MotionEvent obtain(MotionEvent o) { + static public MotionEvent obtainNoHistory(MotionEvent o) { MotionEvent ev = obtain(); ev.mDeviceId = o.mDeviceId; ev.mEdgeFlags = o.mEdgeFlags; ev.mDownTime = o.mDownTime; - ev.mEventTime = o.mEventTime; + ev.mEventTimeNano = o.mEventTimeNano; ev.mAction = o.mAction; - ev.mX = o.mX; + ev.mNumPointers = o.mNumPointers; ev.mRawX = o.mRawX; - ev.mY = o.mY; ev.mRawY = o.mRawY; - ev.mPressure = o.mPressure; - ev.mSize = o.mSize; ev.mMetaState = o.mMetaState; ev.mXPrecision = o.mXPrecision; ev.mYPrecision = o.mYPrecision; - final int N = o.mNumHistory; - ev.mNumHistory = N; - if (N > 0) { - // could be more efficient about this... - ev.mHistory = (float[])o.mHistory.clone(); - ev.mHistoryTimes = (long[])o.mHistoryTimes.clone(); + + ev.mNumSamples = 1; + ev.mTimeSamples[0] = o.mTimeSamples[0]; + + final int NP = (ev.mNumPointers=o.mNumPointers); + if (ev.mPointerIdentifiers.length >= NP) { + System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, NP); + } else { + ev.mPointerIdentifiers = (int[])o.mPointerIdentifiers.clone(); } + + final int ND = NP * NUM_SAMPLE_DATA; + if (ev.mDataSamples.length >= ND) { + System.arraycopy(o.mDataSamples, 0, ev.mDataSamples, 0, ND); + } else { + ev.mDataSamples = (float[])o.mDataSamples.clone(); + } + return ev; } @@ -305,7 +608,7 @@ public final class MotionEvent implements Parcelable { synchronized (gRecyclerLock) { if (gRecyclerUsed < MAX_RECYCLED) { gRecyclerUsed++; - mNumHistory = 0; + mNumSamples = 0; mNext = gRecyclerTop; gRecyclerTop = this; } @@ -333,44 +636,145 @@ public final class MotionEvent implements Parcelable { * Returns the time (in ms) when this specific event was generated. */ public final long getEventTime() { - return mEventTime; + return mTimeSamples[0]; } /** - * Returns the X coordinate of this event. Whole numbers are pixels; the - * value may have a fraction for input devices that are sub-pixel precise. + * Returns the time (in ns) when this specific event was generated. + * The value is in nanosecond precision but it may not have nanosecond accuracy. + * + * @hide + */ + public final long getEventTimeNano() { + return mEventTimeNano; + } + + /** + * {@link #getX(int)} for the first pointer index (may be an + * arbitrary pointer identifier). */ public final float getX() { - return mX; + return mDataSamples[SAMPLE_X]; } /** - * Returns the Y coordinate of this event. Whole numbers are pixels; the - * value may have a fraction for input devices that are sub-pixel precise. + * {@link #getY(int)} for the first pointer index (may be an + * arbitrary pointer identifier). */ public final float getY() { - return mY; + return mDataSamples[SAMPLE_Y]; + } + + /** + * {@link #getPressure(int)} for the first pointer index (may be an + * arbitrary pointer identifier). + */ + public final float getPressure() { + return mDataSamples[SAMPLE_PRESSURE]; + } + + /** + * {@link #getSize(int)} for the first pointer index (may be an + * arbitrary pointer identifier). + */ + public final float getSize() { + return mDataSamples[SAMPLE_SIZE]; + } + + /** + * The number of pointers of data contained in this event. Always + * >= 1. + */ + public final int getPointerCount() { + return mNumPointers; + } + + /** + * Return the pointer identifier associated with a particular pointer + * data index is this event. The identifier tells you the actual pointer + * number associated with the data, accounting for individual pointers + * going up and down since the start of the current gesture. + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. + */ + public final int getPointerId(int pointerIndex) { + return mPointerIdentifiers[pointerIndex]; + } + + /** + * Given a pointer identifier, find the index of its data in the event. + * + * @param pointerId The identifier of the pointer to be found. + * @return Returns either the index of the pointer (for use with + * {@link #getX(int) et al.), or -1 if there is no data available for + * that pointer identifier. + */ + public final int findPointerIndex(int pointerId) { + int i = mNumPointers; + while (i > 0) { + i--; + if (mPointerIdentifiers[i] == pointerId) { + return i; + } + } + return -1; + } + + /** + * Returns the X coordinate of this event for the given pointer + * <em>index</em> (use {@link #getPointerId(int)} to find the pointer + * identifier for this index). + * Whole numbers are pixels; the + * value may have a fraction for input devices that are sub-pixel precise. + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. + */ + public final float getX(int pointerIndex) { + return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_X]; } /** - * Returns the current pressure of this event. The pressure generally + * Returns the Y coordinate of this event for the given pointer + * <em>index</em> (use {@link #getPointerId(int)} to find the pointer + * identifier for this index). + * Whole numbers are pixels; the + * value may have a fraction for input devices that are sub-pixel precise. + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. + */ + public final float getY(int pointerIndex) { + return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_Y]; + } + + /** + * Returns the current pressure of this event for the given pointer + * <em>index</em> (use {@link #getPointerId(int)} to find the pointer + * identifier for this index). + * The pressure generally * ranges from 0 (no pressure at all) to 1 (normal pressure), however * values higher than 1 may be generated depending on the calibration of * the input device. + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. */ - public final float getPressure() { - return mPressure; + public final float getPressure(int pointerIndex) { + return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_PRESSURE]; } /** - * Returns a scaled value of the approximate size, of the area being pressed when - * touched with the finger. The actual value in pixels corresponding to the finger - * touch is normalized with the device specific range of values + * Returns a scaled value of the approximate size for the given pointer + * <em>index</em> (use {@link #getPointerId(int)} to find the pointer + * identifier for this index). + * This represents some approximation of the area of the screen being + * pressed; the actual value in pixels corresponding to the + * touch is normalized with the device specific range of values * and scaled to a value between 0 and 1. The value of size can be used to * determine fat touch events. + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. */ - public final float getSize() { - return mSize; + public final float getSize(int pointerIndex) { + return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_SIZE]; } /** @@ -436,7 +840,7 @@ public final class MotionEvent implements Parcelable { * @return Returns the number of historical points in the event. */ public final int getHistorySize() { - return mNumHistory; + return mNumSamples - 1; } /** @@ -450,63 +854,111 @@ public final class MotionEvent implements Parcelable { * @see #getEventTime */ public final long getHistoricalEventTime(int pos) { - return mHistoryTimes[pos]; + return mTimeSamples[pos + 1]; } /** - * Returns a historical X coordinate that occurred between this event - * and the previous event. Only applies to ACTION_MOVE events. + * {@link #getHistoricalX(int)} for the first pointer index (may be an + * arbitrary pointer identifier). + */ + public final float getHistoricalX(int pos) { + return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_X]; + } + + /** + * {@link #getHistoricalY(int)} for the first pointer index (may be an + * arbitrary pointer identifier). + */ + public final float getHistoricalY(int pos) { + return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_Y]; + } + + /** + * {@link #getHistoricalPressure(int)} for the first pointer index (may be an + * arbitrary pointer identifier). + */ + public final float getHistoricalPressure(int pos) { + return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_PRESSURE]; + } + + /** + * {@link #getHistoricalSize(int)} for the first pointer index (may be an + * arbitrary pointer identifier). + */ + public final float getHistoricalSize(int pos) { + return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_SIZE]; + } + + /** + * Returns a historical X coordinate, as per {@link #getX(int)}, that + * occurred between this event and the previous event for the given pointer. + * Only applies to ACTION_MOVE events. * + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. * @param pos Which historical value to return; must be less than * {@link #getHistorySize} * * @see #getHistorySize * @see #getX */ - public final float getHistoricalX(int pos) { - return mHistory[pos*4]; + public final float getHistoricalX(int pointerIndex, int pos) { + return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_X]; } /** - * Returns a historical Y coordinate that occurred between this event - * and the previous event. Only applies to ACTION_MOVE events. + * Returns a historical Y coordinate, as per {@link #getY(int)}, that + * occurred between this event and the previous event for the given pointer. + * Only applies to ACTION_MOVE events. * + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. * @param pos Which historical value to return; must be less than * {@link #getHistorySize} * * @see #getHistorySize * @see #getY */ - public final float getHistoricalY(int pos) { - return mHistory[pos*4 + 1]; + public final float getHistoricalY(int pointerIndex, int pos) { + return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_Y]; } /** - * Returns a historical pressure coordinate that occurred between this event - * and the previous event. Only applies to ACTION_MOVE events. + * Returns a historical pressure coordinate, as per {@link #getPressure(int)}, + * that occurred between this event and the previous event for the given + * pointer. Only applies to ACTION_MOVE events. * + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getPressure */ - public final float getHistoricalPressure(int pos) { - return mHistory[pos*4 + 2]; + public final float getHistoricalPressure(int pointerIndex, int pos) { + return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_PRESSURE]; } /** - * Returns a historical size coordinate that occurred between this event - * and the previous event. Only applies to ACTION_MOVE events. + * Returns a historical size coordinate, as per {@link #getSize(int)}, that + * occurred between this event and the previous event for the given pointer. + * Only applies to ACTION_MOVE events. * + * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0 + * (the first pointer that is down) to {@link #getPointerCount()}-1. * @param pos Which historical value to return; must be less than * {@link #getHistorySize} - * + * * @see #getHistorySize * @see #getSize */ - public final float getHistoricalSize(int pos) { - return mHistory[pos*4 + 3]; + public final float getHistoricalSize(int pointerIndex, int pos) { + return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_SIZE]; } /** @@ -556,16 +1008,11 @@ public final class MotionEvent implements Parcelable { * @param deltaY Amount to add to the current Y coordinate of the event. */ public final void offsetLocation(float deltaX, float deltaY) { - mX += deltaX; - mY += deltaY; - final int N = mNumHistory*4; - if (N <= 0) { - return; - } - final float[] pos = mHistory; - for (int i=0; i<N; i+=4) { - pos[i] += deltaX; - pos[i+1] += deltaY; + final int N = mNumPointers*mNumSamples*4; + final float[] pos = mDataSamples; + for (int i=0; i<N; i+=NUM_SAMPLE_DATA) { + pos[i+SAMPLE_X] += deltaX; + pos[i+SAMPLE_Y] += deltaY; } } @@ -577,8 +1024,8 @@ public final class MotionEvent implements Parcelable { * @param y New absolute Y location. */ public final void setLocation(float x, float y) { - float deltaX = x-mX; - float deltaY = y-mY; + float deltaX = x-mDataSamples[SAMPLE_X]; + float deltaY = y-mDataSamples[SAMPLE_Y]; if (deltaX != 0 || deltaY != 0) { offsetLocation(deltaX, deltaY); } @@ -590,58 +1037,119 @@ public final class MotionEvent implements Parcelable { * the future, the current values in the event will be added to a list of * historic values. * + * @param eventTime The time stamp for this data. * @param x The new X position. * @param y The new Y position. * @param pressure The new pressure. * @param size The new size. + * @param metaState Meta key state. */ public final void addBatch(long eventTime, float x, float y, float pressure, float size, int metaState) { - float[] history = mHistory; - long[] historyTimes = mHistoryTimes; - int N; - int avail; - if (history == null) { - mHistory = history = new float[8*4]; - mHistoryTimes = historyTimes = new long[8]; - mNumHistory = N = 0; - avail = 8; - } else { - N = mNumHistory; - avail = history.length/4; - if (N == avail) { - avail += 8; - float[] newHistory = new float[avail*4]; - System.arraycopy(history, 0, newHistory, 0, N*4); - mHistory = history = newHistory; - long[] newHistoryTimes = new long[avail]; - System.arraycopy(historyTimes, 0, newHistoryTimes, 0, N); - mHistoryTimes = historyTimes = newHistoryTimes; - } + float[] data = mDataSamples; + long[] times = mTimeSamples; + + final int NP = mNumPointers; + final int NS = mNumSamples; + final int NI = NP*NS; + final int ND = NI * NUM_SAMPLE_DATA; + if (data.length <= ND) { + final int NEW_ND = ND + (NP * (BASE_AVAIL_SAMPLES * NUM_SAMPLE_DATA)); + float[] newData = new float[NEW_ND]; + System.arraycopy(data, 0, newData, 0, ND); + mDataSamples = data = newData; + } + if (times.length <= NS) { + final int NEW_NS = NS + BASE_AVAIL_SAMPLES; + long[] newHistoryTimes = new long[NEW_NS]; + System.arraycopy(times, 0, newHistoryTimes, 0, NS); + mTimeSamples = times = newHistoryTimes; } + + times[NS] = times[0]; + times[0] = eventTime; + + final int pos = NS*NUM_SAMPLE_DATA; + data[pos+SAMPLE_X] = data[SAMPLE_X]; + data[pos+SAMPLE_Y] = data[SAMPLE_Y]; + data[pos+SAMPLE_PRESSURE] = data[SAMPLE_PRESSURE]; + data[pos+SAMPLE_SIZE] = data[SAMPLE_SIZE]; + data[SAMPLE_X] = x; + data[SAMPLE_Y] = y; + data[SAMPLE_PRESSURE] = pressure; + data[SAMPLE_SIZE] = size; + mNumSamples = NS+1; - historyTimes[N] = mEventTime; + mRawX = x; + mRawY = y; + mMetaState |= metaState; + } - final int pos = N*4; - history[pos] = mX; - history[pos+1] = mY; - history[pos+2] = mPressure; - history[pos+3] = mSize; - mNumHistory = N+1; + /** + * Add a new movement to the batch of movements in this event. The + * input data must contain (NUM_SAMPLE_DATA * {@link #getPointerCount()}) + * samples of data. + * + * @param eventTime The time stamp for this data. + * @param inData The actual data. + * @param metaState Meta key state. + * + * @hide + */ + public final void addBatch(long eventTime, float[] inData, int metaState) { + float[] data = mDataSamples; + long[] times = mTimeSamples; + + final int NP = mNumPointers; + final int NS = mNumSamples; + final int NI = NP*NS; + final int ND = NI * NUM_SAMPLE_DATA; + if (data.length < (ND+(NP*NUM_SAMPLE_DATA))) { + final int NEW_ND = ND + (NP * (BASE_AVAIL_SAMPLES * NUM_SAMPLE_DATA)); + float[] newData = new float[NEW_ND]; + System.arraycopy(data, 0, newData, 0, ND); + mDataSamples = data = newData; + } + if (times.length < (NS+1)) { + final int NEW_NS = NS + BASE_AVAIL_SAMPLES; + long[] newHistoryTimes = new long[NEW_NS]; + System.arraycopy(times, 0, newHistoryTimes, 0, NS); + mTimeSamples = times = newHistoryTimes; + } + + times[NS] = times[0]; + times[0] = eventTime; + + System.arraycopy(data, 0, data, ND, mNumPointers*NUM_SAMPLE_DATA); + System.arraycopy(inData, 0, data, 0, mNumPointers*NUM_SAMPLE_DATA); + + mNumSamples = NS+1; - mEventTime = eventTime; - mX = mRawX = x; - mY = mRawY = y; - mPressure = pressure; - mSize = size; + mRawX = inData[SAMPLE_X]; + mRawY = inData[SAMPLE_Y]; mMetaState |= metaState; + + if (DEBUG_POINTERS) { + StringBuilder sb = new StringBuilder(128); + sb.append("Add:"); + for (int i=0; i<mNumPointers; i++) { + sb.append(" #"); + sb.append(mPointerIdentifiers[i]); + sb.append("("); + sb.append(mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_X]); + sb.append(","); + sb.append(mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_Y]); + sb.append(")"); + } + Log.v("MotionEvent", sb.toString()); + } } @Override public String toString() { return "MotionEvent{" + Integer.toHexString(System.identityHashCode(this)) - + " action=" + mAction + " x=" + mX - + " y=" + mY + " pressure=" + mPressure + " size=" + mSize + "}"; + + " action=" + mAction + " x=" + getX() + + " y=" + getY() + " pressure=" + getPressure() + " size=" + getSize() + "}"; } public static final Parcelable.Creator<MotionEvent> CREATOR @@ -663,26 +1171,29 @@ public final class MotionEvent implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeLong(mDownTime); - out.writeLong(mEventTime); + out.writeLong(mEventTimeNano); out.writeInt(mAction); - out.writeFloat(mX); - out.writeFloat(mY); - out.writeFloat(mPressure); - out.writeFloat(mSize); out.writeInt(mMetaState); out.writeFloat(mRawX); out.writeFloat(mRawY); - final int N = mNumHistory; - out.writeInt(N); - if (N > 0) { - final int N4 = N*4; + final int NP = mNumPointers; + out.writeInt(NP); + final int NS = mNumSamples; + out.writeInt(NS); + final int NI = NP*NS; + if (NI > 0) { int i; - float[] history = mHistory; - for (i=0; i<N4; i++) { + int[] state = mPointerIdentifiers; + for (i=0; i<NP; i++) { + out.writeInt(state[i]); + } + final int ND = NI*NUM_SAMPLE_DATA; + float[] history = mDataSamples; + for (i=0; i<ND; i++) { out.writeFloat(history[i]); } - long[] times = mHistoryTimes; - for (i=0; i<N; i++) { + long[] times = mTimeSamples; + for (i=0; i<NS; i++) { out.writeLong(times[i]); } } @@ -694,30 +1205,37 @@ public final class MotionEvent implements Parcelable { private void readFromParcel(Parcel in) { mDownTime = in.readLong(); - mEventTime = in.readLong(); + mEventTimeNano = in.readLong(); mAction = in.readInt(); - mX = in.readFloat(); - mY = in.readFloat(); - mPressure = in.readFloat(); - mSize = in.readFloat(); mMetaState = in.readInt(); mRawX = in.readFloat(); mRawY = in.readFloat(); - final int N = in.readInt(); - if ((mNumHistory=N) > 0) { - final int N4 = N*4; - float[] history = mHistory; - if (history == null || history.length < N4) { - mHistory = history = new float[N4 + (4*4)]; + final int NP = in.readInt(); + mNumPointers = NP; + final int NS = in.readInt(); + mNumSamples = NS; + final int NI = NP*NS; + if (NI > 0) { + int[] ids = mPointerIdentifiers; + if (ids.length < NP) { + mPointerIdentifiers = ids = new int[NP]; + } + for (int i=0; i<NP; i++) { + ids[i] = in.readInt(); + } + float[] history = mDataSamples; + final int ND = NI*NUM_SAMPLE_DATA; + if (history.length < ND) { + mDataSamples = history = new float[ND]; } - for (int i=0; i<N4; i++) { + for (int i=0; i<ND; i++) { history[i] = in.readFloat(); } - long[] times = mHistoryTimes; - if (times == null || times.length < N) { - mHistoryTimes = times = new long[N + 4]; + long[] times = mTimeSamples; + if (times == null || times.length < NS) { + mTimeSamples = times = new long[NS]; } - for (int i=0; i<N; i++) { + for (int i=0; i<NS; i++) { times[i] = in.readLong(); } } diff --git a/core/java/android/view/RawInputEvent.java b/core/java/android/view/RawInputEvent.java index 30da83e..8b3cdd4 100644 --- a/core/java/android/view/RawInputEvent.java +++ b/core/java/android/view/RawInputEvent.java @@ -13,6 +13,8 @@ public class RawInputEvent { public static final int CLASS_ALPHAKEY = 0x00000002; public static final int CLASS_TOUCHSCREEN = 0x00000004; public static final int CLASS_TRACKBALL = 0x00000008; + public static final int CLASS_TOUCHSCREEN_MT = 0x00000010; + public static final int CLASS_DPAD = 0x00000020; // More special classes for QueuedEvent below. public static final int CLASS_CONFIGURATION_CHANGED = 0x10000000; @@ -158,8 +160,24 @@ public class RawInputEvent { public static final int ABS_TOOL_WIDTH = 0x1c; public static final int ABS_VOLUME = 0x20; public static final int ABS_MISC = 0x28; + public static final int ABS_MT_TOUCH_MAJOR = 0x30; + public static final int ABS_MT_TOUCH_MINOR = 0x31; + public static final int ABS_MT_WIDTH_MAJOR = 0x32; + public static final int ABS_MT_WIDTH_MINOR = 0x33; + public static final int ABS_MT_ORIENTATION = 0x34; + public static final int ABS_MT_POSITION_X = 0x35; + public static final int ABS_MT_POSITION_Y = 0x36; + public static final int ABS_MT_TOOL_TYPE = 0x37; + public static final int ABS_MT_BLOB_ID = 0x38; public static final int ABS_MAX = 0x3f; + // Switch events + public static final int SW_LID = 0x00; + + public static final int SYN_REPORT = 0; + public static final int SYN_CONFIG = 1; + public static final int SYN_MT_REPORT = 2; + public int deviceId; public int type; public int scancode; diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 5100fff..b85667b 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -34,12 +34,18 @@ public class Surface implements Parcelable { /** Surface is created hidden */ public static final int HIDDEN = 0x00000004; - /** The surface is to be used by hardware accelerators or DMA engines */ + /** The surface is to be used by hardware accelerators or DMA engines + * @deprecated this is ignored, this value is set automatically when needed. + */ + @Deprecated public static final int HARDWARE = 0x00000010; /** Implies "HARDWARE", the surface is to be used by the GPU * additionally the backbuffer is never preserved for these - * surfaces. */ + * surfaces. + * @deprecated this is ignored, this value is set automatically when needed. + */ + @Deprecated public static final int GPU = 0x00000028; /** The surface contains secure content, special measures will @@ -135,6 +141,8 @@ public class Surface implements Parcelable { @SuppressWarnings("unused") private int mSurface; @SuppressWarnings("unused") + private int mSurfaceControl; + @SuppressWarnings("unused") private int mSaveCount; @SuppressWarnings("unused") private Canvas mCanvas; @@ -173,7 +181,7 @@ public class Surface implements Parcelable { public Surface(SurfaceSession s, int pid, int display, int w, int h, int format, int flags) throws OutOfResourcesException { - mCanvas = new Canvas(); + mCanvas = new CompatibleCanvas(); init(s,pid,display,w,h,format,flags); } @@ -238,7 +246,7 @@ public class Surface implements Parcelable { }; /** - * Sets the display metrics used to provide canva's width/height in comaptibility mode. + * Sets the display metrics used to provide canva's width/height in compatibility mode. */ void setCompatibleDisplayMetrics(DisplayMetrics metrics, Translator translator) { mCompatibleDisplayMetrics = metrics; @@ -263,11 +271,16 @@ public class Surface implements Parcelable { */ public native boolean isValid(); - /** Call this free the surface up. {@hide} */ - public native void clear(); + /** Free all server-side state associated with this surface and + * release this object's reference. {@hide} */ + public native void destroy(); + + /** Release the local reference to the server-side surface. @hide */ + public native void release(); /** draw into a surface */ - public Canvas lockCanvas(Rect dirty) throws OutOfResourcesException { + public Canvas lockCanvas(Rect dirty) throws OutOfResourcesException, IllegalArgumentException + { /* the dirty rectangle may be expanded to the surface's size, if * for instance it has been resized or if the bits were lost, since * the last call. @@ -349,7 +362,7 @@ public class Surface implements Parcelable { @Override public String toString() { - return "Surface(native-token=" + mSurface + ")"; + return "Surface(native-token=" + mSurfaceControl + ")"; } private Surface(Parcel source) throws OutOfResourcesException { @@ -362,7 +375,7 @@ public class Surface implements Parcelable { public native void readFromParcel(Parcel source); public native void writeToParcel(Parcel dest, int flags); - + public static final Parcelable.Creator<Surface> CREATOR = new Parcelable.Creator<Surface>() { @@ -383,7 +396,7 @@ public class Surface implements Parcelable { /* no user serviceable parts here ... */ @Override protected void finalize() throws Throwable { - clear(); + release(); } private native void init(SurfaceSession s, diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java index 3d0dda3..64a10d1 100644 --- a/core/java/android/view/SurfaceHolder.java +++ b/core/java/android/view/SurfaceHolder.java @@ -38,8 +38,6 @@ public interface SurfaceHolder { * Surface type. * * @see #SURFACE_TYPE_NORMAL - * @see #SURFACE_TYPE_HARDWARE - * @see #SURFACE_TYPE_GPU * @see #SURFACE_TYPE_PUSH_BUFFERS */ @@ -47,9 +45,15 @@ public interface SurfaceHolder { * contiguous, cached/buffered RAM. */ public static final int SURFACE_TYPE_NORMAL = MEMORY_TYPE_NORMAL; /** Surface type: creates a suited to be used with DMA engines and - * hardware accelerators. */ + * hardware accelerators. + * @deprecated this is ignored, this value is set automatically when needed. + */ + @Deprecated public static final int SURFACE_TYPE_HARDWARE = MEMORY_TYPE_HARDWARE; - /** Surface type: creates a surface suited to be used with the GPU */ + /** Surface type: creates a surface suited to be used with the GPU + * @deprecated this is ignored, this value is set automatically when needed. + */ + @Deprecated public static final int SURFACE_TYPE_GPU = MEMORY_TYPE_GPU; /** Surface type: creates a "push" surface, that is a surface that * doesn't owns its buffers. With such a surface lockCanvas will fail. */ @@ -139,11 +143,7 @@ public interface SurfaceHolder { public boolean isCreating(); /** - * Sets the surface's type. Surfaces intended to be used with OpenGL ES - * should be of SURFACE_TYPE_GPU, surfaces accessed by DMA engines and - * hardware accelerators should be of type SURFACE_TYPE_HARDWARE. - * Failing to set the surface's type appropriately could result in - * degraded performance or failure. + * Sets the surface's type. * * @param type The surface's memory type. */ diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 4546572..ca5e1de 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -16,6 +16,8 @@ package android.view; +import com.android.internal.view.BaseIWindow; + import android.content.Context; import android.content.res.Resources; import android.content.res.CompatibilityInfo.Translator; @@ -122,6 +124,8 @@ public class SurfaceView extends View { }; boolean mRequestedVisible = false; + boolean mWindowVisibility = false; + boolean mViewVisibility = false; int mRequestedWidth = -1; int mRequestedHeight = -1; int mRequestedFormat = PixelFormat.OPAQUE; @@ -174,12 +178,22 @@ public class SurfaceView extends View { mSession = getWindowSession(); mLayout.token = getWindowToken(); mLayout.setTitle("SurfaceView"); + mViewVisibility = getVisibility() == VISIBLE; } @Override protected void onWindowVisibilityChanged(int visibility) { super.onWindowVisibilityChanged(visibility); - mRequestedVisible = visibility == VISIBLE; + mWindowVisibility = visibility == VISIBLE; + mRequestedVisible = mWindowVisibility && mViewVisibility; + updateWindow(false); + } + + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + mViewVisibility = visibility == VISIBLE; + mRequestedVisible = mWindowVisibility && mViewVisibility; updateWindow(false); } @@ -222,6 +236,10 @@ public class SurfaceView extends View { @Override public boolean gatherTransparentRegion(Region region) { + if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { + return super.gatherTransparentRegion(region); + } + boolean opaque = true; if ((mPrivateFlags & SKIP_DRAW) == 0) { // this view draws, remove it from the transparent region @@ -245,20 +263,24 @@ public class SurfaceView extends View { @Override public void draw(Canvas canvas) { - // draw() is not called when SKIP_DRAW is set - if ((mPrivateFlags & SKIP_DRAW) == 0) { - // punch a whole in the view-hierarchy below us - canvas.drawColor(0, PorterDuff.Mode.CLEAR); + if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { + // draw() is not called when SKIP_DRAW is set + if ((mPrivateFlags & SKIP_DRAW) == 0) { + // punch a whole in the view-hierarchy below us + canvas.drawColor(0, PorterDuff.Mode.CLEAR); + } } super.draw(canvas); } @Override protected void dispatchDraw(Canvas canvas) { - // if SKIP_DRAW is cleared, draw() has already punched a hole - if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { - // punch a whole in the view-hierarchy below us - canvas.drawColor(0, PorterDuff.Mode.CLEAR); + if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { + // if SKIP_DRAW is cleared, draw() has already punched a hole + if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { + // punch a whole in the view-hierarchy below us + canvas.drawColor(0, PorterDuff.Mode.CLEAR); + } } // reposition ourselves where the surface is mHaveFrame = true; @@ -267,6 +289,41 @@ public class SurfaceView extends View { } /** + * Control whether the surface view's surface is placed on top of another + * regular surface view in the window (but still behind the window itself). + * This is typically used to place overlays on top of an underlying media + * surface view. + * + * <p>Note that this must be set before the surface view's containing + * window is attached to the window manager. + * + * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}. + */ + public void setZOrderMediaOverlay(boolean isMediaOverlay) { + mWindowType = isMediaOverlay + ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY + : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + } + + /** + * Control whether the surface view's surface is placed on top of its + * window. Normally it is placed behind the window, to allow it to + * (for the most part) appear to composite with the views in the + * hierarchy. By setting this, you cause it to be placed above the + * window. This means that none of the contents of the window this + * SurfaceView is in will be visible on top of its surface. + * + * <p>Note that this must be set before the surface view's containing + * window is attached to the window manager. + * + * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}. + */ + public void setZOrderOnTop(boolean onTop) { + mWindowType = onTop ? WindowManager.LayoutParams.TYPE_APPLICATION_PANEL + : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + } + + /** * Hack to allow special layering of windows. The type is one of the * types in WindowManager.LayoutParams. This is a hack so: * @hide @@ -280,7 +337,9 @@ public class SurfaceView extends View { return; } ViewRoot viewRoot = (ViewRoot) getRootView().getParent(); - mTranslator = viewRoot.mTranslator; + if (viewRoot != null) { + mTranslator = viewRoot.mTranslator; + } Resources res = getContext().getResources(); if (mTranslator != null || !res.getCompatibilityInfo().supportsScreen()) { @@ -329,7 +388,9 @@ public class SurfaceView extends View { } mLayout.format = mRequestedFormat; - mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_SCALED | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE @@ -435,7 +496,7 @@ public class SurfaceView extends View { updateWindow(false); } - private static class MyWindow extends IWindow.Stub { + private static class MyWindow extends BaseIWindow { private final WeakReference<SurfaceView> mSurfaceView; public MyWindow(SurfaceView surfaceView) { @@ -477,7 +538,8 @@ public class SurfaceView extends View { } } - public void dispatchPointer(MotionEvent event, long eventTime) { + public void dispatchPointer(MotionEvent event, long eventTime, + boolean callWhenDone) { Log.w("SurfaceView", "Unexpected pointer event in surface: " + event); //if (mSession != null && mSurface != null) { // try { @@ -487,7 +549,8 @@ public class SurfaceView extends View { //} } - public void dispatchTrackball(MotionEvent event, long eventTime) { + public void dispatchTrackball(MotionEvent event, long eventTime, + boolean callWhenDone) { Log.w("SurfaceView", "Unexpected trackball event in surface: " + event); //if (mSession != null && mSurface != null) { // try { @@ -569,9 +632,14 @@ public class SurfaceView extends View { public void setType(int type) { switch (type) { - case SURFACE_TYPE_NORMAL: case SURFACE_TYPE_HARDWARE: case SURFACE_TYPE_GPU: + // these are deprecated, treat as "NORMAL" + type = SURFACE_TYPE_NORMAL; + break; + } + switch (type) { + case SURFACE_TYPE_NORMAL: case SURFACE_TYPE_PUSH_BUFFERS: mRequestedType = type; if (mWindow != null) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 7ed2712..0b87536 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -24,6 +24,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Interpolator; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; @@ -46,7 +47,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.util.AttributeSet; import android.util.Config; -import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.Pool; @@ -59,6 +59,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEventSource; import android.view.accessibility.AccessibilityManager; import android.view.animation.Animation; +import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; @@ -515,7 +516,8 @@ import java.util.WeakHashMap; * The framework provides basic support for views that wish to internally * scroll their content. This includes keeping track of the X and Y scroll * offset as well as mechanisms for drawing scrollbars. See - * {@link #scrollBy(int, int)}, {@link #scrollTo(int, int)} for more details. + * {@link #scrollBy(int, int)}, {@link #scrollTo(int, int)}, and + * {@link #awakenScrollBars()} for more details. * </p> * * <a name="Tags"></a> @@ -572,6 +574,8 @@ import java.util.WeakHashMap; * @attr ref android.R.styleable#View_scrollbarSize * @attr ref android.R.styleable#View_scrollbarStyle * @attr ref android.R.styleable#View_scrollbars + * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade + * @attr ref android.R.styleable#View_scrollbarFadeDuration * @attr ref android.R.styleable#View_scrollbarTrackHorizontal * @attr ref android.R.styleable#View_scrollbarThumbHorizontal * @attr ref android.R.styleable#View_scrollbarThumbVertical @@ -2215,12 +2219,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility protected void initializeScrollbars(TypedArray a) { initScrollCache(); - if (mScrollCache.scrollBar == null) { - mScrollCache.scrollBar = new ScrollBarDrawable(); - } - final ScrollabilityCache scrollabilityCache = mScrollCache; - + + if (scrollabilityCache.scrollBar == null) { + scrollabilityCache.scrollBar = new ScrollBarDrawable(); + } + + final boolean fadeScrollbars = a.getBoolean(R.styleable.View_fadeScrollbars, false); + + if (!fadeScrollbars) { + scrollabilityCache.state = ScrollabilityCache.ON; + } + scrollabilityCache.fadeScrollBars = fadeScrollbars; + + + scrollabilityCache.scrollBarFadeDuration = a.getInt( + R.styleable.View_scrollbarFadeDuration, ViewConfiguration + .getScrollBarFadeDuration()); + scrollabilityCache.scrollBarDefaultDelayBeforeFade = a.getInt( + R.styleable.View_scrollbarDefaultDelayBeforeFade, + ViewConfiguration.getScrollDefaultDelay()); + + scrollabilityCache.scrollBarSize = a.getDimensionPixelSize( com.android.internal.R.styleable.View_scrollbarSize, ViewConfiguration.get(mContext).getScaledScrollBarSize()); @@ -2264,7 +2284,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ private void initScrollCache() { if (mScrollCache == null) { - mScrollCache = new ScrollabilityCache(ViewConfiguration.get(mContext)); + mScrollCache = new ScrollabilityCache(ViewConfiguration.get(mContext), this); } } @@ -2601,6 +2621,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (mOnFocusChangeListener != null) { mOnFocusChangeListener.onFocusChange(this, gainFocus); } + + if (mAttachInfo != null) { + mAttachInfo.mKeyDispatchState.reset(this); + } } /** @@ -2983,6 +3007,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @param enabled True if this view is enabled, false otherwise. */ public void setEnabled(boolean enabled) { + if (enabled == isEnabled()) return; + setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK); /* @@ -3608,6 +3634,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState} + * for this view's window. Returns null if the view is not currently attached + * to the window. Normally you will not need to use this directly, but + * just use the standard high-level event callbacks like {@link #onKeyDown}. + */ + public KeyEvent.DispatcherState getKeyDispatcherState() { + return mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null; + } + + /** * Dispatch a key event before it is processed by any input method * associated with the view hierarchy. This can be used to intercept * key events in special situations before the IME consumes them; a @@ -3644,7 +3680,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility return true; } - return event.dispatch(this); + return event.dispatch(this, mAttachInfo != null + ? mAttachInfo.mKeyDispatchState : null, this); } /** @@ -3909,6 +3946,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent) + * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle + * the event). + */ + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + return false; + } + + /** * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent) * KeyEvent.Callback.onKeyMultiple()}: perform clicking of the view * when {@link KeyEvent#KEYCODE_DPAD_CENTER} or @@ -4646,7 +4692,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mScrollX = x; mScrollY = y; onScrollChanged(mScrollX, mScrollY, oldX, oldY); - invalidate(); + if (!awakenScrollBars()) { + invalidate(); + } } } @@ -4662,6 +4710,160 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * <p>Trigger the scrollbars to draw. When invoked this method starts an + * animation to fade the scrollbars out after a default delay. If a subclass + * provides animated scrolling, the start delay should equal the duration + * of the scrolling animation.</p> + * + * <p>The animation starts only if at least one of the scrollbars is + * enabled, as specified by {@link #isHorizontalScrollBarEnabled()} and + * {@link #isVerticalScrollBarEnabled()}. When the animation is started, + * this method returns true, and false otherwise. If the animation is + * started, this method calls {@link #invalidate()}; in that case the + * caller should not call {@link #invalidate()}.</p> + * + * <p>This method should be invoked every time a subclass directly updates + * the scroll parameters.</p> + * + * <p>This method is automatically invoked by {@link #scrollBy(int, int)} + * and {@link #scrollTo(int, int)}.</p> + * + * @return true if the animation is played, false otherwise + * + * @see #awakenScrollBars(int) + * @see #scrollBy(int, int) + * @see #scrollTo(int, int) + * @see #isHorizontalScrollBarEnabled() + * @see #isVerticalScrollBarEnabled() + * @see #setHorizontalScrollBarEnabled(boolean) + * @see #setVerticalScrollBarEnabled(boolean) + */ + protected boolean awakenScrollBars() { + return mScrollCache != null && + awakenScrollBars(mScrollCache.scrollBarDefaultDelayBeforeFade, true); + } + + /** + * <p> + * Trigger the scrollbars to draw. When invoked this method starts an + * animation to fade the scrollbars out after a fixed delay. If a subclass + * provides animated scrolling, the start delay should equal the duration of + * the scrolling animation. + * </p> + * + * <p> + * The animation starts only if at least one of the scrollbars is enabled, + * as specified by {@link #isHorizontalScrollBarEnabled()} and + * {@link #isVerticalScrollBarEnabled()}. When the animation is started, + * this method returns true, and false otherwise. If the animation is + * started, this method calls {@link #invalidate()}; in that case the caller + * should not call {@link #invalidate()}. + * </p> + * + * <p> + * This method should be invoked everytime a subclass directly updates the + * scroll parameters. + * </p> + * + * @param startDelay the delay, in milliseconds, after which the animation + * should start; when the delay is 0, the animation starts + * immediately + * @return true if the animation is played, false otherwise + * + * @see #scrollBy(int, int) + * @see #scrollTo(int, int) + * @see #isHorizontalScrollBarEnabled() + * @see #isVerticalScrollBarEnabled() + * @see #setHorizontalScrollBarEnabled(boolean) + * @see #setVerticalScrollBarEnabled(boolean) + */ + protected boolean awakenScrollBars(int startDelay) { + return awakenScrollBars(startDelay, true); + } + + /** + * <p> + * Trigger the scrollbars to draw. When invoked this method starts an + * animation to fade the scrollbars out after a fixed delay. If a subclass + * provides animated scrolling, the start delay should equal the duration of + * the scrolling animation. + * </p> + * + * <p> + * The animation starts only if at least one of the scrollbars is enabled, + * as specified by {@link #isHorizontalScrollBarEnabled()} and + * {@link #isVerticalScrollBarEnabled()}. When the animation is started, + * this method returns true, and false otherwise. If the animation is + * started, this method calls {@link #invalidate()} if the invalidate parameter + * is set to true; in that case the caller + * should not call {@link #invalidate()}. + * </p> + * + * <p> + * This method should be invoked everytime a subclass directly updates the + * scroll parameters. + * </p> + * + * @param startDelay the delay, in milliseconds, after which the animation + * should start; when the delay is 0, the animation starts + * immediately + * + * @param invalidate Wheter this method should call invalidate + * + * @return true if the animation is played, false otherwise + * + * @see #scrollBy(int, int) + * @see #scrollTo(int, int) + * @see #isHorizontalScrollBarEnabled() + * @see #isVerticalScrollBarEnabled() + * @see #setHorizontalScrollBarEnabled(boolean) + * @see #setVerticalScrollBarEnabled(boolean) + */ + protected boolean awakenScrollBars(int startDelay, boolean invalidate) { + final ScrollabilityCache scrollCache = mScrollCache; + + if (scrollCache == null || !scrollCache.fadeScrollBars) { + return false; + } + + if (scrollCache.scrollBar == null) { + scrollCache.scrollBar = new ScrollBarDrawable(); + } + + if (isHorizontalScrollBarEnabled() || isVerticalScrollBarEnabled()) { + + if (invalidate) { + // Invalidate to show the scrollbars + invalidate(); + } + + if (scrollCache.state == ScrollabilityCache.OFF) { + // FIXME: this is copied from WindowManagerService. + // We should get this value from the system when it + // is possible to do so. + final int KEY_REPEAT_FIRST_DELAY = 750; + startDelay = Math.max(KEY_REPEAT_FIRST_DELAY, startDelay); + } + + // Tell mScrollCache when we should start fading. This may + // extend the fade start time if one was already scheduled + long fadeStartTime = AnimationUtils.currentAnimationTimeMillis() + startDelay; + scrollCache.fadeStartTime = fadeStartTime; + scrollCache.state = ScrollabilityCache.ON; + + // Schedule our fader to run, unscheduling any old ones first + if (mAttachInfo != null) { + mAttachInfo.mHandler.removeCallbacks(scrollCache); + mAttachInfo.mHandler.postAtTime(scrollCache, fadeStartTime); + } + + return true; + } + + return false; + } + + /** * Mark the the area defined by dirty as needing to be drawn. If the view is * visible, {@link #onDraw} will be called at some point in the future. * This must be called from a UI thread. To call from a non-UI thread, call @@ -4754,8 +4956,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * invalidate/draw passes. * * @return True if this View is guaranteed to be fully opaque, false otherwise. - * - * @hide Pending API council approval */ @ViewDebug.ExportedProperty public boolean isOpaque() { @@ -5152,7 +5352,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private void recomputePadding() { setPadding(mPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom); } - + + /** + * Define whether scrollbars will fade when the view is not scrolling. + * + * @param fadeScrollbars wheter to enable fading + * + */ + public void setScrollbarFadingEnabled(boolean fadeScrollbars) { + initScrollCache(); + final ScrollabilityCache scrollabilityCache = mScrollCache; + scrollabilityCache.fadeScrollBars = fadeScrollbars; + if (fadeScrollbars) { + scrollabilityCache.state = ScrollabilityCache.OFF; + } else { + scrollabilityCache.state = ScrollabilityCache.ON; + } + } + + /** + * + * Returns true if scrollbars will fade when this view is not scrolling + * + * @return true if scrollbar fading is enabled + */ + public boolean isScrollbarFadingEnabled() { + return mScrollCache != null && mScrollCache.fadeScrollBars; + } + /** * <p>Specify the style of the scrollbars. The scrollbars can be overlaid or * inset. When inset, they add to the padding of the view. And the scrollbars @@ -5319,11 +5546,49 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * scrollbars are painted only if they have been awakened first.</p> * * @param canvas the canvas on which to draw the scrollbars + * + * @see #awakenScrollBars(int) */ - private void onDrawScrollBars(Canvas canvas) { + protected final void onDrawScrollBars(Canvas canvas) { // scrollbars are drawn only when the animation is running final ScrollabilityCache cache = mScrollCache; if (cache != null) { + + int state = cache.state; + + if (state == ScrollabilityCache.OFF) { + return; + } + + boolean invalidate = false; + + if (state == ScrollabilityCache.FADING) { + // We're fading -- get our fade interpolation + if (cache.interpolatorValues == null) { + cache.interpolatorValues = new float[1]; + } + + float[] values = cache.interpolatorValues; + + // Stops the animation if we're done + if (cache.scrollBarInterpolator.timeToValues(values) == + Interpolator.Result.FREEZE_END) { + cache.state = ScrollabilityCache.OFF; + } else { + cache.scrollBar.setAlpha(Math.round(values[0])); + } + + // This will make the scroll bars inval themselves after + // drawing. We only want this when we're fading so that + // we prevent excessive redraws + invalidate = true; + } else { + // We're just on -- but we may have been fading before so + // reset alpha + cache.scrollBar.setAlpha(255); + } + + final int viewFlags = mViewFlags; final boolean drawHorizontalScrollBar = @@ -5342,12 +5607,41 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility size = cache.scrollBarSize; } + final int scrollX = mScrollX; + final int scrollY = mScrollY; + final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; + + int left, top, right, bottom; + if (drawHorizontalScrollBar) { - onDrawHorizontalScrollBar(canvas, scrollBar, width, height, size); + scrollBar.setParameters(computeHorizontalScrollRange(), + computeHorizontalScrollOffset(), + computeHorizontalScrollExtent(), false); + final int verticalScrollBarGap = drawVerticalScrollBar ? + getVerticalScrollbarWidth() : 0; + top = scrollY + height - size - (mUserPaddingBottom & inside); + left = scrollX + (mPaddingLeft & inside); + right = scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap; + bottom = top + size; + onDrawHorizontalScrollBar(canvas, scrollBar, left, top, right, bottom); + if (invalidate) { + invalidate(left, top, right, bottom); + } } if (drawVerticalScrollBar) { - onDrawVerticalScrollBar(canvas, scrollBar, width, height, size); + scrollBar.setParameters(computeVerticalScrollRange(), + computeVerticalScrollOffset(), + computeVerticalScrollExtent(), true); + // TODO: Deal with RTL languages to position scrollbar on left + left = scrollX + width - size - (mUserPaddingRight & inside); + top = scrollY + (mPaddingTop & inside); + right = left + size; + bottom = scrollY + height - (mUserPaddingBottom & inside); + onDrawVerticalScrollBar(canvas, scrollBar, left, top, right, bottom); + if (invalidate) { + invalidate(left, top, right, bottom); + } } } } @@ -5367,44 +5661,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * <p>Draw the horizontal scrollbar if * {@link #isHorizontalScrollBarEnabled()} returns true.</p> * - * <p>The length of the scrollbar and its thumb is computed according to the - * values returned by {@link #computeHorizontalScrollRange()}, - * {@link #computeHorizontalScrollExtent()} and - * {@link #computeHorizontalScrollOffset()}. Refer to - * {@link android.widget.ScrollBarDrawable} for more information about how - * these values relate to each other.</p> - * * @param canvas the canvas on which to draw the scrollbar * @param scrollBar the scrollbar's drawable - * @param width the width of the drawing surface - * @param height the height of the drawing surface - * @param size the size of the scrollbar * * @see #isHorizontalScrollBarEnabled() * @see #computeHorizontalScrollRange() * @see #computeHorizontalScrollExtent() * @see #computeHorizontalScrollOffset() * @see android.widget.ScrollBarDrawable + * @hide */ - private void onDrawHorizontalScrollBar(Canvas canvas, ScrollBarDrawable scrollBar, int width, - int height, int size) { - - final int viewFlags = mViewFlags; - final int scrollX = mScrollX; - final int scrollY = mScrollY; - final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; - final int top = scrollY + height - size - (mUserPaddingBottom & inside); - - final int verticalScrollBarGap = - (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL ? - getVerticalScrollbarWidth() : 0; - - scrollBar.setBounds(scrollX + (mPaddingLeft & inside), top, - scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap, top + size); - scrollBar.setParameters( - computeHorizontalScrollRange(), - computeHorizontalScrollOffset(), - computeHorizontalScrollExtent(), false); + protected void onDrawHorizontalScrollBar(Canvas canvas, + Drawable scrollBar, + int l, int t, int r, int b) { + scrollBar.setBounds(l, t, r, b); scrollBar.draw(canvas); } @@ -5412,40 +5682,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * <p>Draw the vertical scrollbar if {@link #isVerticalScrollBarEnabled()} * returns true.</p> * - * <p>The length of the scrollbar and its thumb is computed according to the - * values returned by {@link #computeVerticalScrollRange()}, - * {@link #computeVerticalScrollExtent()} and - * {@link #computeVerticalScrollOffset()}. Refer to - * {@link android.widget.ScrollBarDrawable} for more information about how - * these values relate to each other.</p> - * * @param canvas the canvas on which to draw the scrollbar * @param scrollBar the scrollbar's drawable - * @param width the width of the drawing surface - * @param height the height of the drawing surface - * @param size the size of the scrollbar * * @see #isVerticalScrollBarEnabled() * @see #computeVerticalScrollRange() * @see #computeVerticalScrollExtent() * @see #computeVerticalScrollOffset() * @see android.widget.ScrollBarDrawable + * @hide */ - private void onDrawVerticalScrollBar(Canvas canvas, ScrollBarDrawable scrollBar, int width, - int height, int size) { - - final int scrollX = mScrollX; - final int scrollY = mScrollY; - final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; - // TODO: Deal with RTL languages to position scrollbar on left - final int left = scrollX + width - size - (mUserPaddingRight & inside); - - scrollBar.setBounds(left, scrollY + (mPaddingTop & inside), - left + size, scrollY + height - (mUserPaddingBottom & inside)); - scrollBar.setParameters( - computeVerticalScrollRange(), - computeVerticalScrollOffset(), - computeVerticalScrollExtent(), true); + protected void onDrawVerticalScrollBar(Canvas canvas, + Drawable scrollBar, + int l, int t, int r, int b) { + scrollBar.setBounds(l, t, r, b); scrollBar.draw(canvas); } @@ -5936,11 +6186,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; - final boolean opaque = drawingCacheBackgroundColor != 0 || - (mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE); + final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque(); + final boolean translucentWindow = attachInfo != null && attachInfo.mTranslucentWindow; if (width <= 0 || height <= 0 || - (width * height * (opaque ? 2 : 4) > // Projected bitmap size in bytes + // Projected bitmap size in bytes + (width * height * (opaque && !translucentWindow ? 2 : 4) > ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { destroyDrawingCache(); return; @@ -5951,7 +6202,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { - Bitmap.Config quality; if (!opaque) { switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) { @@ -5969,7 +6219,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility break; } } else { - quality = Bitmap.Config.RGB_565; + // Optimization for translucent windows + // If the window is translucent, use a 32 bits bitmap to benefit from memcpy() + quality = translucentWindow ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; } // Try to cleanup memory @@ -5983,6 +6235,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } else { mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap); } + if (opaque && translucentWindow) bitmap.setHasAlpha(false); } catch (OutOfMemoryError e) { // If there is not enough memory to create the bitmap cache, just // ignore the issue as bitmap caches are not required to draw the @@ -6057,16 +6310,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * some form of this public, but should think about the API. */ Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor) { - final int width = mRight - mLeft; - final int height = mBottom - mTop; + int width = mRight - mLeft; + int height = mBottom - mTop; - Bitmap bitmap = Bitmap.createBitmap(width, height, quality); + final AttachInfo attachInfo = mAttachInfo; + final float scale = attachInfo != null ? attachInfo.mApplicationScale : 1.0f; + width = (int) ((width * scale) + 0.5f); + height = (int) ((height * scale) + 0.5f); + + Bitmap bitmap = Bitmap.createBitmap(width > 0 ? width : 1, height > 0 ? height : 1, quality); if (bitmap == null) { throw new OutOfMemoryError(); } + bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); + Canvas canvas; - final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { canvas = attachInfo.mCanvas; if (canvas == null) { @@ -6089,6 +6348,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility computeScroll(); final int restoreCount = canvas.save(); + canvas.scale(scale, scale); canvas.translate(-mScrollX, -mScrollY); // Temporarily remove the dirty mask @@ -6911,7 +7171,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * Set the background to a given resource. The resource should refer to - * a Drawable object. + * a Drawable object or 0 to remove the background. * @param resid The identifier of the resource. * @attr ref android.R.styleable#View_background */ @@ -8087,6 +8347,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * This needs to be a better API (NOT ON VIEW) before it is exposed. If + * it is ever exposed at all. + * @hide + */ + public void onCloseSystemDialogs(String reason) { + } + + /** * Given a Drawable whose bounds have been set to draw into this view, * update a Region being computed for {@link #gatherTransparentRegion} so * that any non-transparent parts of the Drawable are removed from the @@ -8554,6 +8822,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility int mWindowTop; /** + * Indicates whether the window is translucent/transparent + */ + boolean mTranslucentWindow; + + /** * For windows that are full-screen but using insets to layout inside * of the screen decorations, these are the current insets for the * content of the window. @@ -8584,6 +8857,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ final ArrayList<View> mScrollContainers = new ArrayList<View>(); + final KeyEvent.DispatcherState mKeyDispatchState + = new KeyEvent.DispatcherState(); + /** * Indicates whether the view's window currently has the focus. */ @@ -8709,21 +8985,62 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * is supported. This avoids keeping too many unused fields in most * instances of View.</p> */ - private static class ScrollabilityCache { + private static class ScrollabilityCache implements Runnable { + + /** + * Scrollbars are not visible + */ + public static final int OFF = 0; + + /** + * Scrollbars are visible + */ + public static final int ON = 1; + + /** + * Scrollbars are fading away + */ + public static final int FADING = 2; + + public boolean fadeScrollBars; + public int fadingEdgeLength; + public int scrollBarDefaultDelayBeforeFade; + public int scrollBarFadeDuration; public int scrollBarSize; public ScrollBarDrawable scrollBar; + public float[] interpolatorValues; + public View host; public final Paint paint; public final Matrix matrix; public Shader shader; + public final Interpolator scrollBarInterpolator = new Interpolator(1, 2); + + private final float[] mOpaque = {255.0f}; + private final float[] mTransparent = {0.0f}; + + /** + * When fading should start. This time moves into the future every time + * a new scroll happens. Measured based on SystemClock.uptimeMillis() + */ + public long fadeStartTime; + + + /** + * The current state of the scrollbars: ON, OFF, or FADING + */ + public int state = OFF; + private int mLastColor; - public ScrollabilityCache(ViewConfiguration configuration) { + public ScrollabilityCache(ViewConfiguration configuration, View host) { fadingEdgeLength = configuration.getScaledFadingEdgeLength(); scrollBarSize = configuration.getScaledScrollBarSize(); + scrollBarDefaultDelayBeforeFade = ViewConfiguration.getScrollDefaultDelay(); + scrollBarFadeDuration = ViewConfiguration.getScrollBarFadeDuration(); paint = new Paint(); matrix = new Matrix(); @@ -8733,6 +9050,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility paint.setShader(shader); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + this.host = host; } public void setFadeColor(int color) { @@ -8740,12 +9058,40 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mLastColor = color; color |= 0xFF000000; - shader = new LinearGradient(0, 0, 0, 1, color, 0, Shader.TileMode.CLAMP); + shader = new LinearGradient(0, 0, 0, 1, color | 0xFF000000, + color & 0x00FFFFFF, Shader.TileMode.CLAMP); paint.setShader(shader); // Restore the default transfer mode (src_over) paint.setXfermode(null); } } + + public void run() { + long now = AnimationUtils.currentAnimationTimeMillis(); + if (now >= fadeStartTime) { + + // the animation fades the scrollbars out by changing + // the opacity (alpha) from fully opaque to fully + // transparent + int nextFrame = (int) now; + int framesCount = 0; + + Interpolator interpolator = scrollBarInterpolator; + + // Start opaque + interpolator.setKeyFrame(framesCount++, nextFrame, mOpaque); + + // End transparent + nextFrame += scrollBarFadeDuration; + interpolator.setKeyFrame(framesCount, nextFrame, mTransparent); + + state = FADING; + + // Kick off the fade animation + host.invalidate(); + } + } + } } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 0e36ec2..993048f 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -31,6 +31,16 @@ public class ViewConfiguration { private static final int SCROLL_BAR_SIZE = 10; /** + * Duration of the fade when scrollbars fade away in milliseconds + */ + private static final int SCROLL_BAR_FADE_DURATION = 250; + + /** + * Default delay before the scrollbars fade in milliseconds + */ + private static final int SCROLL_BAR_DEFAULT_DELAY = 300; + + /** * Defines the length of the fading edges in pixels */ private static final int FADING_EDGE_LENGTH = 12; @@ -89,7 +99,7 @@ public class ViewConfiguration { /** * Distance a touch can wander before we think the user is scrolling in pixels */ - private static final int TOUCH_SLOP = 25; + private static final int TOUCH_SLOP = 16; /** * Distance between the first touch and second touch to still be considered a double tap @@ -221,6 +231,20 @@ public class ViewConfiguration { } /** + * @return Duration of the fade when scrollbars fade away in milliseconds + */ + public static int getScrollBarFadeDuration() { + return SCROLL_BAR_FADE_DURATION; + } + + /** + * @return Default delay before the scrollbars fade in milliseconds + */ + public static int getScrollDefaultDelay() { + return SCROLL_BAR_DEFAULT_DELAY; + } + + /** * @return the length of the fading edges in pixels * * @deprecated Use {@link #getScaledFadingEdgeLength()} instead. diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f7b7f02..e2f15c7 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -150,6 +150,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)} * to get the index of the child to draw for that iteration. + * + * @hide */ protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400; @@ -1307,11 +1309,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * if you want to change the drawing order of children. By default, it * returns i. * <p> - * NOTE: In order for this method to be called, the - * {@link #FLAG_USE_CHILD_DRAWING_ORDER} must be set. + * NOTE: In order for this method to be called, you must enable child ordering + * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}. * * @param i The current iteration. * @return The index of the child to draw this iteration. + * + * @see #setChildrenDrawingOrderEnabled(boolean) + * @see #isChildrenDrawingOrderEnabled() */ protected int getChildDrawingOrder(int childCount, int i) { return i; @@ -2706,6 +2711,35 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled); } + /** + * Indicates whether the ViewGroup is drawing its children in the order defined by + * {@link #getChildDrawingOrder(int, int)}. + * + * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)}, + * false otherwise + * + * @see #setChildrenDrawingOrderEnabled(boolean) + * @see #getChildDrawingOrder(int, int) + */ + @ViewDebug.ExportedProperty + protected boolean isChildrenDrawingOrderEnabled() { + return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER; + } + + /** + * Tells the ViewGroup whether to draw its children in the order defined by the method + * {@link #getChildDrawingOrder(int, int)}. + * + * @param enabled true if the order of the children when drawing is determined by + * {@link #getChildDrawingOrder(int, int)}, false otherwise + * + * @see #isChildrenDrawingOrderEnabled() + * @see #getChildDrawingOrder(int, int) + */ + protected void setChildrenDrawingOrderEnabled(boolean enabled) { + setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled); + } + private void setBooleanFlag(int flag, boolean value) { if (value) { mGroupFlags |= flag; diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 0c5d853..bef3e58 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -79,6 +79,9 @@ public final class ViewRoot extends Handler implements ViewParent, private static final boolean DEBUG_IMF = false || LOCAL_LOGV; private static final boolean WATCH_POINTER = false; + private static final boolean MEASURE_LATENCY = false; + private static LatencyTimer lt; + /** * Maximum time we allow the user to roll the trackball enough to generate * a key event, before resetting the counters. @@ -148,7 +151,8 @@ public final class ViewRoot extends Handler implements ViewParent, boolean mWindowAttributesChanged = false; // These can be accessed by any thread, must be protected with a lock. - Surface mSurface; + // Surface can never be reassigned or cleared (use Surface.clear()). + private final Surface mSurface = new Surface(); boolean mAdded; boolean mAddedTouchMode; @@ -188,18 +192,11 @@ public final class ViewRoot extends Handler implements ViewParent, private final int mDensity; - public ViewRoot(Context context) { - super(); - - ++sInstanceCount; - - // Initialize the statics when this class is first instantiated. This is - // done here instead of in the static block because Zygote does not - // allow the spawning of threads. + public static IWindowSession getWindowSession(Looper mainLooper) { synchronized (mStaticInit) { if (!mInitialized) { try { - InputMethodManager imm = InputMethodManager.getInstance(context); + InputMethodManager imm = InputMethodManager.getInstance(mainLooper); sWindowSession = IWindowManager.Stub.asInterface( ServiceManager.getService("window")) .openSession(imm.getClient(), imm.getInputContext()); @@ -207,8 +204,24 @@ public final class ViewRoot extends Handler implements ViewParent, } catch (RemoteException e) { } } + return sWindowSession; } + } + + public ViewRoot(Context context) { + super(); + + if (MEASURE_LATENCY && lt == null) { + lt = new LatencyTimer(100, 1000); + } + + ++sInstanceCount; + // Initialize the statics when this class is first instantiated. This is + // done here instead of in the static block because Zygote does not + // allow the spawning of threads. + getWindowSession(context.getMainLooper()); + mThread = Thread.currentThread(); mLocation = new WindowLeaked(null); mLocation.fillInStackTrace(); @@ -224,7 +237,6 @@ public final class ViewRoot extends Handler implements ViewParent, mTransparentRegion = new Region(); mPreviousTransparentRegion = new Region(); mFirst = true; // true for the first time the view is added - mSurface = new Surface(); mAdded = false; mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this); mViewConfiguration = ViewConfiguration.get(context); @@ -396,7 +408,7 @@ public final class ViewRoot extends Handler implements ViewParent, } boolean restore = false; - if (attrs != null && mTranslator != null) { + if (mTranslator != null) { restore = true; attrs.backup(); mTranslator.translateWindowLayout(attrs); @@ -410,7 +422,7 @@ public final class ViewRoot extends Handler implements ViewParent, mSoftInputMode = attrs.softInputMode; mWindowAttributesChanged = true; mAttachInfo.mRootView = view; - mAttachInfo.mScalingRequired = mTranslator == null ? false : true; + mAttachInfo.mScalingRequired = mTranslator != null; mAttachInfo.mApplicationScale = mTranslator == null ? 1.0f : mTranslator.applicationScale; if (panelParentView != null) { @@ -668,13 +680,13 @@ public final class ViewRoot extends Handler implements ViewParent, // object is not initialized to its backing store, but soon it // will be (assuming the window is visible). attachInfo.mSurface = mSurface; + attachInfo.mTranslucentWindow = lp.format != PixelFormat.OPAQUE; attachInfo.mHasWindowFocus = false; attachInfo.mWindowVisibility = viewVisibility; attachInfo.mRecomputeGlobalAttributes = false; attachInfo.mKeepScreenOn = false; viewVisibilityChanged = false; host.dispatchAttachedToWindow(attachInfo, 0); - getRunQueue().executeActions(attachInfo.mHandler); //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); } else { @@ -707,6 +719,10 @@ public final class ViewRoot extends Handler implements ViewParent, boolean insetsChanged = false; if (mLayoutRequested) { + // Execute enqueued actions on every layout in case a view that was detached + // enqueued an action after being detached + getRunQueue().executeActions(attachInfo.mHandler); + if (mFirst) { host.fitSystemWindows(mAttachInfo.mContentInsets); // make sure touch mode code executes by setting cached value @@ -882,6 +898,7 @@ public final class ViewRoot extends Handler implements ViewParent, // all at once. newSurface = true; fullRedrawNeeded = true; + mPreviousTransparentRegion.setEmpty(); if (mGlWanted && !mUseGL) { initializeGL(); @@ -1555,10 +1572,12 @@ public final class ViewRoot extends Handler implements ViewParent, mView = null; mAttachInfo.mRootView = null; + mAttachInfo.mSurface = null; if (mUseGL) { destroyGL(); } + mSurface.release(); try { sWindowSession.remove(mWindow); @@ -1593,6 +1612,7 @@ public final class ViewRoot extends Handler implements ViewParent, public final static int DISPATCH_KEY_FROM_IME = 1011; public final static int FINISH_INPUT_CONNECTION = 1012; public final static int CHECK_FOCUS = 1013; + public final static int CLOSE_SYSTEM_DIALOGS = 1014; @Override public void handleMessage(Message msg) { @@ -1628,16 +1648,24 @@ public final class ViewRoot extends Handler implements ViewParent, break; case DISPATCH_POINTER: { MotionEvent event = (MotionEvent)msg.obj; - - boolean didFinish; + boolean callWhenDone = msg.arg1 != 0; + if (event == null) { try { + long timeBeforeGettingEvents; + if (MEASURE_LATENCY) { + timeBeforeGettingEvents = System.nanoTime(); + } + event = sWindowSession.getPendingPointerMove(mWindow); + + if (MEASURE_LATENCY && event != null) { + lt.sample("9 Client got events ", System.nanoTime() - event.getEventTimeNano()); + lt.sample("8 Client getting events ", timeBeforeGettingEvents - event.getEventTimeNano()); + } } catch (RemoteException e) { } - didFinish = true; - } else { - didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE; + callWhenDone = false; } if (event != null && mTranslator != null) { mTranslator.translateEventInScreenToAppWindow(event); @@ -1654,8 +1682,16 @@ public final class ViewRoot extends Handler implements ViewParent, if(Config.LOGV) { captureMotionLog("captureDispatchPointer", event); } - event.offsetLocation(0, mCurScrollY); + if (mCurScrollY != 0) { + event.offsetLocation(0, mCurScrollY); + } + if (MEASURE_LATENCY) { + lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano()); + } handled = mView.dispatchTouchEvent(event); + if (MEASURE_LATENCY) { + lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano()); + } if (!handled && isDown) { int edgeSlop = mViewConfiguration.getScaledEdgeSlop(); @@ -1701,7 +1737,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } } finally { - if (!didFinish) { + if (callWhenDone) { try { sWindowSession.finishKey(mWindow); } catch (RemoteException e) { @@ -1716,7 +1752,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } break; case DISPATCH_TRACKBALL: - deliverTrackballEvent((MotionEvent)msg.obj); + deliverTrackballEvent((MotionEvent)msg.obj, msg.arg1 != 0); break; case DISPATCH_APP_VISIBILITY: handleAppVisibility(msg.arg1 != 0); @@ -1779,6 +1815,7 @@ public final class ViewRoot extends Handler implements ViewParent, if (hasWindowFocus && imm != null && mLastWasImTarget) { imm.startGettingWindowFocus(mView); } + mAttachInfo.mKeyDispatchState.reset(); mView.dispatchWindowFocusChanged(hasWindowFocus); } @@ -1806,7 +1843,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } break; case DIE: - dispatchDetachedFromWindow(); + doDie(); break; case DISPATCH_KEY_FROM_IME: { if (LOCAL_LOGV) Log.v( @@ -1833,6 +1870,11 @@ public final class ViewRoot extends Handler implements ViewParent, imm.checkFocus(); } } break; + case CLOSE_SYSTEM_DIALOGS: { + if (mView != null) { + mView.onCloseSystemDialogs((String)msg.obj); + } + } break; } } @@ -1958,16 +2000,13 @@ public final class ViewRoot extends Handler implements ViewParent, } - private void deliverTrackballEvent(MotionEvent event) { - boolean didFinish; + private void deliverTrackballEvent(MotionEvent event, boolean callWhenDone) { if (event == null) { try { event = sWindowSession.getPendingTrackballMove(mWindow); } catch (RemoteException e) { } - didFinish = true; - } else { - didFinish = false; + callWhenDone = false; } if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event); @@ -1985,7 +2024,7 @@ public final class ViewRoot extends Handler implements ViewParent, } } finally { if (handled) { - if (!didFinish) { + if (callWhenDone) { try { sWindowSession.finishKey(mWindow); } catch (RemoteException e) { @@ -2101,7 +2140,7 @@ public final class ViewRoot extends Handler implements ViewParent, mLastTrackballTime = curTime; } } finally { - if (!didFinish) { + if (callWhenDone) { try { sWindowSession.finishKey(mWindow); } catch (RemoteException e) { @@ -2483,6 +2522,14 @@ public final class ViewRoot extends Handler implements ViewParent, } public void die(boolean immediate) { + if (immediate) { + doDie(); + } else { + sendEmptyMessage(DIE); + } + } + + void doDie() { checkThread(); if (Config.LOGV) Log.v("ViewRoot", "DIE in " + this + " of " + mSurface); synchronized (this) { @@ -2502,15 +2549,11 @@ public final class ViewRoot extends Handler implements ViewParent, } } - mSurface = null; + mSurface.release(); } if (mAdded) { mAdded = false; - if (immediate) { - dispatchDetachedFromWindow(); - } else if (mView != null) { - sendEmptyMessage(DIE); - } + dispatchDetachedFromWindow(); } } } @@ -2564,15 +2607,19 @@ public final class ViewRoot extends Handler implements ViewParent, sendMessageAtTime(msg, event.getEventTime()); } - public void dispatchPointer(MotionEvent event, long eventTime) { + public void dispatchPointer(MotionEvent event, long eventTime, + boolean callWhenDone) { Message msg = obtainMessage(DISPATCH_POINTER); msg.obj = event; + msg.arg1 = callWhenDone ? 1 : 0; sendMessageAtTime(msg, eventTime); } - public void dispatchTrackball(MotionEvent event, long eventTime) { + public void dispatchTrackball(MotionEvent event, long eventTime, + boolean callWhenDone) { Message msg = obtainMessage(DISPATCH_TRACKBALL); msg.obj = event; + msg.arg1 = callWhenDone ? 1 : 0; sendMessageAtTime(msg, eventTime); } @@ -2595,6 +2642,13 @@ public final class ViewRoot extends Handler implements ViewParent, sendMessage(msg); } + public void dispatchCloseSystemDialogs(String reason) { + Message msg = Message.obtain(); + msg.what = CLOSE_SYSTEM_DIALOGS; + msg.obj = reason; + sendMessage(msg); + } + /** * The window is getting focus so if there is anything focused/selected * send an {@link AccessibilityEvent} to announce that. @@ -2745,19 +2799,25 @@ public final class ViewRoot extends Handler implements ViewParent, } } - public void dispatchPointer(MotionEvent event, long eventTime) { + public void dispatchPointer(MotionEvent event, long eventTime, + boolean callWhenDone) { final ViewRoot viewRoot = mViewRoot.get(); - if (viewRoot != null) { - viewRoot.dispatchPointer(event, eventTime); + if (viewRoot != null) { + if (MEASURE_LATENCY) { + // Note: eventTime is in milliseconds + ViewRoot.lt.sample("* ViewRoot b4 dispatchPtr", System.nanoTime() - eventTime * 1000000); + } + viewRoot.dispatchPointer(event, eventTime, callWhenDone); } else { new EventCompletion(mMainLooper, this, null, true, event); } } - public void dispatchTrackball(MotionEvent event, long eventTime) { + public void dispatchTrackball(MotionEvent event, long eventTime, + boolean callWhenDone) { final ViewRoot viewRoot = mViewRoot.get(); if (viewRoot != null) { - viewRoot.dispatchTrackball(event, eventTime); + viewRoot.dispatchTrackball(event, eventTime, callWhenDone); } else { new EventCompletion(mMainLooper, this, null, false, event); } @@ -2827,6 +2887,33 @@ public final class ViewRoot extends Handler implements ViewParent, } } } + + public void closeSystemDialogs(String reason) { + final ViewRoot viewRoot = mViewRoot.get(); + if (viewRoot != null) { + viewRoot.dispatchCloseSystemDialogs(reason); + } + } + + public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, + boolean sync) { + if (sync) { + try { + sWindowSession.wallpaperOffsetsComplete(asBinder()); + } catch (RemoteException e) { + } + } + } + + public void dispatchWallpaperCommand(String action, int x, int y, + int z, Bundle extras, boolean sync) { + if (sync) { + try { + sWindowSession.wallpaperCommandComplete(asBinder(), null); + } catch (RemoteException e) { + } + } + } } /** @@ -3107,7 +3194,7 @@ public final class ViewRoot extends Handler implements ViewParent, handler.postDelayed(handlerAction.action, handlerAction.delay); } - mActions.clear(); + actions.clear(); } } @@ -3121,7 +3208,6 @@ public final class ViewRoot extends Handler implements ViewParent, if (o == null || getClass() != o.getClass()) return false; HandlerAction that = (HandlerAction) o; - return !(action != null ? !action.equals(that.action) : that.action != null); } diff --git a/core/java/android/view/ViewStub.java b/core/java/android/view/ViewStub.java index e159de4..703a38f 100644 --- a/core/java/android/view/ViewStub.java +++ b/core/java/android/view/ViewStub.java @@ -23,6 +23,8 @@ import android.util.AttributeSet; import com.android.internal.R; +import java.lang.ref.WeakReference; + /** * A ViewStub is an invisible, zero-sized View that can be used to lazily inflate * layout resources at runtime. @@ -68,6 +70,8 @@ public final class ViewStub extends View { private int mLayoutResource = 0; private int mInflatedId; + private WeakReference<View> mInflatedViewRef; + private OnInflateListener mInflateListener; public ViewStub(Context context) { @@ -196,9 +200,15 @@ public final class ViewStub extends View { */ @Override public void setVisibility(int visibility) { - super.setVisibility(visibility); - - if (visibility == VISIBLE || visibility == INVISIBLE) { + if (mInflatedViewRef != null) { + View view = mInflatedViewRef.get(); + if (view != null) { + view.setVisibility(visibility); + } else { + throw new IllegalStateException("setVisibility called on un-referenced view"); + } + } else if (visibility == VISIBLE || visibility == INVISIBLE) { + super.setVisibility(visibility); inflate(); } } @@ -234,6 +244,8 @@ public final class ViewStub extends View { parent.addView(view, index); } + mInflatedViewRef = new WeakReference(view); + if (mInflateListener != null) { mInflateListener.onInflate(this, view); } diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index a573983..e21824e 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -23,7 +23,9 @@ import android.content.res.Resources; import android.media.AudioManager; import android.media.AudioService; import android.media.AudioSystem; +import android.media.RingtoneManager; import android.media.ToneGenerator; +import android.net.Uri; import android.os.Handler; import android.os.Message; import android.os.Vibrator; @@ -44,7 +46,7 @@ import android.widget.Toast; public class VolumePanel extends Handler { private static final String TAG = "VolumePanel"; - private static boolean LOGD = false || Config.LOGD; + private static boolean LOGD = false; /** * The delay before playing a sound. This small period exists so the user @@ -86,6 +88,7 @@ public class VolumePanel extends Handler protected Context mContext; private AudioManager mAudioManager; protected AudioService mAudioService; + private boolean mRingIsSilent; private final Toast mToast; private final View mView; @@ -138,7 +141,7 @@ public class VolumePanel extends Handler onShowVolumeChanged(streamType, flags); } - if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0) { + if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) { removeMessages(MSG_PLAY_SOUND); sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY); } @@ -157,6 +160,7 @@ public class VolumePanel extends Handler int index = mAudioService.getStreamVolume(streamType); int message = UNKNOWN_VOLUME_TEXT; int additionalMessage = 0; + mRingIsSilent = false; if (LOGD) { Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType @@ -169,8 +173,15 @@ public class VolumePanel extends Handler switch (streamType) { case AudioManager.STREAM_RING: { + setRingerIcon(); message = RINGTONE_VOLUME_TEXT; - setRingerIcon(index); + Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri( + mContext, RingtoneManager.TYPE_RINGTONE); + if (ringuri == null) { + additionalMessage = + com.android.internal.R.string.volume_music_hint_silent_ringtone_selected; + mRingIsSilent = true; + } break; } @@ -208,6 +219,13 @@ public class VolumePanel extends Handler case AudioManager.STREAM_NOTIFICATION: { message = NOTIFICATION_VOLUME_TEXT; setSmallIcon(index); + Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri( + mContext, RingtoneManager.TYPE_NOTIFICATION); + if (ringuri == null) { + additionalMessage = + com.android.internal.R.string.volume_music_hint_silent_ringtone_selected; + mRingIsSilent = true; + } break; } @@ -254,7 +272,6 @@ public class VolumePanel extends Handler mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) { sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY); } - } protected void onPlaySound(int streamType, int flags) { @@ -337,17 +354,15 @@ 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) { + private void setRingerIcon() { mSmallStreamIcon.setVisibility(View.GONE); mLargeStreamIcon.setVisibility(View.VISIBLE); int ringerMode = mAudioService.getRingerMode(); int icon; - if (LOGD) Log.d(TAG, "setRingerIcon(index: " + index+ "), ringerMode: " + ringerMode); + if (LOGD) Log.d(TAG, "setRingerIcon(), ringerMode: " + ringerMode); if (ringerMode == AudioManager.RINGER_MODE_SILENT) { icon = com.android.internal.R.drawable.ic_volume_off; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 576c8c1..1932765 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -56,11 +56,11 @@ public abstract class Window { public static final int FEATURE_CONTEXT_MENU = 6; /** Flag for custom title. You cannot combine this feature with other title features. */ public static final int FEATURE_CUSTOM_TITLE = 7; - /* Flag for asking for an OpenGL enabled window. + /** Flag for asking for an OpenGL enabled window. All 2D graphics will be handled by OpenGL ES. - Private for now, until it is better tested (not shipping in 1.0) + @hide */ - private static final int FEATURE_OPENGL = 8; + public static final int FEATURE_OPENGL = 8; /** Flag for setting the progress bar's visibility to VISIBLE */ public static final int PROGRESS_VISIBILITY_ON = -1; /** Flag for setting the progress bar's visibility to GONE */ @@ -237,7 +237,6 @@ public abstract class Window { /** * This is called whenever the current window attributes change. * - */ public void onWindowAttributesChanged(WindowManager.LayoutParams attrs); @@ -252,13 +251,29 @@ public abstract class Window { public void onContentChanged(); /** - * This hook is called whenever the window focus changes. + * This hook is called whenever the window focus changes. See + * {@link View#onWindowFocusChanged(boolean) + * View.onWindowFocusChanged(boolean)} for more information. * * @param hasFocus Whether the window now has focus. */ public void onWindowFocusChanged(boolean hasFocus); /** + * Called when the window has been attached to the window manager. + * See {@link View#onAttachedToWindow() View.onAttachedToWindow()} + * for more information. + */ + public void onAttachedToWindow(); + + /** + * Called when the window has been attached to the window manager. + * See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()} + * for more information. + */ + public void onDetachedFromWindow(); + + /** * Called when a panel is being closed. If another logical subsequent * panel is being opened (and this panel is being closed to make room for the subsequent * panel), this method will NOT be called. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c0be9e8..6696533 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -282,11 +282,6 @@ public interface WindowManager extends ViewManager { /** * Window type: panel that slides out from the status bar */ - public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+8; - - /** - * Window type: panel that slides out from the status bar - */ public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8; /** @@ -314,6 +309,17 @@ public interface WindowManager extends ViewManager { public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12; /** + * Window type: wallpaper window, placed behind any window that wants + * to sit on top of the wallpaper. + */ + public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13; + + /** + * Window type: panel that slides out from the status bar + */ + public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14; + + /** * End of types of system windows. */ public static final int LAST_SYSTEM_WINDOW = 2999; @@ -323,8 +329,6 @@ public interface WindowManager extends ViewManager { * Default is normal. * * @see #MEMORY_TYPE_NORMAL - * @see #MEMORY_TYPE_HARDWARE - * @see #MEMORY_TYPE_GPU * @see #MEMORY_TYPE_PUSH_BUFFERS */ public int memoryType; @@ -332,10 +336,16 @@ public interface WindowManager extends ViewManager { /** Memory type: The window's surface is allocated in main memory. */ public static final int MEMORY_TYPE_NORMAL = 0; /** Memory type: The window's surface is configured to be accessible - * by DMA engines and hardware accelerators. */ + * by DMA engines and hardware accelerators. + * @deprecated this is ignored, this value is set automatically when needed. + */ + @Deprecated public static final int MEMORY_TYPE_HARDWARE = 1; /** Memory type: The window's surface is configured to be accessible - * by graphics accelerators. */ + * by graphics accelerators. + * @deprecated this is ignored, this value is set automatically when needed. + */ + @Deprecated public static final int MEMORY_TYPE_GPU = 2; /** Memory type: The window's surface doesn't own its buffers and * therefore cannot be locked. Instead the buffers are pushed to @@ -478,17 +488,53 @@ public interface WindowManager extends ViewManager { * is locked. This will let application windows take precedence over * key guard or any other lock screens. Can be used with * {@link #FLAG_KEEP_SCREEN_ON} to turn screen on and display windows - * directly before showing the key guard window - * - * {@hide} */ + * directly before showing the key guard window. Can be used with + * {@link #FLAG_DISMISS_KEYGUARD} to automatically fully dismisss + * non-secure keyguards. This flag only applies to the top-most + * full-screen window. + */ public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000; + /** Window flag: ask that the system wallpaper be shown behind + * your window. The window surface must be translucent to be able + * to actually see the wallpaper behind it; this flag just ensures + * that the wallpaper surface will be there if this window actually + * has translucent regions. + */ + public static final int FLAG_SHOW_WALLPAPER = 0x00100000; + + /** Window flag: when set as a window is being added or made + * visible, once the window has been shown then the system will + * poke the power manager's user activity (as if the user had woken + * up the device) to turn the screen on. */ + public static final int FLAG_TURN_SCREEN_ON = 0x00200000; + + /** Window flag: when set the window will cause the keyguard to + * be dismissed, only if it is not a secure lock keyguard. Because such + * a keyguard is not needed for security, it will never re-appear if + * the user navigates to another window (in contrast to + * {@link #FLAG_SHOW_WHEN_LOCKED}, which will only temporarily + * hide both secure and non-secure keyguards but ensure they reappear + * when the user moves to another UI that doesn't hide them). + * If the keyguard is currently active and is secure (requires an + * unlock pattern) than the user will still need to confirm it before + * seeing this window, unless {@link #FLAG_SHOW_WHEN_LOCKED} has + * also been set. */ + public static final int FLAG_DISMISS_KEYGUARD = 0x00400000; + + /** Window flag: *sigh* The lock screen wants to continue running its + * animation while it is fading. A kind-of hack to allow this. Maybe + * in the future we just make this the default behavior. + * + * {@hide} */ + public static final int FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000; + /** Window flag: special flag to limit the size of the window to be * original size ([320x480] x density). Used to create window for applications * running under compatibility mode. * * {@hide} */ - public static final int FLAG_COMPATIBLE_WINDOW = 0x00100000; + public static final int FLAG_COMPATIBLE_WINDOW = 0x20000000; /** Window flag: a special option intended for system dialogs. When * this flag is set, the window will demand focus unconditionally when diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 1371932..1ab46fc 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -263,6 +263,13 @@ public interface WindowManagerPolicy { boolean isVisibleLw(); /** + * Like {@link #isVisibleLw}, but also counts a window that is currently + * "hidden" behind the keyguard as visible. This allows us to apply + * things like window flags that impact the keyguard. + */ + boolean isVisibleOrBehindKeyguardLw(); + + /** * Is this window currently visible to the user on-screen? It is * displayed either if it is visible or it is currently running an * animation before no longer being visible. Must be called with the @@ -314,36 +321,60 @@ public interface WindowManagerPolicy { public boolean showLw(boolean doAnimation); } - /** No transition happening. */ + /** + * Bit mask that is set for all enter transition. + */ + public final int TRANSIT_ENTER_MASK = 0x1000; + + /** + * Bit mask that is set for all exit transitions. + */ + public final int TRANSIT_EXIT_MASK = 0x2000; + + /** Not set up for a transition. */ + public final int TRANSIT_UNSET = -1; + /** No animation for transition. */ public final int TRANSIT_NONE = 0; /** Window has been added to the screen. */ - public final int TRANSIT_ENTER = 1; + public final int TRANSIT_ENTER = 1 | TRANSIT_ENTER_MASK; /** Window has been removed from the screen. */ - public final int TRANSIT_EXIT = 2; + public final int TRANSIT_EXIT = 2 | TRANSIT_EXIT_MASK; /** Window has been made visible. */ - public final int TRANSIT_SHOW = 3; + public final int TRANSIT_SHOW = 3 | TRANSIT_ENTER_MASK; /** Window has been made invisible. */ - public final int TRANSIT_HIDE = 4; + public final int TRANSIT_HIDE = 4 | TRANSIT_EXIT_MASK; /** The "application starting" preview window is no longer needed, and will * animate away to show the real window. */ public final int TRANSIT_PREVIEW_DONE = 5; /** A window in a new activity is being opened on top of an existing one * in the same task. */ - public final int TRANSIT_ACTIVITY_OPEN = 6; + public final int TRANSIT_ACTIVITY_OPEN = 6 | TRANSIT_ENTER_MASK; /** The window in the top-most activity is being closed to reveal the * previous activity in the same task. */ - public final int TRANSIT_ACTIVITY_CLOSE = 7; + public final int TRANSIT_ACTIVITY_CLOSE = 7 | TRANSIT_EXIT_MASK; /** A window in a new task is being opened on top of an existing one * in another activity's task. */ - public final int TRANSIT_TASK_OPEN = 8; + public final int TRANSIT_TASK_OPEN = 8 | TRANSIT_ENTER_MASK; /** A window in the top-most activity is being closed to reveal the * previous activity in a different task. */ - public final int TRANSIT_TASK_CLOSE = 9; + public final int TRANSIT_TASK_CLOSE = 9 | TRANSIT_EXIT_MASK; /** A window in an existing task is being displayed on top of an existing one * in another activity's task. */ - public final int TRANSIT_TASK_TO_FRONT = 10; + public final int TRANSIT_TASK_TO_FRONT = 10 | TRANSIT_ENTER_MASK; /** A window in an existing task is being put below all other tasks. */ - public final int TRANSIT_TASK_TO_BACK = 11; + public final int TRANSIT_TASK_TO_BACK = 11 | TRANSIT_EXIT_MASK; + /** A window in a new activity that doesn't have a wallpaper is being + * opened on top of one that does, effectively closing the wallpaper. */ + public final int TRANSIT_WALLPAPER_CLOSE = 12 | TRANSIT_EXIT_MASK; + /** A window in a new activity that does have a wallpaper is being + * opened on one that didn't, effectively opening the wallpaper. */ + public final int TRANSIT_WALLPAPER_OPEN = 13 | TRANSIT_ENTER_MASK; + /** A window in a new activity is being opened on top of an existing one, + * and both are on top of the wallpaper. */ + public final int TRANSIT_WALLPAPER_INTRA_OPEN = 14 | TRANSIT_ENTER_MASK; + /** The window in the top-most activity is being closed to reveal the + * previous activity, and both are on top of he wallpaper. */ + public final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15 | TRANSIT_EXIT_MASK; /** Screen turned off because of power button */ public final int OFF_BECAUSE_OF_USER = 1; @@ -424,6 +455,27 @@ public interface WindowManagerPolicy { public int subWindowTypeToLayerLw(int type); /** + * Get the highest layer (actually one more than) that the wallpaper is + * allowed to be in. + */ + public int getMaxWallpaperLayer(); + + /** + * Return whether the given window should forcibly hide everything + * behind it. Typically returns true for the keyguard. + */ + public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs); + + /** + * Determine if a window that is behind one that is force hiding + * (as determined by {@link #doesForceHide}) should actually be hidden. + * For example, typically returns false for the status bar. Be careful + * to return false for any window that you may hide yourself, since this + * will conflict with what you set. + */ + public boolean canBeForceHidden(WindowState win, WindowManager.LayoutParams attrs); + + /** * Called when the system would like to show a UI to indicate that an * application is starting. You can use this to add a * APPLICATION_STARTING_TYPE window with the given appToken to the window @@ -504,6 +556,11 @@ public interface WindowManagerPolicy { public int selectAnimationLw(WindowState win, int transit); /** + * Create and return an animation to re-display a force hidden window. + */ + public Animation createForceHideEnterAnimation(); + + /** * Called from the key queue thread before a key is dispatched to the * input thread. * @@ -533,14 +590,15 @@ public interface WindowManagerPolicy { * @param win The window that currently has focus. This is where the key * event will normally go. * @param code Key code. - * @param metaKeys TODO + * @param metaKeys bit mask of meta keys that are held. * @param down Is this a key press (true) or release (false)? * @param repeatCount Number of times a key down has repeated. + * @param flags event's flags. * @return Returns true if the policy consumed the event and it should * not be further dispatched. */ public boolean interceptKeyTi(WindowState win, int code, - int metaKeys, boolean down, int repeatCount); + int metaKeys, boolean down, int repeatCount, int flags); /** * Called when layout of the windows is about to start. @@ -581,11 +639,18 @@ public interface WindowManagerPolicy { * returned, all windows given to layoutWindow() <em>must</em> have had a * frame assigned. * - * @return Return true if layout state may have changed (so that another - * layout will be performed). + * @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT} + * and {@link #FINISH_LAYOUT_REDO_CONFIG}. */ - public boolean finishLayoutLw(); + public int finishLayoutLw(); + /** Layout state may have changed (so another layout will be performed) */ + static final int FINISH_LAYOUT_REDO_LAYOUT = 0x0001; + /** Configuration state may have changed */ + static final int FINISH_LAYOUT_REDO_CONFIG = 0x0002; + /** Wallpaper may need to move */ + static final int FINISH_LAYOUT_REDO_WALLPAPER = 0x0004; + /** * Called when animation of the windows is about to start. * @@ -755,7 +820,7 @@ public interface WindowManagerPolicy { boolean displayEnabled); /** - * Called when the system is mostly done booting to dentermine whether + * Called when the system is mostly done booting to determine whether * the system should go into safe mode. */ public boolean detectSafeMode(); @@ -792,8 +857,20 @@ public interface WindowManagerPolicy { public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always); /** + * A special function that is called from the very low-level input queue + * to provide feedback to the user. Currently only called for virtual + * keys. + */ + public void keyFeedbackFromInput(KeyEvent event); + + /** * Called when we have stopped keeping the screen on because a window * requesting this is no longer visible. */ public void screenOnStoppedLw(); + + /** + * Return false to disable key repeat events from being generated. + */ + public boolean allowKeyRepeat(); } diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index 2f5e601..c8396c4 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -175,6 +175,11 @@ public abstract class Animation implements Cloneable { */ private int mZAdjustment; + /** + * Don't animate the wallpaper. + */ + private boolean mDetachWallpaper = false; + private boolean mMore = true; private boolean mOneMoreTime = true; @@ -218,6 +223,8 @@ public abstract class Animation implements Cloneable { setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL)); + setDetachWallpaper(a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false)); + ensureInterpolator(); a.recycle(); @@ -515,6 +522,19 @@ public abstract class Animation implements Cloneable { } /** + * If detachWallpaper is true, and this is a window animation of a window + * that has a wallpaper background, then the window will be detached from + * the wallpaper while it runs. That is, the animation will only be applied + * to the window, and the wallpaper behind it will remain static. + * + * @param detachWallpaper true if the wallpaper should be detached from the animation + * @attr ref android.R.styleable#Animation_detachWallpaper + */ + public void setDetachWallpaper(boolean detachWallpaper) { + mDetachWallpaper = detachWallpaper; + } + + /** * Gets the acceleration curve type for this animation. * * @return the {@link Interpolator} associated to this animation @@ -611,6 +631,14 @@ public abstract class Animation implements Cloneable { } /** + * Return value of {@link #setDetachWallpaper(boolean)}. + * @attr ref android.R.styleable#Animation_detachWallpaper + */ + public boolean getDetachWallpaper() { + return mDetachWallpaper; + } + + /** * <p>Indicates whether or not this animation will affect the transformation * matrix. For instance, a fade animation will not affect the matrix whereas * a scale animation will.</p> diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index b4c5b72..316bcd6 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -40,7 +40,7 @@ import java.io.IOException; * This class is used to specify meta information of an input method. */ public final class InputMethodInfo implements Parcelable { - static final String TAG = "InputMethodMetaInfo"; + static final String TAG = "InputMethodInfo"; /** * The Service that implements this input method component. @@ -244,7 +244,7 @@ public final class InputMethodInfo implements Parcelable { @Override public String toString() { - return "InputMethodMetaInfo{" + mId + return "InputMethodInfo{" + mId + ", settings: " + mSettingsActivityName + "}"; } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index d797890..e30687f 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -446,13 +446,22 @@ public final class InputMethodManager { * @hide */ static public InputMethodManager getInstance(Context context) { + return getInstance(context.getMainLooper()); + } + + /** + * Internally, the input method manager can't be context-dependent, so + * we have this here for the places that need it. + * @hide + */ + static public InputMethodManager getInstance(Looper mainLooper) { synchronized (mInstanceSync) { if (mInstance != null) { return mInstance; } IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE); IInputMethodManager service = IInputMethodManager.Stub.asInterface(b); - mInstance = new InputMethodManager(service, context.getMainLooper()); + mInstance = new InputMethodManager(service, mainLooper); } return mInstance; } |