Initial checkin of spot presentation for touchpad gestures. (DO NOT MERGE)

Added a new PointerIcon API (hidden for now) for loading
pointer icons.

Fixed a starvation problem in the native Looper's sendMessage
implementation which caused new messages to be posted ahead
of old messages sent with sendMessageDelayed.

Redesigned the touch pad gestures to be defined in terms of
more fluid finger / spot movements.  The objective is to reinforce
the natural mapping between fingers and spots which means there
must not be any discontinuities in spot motion relative to
the fingers.

Removed the SpotController stub and folded its responsibilities
into PointerController.

Change-Id: Ib647dbd7a57a7f30dd9c6e2c260df51d7bbdd18e
This commit is contained in:
Jeff Brown
2011-04-12 22:39:53 -07:00
committed by Jeff Brown
parent a6dbfdd3a8
commit 86ea1f5f52
29 changed files with 2003 additions and 722 deletions

View File

@@ -14,32 +14,6 @@
* limitations under the License.
*/
#define LOG_TAG "SpotController"
package android.view;
//#define LOG_NDEBUG 0
// Log debug messages about spot updates
#define DEBUG_SPOT_UPDATES 0
#include "SpotController.h"
#include <cutils/log.h>
namespace android {
// --- SpotController ---
SpotController::SpotController(const sp<Looper>& looper,
const sp<SpriteController>& spriteController) :
mLooper(looper), mSpriteController(spriteController) {
mHandler = new WeakMessageHandler(this);
}
SpotController::~SpotController() {
mLooper->removeMessages(mHandler);
}
void SpotController:: handleMessage(const Message& message) {
}
} // namespace android
parcelable PointerIcon;

View File

@@ -0,0 +1,435 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import com.android.internal.util.XmlUtils;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
/**
* Represents an icon that can be used as a mouse pointer.
* <p>
* Pointer icons can be provided either by the system using system styles,
* or by applications using bitmaps or application resources.
* </p>
*
* @hide
*/
public final class PointerIcon implements Parcelable {
private static final String TAG = "PointerIcon";
/** Style constant: Custom icon with a user-supplied bitmap. */
public static final int STYLE_CUSTOM = -1;
/** Style constant: Null icon. It has no bitmap. */
public static final int STYLE_NULL = 0;
/** Style constant: Arrow icon. (Default mouse pointer) */
public static final int STYLE_ARROW = 1000;
/** {@hide} Style constant: Spot hover icon for touchpads. */
public static final int STYLE_SPOT_HOVER = 2000;
/** {@hide} Style constant: Spot touch icon for touchpads. */
public static final int STYLE_SPOT_TOUCH = 2001;
/** {@hide} Style constant: Spot anchor icon for touchpads. */
public static final int STYLE_SPOT_ANCHOR = 2002;
// OEM private styles should be defined starting at this range to avoid
// conflicts with any system styles that may be defined in the future.
private static final int STYLE_OEM_FIRST = 10000;
// The default pointer icon.
private static final int STYLE_DEFAULT = STYLE_ARROW;
private static final PointerIcon gNullIcon = new PointerIcon(STYLE_NULL);
private final int mStyle;
private int mSystemIconResourceId;
private Bitmap mBitmap;
private float mHotSpotX;
private float mHotSpotY;
private PointerIcon(int style) {
mStyle = style;
}
/**
* Gets a special pointer icon that has no bitmap.
*
* @return The null pointer icon.
*
* @see #STYLE_NULL
*/
public static PointerIcon getNullIcon() {
return gNullIcon;
}
/**
* Gets the default pointer icon.
*
* @param context The context.
* @return The default pointer icon.
*
* @throws IllegalArgumentException if context is null.
*/
public static PointerIcon getDefaultIcon(Context context) {
return getSystemIcon(context, STYLE_DEFAULT);
}
/**
* Gets a system pointer icon for the given style.
* If style is not recognized, returns the default pointer icon.
*
* @param context The context.
* @param style The pointer icon style.
* @return The pointer icon.
*
* @throws IllegalArgumentException if context is null.
*/
public static PointerIcon getSystemIcon(Context context, int style) {
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
if (style == STYLE_NULL) {
return gNullIcon;
}
int styleIndex = getSystemIconStyleIndex(style);
if (styleIndex == 0) {
styleIndex = getSystemIconStyleIndex(STYLE_DEFAULT);
}
TypedArray a = context.obtainStyledAttributes(null,
com.android.internal.R.styleable.Pointer,
com.android.internal.R.attr.pointerStyle, 0);
int resourceId = a.getResourceId(styleIndex, -1);
a.recycle();
if (resourceId == -1) {
Log.w(TAG, "Missing theme resources for pointer icon style " + style);
return style == STYLE_DEFAULT ? gNullIcon : getSystemIcon(context, STYLE_DEFAULT);
}
PointerIcon icon = new PointerIcon(style);
if ((resourceId & 0xff000000) == 0x01000000) {
icon.mSystemIconResourceId = resourceId;
} else {
icon.loadResource(context.getResources(), resourceId);
}
return icon;
}
/**
* Creates a custom pointer from the given bitmap and hotspot information.
*
* @param bitmap The bitmap for the icon.
* @param hotspotX The X offset of the pointer icon hotspot in the bitmap.
* Must be within the [0, bitmap.getWidth()) range.
* @param hotspotY The Y offset of the pointer icon hotspot in the bitmap.
* Must be within the [0, bitmap.getHeight()) range.
* @return A pointer icon for this bitmap.
*
* @throws IllegalArgumentException if bitmap is null, or if the x/y hotspot
* parameters are invalid.
*/
public static PointerIcon createCustomIcon(Bitmap bitmap, float hotSpotX, float hotSpotY) {
if (bitmap == null) {
throw new IllegalArgumentException("bitmap must not be null");
}
validateHotSpot(bitmap, hotSpotX, hotSpotY);
PointerIcon icon = new PointerIcon(STYLE_CUSTOM);
icon.mBitmap = bitmap;
icon.mHotSpotX = hotSpotX;
icon.mHotSpotY = hotSpotY;
return icon;
}
/**
* Loads a custom pointer icon from an XML resource.
* <p>
* The XML resource should have the following form:
* <code>
* &lt;?xml version="1.0" encoding="utf-8"?&gt;
* &lt;pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
* android:bitmap="@drawable/my_pointer_bitmap"
* android:hotSpotX="24"
* android:hotSpotY="24" /&gt;
* </code>
* </p>
*
* @param resources The resources object.
* @param resourceId The resource id.
* @return The pointer icon.
*
* @throws IllegalArgumentException if resources is null.
* @throws Resources.NotFoundException if the resource was not found or the drawable
* linked in the resource was not found.
*/
public static PointerIcon loadCustomIcon(Resources resources, int resourceId) {
if (resources == null) {
throw new IllegalArgumentException("resources must not be null");
}
PointerIcon icon = new PointerIcon(STYLE_CUSTOM);
icon.loadResource(resources, resourceId);
return icon;
}
/**
* Loads the bitmap and hotspot information for a pointer icon, if it is not already loaded.
* Returns a pointer icon (not necessarily the same instance) with the information filled in.
*
* @param context The context.
* @return The loaded pointer icon.
*
* @throws IllegalArgumentException if context is null.
* @see #isLoaded()
* @hide
*/
public PointerIcon load(Context context) {
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
if (mSystemIconResourceId == 0 || mBitmap != null) {
return this;
}
PointerIcon result = new PointerIcon(mStyle);
result.mSystemIconResourceId = mSystemIconResourceId;
result.loadResource(context.getResources(), mSystemIconResourceId);
return result;
}
/**
* Returns true if the pointer icon style is {@link #STYLE_NULL}.
*
* @return True if the pointer icon style is {@link #STYLE_NULL}.
*/
public boolean isNullIcon() {
return mStyle == STYLE_NULL;
}
/**
* Returns true if the pointer icon has been loaded and its bitmap and hotspot
* information are available.
*
* @return True if the pointer icon is loaded.
* @see #load(Context)
*/
public boolean isLoaded() {
return mBitmap != null || mStyle == STYLE_NULL;
}
/**
* Gets the style of the pointer icon.
*
* @return The pointer icon style.
*/
public int getStyle() {
return mStyle;
}
/**
* Gets the bitmap of the pointer icon.
*
* @return The pointer icon bitmap, or null if the style is {@link #STYLE_NULL}.
*
* @throws IllegalStateException if the bitmap is not loaded.
* @see #isLoaded()
* @see #load(Context)
*/
public Bitmap getBitmap() {
throwIfIconIsNotLoaded();
return mBitmap;
}
/**
* Gets the X offset of the pointer icon hotspot.
*
* @return The hotspot X offset.
*
* @throws IllegalStateException if the bitmap is not loaded.
* @see #isLoaded()
* @see #load(Context)
*/
public float getHotSpotX() {
throwIfIconIsNotLoaded();
return mHotSpotX;
}
/**
* Gets the Y offset of the pointer icon hotspot.
*
* @return The hotspot Y offset.
*
* @throws IllegalStateException if the bitmap is not loaded.
* @see #isLoaded()
* @see #load(Context)
*/
public float getHotSpotY() {
throwIfIconIsNotLoaded();
return mHotSpotY;
}
private void throwIfIconIsNotLoaded() {
if (!isLoaded()) {
throw new IllegalStateException("The icon is not loaded.");
}
}
public static final Parcelable.Creator<PointerIcon> CREATOR
= new Parcelable.Creator<PointerIcon>() {
public PointerIcon createFromParcel(Parcel in) {
int style = in.readInt();
if (style == STYLE_NULL) {
return getNullIcon();
}
int systemIconResourceId = in.readInt();
if (systemIconResourceId != 0) {
PointerIcon icon = new PointerIcon(style);
icon.mSystemIconResourceId = systemIconResourceId;
return icon;
}
Bitmap bitmap = Bitmap.CREATOR.createFromParcel(in);
float hotSpotX = in.readFloat();
float hotSpotY = in.readFloat();
return PointerIcon.createCustomIcon(bitmap, hotSpotX, hotSpotY);
}
public PointerIcon[] newArray(int size) {
return new PointerIcon[size];
}
};
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mStyle);
if (mStyle != STYLE_NULL) {
out.writeInt(mSystemIconResourceId);
if (mSystemIconResourceId == 0) {
mBitmap.writeToParcel(out, flags);
out.writeFloat(mHotSpotX);
out.writeFloat(mHotSpotY);
}
}
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || !(other instanceof PointerIcon)) {
return false;
}
PointerIcon otherIcon = (PointerIcon) other;
if (mStyle != otherIcon.mStyle
|| mSystemIconResourceId != otherIcon.mSystemIconResourceId) {
return false;
}
if (mSystemIconResourceId == 0 && (mBitmap != otherIcon.mBitmap
|| mHotSpotX != otherIcon.mHotSpotX
|| mHotSpotY != otherIcon.mHotSpotY)) {
return false;
}
return true;
}
private void loadResource(Resources resources, int resourceId) {
XmlResourceParser parser = resources.getXml(resourceId);
final int bitmapRes;
final float hotSpotX;
final float hotSpotY;
try {
XmlUtils.beginDocument(parser, "pointer-icon");
TypedArray a = resources.obtainAttributes(
parser, com.android.internal.R.styleable.PointerIcon);
bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
hotSpotX = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
hotSpotY = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
a.recycle();
} catch (Exception ex) {
throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex);
} finally {
parser.close();
}
if (bitmapRes == 0) {
throw new IllegalArgumentException("<pointer-icon> is missing bitmap attribute.");
}
Drawable drawable = resources.getDrawable(bitmapRes);
if (!(drawable instanceof BitmapDrawable)) {
throw new IllegalArgumentException("<pointer-icon> bitmap attribute must "
+ "refer to a bitmap drawable.");
}
// Set the properties now that we have successfully loaded the icon.
mBitmap = ((BitmapDrawable)drawable).getBitmap();
mHotSpotX = hotSpotX;
mHotSpotY = hotSpotY;
}
private static void validateHotSpot(Bitmap bitmap, float hotSpotX, float hotSpotY) {
if (hotSpotX < 0 || hotSpotX >= bitmap.getWidth()) {
throw new IllegalArgumentException("x hotspot lies outside of the bitmap area");
}
if (hotSpotY < 0 || hotSpotY >= bitmap.getHeight()) {
throw new IllegalArgumentException("y hotspot lies outside of the bitmap area");
}
}
private static int getSystemIconStyleIndex(int style) {
switch (style) {
case STYLE_ARROW:
return com.android.internal.R.styleable.Pointer_pointerIconArrow;
case STYLE_SPOT_HOVER:
return com.android.internal.R.styleable.Pointer_pointerIconSpotHover;
case STYLE_SPOT_TOUCH:
return com.android.internal.R.styleable.Pointer_pointerIconSpotTouch;
case STYLE_SPOT_ANCHOR:
return com.android.internal.R.styleable.Pointer_pointerIconSpotAnchor;
default:
return 0;
}
}
}

View File

@@ -54,6 +54,7 @@ LOCAL_SRC_FILES:= \
android_view_KeyCharacterMap.cpp \
android_view_GLES20Canvas.cpp \
android_view_MotionEvent.cpp \
android_view_PointerIcon.cpp \
android_view_VelocityTracker.cpp \
android_text_AndroidCharacter.cpp \
android_text_AndroidBidi.cpp \

View File

@@ -170,6 +170,7 @@ extern int register_android_view_InputChannel(JNIEnv* env);
extern int register_android_view_InputQueue(JNIEnv* env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
extern int register_android_view_PointerIcon(JNIEnv* env);
extern int register_android_view_VelocityTracker(JNIEnv* env);
extern int register_android_content_res_ObbScanner(JNIEnv* env);
extern int register_android_content_res_Configuration(JNIEnv* env);
@@ -1303,6 +1304,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_view_InputQueue),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
REG_JNI(register_android_view_PointerIcon),
REG_JNI(register_android_view_VelocityTracker),
REG_JNI(register_android_content_res_ObbScanner),

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "PointerIcon-JNI"
#include "JNIHelp.h"
#include "android_view_PointerIcon.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
#include <android/graphics/GraphicsJNI.h>
namespace android {
static struct {
jclass clazz;
jfieldID mStyle;
jfieldID mBitmap;
jfieldID mHotSpotX;
jfieldID mHotSpotY;
jmethodID getSystemIcon;
jmethodID load;
} gPointerIconClassInfo;
// --- Global Functions ---
jobject android_view_PointerIcon_getSystemIcon(JNIEnv* env, jobject contextObj, int32_t style) {
jobject pointerIconObj = env->CallStaticObjectMethod(gPointerIconClassInfo.clazz,
gPointerIconClassInfo.getSystemIcon, contextObj, style);
if (env->ExceptionCheck()) {
LOGW("An exception occurred while getting a pointer icon with style %d.", style);
LOGW_EX(env);
env->ExceptionClear();
return NULL;
}
return pointerIconObj;
}
status_t android_view_PointerIcon_load(JNIEnv* env, jobject pointerIconObj, jobject contextObj,
PointerIcon* outPointerIcon) {
outPointerIcon->reset();
if (!pointerIconObj) {
return OK;
}
jobject loadedPointerIconObj = env->CallObjectMethod(pointerIconObj,
gPointerIconClassInfo.load, contextObj);
if (env->ExceptionCheck() || !loadedPointerIconObj) {
LOGW("An exception occurred while loading a pointer icon.");
LOGW_EX(env);
env->ExceptionClear();
return UNKNOWN_ERROR;
}
outPointerIcon->style = env->GetIntField(loadedPointerIconObj,
gPointerIconClassInfo.mStyle);
outPointerIcon->hotSpotX = env->GetFloatField(loadedPointerIconObj,
gPointerIconClassInfo.mHotSpotX);
outPointerIcon->hotSpotY = env->GetFloatField(loadedPointerIconObj,
gPointerIconClassInfo.mHotSpotY);
jobject bitmapObj = env->GetObjectField(loadedPointerIconObj, gPointerIconClassInfo.mBitmap);
if (bitmapObj) {
SkBitmap* bitmap = GraphicsJNI::getNativeBitmap(env, bitmapObj);
if (bitmap) {
outPointerIcon->bitmap = *bitmap; // use a shared pixel ref
}
env->DeleteLocalRef(bitmapObj);
}
env->DeleteLocalRef(loadedPointerIconObj);
return OK;
}
status_t android_view_PointerIcon_loadSystemIcon(JNIEnv* env, jobject contextObj,
int32_t style, PointerIcon* outPointerIcon) {
jobject pointerIconObj = android_view_PointerIcon_getSystemIcon(env, contextObj, style);
if (!pointerIconObj) {
outPointerIcon->reset();
return UNKNOWN_ERROR;
}
status_t status = android_view_PointerIcon_load(env, pointerIconObj,
contextObj, outPointerIcon);
env->DeleteLocalRef(pointerIconObj);
return status;
}
// --- JNI Registration ---
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className); \
var = jclass(env->NewGlobalRef(var));
#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \
var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method " methodName);
#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method " methodName);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
int register_android_view_PointerIcon(JNIEnv* env) {
FIND_CLASS(gPointerIconClassInfo.clazz, "android/view/PointerIcon");
GET_FIELD_ID(gPointerIconClassInfo.mBitmap, gPointerIconClassInfo.clazz,
"mBitmap", "Landroid/graphics/Bitmap;");
GET_FIELD_ID(gPointerIconClassInfo.mStyle, gPointerIconClassInfo.clazz,
"mStyle", "I");
GET_FIELD_ID(gPointerIconClassInfo.mHotSpotX, gPointerIconClassInfo.clazz,
"mHotSpotX", "F");
GET_FIELD_ID(gPointerIconClassInfo.mHotSpotY, gPointerIconClassInfo.clazz,
"mHotSpotY", "F");
GET_STATIC_METHOD_ID(gPointerIconClassInfo.getSystemIcon, gPointerIconClassInfo.clazz,
"getSystemIcon", "(Landroid/content/Context;I)Landroid/view/PointerIcon;");
GET_METHOD_ID(gPointerIconClassInfo.load, gPointerIconClassInfo.clazz,
"load", "(Landroid/content/Context;)Landroid/view/PointerIcon;");
return 0;
}
} // namespace android

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _ANDROID_VIEW_POINTER_ICON_H
#define _ANDROID_VIEW_POINTER_ICON_H
#include "jni.h"
#include <utils/Errors.h>
#include <SkBitmap.h>
namespace android {
/* Pointer icon styles.
* Must match the definition in android.view.PointerIcon.
*/
enum {
POINTER_ICON_STYLE_CUSTOM = -1,
POINTER_ICON_STYLE_NULL = 0,
POINTER_ICON_STYLE_ARROW = 1000,
POINTER_ICON_STYLE_SPOT_HOVER = 2000,
POINTER_ICON_STYLE_SPOT_TOUCH = 2001,
POINTER_ICON_STYLE_SPOT_ANCHOR = 2002,
};
/*
* Describes a pointer icon.
*/
struct PointerIcon {
inline PointerIcon() {
reset();
}
int32_t style;
SkBitmap bitmap;
float hotSpotX;
float hotSpotY;
inline bool isNullIcon() {
return style == POINTER_ICON_STYLE_NULL;
}
inline void reset() {
style = POINTER_ICON_STYLE_NULL;
bitmap.reset();
hotSpotX = 0;
hotSpotY = 0;
}
};
/* Gets a system pointer icon with the specified style. */
extern jobject android_view_PointerIcon_getSystemIcon(JNIEnv* env,
jobject contextObj, int32_t style);
/* Loads the bitmap associated with a pointer icon.
* If pointerIconObj is NULL, returns OK and a pointer icon with POINTER_ICON_STYLE_NULL. */
extern status_t android_view_PointerIcon_load(JNIEnv* env,
jobject pointerIconObj, jobject contextObj, PointerIcon* outPointerIcon);
/* Loads the bitmap associated with a pointer icon by style.
* If pointerIconObj is NULL, returns OK and a pointer icon with POINTER_ICON_STYLE_NULL. */
extern status_t android_view_PointerIcon_loadSystemIcon(JNIEnv* env,
jobject contextObj, int32_t style, PointerIcon* outPointerIcon);
} // namespace android
#endif // _ANDROID_OS_POINTER_ICON_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_spot_anchor"
android:hotSpotX="33"
android:hotSpotY="33" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_spot_hover"
android:hotSpotX="33"
android:hotSpotY="33" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_spot_touch"
android:hotSpotX="24"
android:hotSpotY="24" />

View File

@@ -746,6 +746,13 @@
<!-- Default style for the Switch widget. -->
<attr name="switchStyle" format="reference" />
<!-- ============== -->
<!-- Pointer styles -->
<!-- ============== -->
<eat-comment />
<!-- Reference to the Pointer style -->
<attr name="pointerStyle" format="reference" />
</declare-styleable>
<!-- **************************************************************** -->
@@ -4873,6 +4880,17 @@
<attr name="switchPadding" format="dimension" />
</declare-styleable>
<declare-styleable name="Pointer">
<!-- Reference to a pointer icon drawable with STYLE_ARROW -->
<attr name="pointerIconArrow" format="reference" />
<!-- Reference to a pointer icon drawable with STYLE_SPOT_HOVER -->
<attr name="pointerIconSpotHover" format="reference" />
<!-- Reference to a pointer icon drawable with STYLE_SPOT_TOUCH -->
<attr name="pointerIconSpotTouch" format="reference" />
<!-- Reference to a pointer icon drawable with STYLE_SPOT_ANCHOR -->
<attr name="pointerIconSpotAnchor" format="reference" />
</declare-styleable>
<declare-styleable name="PointerIcon">
<!-- Drawable to use as the icon bitmap. -->
<attr name="bitmap" format="reference" />

View File

@@ -2187,4 +2187,12 @@
<item name="android:borderLeft">@dimen/preference_fragment_padding_side</item>
<item name="android:borderRight">@dimen/preference_fragment_padding_side</item>
</style>
<!-- Pointer styles -->
<style name="Pointer">
<item name="android:pointerIconArrow">@android:drawable/pointer_arrow_icon</item>
<item name="android:pointerIconSpotHover">@android:drawable/pointer_spot_hover_icon</item>
<item name="android:pointerIconSpotTouch">@android:drawable/pointer_spot_touch_icon</item>
<item name="android:pointerIconSpotAnchor">@android:drawable/pointer_spot_anchor_icon</item>
</style>
</resources>

View File

@@ -321,6 +321,8 @@
<item name="fastScrollOverlayPosition">floating</item>
<item name="fastScrollTextColor">@android:color/primary_text_dark</item>
<!-- Pointer style -->
<item name="pointerStyle">@android:style/Pointer</item>
</style>
<!-- Variant of the default (dark) theme with no title bar -->

View File

@@ -608,6 +608,11 @@ private:
// Oldest sample to consider when calculating the velocity.
static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms
// When the total duration of the window of samples being averaged is less
// than the window size, the resulting velocity is scaled to reduce the impact
// of overestimation in short traces.
static const nsecs_t MIN_WINDOW = 100 * 1000000; // 100 ms
// The minimum duration between samples when estimating velocity.
static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms

View File

@@ -831,6 +831,7 @@ bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const
const Position& oldestPosition =
oldestMovement.positions[oldestMovement.idBits.getIndexOfBit(id)];
nsecs_t lastDuration = 0;
while (numTouches-- > 1) {
if (++index == HISTORY_SIZE) {
index = 0;
@@ -857,6 +858,14 @@ bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const
// Make sure we used at least one sample.
if (samplesUsed != 0) {
// Scale the velocity linearly if the window of samples is small.
nsecs_t totalDuration = newestMovement.eventTime - oldestMovement.eventTime;
if (totalDuration < MIN_WINDOW) {
float scale = float(totalDuration) / float(MIN_WINDOW);
accumVx *= scale;
accumVy *= scale;
}
*outVx = accumVx;
*outVy = accumVy;
return true;

View File

@@ -662,7 +662,8 @@ void Looper::wakeAndLock() {
#endif
void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
sendMessageAtTime(LLONG_MIN, handler, message);
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
sendMessageAtTime(now, handler, message);
}
void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,

View File

@@ -23,7 +23,6 @@ LOCAL_SRC_FILES:= \
InputReader.cpp \
InputWindow.cpp \
PointerController.cpp \
SpotController.cpp \
SpriteController.cpp
LOCAL_SHARED_LIBRARIES := \

File diff suppressed because it is too large Load Diff

View File

@@ -20,7 +20,6 @@
#include "EventHub.h"
#include "InputDispatcher.h"
#include "PointerController.h"
#include "SpotController.h"
#include <ui/Input.h>
#include <ui/DisplayInfo.h>
@@ -90,9 +89,6 @@ public:
/* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
/* Gets a spot controller associated with the specified touch pad device. */
virtual sp<SpotControllerInterface> obtainSpotController(int32_t deviceId) = 0;
};
@@ -648,6 +644,20 @@ protected:
idBits.clear();
buttonState = 0;
}
void getCentroid(float* outX, float* outY) {
float x = 0, y = 0;
if (pointerCount != 0) {
for (uint32_t i = 0; i < pointerCount; i++) {
x += pointers[i].x;
y += pointers[i].y;
}
x /= pointerCount;
y /= pointerCount;
}
*outX = x;
*outY = y;
}
};
// Input sources supported by the device.
@@ -670,6 +680,12 @@ protected:
bool useJumpyTouchFilter;
bool useAveragingTouchFilter;
nsecs_t virtualKeyQuietTime;
enum GestureMode {
GESTURE_MODE_POINTER,
GESTURE_MODE_SPOTS,
};
GestureMode gestureMode;
} mParameters;
// Immutable calibration parameters in parsed form.
@@ -841,8 +857,8 @@ protected:
float pointerGestureXZoomScale;
float pointerGestureYZoomScale;
// The maximum swipe width squared.
int32_t pointerGestureMaxSwipeWidthSquared;
// The maximum swipe width.
float pointerGestureMaxSwipeWidth;
} mLocked;
virtual void configureParameters();
@@ -929,28 +945,32 @@ private:
// Emits HOVER_MOVE events at the pointer location.
HOVER,
// More than two fingers involved but they haven't moved enough for us
// to figure out what is intended.
INDETERMINATE_MULTITOUCH,
// Exactly two fingers but neither have moved enough to clearly indicate
// whether a swipe or freeform gesture was intended. We consider the
// pointer to be pressed so this enables clicking or long-pressing on buttons.
// Pointer does not move.
// Emits DOWN, MOVE and UP events with a single stationary pointer coordinate.
PRESS,
// Exactly two fingers moving in the same direction, button is not pressed.
// Pointer does not move.
// Emits DOWN, MOVE and UP events with a single pointer coordinate that
// follows the midpoint between both fingers.
// The centroid is fixed when entering this state.
SWIPE,
// Two or more fingers moving in arbitrary directions, button is not pressed.
// Pointer does not move.
// Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow
// each finger individually relative to the initial centroid of the finger.
// The centroid is fixed when entering this state.
FREEFORM,
// Waiting for quiet time to end before starting the next gesture.
QUIET,
};
// Time the first finger went down.
nsecs_t firstTouchTime;
// The active pointer id from the raw touch data.
int32_t activeTouchId; // -1 if none
@@ -959,32 +979,20 @@ private:
// Pointer coords and ids for the current and previous pointer gesture.
Mode currentGestureMode;
uint32_t currentGesturePointerCount;
BitSet32 currentGestureIdBits;
uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];
PointerCoords currentGestureCoords[MAX_POINTERS];
Mode lastGestureMode;
uint32_t lastGesturePointerCount;
BitSet32 lastGestureIdBits;
uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1];
PointerCoords lastGestureCoords[MAX_POINTERS];
// Tracks for all pointers originally went down.
TouchData touchOrigin;
// Describes how touch ids are mapped to gesture ids for freeform gestures.
uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1];
// Initial centroid of the movement.
// Used to calculate how far the touch pointers have moved since the gesture started.
int32_t initialCentroidX;
int32_t initialCentroidY;
// Initial pointer location.
// Used to track where the pointer was when the gesture started.
float initialPointerX;
float initialPointerY;
// Pointer coords and ids for the current spots.
PointerControllerInterface::SpotGesture spotGesture;
BitSet32 spotIdBits; // same set of ids as touch ids
uint32_t spotIdToIndex[MAX_POINTER_ID + 1];
PointerCoords spotCoords[MAX_POINTERS];
// Time the pointer gesture last went down.
nsecs_t downTime;
@@ -992,26 +1000,34 @@ private:
// Time we started waiting for a tap gesture.
nsecs_t tapTime;
// Location of initial tap.
float tapX, tapY;
// Time we started waiting for quiescence.
nsecs_t quietTime;
// Reference points for multitouch gestures.
float referenceTouchX; // reference touch X/Y coordinates in surface units
float referenceTouchY;
float referenceGestureX; // reference gesture X/Y coordinates in pixels
float referenceGestureY;
// Describes how touch ids are mapped to gesture ids for freeform gestures.
uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1];
// A velocity tracker for determining whether to switch active pointers during drags.
VelocityTracker velocityTracker;
void reset() {
firstTouchTime = LLONG_MIN;
activeTouchId = -1;
activeGestureId = -1;
currentGestureMode = NEUTRAL;
currentGesturePointerCount = 0;
currentGestureIdBits.clear();
lastGestureMode = NEUTRAL;
lastGesturePointerCount = 0;
lastGestureIdBits.clear();
touchOrigin.clear();
initialCentroidX = 0;
initialCentroidY = 0;
initialPointerX = 0;
initialPointerY = 0;
spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
spotIdBits.clear();
downTime = 0;
velocityTracker.clear();
resetTapTime();
@@ -1035,6 +1051,7 @@ private:
void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags);
void preparePointerGestures(nsecs_t when,
bool* outCancelPreviousGesture, bool* outFinishPreviousGesture);
void moveSpotsLocked();
// Dispatches a motion event.
// If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the

View File

@@ -36,40 +36,49 @@ namespace android {
// --- PointerController ---
// Time to wait before starting the fade when the pointer is inactive.
static const nsecs_t INACTIVITY_FADE_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
static const nsecs_t INACTIVITY_FADE_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
// Time to wait between animation frames.
static const nsecs_t ANIMATION_FRAME_INTERVAL = 1000000000LL / 60;
// Time to spend fading out the spot completely.
static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
// Time to spend fading out the pointer completely.
static const nsecs_t FADE_DURATION = 500 * 1000000LL; // 500 ms
// Time to wait between frames.
static const nsecs_t FADE_FRAME_INTERVAL = 1000000000LL / 60;
// Amount to subtract from alpha per frame.
static const float FADE_DECAY_PER_FRAME = float(FADE_FRAME_INTERVAL) / FADE_DURATION;
static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
PointerController::PointerController(const sp<Looper>& looper,
const sp<SpriteController>& spriteController) :
mLooper(looper), mSpriteController(spriteController) {
// --- PointerController ---
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
mHandler = new WeakMessageHandler(this);
AutoMutex _l(mLock);
mLocked.animationPending = false;
mLocked.displayWidth = -1;
mLocked.displayHeight = -1;
mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
mLocked.presentation = PRESENTATION_POINTER;
mLocked.presentationChanged = false;
mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL;
mLocked.pointerIsFading = true; // keep the pointer initially faded
mLocked.pointerX = 0;
mLocked.pointerY = 0;
mLocked.pointerAlpha = 0.0f;
mLocked.pointerSprite = mSpriteController->createSprite();
mLocked.pointerIconChanged = false;
mLocked.buttonState = 0;
mLocked.fadeAlpha = 1;
mLocked.inactivityFadeDelay = INACTIVITY_FADE_DELAY_NORMAL;
mLocked.visible = false;
mLocked.sprite = mSpriteController->createSprite();
loadResources();
}
PointerController::~PointerController() {
@@ -77,7 +86,13 @@ PointerController::~PointerController() {
AutoMutex _l(mLock);
mLocked.sprite.clear();
mLocked.pointerSprite.clear();
for (size_t i = 0; i < mLocked.spots.size(); i++) {
delete mLocked.spots.itemAt(i);
}
mLocked.spots.clear();
mLocked.recycledSprites.clear();
}
bool PointerController::getBounds(float* outMinX, float* outMinY,
@@ -130,8 +145,6 @@ void PointerController::setButtonState(uint32_t buttonState) {
if (mLocked.buttonState != buttonState) {
mLocked.buttonState = buttonState;
unfadeBeforeUpdateLocked();
updateLocked();
}
}
@@ -167,8 +180,7 @@ void PointerController::setPositionLocked(float x, float y) {
} else {
mLocked.pointerY = y;
}
unfadeBeforeUpdateLocked();
updateLocked();
updatePointerLocked();
}
}
@@ -182,32 +194,105 @@ void PointerController::getPosition(float* outX, float* outY) const {
void PointerController::fade() {
AutoMutex _l(mLock);
startFadeLocked();
sendImmediateInactivityTimeoutLocked();
}
void PointerController::unfade() {
AutoMutex _l(mLock);
if (unfadeBeforeUpdateLocked()) {
updateLocked();
// Always reset the inactivity timer.
resetInactivityTimeoutLocked();
// Unfade immediately if needed.
if (mLocked.pointerIsFading) {
mLocked.pointerIsFading = false;
mLocked.pointerAlpha = 1.0f;
updatePointerLocked();
}
}
void PointerController::setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay) {
void PointerController::setPresentation(Presentation presentation) {
AutoMutex _l(mLock);
if (mLocked.inactivityFadeDelay != inactivityFadeDelay) {
mLocked.inactivityFadeDelay = inactivityFadeDelay;
startInactivityFadeDelayLocked();
if (mLocked.presentation != presentation) {
mLocked.presentation = presentation;
mLocked.presentationChanged = true;
if (presentation != PRESENTATION_SPOT) {
fadeOutAndReleaseAllSpotsLocked();
}
updatePointerLocked();
}
}
void PointerController::updateLocked() {
mLocked.sprite->openTransaction();
mLocked.sprite->setPosition(mLocked.pointerX, mLocked.pointerY);
mLocked.sprite->setAlpha(mLocked.fadeAlpha);
mLocked.sprite->setVisible(mLocked.visible);
mLocked.sprite->closeTransaction();
void PointerController::setSpots(SpotGesture spotGesture,
const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
#if DEBUG_POINTER_UPDATES
LOGD("setSpots: spotGesture=%d", spotGesture);
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.firstMarkedBit();
idBits.clearBit(id);
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
c.getAxisValue(AMOTION_EVENT_AXIS_X),
c.getAxisValue(AMOTION_EVENT_AXIS_Y),
c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
}
#endif
AutoMutex _l(mLock);
mSpriteController->openTransaction();
// Add or move spots for fingers that are down.
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.firstMarkedBit();
idBits.clearBit(id);
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
? mResources.spotTouch : mResources.spotHover;
float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
Spot* spot = getSpotLocked(id);
if (!spot) {
spot = createAndAddSpotLocked(id);
}
spot->updateSprite(&icon, x, y);
}
// Remove spots for fingers that went up.
for (size_t i = 0; i < mLocked.spots.size(); i++) {
Spot* spot = mLocked.spots.itemAt(i);
if (spot->id != Spot::INVALID_ID
&& !spotIdBits.hasBit(spot->id)) {
fadeOutAndReleaseSpotLocked(spot);
}
}
mSpriteController->closeTransaction();
}
void PointerController::clearSpots() {
#if DEBUG_POINTER_UPDATES
LOGD("clearSpots");
#endif
AutoMutex _l(mLock);
fadeOutAndReleaseAllSpotsLocked();
}
void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) {
AutoMutex _l(mLock);
if (mLocked.inactivityTimeout != inactivityTimeout) {
mLocked.inactivityTimeout = inactivityTimeout;
resetInactivityTimeoutLocked();
}
}
void PointerController::setDisplaySize(int32_t width, int32_t height) {
@@ -226,7 +311,8 @@ void PointerController::setDisplaySize(int32_t width, int32_t height) {
mLocked.pointerY = 0;
}
updateLocked();
fadeOutAndReleaseAllSpotsLocked();
updatePointerLocked();
}
}
@@ -283,74 +369,217 @@ void PointerController::setDisplayOrientation(int32_t orientation) {
mLocked.pointerY = y - 0.5f;
mLocked.displayOrientation = orientation;
updateLocked();
updatePointerLocked();
}
}
void PointerController::setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) {
void PointerController::setPointerIcon(const SpriteIcon& icon) {
AutoMutex _l(mLock);
mLocked.sprite->setBitmap(bitmap, hotSpotX, hotSpotY);
mLocked.pointerIcon = icon.copy();
mLocked.pointerIconChanged = true;
updatePointerLocked();
}
void PointerController::handleMessage(const Message& message) {
switch (message.what) {
case MSG_FADE_STEP: {
AutoMutex _l(mLock);
fadeStepLocked();
case MSG_ANIMATE:
doAnimate();
break;
case MSG_INACTIVITY_TIMEOUT:
doInactivityTimeout();
break;
}
}
}
bool PointerController::unfadeBeforeUpdateLocked() {
sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
void PointerController::doAnimate() {
AutoMutex _l(mLock);
if (isFadingLocked()) {
mLocked.visible = true;
mLocked.fadeAlpha = 1;
return true; // update required to effect the unfade
}
return false; // update not required
}
bool keepAnimating = false;
mLocked.animationPending = false;
nsecs_t frameDelay = systemTime(SYSTEM_TIME_MONOTONIC) - mLocked.animationTime;
void PointerController::startFadeLocked() {
if (!isFadingLocked()) {
sendFadeStepMessageDelayedLocked(0);
}
}
void PointerController::startInactivityFadeDelayLocked() {
if (!isFadingLocked()) {
sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
}
}
void PointerController::fadeStepLocked() {
if (mLocked.visible) {
mLocked.fadeAlpha -= FADE_DECAY_PER_FRAME;
if (mLocked.fadeAlpha < 0) {
mLocked.fadeAlpha = 0;
mLocked.visible = false;
// Animate pointer fade.
if (mLocked.pointerIsFading) {
mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
if (mLocked.pointerAlpha <= 0) {
mLocked.pointerAlpha = 0;
} else {
sendFadeStepMessageDelayedLocked(FADE_FRAME_INTERVAL);
keepAnimating = true;
}
updateLocked();
updatePointerLocked();
}
// Animate spots that are fading out and being removed.
for (size_t i = 0; i < mLocked.spots.size(); i++) {
Spot* spot = mLocked.spots.itemAt(i);
if (spot->id == Spot::INVALID_ID) {
spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
if (spot->alpha <= 0) {
mLocked.spots.removeAt(i--);
releaseSpotLocked(spot);
} else {
spot->sprite->setAlpha(spot->alpha);
keepAnimating = true;
}
}
}
if (keepAnimating) {
startAnimationLocked();
}
}
bool PointerController::isFadingLocked() {
return !mLocked.visible || mLocked.fadeAlpha != 1;
void PointerController::doInactivityTimeout() {
AutoMutex _l(mLock);
if (!mLocked.pointerIsFading) {
mLocked.pointerIsFading = true;
startAnimationLocked();
}
}
nsecs_t PointerController::getInactivityFadeDelayTimeLocked() {
return mLocked.inactivityFadeDelay == INACTIVITY_FADE_DELAY_SHORT
? INACTIVITY_FADE_DELAY_TIME_SHORT : INACTIVITY_FADE_DELAY_TIME_NORMAL;
void PointerController::startAnimationLocked() {
if (!mLocked.animationPending) {
mLocked.animationPending = true;
mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
mLooper->sendMessageDelayed(ANIMATION_FRAME_INTERVAL, mHandler, Message(MSG_ANIMATE));
}
}
void PointerController::sendFadeStepMessageDelayedLocked(nsecs_t delayTime) {
mLooper->removeMessages(mHandler, MSG_FADE_STEP);
mLooper->sendMessageDelayed(delayTime, mHandler, Message(MSG_FADE_STEP));
void PointerController::resetInactivityTimeoutLocked() {
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
}
void PointerController::sendImmediateInactivityTimeoutLocked() {
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
mLooper->sendMessage(mHandler, MSG_INACTIVITY_TIMEOUT);
}
void PointerController::updatePointerLocked() {
mSpriteController->openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
if (mLocked.pointerAlpha > 0) {
mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
mLocked.pointerSprite->setVisible(true);
} else {
mLocked.pointerSprite->setVisible(false);
}
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
mLocked.pointerSprite->setIcon(mLocked.presentation == PRESENTATION_POINTER
? mLocked.pointerIcon : mResources.spotAnchor);
mLocked.pointerIconChanged = false;
mLocked.presentationChanged = false;
}
mSpriteController->closeTransaction();
}
PointerController::Spot* PointerController::getSpotLocked(uint32_t id) {
for (size_t i = 0; i < mLocked.spots.size(); i++) {
Spot* spot = mLocked.spots.itemAt(i);
if (spot->id == id) {
return spot;
}
}
return NULL;
}
PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) {
// Remove spots until we have fewer than MAX_SPOTS remaining.
while (mLocked.spots.size() >= MAX_SPOTS) {
Spot* spot = removeFirstFadingSpotLocked();
if (!spot) {
spot = mLocked.spots.itemAt(0);
mLocked.spots.removeAt(0);
}
releaseSpotLocked(spot);
}
// Obtain a sprite from the recycled pool.
sp<Sprite> sprite;
if (! mLocked.recycledSprites.isEmpty()) {
sprite = mLocked.recycledSprites.top();
mLocked.recycledSprites.pop();
} else {
sprite = mSpriteController->createSprite();
}
// Return the new spot.
Spot* spot = new Spot(id, sprite);
mLocked.spots.push(spot);
return spot;
}
PointerController::Spot* PointerController::removeFirstFadingSpotLocked() {
for (size_t i = 0; i < mLocked.spots.size(); i++) {
Spot* spot = mLocked.spots.itemAt(i);
if (spot->id == Spot::INVALID_ID) {
mLocked.spots.removeAt(i);
return spot;
}
}
return NULL;
}
void PointerController::releaseSpotLocked(Spot* spot) {
spot->sprite->clearIcon();
if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
mLocked.recycledSprites.push(spot->sprite);
}
delete spot;
}
void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) {
if (spot->id != Spot::INVALID_ID) {
spot->id = Spot::INVALID_ID;
startAnimationLocked();
}
}
void PointerController::fadeOutAndReleaseAllSpotsLocked() {
for (size_t i = 0; i < mLocked.spots.size(); i++) {
Spot* spot = mLocked.spots.itemAt(i);
fadeOutAndReleaseSpotLocked(spot);
}
}
void PointerController::loadResources() {
mPolicy->loadPointerResources(&mResources);
}
// --- PointerController::Spot ---
void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) {
sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
sprite->setAlpha(alpha);
sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
sprite->setPosition(x, y);
this->x = x;
this->y = y;
if (icon != lastIcon) {
lastIcon = icon;
if (icon) {
sprite->setIcon(*icon);
sprite->setVisible(true);
} else {
sprite->setVisible(false);
}
}
}
} // namespace android

View File

@@ -30,7 +30,10 @@
namespace android {
/**
* Interface for tracking a single (mouse) pointer.
* Interface for tracking a mouse / touch pad pointer and touch pad spots.
*
* The spots are sprites on screen that visually represent the positions of
* fingers
*
* The pointer controller is responsible for providing synchronization and for tracking
* display orientation changes if needed.
@@ -64,8 +67,95 @@ public:
/* Fades the pointer out now. */
virtual void fade() = 0;
/* Makes the pointer visible if it has faded out. */
/* Makes the pointer visible if it has faded out.
* The pointer never unfades itself automatically. This method must be called
* by the client whenever the pointer is moved or a button is pressed and it
* wants to ensure that the pointer becomes visible again. */
virtual void unfade() = 0;
enum Presentation {
// Show the mouse pointer.
PRESENTATION_POINTER,
// Show spots and a spot anchor in place of the mouse pointer.
PRESENTATION_SPOT,
};
/* Sets the mode of the pointer controller. */
virtual void setPresentation(Presentation presentation) = 0;
// Describes the current gesture.
enum SpotGesture {
// No gesture.
// Do not display any spots.
SPOT_GESTURE_NEUTRAL,
// Tap at current location.
// Briefly display one spot at the tapped location.
SPOT_GESTURE_TAP,
// Button pressed but no finger is down.
// Display spot at pressed location.
SPOT_GESTURE_BUTTON_CLICK,
// Button pressed and a finger is down.
// Display spot at pressed location.
SPOT_GESTURE_BUTTON_DRAG,
// One finger down and hovering.
// Display spot at the hovered location.
SPOT_GESTURE_HOVER,
// Two fingers down but not sure in which direction they are moving so we consider
// it a press at the pointer location.
// Display two spots near the pointer location.
SPOT_GESTURE_PRESS,
// Two fingers down and moving in same direction.
// Display two spots near the pointer location.
SPOT_GESTURE_SWIPE,
// Two or more fingers down and moving in arbitrary directions.
// Display two or more spots near the pointer location, one for each finger.
SPOT_GESTURE_FREEFORM,
};
/* Sets the spots for the current gesture.
* The spots are not subject to the inactivity timeout like the pointer
* itself it since they are expected to remain visible for so long as
* the fingers are on the touch pad.
*
* The values of the AMOTION_EVENT_AXIS_PRESSURE axis is significant.
* For spotCoords, pressure != 0 indicates that the spot's location is being
* pressed (not hovering).
*/
virtual void setSpots(SpotGesture spotGesture,
const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits) = 0;
/* Removes all spots. */
virtual void clearSpots() = 0;
};
/*
* Pointer resources.
*/
struct PointerResources {
SpriteIcon spotHover;
SpriteIcon spotTouch;
SpriteIcon spotAnchor;
};
/*
* Pointer controller policy interface.
*
* The pointer controller policy is used by the pointer controller to interact with
* the Window Manager and other system components.
*
* The actual implementation is partially supported by callbacks into the DVM
* via JNI. This interface is also mocked in the unit tests.
*/
class PointerControllerPolicyInterface : public virtual RefBase {
protected:
PointerControllerPolicyInterface() { }
virtual ~PointerControllerPolicyInterface() { }
public:
virtual void loadPointerResources(PointerResources* outResources) = 0;
};
@@ -79,12 +169,13 @@ protected:
virtual ~PointerController();
public:
enum InactivityFadeDelay {
INACTIVITY_FADE_DELAY_NORMAL = 0,
INACTIVITY_FADE_DELAY_SHORT = 1,
enum InactivityTimeout {
INACTIVITY_TIMEOUT_NORMAL = 0,
INACTIVITY_TIMEOUT_SHORT = 1,
};
PointerController(const sp<Looper>& looper, const sp<SpriteController>& spriteController);
PointerController(const sp<PointerControllerPolicyInterface>& policy,
const sp<Looper>& looper, const sp<SpriteController>& spriteController);
virtual bool getBounds(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const;
@@ -96,51 +187,101 @@ public:
virtual void fade();
virtual void unfade();
virtual void setPresentation(Presentation presentation);
virtual void setSpots(SpotGesture spotGesture,
const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
virtual void clearSpots();
void setDisplaySize(int32_t width, int32_t height);
void setDisplayOrientation(int32_t orientation);
void setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY);
void setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay);
void setPointerIcon(const SpriteIcon& icon);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
private:
static const size_t MAX_RECYCLED_SPRITES = 12;
static const size_t MAX_SPOTS = 12;
enum {
MSG_FADE_STEP = 0,
MSG_ANIMATE,
MSG_INACTIVITY_TIMEOUT,
};
struct Spot {
static const uint32_t INVALID_ID = 0xffffffff;
uint32_t id;
sp<Sprite> sprite;
float alpha;
float scale;
float x, y;
inline Spot(uint32_t id, const sp<Sprite>& sprite)
: id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
x(0.0f), y(0.0f), lastIcon(NULL) { }
void updateSprite(const SpriteIcon* icon, float x, float y);
private:
const SpriteIcon* lastIcon;
};
mutable Mutex mLock;
sp<PointerControllerPolicyInterface> mPolicy;
sp<Looper> mLooper;
sp<SpriteController> mSpriteController;
sp<WeakMessageHandler> mHandler;
PointerResources mResources;
struct Locked {
bool animationPending;
nsecs_t animationTime;
int32_t displayWidth;
int32_t displayHeight;
int32_t displayOrientation;
InactivityTimeout inactivityTimeout;
Presentation presentation;
bool presentationChanged;
bool pointerIsFading;
float pointerX;
float pointerY;
float pointerAlpha;
sp<Sprite> pointerSprite;
SpriteIcon pointerIcon;
bool pointerIconChanged;
uint32_t buttonState;
float fadeAlpha;
InactivityFadeDelay inactivityFadeDelay;
bool visible;
sp<Sprite> sprite;
Vector<Spot*> spots;
Vector<sp<Sprite> > recycledSprites;
} mLocked;
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
void updateLocked();
void handleMessage(const Message& message);
bool unfadeBeforeUpdateLocked();
void startFadeLocked();
void startInactivityFadeDelayLocked();
void fadeStepLocked();
bool isFadingLocked();
nsecs_t getInactivityFadeDelayTimeLocked();
void sendFadeStepMessageDelayedLocked(nsecs_t delayTime);
void doAnimate();
void doInactivityTimeout();
void startAnimationLocked();
void resetInactivityTimeoutLocked();
void sendImmediateInactivityTimeoutLocked();
void updatePointerLocked();
Spot* getSpotLocked(uint32_t id);
Spot* createAndAddSpotLocked(uint32_t id);
Spot* removeFirstFadingSpotLocked();
void releaseSpotLocked(Spot* spot);
void fadeOutAndReleaseSpotLocked(Spot* spot);
void fadeOutAndReleaseAllSpotsLocked();
void loadResources();
};
} // namespace android

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _UI_SPOT_CONTROLLER_H
#define _UI_SPOT_CONTROLLER_H
#include "SpriteController.h"
#include <utils/RefBase.h>
#include <utils/Looper.h>
#include <SkBitmap.h>
namespace android {
/*
* Interface for displaying spots on screen that visually represent the positions
* of fingers on a touch pad.
*
* The spot controller is responsible for providing synchronization and for tracking
* display orientation changes if needed.
*/
class SpotControllerInterface : public virtual RefBase {
protected:
SpotControllerInterface() { }
virtual ~SpotControllerInterface() { }
public:
};
/*
* Sprite-based spot controller implementation.
*/
class SpotController : public SpotControllerInterface, public MessageHandler {
protected:
virtual ~SpotController();
public:
SpotController(const sp<Looper>& looper, const sp<SpriteController>& spriteController);
private:
mutable Mutex mLock;
sp<Looper> mLooper;
sp<SpriteController> mSpriteController;
sp<WeakMessageHandler> mHandler;
struct Locked {
} mLocked;
void handleMessage(const Message& message);
};
} // namespace android
#endif // _UI_SPOT_CONTROLLER_H

View File

@@ -36,6 +36,9 @@ namespace android {
SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLayer) :
mLooper(looper), mOverlayLayer(overlayLayer) {
mHandler = new WeakMessageHandler(this);
mLocked.transactionNestingCount = 0;
mLocked.deferredSpriteUpdate = false;
}
SpriteController::~SpriteController() {
@@ -51,17 +54,40 @@ sp<Sprite> SpriteController::createSprite() {
return new SpriteImpl(this);
}
void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
bool wasEmpty = mInvalidatedSprites.isEmpty();
mInvalidatedSprites.push(sprite);
if (wasEmpty) {
void SpriteController::openTransaction() {
AutoMutex _l(mLock);
mLocked.transactionNestingCount += 1;
}
void SpriteController::closeTransaction() {
AutoMutex _l(mLock);
LOG_ALWAYS_FATAL_IF(mLocked.transactionNestingCount == 0,
"Sprite closeTransaction() called but there is no open sprite transaction");
mLocked.transactionNestingCount -= 1;
if (mLocked.transactionNestingCount == 0 && mLocked.deferredSpriteUpdate) {
mLocked.deferredSpriteUpdate = false;
mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
}
}
void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
bool wasEmpty = mLocked.invalidatedSprites.isEmpty();
mLocked.invalidatedSprites.push(sprite);
if (wasEmpty) {
if (mLocked.transactionNestingCount != 0) {
mLocked.deferredSpriteUpdate = true;
} else {
mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
}
}
}
void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl) {
bool wasEmpty = mDisposedSurfaces.isEmpty();
mDisposedSurfaces.push(surfaceControl);
bool wasEmpty = mLocked.disposedSurfaces.isEmpty();
mLocked.disposedSurfaces.push(surfaceControl);
if (wasEmpty) {
mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES));
}
@@ -89,14 +115,14 @@ void SpriteController::doUpdateSprites() {
{ // acquire lock
AutoMutex _l(mLock);
numSprites = mInvalidatedSprites.size();
numSprites = mLocked.invalidatedSprites.size();
for (size_t i = 0; i < numSprites; i++) {
const sp<SpriteImpl>& sprite = mInvalidatedSprites.itemAt(i);
const sp<SpriteImpl>& sprite = mLocked.invalidatedSprites.itemAt(i);
updates.push(SpriteUpdate(sprite, sprite->getStateLocked()));
sprite->resetDirtyLocked();
}
mInvalidatedSprites.clear();
mLocked.invalidatedSprites.clear();
} // release lock
// Create missing surfaces.
@@ -105,8 +131,8 @@ void SpriteController::doUpdateSprites() {
SpriteUpdate& update = updates.editItemAt(i);
if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
update.state.surfaceWidth = update.state.bitmap.width();
update.state.surfaceHeight = update.state.bitmap.height();
update.state.surfaceWidth = update.state.icon.bitmap.width();
update.state.surfaceHeight = update.state.icon.bitmap.height();
update.state.surfaceDrawn = false;
update.state.surfaceVisible = false;
update.state.surfaceControl = obtainSurface(
@@ -123,8 +149,8 @@ void SpriteController::doUpdateSprites() {
SpriteUpdate& update = updates.editItemAt(i);
if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
int32_t desiredWidth = update.state.bitmap.width();
int32_t desiredHeight = update.state.bitmap.height();
int32_t desiredWidth = update.state.icon.bitmap.width();
int32_t desiredHeight = update.state.icon.bitmap.height();
if (update.state.surfaceWidth < desiredWidth
|| update.state.surfaceHeight < desiredHeight) {
if (!haveGlobalTransaction) {
@@ -187,16 +213,16 @@ void SpriteController::doUpdateSprites() {
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
surfaceCanvas.drawBitmap(update.state.bitmap, 0, 0, &paint);
surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
if (surfaceInfo.w > uint32_t(update.state.bitmap.width())) {
if (surfaceInfo.w > uint32_t(update.state.icon.bitmap.width())) {
paint.setColor(0); // transparent fill color
surfaceCanvas.drawRectCoords(update.state.bitmap.width(), 0,
surfaceInfo.w, update.state.bitmap.height(), paint);
surfaceCanvas.drawRectCoords(update.state.icon.bitmap.width(), 0,
surfaceInfo.w, update.state.icon.bitmap.height(), paint);
}
if (surfaceInfo.h > uint32_t(update.state.bitmap.height())) {
if (surfaceInfo.h > uint32_t(update.state.icon.bitmap.height())) {
paint.setColor(0); // transparent fill color
surfaceCanvas.drawRectCoords(0, update.state.bitmap.height(),
surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(),
surfaceInfo.w, surfaceInfo.h, paint);
}
@@ -246,8 +272,8 @@ void SpriteController::doUpdateSprites() {
&& (becomingVisible || (update.state.dirty & (DIRTY_POSITION
| DIRTY_HOTSPOT)))) {
status = update.state.surfaceControl->setPosition(
update.state.positionX - update.state.hotSpotX,
update.state.positionY - update.state.hotSpotY);
update.state.positionX - update.state.icon.hotSpotX,
update.state.positionY - update.state.icon.hotSpotY);
if (status) {
LOGE("Error %d setting sprite surface position.", status);
}
@@ -329,8 +355,10 @@ void SpriteController::doDisposeSurfaces() {
// Collect disposed surfaces.
Vector<sp<SurfaceControl> > disposedSurfaces;
{ // acquire lock
disposedSurfaces = mDisposedSurfaces;
mDisposedSurfaces.clear();
AutoMutex _l(mLock);
disposedSurfaces = mLocked.disposedSurfaces;
mLocked.disposedSurfaces.clear();
} // release lock
// Release the last reference to each surface outside of the lock.
@@ -349,7 +377,8 @@ sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height
sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
getpid(), String8("Sprite"), 0, width, height, PIXEL_FORMAT_RGBA_8888);
if (surfaceControl == NULL) {
if (surfaceControl == NULL || !surfaceControl->isValid()
|| !surfaceControl->getSurface()->isValid()) {
LOGE("Error creating sprite surface.");
return NULL;
}
@@ -360,7 +389,7 @@ sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height
// --- SpriteController::SpriteImpl ---
SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) :
mController(controller), mTransactionNestingCount(0) {
mController(controller) {
}
SpriteController::SpriteImpl::~SpriteImpl() {
@@ -368,27 +397,33 @@ SpriteController::SpriteImpl::~SpriteImpl() {
// Let the controller take care of deleting the last reference to sprite
// surfaces so that we do not block the caller on an IPC here.
if (mState.surfaceControl != NULL) {
mController->disposeSurfaceLocked(mState.surfaceControl);
mState.surfaceControl.clear();
if (mLocked.state.surfaceControl != NULL) {
mController->disposeSurfaceLocked(mLocked.state.surfaceControl);
mLocked.state.surfaceControl.clear();
}
}
void SpriteController::SpriteImpl::setBitmap(const SkBitmap* bitmap,
float hotSpotX, float hotSpotY) {
void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
AutoMutex _l(mController->mLock);
if (bitmap) {
bitmap->copyTo(&mState.bitmap, SkBitmap::kARGB_8888_Config);
} else {
mState.bitmap.reset();
}
uint32_t dirty;
if (icon.isValid()) {
icon.bitmap.copyTo(&mLocked.state.icon.bitmap, SkBitmap::kARGB_8888_Config);
uint32_t dirty = DIRTY_BITMAP;
if (mState.hotSpotX != hotSpotX || mState.hotSpotY != hotSpotY) {
mState.hotSpotX = hotSpotX;
mState.hotSpotY = hotSpotY;
dirty |= DIRTY_HOTSPOT;
if (!mLocked.state.icon.isValid()
|| mLocked.state.icon.hotSpotX != icon.hotSpotX
|| mLocked.state.icon.hotSpotY != icon.hotSpotY) {
mLocked.state.icon.hotSpotX = icon.hotSpotX;
mLocked.state.icon.hotSpotY = icon.hotSpotY;
dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
} else {
dirty = DIRTY_BITMAP;
}
} else if (mLocked.state.icon.isValid()) {
mLocked.state.icon.bitmap.reset();
dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
} else {
return; // setting to invalid icon and already invalid so nothing to do
}
invalidateLocked(dirty);
@@ -397,8 +432,8 @@ void SpriteController::SpriteImpl::setBitmap(const SkBitmap* bitmap,
void SpriteController::SpriteImpl::setVisible(bool visible) {
AutoMutex _l(mController->mLock);
if (mState.visible != visible) {
mState.visible = visible;
if (mLocked.state.visible != visible) {
mLocked.state.visible = visible;
invalidateLocked(DIRTY_VISIBILITY);
}
}
@@ -406,9 +441,9 @@ void SpriteController::SpriteImpl::setVisible(bool visible) {
void SpriteController::SpriteImpl::setPosition(float x, float y) {
AutoMutex _l(mController->mLock);
if (mState.positionX != x || mState.positionY != y) {
mState.positionX = x;
mState.positionY = y;
if (mLocked.state.positionX != x || mLocked.state.positionY != y) {
mLocked.state.positionX = x;
mLocked.state.positionY = y;
invalidateLocked(DIRTY_POSITION);
}
}
@@ -416,8 +451,8 @@ void SpriteController::SpriteImpl::setPosition(float x, float y) {
void SpriteController::SpriteImpl::setLayer(int32_t layer) {
AutoMutex _l(mController->mLock);
if (mState.layer != layer) {
mState.layer = layer;
if (mLocked.state.layer != layer) {
mLocked.state.layer = layer;
invalidateLocked(DIRTY_LAYER);
}
}
@@ -425,8 +460,8 @@ void SpriteController::SpriteImpl::setLayer(int32_t layer) {
void SpriteController::SpriteImpl::setAlpha(float alpha) {
AutoMutex _l(mController->mLock);
if (mState.alpha != alpha) {
mState.alpha = alpha;
if (mLocked.state.alpha != alpha) {
mLocked.state.alpha = alpha;
invalidateLocked(DIRTY_ALPHA);
}
}
@@ -435,38 +470,19 @@ void SpriteController::SpriteImpl::setTransformationMatrix(
const SpriteTransformationMatrix& matrix) {
AutoMutex _l(mController->mLock);
if (mState.transformationMatrix != matrix) {
mState.transformationMatrix = matrix;
if (mLocked.state.transformationMatrix != matrix) {
mLocked.state.transformationMatrix = matrix;
invalidateLocked(DIRTY_TRANSFORMATION_MATRIX);
}
}
void SpriteController::SpriteImpl::openTransaction() {
AutoMutex _l(mController->mLock);
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
bool wasDirty = mLocked.state.dirty;
mLocked.state.dirty |= dirty;
mTransactionNestingCount += 1;
}
void SpriteController::SpriteImpl::closeTransaction() {
AutoMutex _l(mController->mLock);
LOG_ALWAYS_FATAL_IF(mTransactionNestingCount == 0,
"Sprite closeTransaction() called but there is no open sprite transaction");
mTransactionNestingCount -= 1;
if (mTransactionNestingCount == 0 && mState.dirty) {
if (!wasDirty) {
mController->invalidateSpriteLocked(this);
}
}
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
if (mTransactionNestingCount > 0) {
bool wasDirty = mState.dirty;
mState.dirty |= dirty;
if (!wasDirty) {
mController->invalidateSpriteLocked(this);
}
}
}
} // namespace android

View File

@@ -33,6 +33,8 @@ namespace android {
*/
struct SpriteTransformationMatrix {
inline SpriteTransformationMatrix() : dsdx(1.0f), dtdx(0.0f), dsdy(0.0f), dtdy(1.0f) { }
inline SpriteTransformationMatrix(float dsdx, float dtdx, float dsdy, float dtdy) :
dsdx(dsdx), dtdx(dtdx), dsdy(dsdy), dtdy(dtdy) { }
float dsdx;
float dtdx;
@@ -51,6 +53,35 @@ struct SpriteTransformationMatrix {
}
};
/*
* Icon that a sprite displays, including its hotspot.
*/
struct SpriteIcon {
inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
SkBitmap bitmap;
float hotSpotX;
float hotSpotY;
inline SpriteIcon copy() const {
SkBitmap bitmapCopy;
bitmap.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config);
return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
}
inline void reset() {
bitmap.reset();
hotSpotX = 0;
hotSpotY = 0;
}
inline bool isValid() const {
return !bitmap.isNull() && !bitmap.empty();
}
};
/*
* A sprite is a simple graphical object that is displayed on-screen above other layers.
* The basic sprite class is an interface.
@@ -62,9 +93,21 @@ protected:
virtual ~Sprite() { }
public:
enum {
// The base layer for pointer sprites.
BASE_LAYER_POINTER = 0, // reserve space for 1 pointer
// The base layer for spot sprites.
BASE_LAYER_SPOT = 1, // reserve space for MAX_POINTER_ID spots
};
/* Sets the bitmap that is drawn by the sprite.
* The sprite retains a copy of the bitmap for subsequent rendering. */
virtual void setBitmap(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) = 0;
virtual void setIcon(const SpriteIcon& icon) = 0;
inline void clearIcon() {
setIcon(SpriteIcon());
}
/* Sets whether the sprite is visible. */
virtual void setVisible(bool visible) = 0;
@@ -81,14 +124,6 @@ public:
/* Sets the sprite transformation matrix. */
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
/* Opens or closes a transaction to perform a batch of sprite updates as part of
* a single operation such as setPosition and setAlpha. It is not necessary to
* open a transaction when updating a single property.
* Calls to openTransaction() nest and must be matched by an equal number
* of calls to closeTransaction(). */
virtual void openTransaction() = 0;
virtual void closeTransaction() = 0;
};
/*
@@ -112,6 +147,14 @@ public:
/* Creates a new sprite, initially invisible. */
sp<Sprite> createSprite();
/* Opens or closes a transaction to perform a batch of sprite updates as part of
* a single operation such as setPosition and setAlpha. It is not necessary to
* open a transaction when updating a single property.
* Calls to openTransaction() nest and must be matched by an equal number
* of calls to closeTransaction(). */
void openTransaction();
void closeTransaction();
private:
enum {
MSG_UPDATE_SPRITES,
@@ -135,16 +178,14 @@ private:
* Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */
struct SpriteState {
inline SpriteState() :
dirty(0), hotSpotX(0), hotSpotY(0), visible(false),
dirty(0), visible(false),
positionX(0), positionY(0), layer(0), alpha(1.0f),
surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
}
uint32_t dirty;
SkBitmap bitmap;
float hotSpotX;
float hotSpotY;
SpriteIcon icon;
bool visible;
float positionX;
float positionY;
@@ -159,7 +200,7 @@ private:
bool surfaceVisible;
inline bool wantSurfaceVisible() const {
return visible && alpha > 0.0f && !bitmap.isNull() && !bitmap.empty();
return visible && alpha > 0.0f && icon.isValid();
}
};
@@ -177,37 +218,36 @@ private:
public:
SpriteImpl(const sp<SpriteController> controller);
virtual void setBitmap(const SkBitmap* bitmap, float hotSpotX, float hotSpotY);
virtual void setIcon(const SpriteIcon& icon);
virtual void setVisible(bool visible);
virtual void setPosition(float x, float y);
virtual void setLayer(int32_t layer);
virtual void setAlpha(float alpha);
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
virtual void openTransaction();
virtual void closeTransaction();
inline const SpriteState& getStateLocked() const {
return mState;
return mLocked.state;
}
inline void resetDirtyLocked() {
mState.dirty = 0;
mLocked.state.dirty = 0;
}
inline void setSurfaceLocked(const sp<SurfaceControl>& surfaceControl,
int32_t width, int32_t height, bool drawn, bool visible) {
mState.surfaceControl = surfaceControl;
mState.surfaceWidth = width;
mState.surfaceHeight = height;
mState.surfaceDrawn = drawn;
mState.surfaceVisible = visible;
mLocked.state.surfaceControl = surfaceControl;
mLocked.state.surfaceWidth = width;
mLocked.state.surfaceHeight = height;
mLocked.state.surfaceDrawn = drawn;
mLocked.state.surfaceVisible = visible;
}
private:
sp<SpriteController> mController;
SpriteState mState; // guarded by mController->mLock
uint32_t mTransactionNestingCount; // guarded by mController->mLock
struct Locked {
SpriteState state;
} mLocked; // guarded by mController->mLock
void invalidateLocked(uint32_t dirty);
};
@@ -232,8 +272,12 @@ private:
sp<SurfaceComposerClient> mSurfaceComposerClient;
Vector<sp<SpriteImpl> > mInvalidatedSprites; // guarded by mLock
Vector<sp<SurfaceControl> > mDisposedSurfaces; // guarded by mLock
struct Locked {
Vector<sp<SpriteImpl> > invalidatedSprites;
Vector<sp<SurfaceControl> > disposedSurfaces;
uint32_t transactionNestingCount;
bool deferredSpriteUpdate;
} mLocked; // guarded by mLock
void invalidateSpriteLocked(const sp<SpriteImpl>& sprite);
void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl);

View File

@@ -97,6 +97,16 @@ private:
virtual void unfade() {
}
virtual void setPresentation(Presentation presentation) {
}
virtual void setSpots(SpotGesture spotGesture,
const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
}
virtual void clearSpots() {
}
};
@@ -192,10 +202,6 @@ private:
virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
return mPointerControllers.valueFor(deviceId);
}
virtual sp<SpotControllerInterface> obtainSpotController(int32_t device) {
return NULL;
}
};

View File

@@ -23,12 +23,6 @@ import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.os.Looper;
import android.os.MessageQueue;
@@ -39,6 +33,7 @@ import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.PointerIcon;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
@@ -62,7 +57,8 @@ public class InputManager {
private final Context mContext;
private final WindowManagerService mWindowManagerService;
private static native void nativeInit(Callbacks callbacks, MessageQueue messageQueue);
private static native void nativeInit(Context context,
Callbacks callbacks, MessageQueue messageQueue);
private static native void nativeStart();
private static native void nativeSetDisplaySize(int displayId, int width, int height);
private static native void nativeSetDisplayOrientation(int displayId, int rotation);
@@ -125,7 +121,7 @@ public class InputManager {
Looper looper = windowManagerService.mH.getLooper();
Slog.i(TAG, "Initializing input manager");
nativeInit(mCallbacks, looper.getQueue());
nativeInit(mContext, mCallbacks, looper.getQueue());
}
public void start() {
@@ -370,48 +366,6 @@ public class InputManager {
}
}
private static final class PointerIcon {
public Bitmap bitmap;
public float hotSpotX;
public float hotSpotY;
public static PointerIcon load(Resources resources, int resourceId) {
PointerIcon icon = new PointerIcon();
XmlResourceParser parser = resources.getXml(resourceId);
final int bitmapRes;
try {
XmlUtils.beginDocument(parser, "pointer-icon");
TypedArray a = resources.obtainAttributes(
parser, com.android.internal.R.styleable.PointerIcon);
bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
icon.hotSpotX = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
icon.hotSpotY = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
a.recycle();
} catch (Exception ex) {
Slog.e(TAG, "Exception parsing pointer icon resource.", ex);
return null;
} finally {
parser.close();
}
if (bitmapRes == 0) {
Slog.e(TAG, "<pointer-icon> is missing bitmap attribute");
return null;
}
Drawable drawable = resources.getDrawable(bitmapRes);
if (!(drawable instanceof BitmapDrawable)) {
Slog.e(TAG, "<pointer-icon> bitmap attribute must refer to a bitmap drawable");
return null;
}
icon.bitmap = ((BitmapDrawable)drawable).getBitmap();
return icon;
}
}
/*
* Callbacks from native.
*/
@@ -564,8 +518,7 @@ public class InputManager {
@SuppressWarnings("unused")
public PointerIcon getPointerIcon() {
return PointerIcon.load(mContext.getResources(),
com.android.internal.R.drawable.pointer_arrow_icon);
return PointerIcon.getDefaultIcon(mContext);
}
}
}

View File

@@ -36,13 +36,13 @@
#include <input/InputManager.h>
#include <input/PointerController.h>
#include <input/SpotController.h>
#include <input/SpriteController.h>
#include <android_os_MessageQueue.h>
#include <android_view_KeyEvent.h>
#include <android_view_MotionEvent.h>
#include <android_view_InputChannel.h>
#include <android_view_PointerIcon.h>
#include <android/graphics/GraphicsJNI.h>
#include "com_android_server_PowerManagerService.h"
@@ -104,14 +104,6 @@ static struct {
jfieldID navigation;
} gConfigurationClassInfo;
static struct {
jclass clazz;
jfieldID bitmap;
jfieldID hotSpotX;
jfieldID hotSpotY;
} gPointerIconClassInfo;
// --- Global functions ---
@@ -133,17 +125,30 @@ static jobject getInputWindowHandleObjLocalRef(JNIEnv* env,
getInputWindowHandleObjLocalRef(env);
}
static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t style,
SpriteIcon* outSpriteIcon) {
PointerIcon pointerIcon;
status_t status = android_view_PointerIcon_loadSystemIcon(env,
contextObj, style, &pointerIcon);
if (!status) {
pointerIcon.bitmap.copyTo(&outSpriteIcon->bitmap, SkBitmap::kARGB_8888_Config);
outSpriteIcon->hotSpotX = pointerIcon.hotSpotX;
outSpriteIcon->hotSpotY = pointerIcon.hotSpotY;
}
}
// --- NativeInputManager ---
class NativeInputManager : public virtual RefBase,
public virtual InputReaderPolicyInterface,
public virtual InputDispatcherPolicyInterface {
public virtual InputDispatcherPolicyInterface,
public virtual PointerControllerPolicyInterface {
protected:
virtual ~NativeInputManager();
public:
NativeInputManager(jobject callbacksObj, const sp<Looper>& looper);
NativeInputManager(jobject contextObj, jobject callbacksObj, const sp<Looper>& looper);
inline sp<InputManager> getInputManager() const { return mInputManager; }
@@ -170,7 +175,6 @@ public:
virtual nsecs_t getVirtualKeyQuietTime();
virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
virtual sp<SpotControllerInterface> obtainSpotController(int32_t deviceId);
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -193,9 +197,14 @@ public:
virtual bool checkInjectEventsPermissionNonReentrant(
int32_t injectorPid, int32_t injectorUid);
/* --- PointerControllerPolicyInterface implementation --- */
virtual void loadPointerResources(PointerResources* outResources);
private:
sp<InputManager> mInputManager;
jobject mContextObj;
jobject mCallbacksObj;
sp<Looper> mLooper;
@@ -227,7 +236,7 @@ private:
wp<PointerController> pointerController;
} mLocked;
void updateInactivityFadeDelayLocked(const sp<PointerController>& controller);
void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
void ensureSpriteControllerLocked();
@@ -244,13 +253,15 @@ private:
NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& looper) :
NativeInputManager::NativeInputManager(jobject contextObj,
jobject callbacksObj, const sp<Looper>& looper) :
mLooper(looper),
mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),
mKeyRepeatTimeout(-1), mKeyRepeatDelay(-1),
mMaxEventsPerSecond(-1) {
JNIEnv* env = jniEnv();
mContextObj = env->NewGlobalRef(contextObj);
mCallbacksObj = env->NewGlobalRef(callbacksObj);
{
@@ -269,6 +280,7 @@ NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& l
NativeInputManager::~NativeInputManager() {
JNIEnv* env = jniEnv();
env->DeleteGlobalRef(mContextObj);
env->DeleteGlobalRef(mCallbacksObj);
}
@@ -292,9 +304,13 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c
void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_t height) {
if (displayId == 0) {
AutoMutex _l(mLock);
{ // acquire lock
AutoMutex _l(mLock);
if (mLocked.displayWidth == width && mLocked.displayHeight == height) {
return;
}
if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
mLocked.displayWidth = width;
mLocked.displayHeight = height;
@@ -302,7 +318,7 @@ void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_
if (controller != NULL) {
controller->setDisplaySize(width, height);
}
}
} // release lock
}
}
@@ -432,40 +448,33 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32
if (controller == NULL) {
ensureSpriteControllerLocked();
controller = new PointerController(mLooper, mLocked.spriteController);
controller = new PointerController(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight);
controller->setDisplayOrientation(mLocked.displayOrientation);
JNIEnv* env = jniEnv();
jobject iconObj = env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getPointerIcon);
if (!checkAndClearExceptionFromCallback(env, "getPointerIcon") && iconObj) {
jfloat iconHotSpotX = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotX);
jfloat iconHotSpotY = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotY);
jobject iconBitmapObj = env->GetObjectField(iconObj, gPointerIconClassInfo.bitmap);
if (iconBitmapObj) {
SkBitmap* iconBitmap = GraphicsJNI::getNativeBitmap(env, iconBitmapObj);
if (iconBitmap) {
controller->setPointerIcon(iconBitmap, iconHotSpotX, iconHotSpotY);
}
env->DeleteLocalRef(iconBitmapObj);
jobject pointerIconObj = env->CallObjectMethod(mCallbacksObj,
gCallbacksClassInfo.getPointerIcon);
if (!checkAndClearExceptionFromCallback(env, "getPointerIcon")) {
PointerIcon pointerIcon;
status_t status = android_view_PointerIcon_load(env, pointerIconObj,
mContextObj, &pointerIcon);
if (!status && !pointerIcon.isNullIcon()) {
controller->setPointerIcon(SpriteIcon(pointerIcon.bitmap,
pointerIcon.hotSpotX, pointerIcon.hotSpotY));
} else {
controller->setPointerIcon(SpriteIcon());
}
env->DeleteLocalRef(iconObj);
env->DeleteLocalRef(pointerIconObj);
}
updateInactivityFadeDelayLocked(controller);
updateInactivityTimeoutLocked(controller);
}
return controller;
}
sp<SpotControllerInterface> NativeInputManager::obtainSpotController(int32_t deviceId) {
AutoMutex _l(mLock);
ensureSpriteControllerLocked();
return new SpotController(mLooper, mLocked.spriteController);
}
void NativeInputManager::ensureSpriteControllerLocked() {
if (mLocked.spriteController == NULL) {
JNIEnv* env = jniEnv();
@@ -646,16 +655,16 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) {
sp<PointerController> controller = mLocked.pointerController.promote();
if (controller != NULL) {
updateInactivityFadeDelayLocked(controller);
updateInactivityTimeoutLocked(controller);
}
}
}
void NativeInputManager::updateInactivityFadeDelayLocked(const sp<PointerController>& controller) {
void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller) {
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
controller->setInactivityFadeDelay(lightsOut
? PointerController::INACTIVITY_FADE_DELAY_SHORT
: PointerController::INACTIVITY_FADE_DELAY_NORMAL);
controller->setInactivityTimeout(lightsOut
? PointerController::INACTIVITY_TIMEOUT_SHORT
: PointerController::INACTIVITY_TIMEOUT_NORMAL);
}
bool NativeInputManager::isScreenOn() {
@@ -856,6 +865,17 @@ bool NativeInputManager::checkInjectEventsPermissionNonReentrant(
return result;
}
void NativeInputManager::loadPointerResources(PointerResources* outResources) {
JNIEnv* env = jniEnv();
loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_HOVER,
&outResources->spotHover);
loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_TOUCH,
&outResources->spotTouch);
loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_ANCHOR,
&outResources->spotAnchor);
}
// ----------------------------------------------------------------------------
@@ -871,10 +891,10 @@ static bool checkInputManagerUnitialized(JNIEnv* env) {
}
static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
jobject callbacks, jobject messageQueueObj) {
jobject contextObj, jobject callbacksObj, jobject messageQueueObj) {
if (gNativeInputManager == NULL) {
sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
gNativeInputManager = new NativeInputManager(callbacks, looper);
gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper);
} else {
LOGE("Input manager already initialized.");
jniThrowRuntimeException(env, "Input manager already initialized.");
@@ -1207,7 +1227,8 @@ static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz)
static JNINativeMethod gInputManagerMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",
{ "nativeInit", "(Landroid/content/Context;"
"Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",
(void*) android_server_InputManager_nativeInit },
{ "nativeStart", "()V",
(void*) android_server_InputManager_nativeStart },
@@ -1328,7 +1349,7 @@ int register_android_server_InputManager(JNIEnv* env) {
"getPointerLayer", "()I");
GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, gCallbacksClassInfo.clazz,
"getPointerIcon", "()Lcom/android/server/wm/InputManager$PointerIcon;");
"getPointerIcon", "()Landroid/view/PointerIcon;");
// KeyEvent
@@ -1373,19 +1394,6 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz,
"navigation", "I");
// PointerIcon
FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/wm/InputManager$PointerIcon");
GET_FIELD_ID(gPointerIconClassInfo.bitmap, gPointerIconClassInfo.clazz,
"bitmap", "Landroid/graphics/Bitmap;");
GET_FIELD_ID(gPointerIconClassInfo.hotSpotX, gPointerIconClassInfo.clazz,
"hotSpotX", "F");
GET_FIELD_ID(gPointerIconClassInfo.hotSpotY, gPointerIconClassInfo.clazz,
"hotSpotY", "F");
return 0;
}