Merge "Import translations. DO NOT MERGE"
diff --git a/api/current.txt b/api/current.txt
index 9d87336..2282a33 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -825,6 +825,7 @@
field public static final int persistent = 16842765; // 0x101000d
field public static final int persistentDrawingCache = 16842990; // 0x10100ee
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
+ field public static final int pinned = 16843776; // 0x1010400
field public static final int pivotX = 16843189; // 0x10101b5
field public static final int pivotY = 16843190; // 0x10101b6
field public static final int popupAnimationStyle = 16843465; // 0x10102c9
@@ -2196,6 +2197,8 @@
field public static final int Widget_Quantum_Button_Borderless = 16974393; // 0x1030239
field public static final int Widget_Quantum_Button_Borderless_Small = 16974394; // 0x103023a
field public static final int Widget_Quantum_Button_Inset = 16974395; // 0x103023b
+ field public static final int Widget_Quantum_Button_Paper = 16974503; // 0x10302a7
+ field public static final int Widget_Quantum_Button_Paper_Color = 16974504; // 0x10302a8
field public static final int Widget_Quantum_Button_Small = 16974396; // 0x103023c
field public static final int Widget_Quantum_Button_Toggle = 16974397; // 0x103023d
field public static final int Widget_Quantum_CalendarView = 16974398; // 0x103023e
@@ -2232,6 +2235,8 @@
field public static final int Widget_Quantum_Light_Button = 16974452; // 0x1030274
field public static final int Widget_Quantum_Light_Button_Borderless_Small = 16974453; // 0x1030275
field public static final int Widget_Quantum_Light_Button_Inset = 16974454; // 0x1030276
+ field public static final int Widget_Quantum_Light_Button_Paper = 16974505; // 0x10302a9
+ field public static final int Widget_Quantum_Light_Button_Paper_Color = 16974506; // 0x10302aa
field public static final int Widget_Quantum_Light_Button_Small = 16974455; // 0x1030277
field public static final int Widget_Quantum_Light_Button_Toggle = 16974456; // 0x1030278
field public static final int Widget_Quantum_Light_CalendarView = 16974457; // 0x1030279
@@ -13422,6 +13427,7 @@
public static final class MediaMuxer.OutputFormat {
field public static final int MUXER_OUTPUT_MPEG_4 = 0; // 0x0
+ field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
}
public class MediaPlayer {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 9d0ab3a..02c850b 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -987,7 +987,7 @@
* Implement this to handle requests to delete one or more rows.
* The implementation should apply the selection clause when performing
* deletion, allowing the operation to affect multiple rows in a directory.
- * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()}
+ * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after deleting.
* This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 8c23129..5f2b5f0 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1359,7 +1359,7 @@
float q2 = rotationVector[1];
float q3 = rotationVector[2];
- if (rotationVector.length == 4) {
+ if (rotationVector.length >= 4) {
q0 = rotationVector[3];
} else {
q0 = 1 - q1*q1 - q2*q2 - q3*q3;
@@ -1416,7 +1416,7 @@
* @param Q an array of floats in which to store the computed quaternion
*/
public static void getQuaternionFromVector(float[] Q, float[] rv) {
- if (rv.length == 4) {
+ if (rv.length >= 4) {
Q[0] = rv[3];
} else {
Q[0] = 1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2];
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index f1e7e98..465d142 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -19,6 +19,7 @@
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.IInputDevicesChangedListener;
+import android.hardware.input.TouchCalibration;
import android.os.IBinder;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -39,6 +40,11 @@
// applications, the caller must have the INJECT_EVENTS permission.
boolean injectInputEvent(in InputEvent ev, int mode);
+ // Calibrate input device position
+ TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor, int rotation);
+ void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int rotation,
+ in TouchCalibration calibration);
+
// Keyboard layouts configuration.
KeyboardLayout[] getKeyboardLayouts();
KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index a2aeafb..e3a3830 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -500,6 +500,45 @@
}
/**
+ * Gets the TouchCalibration applied to the specified input device's coordinates.
+ *
+ * @param inputDeviceDescriptor The input device descriptor.
+ * @return The TouchCalibration currently assigned for use with the given
+ * input device. If none is set, an identity TouchCalibration is returned.
+ *
+ * @hide
+ */
+ public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
+ try {
+ return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not get calibration matrix for input device.", ex);
+ return TouchCalibration.IDENTITY;
+ }
+ }
+
+ /**
+ * Sets the TouchCalibration to apply to the specified input device's coordinates.
+ * <p>
+ * This method may have the side-effect of causing the input device in question
+ * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
+ * </p>
+ *
+ * @param inputDeviceDescriptor The input device descriptor.
+ * @param calibration The calibration to be applied
+ *
+ * @hide
+ */
+ public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation,
+ TouchCalibration calibration) {
+ try {
+ mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not set calibration matrix for input device.", ex);
+ }
+ }
+
+ /**
* Gets the mouse pointer speed.
* <p>
* Only returns the permanent mouse pointer speed. Ignores any temporary pointer
diff --git a/core/java/android/hardware/input/TouchCalibration.aidl b/core/java/android/hardware/input/TouchCalibration.aidl
new file mode 100644
index 0000000..2c28774
--- /dev/null
+++ b/core/java/android/hardware/input/TouchCalibration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2014 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.hardware.input;
+
+parcelable TouchCalibration;
diff --git a/core/java/android/hardware/input/TouchCalibration.java b/core/java/android/hardware/input/TouchCalibration.java
new file mode 100644
index 0000000..025fad0
--- /dev/null
+++ b/core/java/android/hardware/input/TouchCalibration.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 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.hardware.input;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Encapsulates calibration data for input devices.
+ *
+ * @hide
+ */
+public class TouchCalibration implements Parcelable {
+
+ public static final TouchCalibration IDENTITY = new TouchCalibration();
+
+ public static final Parcelable.Creator<TouchCalibration> CREATOR
+ = new Parcelable.Creator<TouchCalibration>() {
+ public TouchCalibration createFromParcel(Parcel in) {
+ return new TouchCalibration(in);
+ }
+
+ public TouchCalibration[] newArray(int size) {
+ return new TouchCalibration[size];
+ }
+ };
+
+ private final float mXScale, mXYMix, mXOffset;
+ private final float mYXMix, mYScale, mYOffset;
+
+ /**
+ * Create a new TouchCalibration initialized to the identity transformation.
+ */
+ public TouchCalibration() {
+ this(1,0,0,0,1,0);
+ }
+
+ /**
+ * Create a new TouchCalibration from affine transformation paramters.
+ * @param xScale Influence of input x-axis value on output x-axis value.
+ * @param xyMix Influence of input y-axis value on output x-axis value.
+ * @param xOffset Constant offset to be applied to output x-axis value.
+ * @param yXMix Influence of input x-axis value on output y-axis value.
+ * @param yScale Influence of input y-axis value on output y-axis value.
+ * @param yOffset Constant offset to be applied to output y-axis value.
+ */
+ public TouchCalibration(float xScale, float xyMix, float xOffset,
+ float yxMix, float yScale, float yOffset) {
+ mXScale = xScale;
+ mXYMix = xyMix;
+ mXOffset = xOffset;
+ mYXMix = yxMix;
+ mYScale = yScale;
+ mYOffset = yOffset;
+ }
+
+ public TouchCalibration(Parcel in) {
+ mXScale = in.readFloat();
+ mXYMix = in.readFloat();
+ mXOffset = in.readFloat();
+ mYXMix = in.readFloat();
+ mYScale = in.readFloat();
+ mYOffset = in.readFloat();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeFloat(mXScale);
+ dest.writeFloat(mXYMix);
+ dest.writeFloat(mXOffset);
+ dest.writeFloat(mYXMix);
+ dest.writeFloat(mYScale);
+ dest.writeFloat(mYOffset);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public float[] getAffineTransform() {
+ return new float[] { mXScale, mXYMix, mXOffset, mYXMix, mYScale, mYOffset };
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof TouchCalibration) {
+ TouchCalibration cal = (TouchCalibration)obj;
+
+ return (cal.mXScale == mXScale) &&
+ (cal.mXYMix == mXYMix) &&
+ (cal.mXOffset == mXOffset) &&
+ (cal.mYXMix == mYXMix) &&
+ (cal.mYScale == mYScale) &&
+ (cal.mYOffset == mYOffset);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Float.floatToIntBits(mXScale) ^
+ Float.floatToIntBits(mXYMix) ^
+ Float.floatToIntBits(mXOffset) ^
+ Float.floatToIntBits(mYXMix) ^
+ Float.floatToIntBits(mYScale) ^
+ Float.floatToIntBits(mYOffset);
+ }
+}
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 9522112..0f8da44 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -191,8 +191,6 @@
for (int i = 1; i <= numGroups; i++) {
String s = matcher.group(i);
- System.err.println("Group(" + i + ") : " + s);
-
if (s != null) {
b.append(s);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 904ec44..d72f810 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10541,7 +10541,7 @@
* by the layout system and should not generally be called otherwise, because the property
* may be changed at any time by the layout.
*
- * @param left The bottom of this view, in pixels.
+ * @param left The left of this view, in pixels.
*/
public final void setLeft(int left) {
if (left != mLeft) {
@@ -10608,7 +10608,7 @@
* by the layout system and should not generally be called otherwise, because the property
* may be changed at any time by the layout.
*
- * @param right The bottom of this view, in pixels.
+ * @param right The right of this view, in pixels.
*/
public final void setRight(int right) {
if (right != mRight) {
@@ -17235,7 +17235,7 @@
} else {
long value = mMeasureCache.valueAt(cacheIndex);
// Casting a long to int drops the high 32 bits, no mask needed
- setMeasuredDimension((int) (value >> 32), (int) value);
+ setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
@@ -17330,6 +17330,22 @@
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
+ setMeasuredDimensionRaw(measuredWidth, measuredHeight);
+ }
+
+ /**
+ * Sets the measured dimension without extra processing for things like optical bounds.
+ * Useful for reapplying consistent values that have already been cooked with adjustments
+ * for optical bounds, etc. such as those from the measurement cache.
+ *
+ * @param measuredWidth The measured width of this view. May be a complex
+ * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
+ * {@link #MEASURED_STATE_TOO_SMALL}.
+ * @param measuredHeight The measured height of this view. May be a complex
+ * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
+ * {@link #MEASURED_STATE_TOO_SMALL}.
+ */
+ private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
@@ -19142,7 +19158,18 @@
}
static int adjust(int measureSpec, int delta) {
- return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec));
+ final int mode = getMode(measureSpec);
+ if (mode == UNSPECIFIED) {
+ // No need to adjust size for UNSPECIFIED mode.
+ return makeMeasureSpec(0, UNSPECIFIED);
+ }
+ int size = getSize(measureSpec) + delta;
+ if (size < 0) {
+ Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +
+ ") spec: " + toString(measureSpec) + " delta: " + delta);
+ size = 0;
+ }
+ return makeMeasureSpec(size, mode);
}
/**
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 941cdd0..438a9da 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -296,7 +296,7 @@
// The extra space for the thumb to move on the track
available += mThumbOffset * 2;
- int thumbPos = (int) (scale * available);
+ int thumbPos = (int) (scale * available + 0.5f);
int topBound, bottomBound;
if (gap == Integer.MIN_VALUE) {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index bc051ce..03a053c 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -504,6 +504,7 @@
private String mEnabledInputMethodsStrCache;
private int mCurrentUserId;
+ private int[] mRelatedUserIds = new int[0];
private static void buildEnabledInputMethodsSettingString(
StringBuilder builder, Pair<String, ArrayList<String>> pair) {
@@ -536,6 +537,22 @@
mCurrentUserId = userId;
}
+ public void setRelatedUserIds(int[] relatedUserIds) {
+ synchronized (this) {
+ mRelatedUserIds = relatedUserIds;
+ }
+ }
+
+ public boolean isRelatedToOrCurrentUser(int userId) {
+ synchronized (this) {
+ if (userId == mCurrentUserId) return true;
+ for (int i = 0; i < mRelatedUserIds.length; i++) {
+ if (userId == mRelatedUserIds[i]) return true;
+ }
+ return false;
+ }
+ }
+
public List<InputMethodInfo> getEnabledInputMethodListLocked() {
return createEnabledInputMethodListLocked(
getEnabledInputMethodsAndSubtypeListLocked());
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index e50c1d8..94c3f44 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -818,13 +818,6 @@
mOptions.add(opt);
}
- /*
- * We don't have /tmp on the device, but we often have an SD card. Apps
- * shouldn't use this, but some test suites might want to exercise it.
- */
- opt.optionString = "-Djava.io.tmpdir=/sdcard";
- mOptions.add(opt);
-
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index fc587c0..2b9a5c4 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -115,7 +115,7 @@
}
}
- int32_t* yDivs = chunk->getXDivs();
+ int32_t* yDivs = chunk->getYDivs();
for (int i = 0; i < chunk->numYDivs; i++) {
yDivs[i] = int32_t(yDivs[i] * scale + 0.5f);
if (i > 0 && yDivs[i] == yDivs[i - 1]) {
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 7e958e7..a64d3ba 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -17,10 +17,12 @@
#define LOG_TAG "AudioTrack-JNI"
-#include <jni.h>
#include <JNIHelp.h>
+#include <JniConstants.h>
#include <android_runtime/AndroidRuntime.h>
+#include "ScopedBytes.h"
+
#include <utils/Log.h>
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
@@ -503,13 +505,13 @@
}
// ----------------------------------------------------------------------------
-jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data,
- jint offsetInBytes, jint sizeInBytes) {
+jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data,
+ jint offsetInBytes, jint sizeInBytes, bool blocking = true) {
// give the data to the native AudioTrack object (the data starts at the offset)
ssize_t written = 0;
// regular write() or copy the data to the AudioTrack's shared memory?
if (track->sharedBuffer() == 0) {
- written = track->write(data + offsetInBytes, sizeInBytes);
+ written = track->write(data + offsetInBytes, sizeInBytes, blocking);
// for compatibility with earlier behavior of write(), return 0 in this case
if (written == (ssize_t) WOULD_BLOCK) {
written = 0;
@@ -563,7 +565,8 @@
static jint android_media_AudioTrack_write_byte(JNIEnv *env, jobject thiz,
jbyteArray javaAudioData,
jint offsetInBytes, jint sizeInBytes,
- jint javaAudioFormat) {
+ jint javaAudioFormat,
+ jboolean isWriteBlocking) {
//ALOGV("android_media_AudioTrack_write_byte(offset=%d, sizeInBytes=%d) called",
// offsetInBytes, sizeInBytes);
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
@@ -590,7 +593,8 @@
return 0;
}
- jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes);
+ jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes,
+ isWriteBlocking == JNI_TRUE /* blocking */);
env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0);
@@ -601,6 +605,31 @@
// ----------------------------------------------------------------------------
+static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env, jobject thiz,
+ jbyteArray javaBytes, jint byteOffset, jint offsetInBytes, jint sizeInBytes,
+ jint javaAudioFormat, jboolean isWriteBlocking) {
+ //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
+ // offsetInBytes, sizeInBytes);
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve AudioTrack pointer for write()");
+ return 0;
+ }
+
+ ScopedBytesRO bytes(env, javaBytes);
+ if (bytes.get() == NULL) {
+ ALOGE("Error retrieving source of audio data to play, can't play");
+ return AUDIOTRACK_ERROR_BAD_VALUE;
+ }
+
+ jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get() + byteOffset, offsetInBytes,
+ sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
+
+ return written;
+}
+
+// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz,
jshortArray javaAudioData,
jint offsetInShorts, jint sizeInShorts,
@@ -608,7 +637,8 @@
jint written = android_media_AudioTrack_write_byte(env, thiz,
(jbyteArray) javaAudioData,
offsetInShorts*2, sizeInShorts*2,
- javaAudioFormat);
+ javaAudioFormat,
+ JNI_TRUE /*blocking write, legacy behavior*/);
if (written > 0) {
written /= 2;
}
@@ -890,7 +920,10 @@
(void *)android_media_AudioTrack_setup},
{"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
{"native_release", "()V", (void *)android_media_AudioTrack_release},
- {"native_write_byte", "([BIII)I", (void *)android_media_AudioTrack_write_byte},
+ {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte},
+ {"native_write_native_bytes",
+ "(Ljava/lang/Object;IIIIZ)I",
+ (void *)android_media_AudioTrack_write_native_bytes},
{"native_write_short", "([SIII)I", (void *)android_media_AudioTrack_write_short},
{"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
{"native_get_native_frame_count",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4c0ddeb..b99cb90 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2069,6 +2069,14 @@
android:description="@string/permdesc_setPointerSpeed"
android:protectionLevel="signature" />
+ <!-- Allows low-level access to setting input device calibration.
+ <p>Not for use by normal applications.
+ @hide -->
+ <permission android:name="android.permission.SET_INPUT_CALIBRATION"
+ android:label="@string/permlab_setInputCalibration"
+ android:description="@string/permdesc_setInputCalibration"
+ android:protectionLevel="signature" />
+
<!-- Allows low-level access to setting the keyboard layout.
<p>Not for use by third-party applications.
@hide -->
diff --git a/core/res/res/drawable/btn_borderless_quantum_dark.xml b/core/res/res/drawable/btn_borderless_quantum_dark.xml
index e1bff4f..2c0d3b7 100644
--- a/core/res/res/drawable/btn_borderless_quantum_dark.xml
+++ b/core/res/res/drawable/btn_borderless_quantum_dark.xml
@@ -14,10 +14,6 @@
limitations under the License.
-->
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@color/transparent" />
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/btn_default_pressed_quantum_dark" />
- </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="@color/btn_default_pressed_quantum_dark"
+ android:mask="@drawable/btn_qntm_alpha" />
diff --git a/core/res/res/drawable/btn_borderless_quantum_light.xml b/core/res/res/drawable/btn_borderless_quantum_light.xml
index e7a95b1..2faec20 100644
--- a/core/res/res/drawable/btn_borderless_quantum_light.xml
+++ b/core/res/res/drawable/btn_borderless_quantum_light.xml
@@ -14,10 +14,6 @@
limitations under the License.
-->
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@color/transparent" />
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/btn_default_pressed_quantum_light" />
- </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="@color/btn_default_pressed_quantum_light"
+ android:mask="@drawable/btn_qntm_alpha" />
diff --git a/core/res/res/drawable/btn_color_quantum_dark.xml b/core/res/res/drawable/btn_color_quantum_dark.xml
index 5e44a78..0507755 100644
--- a/core/res/res/drawable/btn_color_quantum_dark.xml
+++ b/core/res/res/drawable/btn_color_quantum_dark.xml
@@ -14,21 +14,16 @@
limitations under the License.
-->
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <selector>
- <item android:state_enabled="false">
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/btn_default_normal_quantum_light" />
- </item>
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/theme_color_500" />
- </item>
- </selector>
- </item>
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/theme_color_300" />
- </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="@color/theme_color_300">
+ <selector>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/btn_qntm_alpha"
+ android:tint="@color/btn_default_normal_quantum_dark" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/btn_qntm_alpha"
+ android:tint="@color/theme_color_500" />
+ </item>
+ </selector>
+</touch-feedback>
diff --git a/core/res/res/drawable/btn_color_quantum_light.xml b/core/res/res/drawable/btn_color_quantum_light.xml
index d6be958..9166b8d 100644
--- a/core/res/res/drawable/btn_color_quantum_light.xml
+++ b/core/res/res/drawable/btn_color_quantum_light.xml
@@ -14,21 +14,16 @@
limitations under the License.
-->
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <selector>
- <item android:state_enabled="false">
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/btn_default_normal_quantum_dark" />
- </item>
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/theme_color_500" />
- </item>
- </selector>
- </item>
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/theme_color_700" />
- </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="@color/theme_color_700">
+ <selector>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/btn_qntm_alpha"
+ android:tint="@color/btn_default_normal_quantum_dark" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/btn_qntm_alpha"
+ android:tint="@color/theme_color_500" />
+ </item>
+ </selector>
+</touch-feedback>
diff --git a/core/res/res/drawable/btn_default_quantum_dark.xml b/core/res/res/drawable/btn_default_quantum_dark.xml
index 7f0cca8..29c3e24 100644
--- a/core/res/res/drawable/btn_default_quantum_dark.xml
+++ b/core/res/res/drawable/btn_default_quantum_dark.xml
@@ -14,13 +14,8 @@
limitations under the License.
-->
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/btn_default_normal_quantum_dark" />
- </item>
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/btn_default_pressed_quantum_dark" />
- </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="@color/btn_default_pressed_quantum_dark">
+ <nine-patch android:src="@drawable/btn_qntm_alpha"
+ android:tint="@color/btn_default_normal_quantum_dark" />
+</touch-feedback>
diff --git a/core/res/res/drawable/btn_default_quantum_light.xml b/core/res/res/drawable/btn_default_quantum_light.xml
index e391a80..c6e828c 100644
--- a/core/res/res/drawable/btn_default_quantum_light.xml
+++ b/core/res/res/drawable/btn_default_quantum_light.xml
@@ -14,13 +14,8 @@
limitations under the License.
-->
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/btn_default_normal_quantum_light" />
- </item>
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="@color/btn_default_pressed_quantum_light" />
- </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="@color/btn_default_pressed_quantum_light">
+ <nine-patch android:src="@drawable/btn_qntm_alpha"
+ android:tint="@color/btn_default_normal_quantum_light" />
+</touch-feedback>
diff --git a/core/res/res/drawable/item_background_borderless_quantum_dark.xml b/core/res/res/drawable/item_background_borderless_quantum_dark.xml
index 1caee4e..3f32850 100644
--- a/core/res/res/drawable/item_background_borderless_quantum_dark.xml
+++ b/core/res/res/drawable/item_background_borderless_quantum_dark.xml
@@ -15,4 +15,4 @@
-->
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/lighter_gray" />
+ android:tint="@color/lighter_gray" />
diff --git a/core/res/res/drawable/item_background_borderless_quantum_light.xml b/core/res/res/drawable/item_background_borderless_quantum_light.xml
index ecf7dfb..09f6ae9 100644
--- a/core/res/res/drawable/item_background_borderless_quantum_light.xml
+++ b/core/res/res/drawable/item_background_borderless_quantum_light.xml
@@ -15,4 +15,4 @@
-->
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/darker_gray" />
+ android:tint="@color/darker_gray" />
diff --git a/core/res/res/drawable/item_background_quantum_dark.xml b/core/res/res/drawable/item_background_quantum_dark.xml
index 5ccaa8e..3f32850 100644
--- a/core/res/res/drawable/item_background_quantum_dark.xml
+++ b/core/res/res/drawable/item_background_quantum_dark.xml
@@ -14,7 +14,5 @@
limitations under the License.
-->
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@color/transparent" />
- <item android:drawable="@color/lighter_gray" />
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="@color/lighter_gray" />
diff --git a/core/res/res/drawable/item_background_quantum_light.xml b/core/res/res/drawable/item_background_quantum_light.xml
index f1453c5..09f6ae9 100644
--- a/core/res/res/drawable/item_background_quantum_light.xml
+++ b/core/res/res/drawable/item_background_quantum_light.xml
@@ -14,7 +14,5 @@
limitations under the License.
-->
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@color/transparent" />
- <item android:drawable="@color/darker_gray" />
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="@color/darker_gray" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f85b193..2043960 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4429,6 +4429,20 @@
<attr name="color" />
</declare-styleable>
+ <!-- Drawable used to show animated touch feedback. -->
+ <declare-styleable name="TouchFeedbackDrawable">
+ <!-- The tint to use for feedback ripples. This attribute is mandatory. -->
+ <attr name="tint" />
+ <!-- Specifies the Porter-Duff blending mode used to apply the tint. The default vlaue is src_atop, which draws over the opaque parts of the drawable. -->
+ <attr name="tintMode" />
+ <!-- Whether to pin feedback ripples to the center of the drawable. Default value is false. -->
+ <attr name="pinned" format="boolean" />
+ <!-- Optional drawable used to mask ripple bounds before projection. -->
+ <attr name="mask" format="reference" />
+ <!-- Optional drawable onto which ripples are projected. -->
+ <attr name="drawable" />
+ </declare-styleable>
+
<declare-styleable name="ScaleDrawable">
<!-- Scale width, expressed as a percentage of the drawable's bound. The value's
format is XX%. For instance: 100%, 12.5%, etc.-->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3ab3e5d..c9d203f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2088,7 +2088,7 @@
<public type="style" name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" id="0x010301e4" />
<!-- ===============================================================
- Resources added in version 20 of the platform
+ Resources added in version 21 of the platform
=============================================================== -->
<eat-comment />
@@ -2106,6 +2106,7 @@
<public type="attr" name="transitionGroup" />
<public type="attr" name="castsShadow" />
<public type="attr" name="requiredForProfile"/>
+ <public type="attr" name="pinned" />
<public type="id" name="shared_element_name" />
@@ -2311,4 +2312,10 @@
<public type="style" name="Quantum.Light.ButtonBar.AlertDialog" />
<public type="style" name="Quantum.Light.ButtonBar" />
<public type="style" name="Quantum.Light.SegmentedButton" />
+
+ <public type="style" name="Widget.Quantum.Button.Paper" />
+ <public type="style" name="Widget.Quantum.Button.Paper.Color" />
+
+ <public type="style" name="Widget.Quantum.Light.Button.Paper" />
+ <public type="style" name="Widget.Quantum.Light.Button.Paper.Color" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index afb7085..f1bcf65 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2012,6 +2012,10 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessNetworkConditions">Allows an application to listen for observations on network conditions. Should never be needed for normal apps.</string>
+ <string name="permlab_setInputCalibration">change input device calibration</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_setInputCalibration">Allows the app to modify the calibration parameters of the touch screen. Should never be needed for normal apps.</string>
+
<!-- Policy administration -->
<!-- Title of policy access to limiting the user's password choices -->
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
new file mode 100644
index 0000000..e6a755f
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2014 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.graphics.drawable;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.Xfermode;
+
+/**
+ * A Drawable that wraps another Drawable.
+ */
+class DrawableWrapper extends Drawable implements Drawable.Callback {
+ private WrapperState mWrapperState;
+
+ /** Local drawable backed by its own constant state. */
+ private Drawable mWrappedDrawable;
+
+ private boolean mMutated;
+
+ /** @hide */
+ @Override
+ public boolean isProjected() {
+ return mWrappedDrawable.isProjected();
+ }
+
+ @Override
+ public void setAutoMirrored(boolean mirrored) {
+ mWrappedDrawable.setAutoMirrored(mirrored);
+ }
+
+ @Override
+ public boolean isAutoMirrored() {
+ return mWrappedDrawable.isAutoMirrored();
+ }
+
+ @Override
+ public int getMinimumWidth() {
+ return mWrappedDrawable.getMinimumWidth();
+ }
+
+ @Override
+ public int getMinimumHeight() {
+ return mWrappedDrawable.getMinimumHeight();
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mWrappedDrawable.getIntrinsicWidth();
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mWrappedDrawable.getIntrinsicHeight();
+ }
+
+ @Override
+ public Drawable getCurrent() {
+ return mWrappedDrawable.getCurrent();
+ }
+
+ @Override
+ public void invalidateDrawable(Drawable who) {
+ final Callback callback = getCallback();
+ if (callback != null) {
+ callback.invalidateDrawable(this);
+ }
+ }
+
+ @Override
+ public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ final Callback callback = getCallback();
+ if (callback != null) {
+ callback.scheduleDrawable(this, what, when);
+ }
+ }
+
+ @Override
+ public void unscheduleDrawable(Drawable who, Runnable what) {
+ final Callback callback = getCallback();
+ if (callback != null) {
+ callback.unscheduleDrawable(this, what);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ mWrappedDrawable.draw(canvas);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return mWrappedDrawable.getChangingConfigurations();
+ }
+
+ @Override
+ public boolean getPadding(Rect padding) {
+ return mWrappedDrawable.getPadding(padding);
+ }
+
+ @Override
+ public Rect getDirtyBounds() {
+ return mWrappedDrawable.getDirtyBounds();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean supportsHotspots() {
+ return mWrappedDrawable.supportsHotspots();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void setHotspot(int id, float x, float y) {
+ mWrappedDrawable.setHotspot(id, x, y);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void removeHotspot(int id) {
+ mWrappedDrawable.removeHotspot(id);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void clearHotspots() {
+ mWrappedDrawable.clearHotspots();
+ }
+
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ // Must call through to super().
+ super.setVisible(visible, restart);
+ return mWrappedDrawable.setVisible(visible, restart);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mWrappedDrawable.setAlpha(alpha);
+ }
+
+ @Override
+ public int getAlpha() {
+ return mWrappedDrawable.getAlpha();
+ }
+
+ /** {@hide} */
+ @Override
+ public void setLayoutDirection(int layoutDirection) {
+ mWrappedDrawable.setLayoutDirection(layoutDirection);
+ }
+
+ /** {@hide} */
+ @Override
+ public int getLayoutDirection() {
+ return mWrappedDrawable.getLayoutDirection();
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ mWrappedDrawable.setColorFilter(cf);
+ }
+
+ @Override
+ public ColorFilter getColorFilter() {
+ return mWrappedDrawable.getColorFilter();
+ }
+
+ @Override
+ public void setFilterBitmap(boolean filter) {
+ mWrappedDrawable.setFilterBitmap(filter);
+ }
+
+ @Override
+ public void setXfermode(Xfermode mode) {
+ mWrappedDrawable.setXfermode(mode);
+ }
+
+ @Override
+ public int getOpacity() {
+ return mWrappedDrawable.getOpacity();
+ }
+
+ @Override
+ public boolean isStateful() {
+ return mWrappedDrawable.isStateful();
+ }
+
+ @Override
+ public final boolean setState(int[] stateSet) {
+ return super.setState(stateSet);
+ }
+
+ @Override
+ public final int[] getState() {
+ return super.getState();
+ }
+
+ @Override
+ protected boolean onStateChange(int[] state) {
+ // Don't override setState(), getState().
+ return mWrappedDrawable.setState(state);
+ }
+
+ @Override
+ protected boolean onLevelChange(int level) {
+ // Don't override setLevel(), getLevel().
+ return mWrappedDrawable.setLevel(level);
+ }
+
+ @Override
+ public final void setBounds(int left, int top, int right, int bottom) {
+ super.setBounds(left, top, right, bottom);
+ }
+
+ @Override
+ public final void setBounds(Rect bounds) {
+ super.setBounds(bounds);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ // Don't override setBounds(), getBounds().
+ mWrappedDrawable.setBounds(bounds);
+ }
+
+ protected void setConstantState(WrapperState wrapperState, Resources res) {
+ mWrapperState = wrapperState;
+
+ // Load a new drawable from the constant state.
+ if (wrapperState == null || wrapperState.mWrappedConstantState == null) {
+ mWrappedDrawable = null;
+ } else if (res != null) {
+ mWrappedDrawable = wrapperState.mWrappedConstantState.newDrawable(res);
+ } else {
+ mWrappedDrawable = wrapperState.mWrappedConstantState.newDrawable();
+ }
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mWrapperState;
+ }
+
+ @Override
+ public Drawable mutate() {
+ if (!mMutated) {
+ mWrappedDrawable = mWrappedDrawable.mutate();
+ mMutated = true;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the wrapped drawable and update the constant state.
+ *
+ * @param drawable
+ * @param res
+ */
+ protected final void setDrawable(Drawable drawable, Resources res) {
+ if (mWrappedDrawable != null) {
+ mWrappedDrawable.setCallback(null);
+ }
+
+ mWrappedDrawable = drawable;
+
+ if (drawable != null) {
+ drawable.setCallback(this);
+
+ mWrapperState.mWrappedConstantState = drawable.getConstantState();
+ } else {
+ mWrapperState.mWrappedConstantState = null;
+ }
+ }
+
+ protected final Drawable getDrawable() {
+ return mWrappedDrawable;
+ }
+
+ static abstract class WrapperState extends ConstantState {
+ ConstantState mWrappedConstantState;
+
+ WrapperState(WrapperState orig) {
+ if (orig != null) {
+ mWrappedConstantState = orig.mWrappedConstantState;
+ }
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return mWrappedConstantState.getChangingConfigurations();
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index 6fbcb53..f4f545c 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -21,15 +21,18 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.SparseArray;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -37,23 +40,9 @@
import java.util.ArrayList;
/**
- * An extension of LayerDrawable that is intended to react to touch hotspots
- * and reveal the second layer atop the first.
- * <p>
- * It can be defined in an XML file with the <code><reveal></code> element.
- * Each Drawable in the transition is defined in a nested <code><item></code>.
- * For more information, see the guide to <a href="{@docRoot}
- * guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
- *
- * @attr ref android.R.styleable#LayerDrawableItem_left
- * @attr ref android.R.styleable#LayerDrawableItem_top
- * @attr ref android.R.styleable#LayerDrawableItem_right
- * @attr ref android.R.styleable#LayerDrawableItem_bottom
- * @attr ref android.R.styleable#LayerDrawableItem_drawable
- * @attr ref android.R.styleable#LayerDrawableItem_id
* @hide
*/
-public class TouchFeedbackDrawable extends Drawable {
+public class TouchFeedbackDrawable extends DrawableWrapper {
private final Rect mTempRect = new Rect();
private final Rect mPaddingRect = new Rect();
@@ -77,32 +66,43 @@
/** Paint used to control appearance of ripples. */
private Paint mRipplePaint;
+ /** Paint used to control reveal layer masking. */
+ private Paint mMaskingPaint;
+
/** Target density of the display into which ripples are drawn. */
private float mDensity = 1.0f;
/** Whether the animation runnable has been posted. */
private boolean mAnimating;
- TouchFeedbackDrawable() {
- this(new TouchFeedbackState(null), null);
+ /** The drawable to use as the mask. */
+ private Drawable mMask;
+
+ /* package */TouchFeedbackDrawable() {
+ this(null, null);
}
TouchFeedbackDrawable(TouchFeedbackState state, Resources res) {
+ mState = new TouchFeedbackState(state);
+
+ setConstantState(mState, res);
+
if (res != null) {
mDensity = res.getDisplayMetrics().density;
}
-
- mState = state;
}
+
+ private void setConstantState(TouchFeedbackState wrapperState, Resources res) {
+ super.setConstantState(wrapperState, res);
- @Override
- public void setColorFilter(ColorFilter cf) {
- // Not supported.
- }
-
- @Override
- public void setAlpha(int alpha) {
- // Not supported.
+ // Load a new mask drawable from the constant state.
+ if (wrapperState == null || wrapperState.mMaskState == null) {
+ mMask = null;
+ } else if (res != null) {
+ mMask = wrapperState.mMaskState.newDrawable(res);
+ } else {
+ mMask = wrapperState.mMaskState.newDrawable();
+ }
}
@Override
@@ -112,9 +112,20 @@
}
@Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+
+ if (mMask != null) {
+ mMask.setBounds(bounds);
+ }
+ }
+
+ @Override
protected boolean onStateChange(int[] stateSet) {
- final ColorStateList stateList = mState.mColorStateList;
- if (stateList != null && mRipplePaint != null) {
+ super.onStateChange(stateSet);
+
+ if (mRipplePaint != null) {
+ final ColorStateList stateList = mState.mTint;
final int newColor = stateList.getColorForState(stateSet, 0);
final int oldColor = mRipplePaint.getColor();
if (oldColor != newColor) {
@@ -132,12 +143,12 @@
*/
@Override
public boolean isProjected() {
- return true;
+ return mState.mProjected;
}
@Override
public boolean isStateful() {
- return mState.mColorStateList != null && mState.mColorStateList.isStateful();
+ return super.isStateful() || mState.mTint.isStateful();
}
@Override
@@ -145,16 +156,78 @@
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs);
- final TypedArray a = r.obtainAttributes(
- attrs, com.android.internal.R.styleable.ColorDrawable);
- mState.mColorStateList = a.getColorStateList(
- com.android.internal.R.styleable.ColorDrawable_color);
+ final TypedArray a = r.obtainAttributes(attrs, R.styleable.TouchFeedbackDrawable);
+
+ mState.mTint = a.getColorStateList(R.styleable.TouchFeedbackDrawable_tint);
+ mState.mTintMode = Drawable.parseTintMode(
+ a.getInt(R.styleable.TouchFeedbackDrawable_tintMode, -1), Mode.SRC_ATOP);
+ mState.mPinned = a.getBoolean(R.styleable.TouchFeedbackDrawable_pinned, false);
+
+ if (mState.mTint == null) {
+ throw new XmlPullParserException(parser.getPositionDescription()
+ + ": <touch-feedback> tag requires a 'tint' attribute");
+ }
+
+ Drawable mask = a.getDrawable(R.styleable.TouchFeedbackDrawable_mask);
+ final int drawableRes = a.getResourceId(R.styleable.TouchFeedbackDrawable_drawable, 0);
a.recycle();
+ final Drawable dr;
+ if (drawableRes != 0) {
+ dr = r.getDrawable(drawableRes);
+ } else {
+ int type;
+ while ((type = parser.next()) == XmlPullParser.TEXT) {
+ // Find the next non-text element.
+ }
+
+ if (type == XmlPullParser.START_TAG) {
+ dr = Drawable.createFromXmlInner(r, parser, attrs);
+ } else {
+ dr = null;
+ }
+ }
+
+ // If no mask is set, implicitly use the lower drawable.
+ if (mask == null) {
+ mask = dr;
+ }
+
+ // If neither a mask not a bottom layer was specified, assume we're
+ // projecting onto a parent surface.
+ mState.mProjected = mask == null && dr == null;
+
+ if (dr != null) {
+ setDrawable(dr, r);
+ } else {
+ // For now at least, we MUST have a wrapped drawable.
+ setDrawable(new ColorDrawable(Color.TRANSPARENT), r);
+ }
+
+ setMaskDrawable(mask, r);
setTargetDensity(r.getDisplayMetrics());
}
/**
+ * Sets the wrapped drawable and update the constant state.
+ *
+ * @param drawable
+ * @param res
+ */
+ void setMaskDrawable(Drawable drawable, Resources res) {
+ mMask = drawable;
+
+ if (drawable != null) {
+ // Nobody cares if the mask has a callback.
+ drawable.setCallback(null);
+
+ mState.mMaskState = drawable.getConstantState();
+ } else {
+ mState.mMaskState = null;
+ }
+ }
+
+ /**
* Set the density at which this drawable will be rendered.
*
* @param metrics The display metrics for this drawable.
@@ -175,6 +248,9 @@
}
/**
+ * TODO: Maybe we should set hotspots for state/id combinations? So touch
+ * would be state_pressed and the pointer ID.
+ *
* @hide until hotspot APIs are finalized
*/
@Override
@@ -186,19 +262,22 @@
final Ripple ripple = mTouchedRipples.get(id);
if (ripple == null) {
+ final Rect bounds = getBounds();
final Rect padding = mPaddingRect;
getPadding(padding);
- final Rect bounds = getBounds();
- final Ripple newRipple = new Ripple(bounds, padding, bounds.exactCenterX(),
- bounds.exactCenterY(), mDensity);
+ if (mState.mPinned) {
+ x = bounds.exactCenterX();
+ y = bounds.exactCenterY();
+ }
+
+ final Ripple newRipple = new Ripple(bounds, padding, x, y, mDensity);
newRipple.enter();
mActiveRipples.add(newRipple);
mTouchedRipples.put(id, newRipple);
- } else {
- // TODO: How do we want to respond to movement?
- //ripple.move(x, y);
+ } else if (!mState.mPinned) {
+ ripple.move(x, y);
}
scheduleAnimation();
@@ -254,7 +333,7 @@
if (mAnimationRunnable == null) {
mAnimationRunnable = new Runnable() {
- @Override
+ @Override
public void run() {
mAnimating = false;
scheduleAnimation();
@@ -269,47 +348,77 @@
@Override
public void draw(Canvas canvas) {
+ // The lower layer always draws normally.
+ super.draw(canvas);
+
+ if (mActiveRipples == null || mActiveRipples.size() == 0) {
+ // No ripples to draw.
+ return;
+ }
+
final ArrayList<Ripple> activeRipples = mActiveRipples;
- if (activeRipples == null || activeRipples.isEmpty()) {
- // Nothing to draw, we're done here.
- return;
- }
+ final Drawable mask = mMask;
+ final Rect bounds = mask == null ? null : mask.getBounds();
- final ColorStateList stateList = mState.mColorStateList;
- if (stateList == null) {
- // No color, we're done here.
- return;
- }
-
- final int color = stateList.getColorForState(getState(), Color.TRANSPARENT);
- if (color == Color.TRANSPARENT) {
- // No color, we're done here.
- return;
- }
-
- if (mRipplePaint == null) {
- mRipplePaint = new Paint();
- mRipplePaint.setAntiAlias(true);
- }
-
- mRipplePaint.setColor(color);
-
- final int restoreCount = canvas.save();
-
- // Draw ripples directly onto the canvas.
+ // Draw ripples into a layer that merges using SRC_IN.
+ boolean hasRipples = false;
+ int rippleRestoreCount = -1;
int n = activeRipples.size();
for (int i = 0; i < n; i++) {
final Ripple ripple = activeRipples.get(i);
if (!ripple.active()) {
+ // TODO: Mark and sweep is more efficient.
activeRipples.remove(i);
i--;
n--;
} else {
- ripple.draw(canvas, mRipplePaint);
+ // If we're masking the ripple layer, make sure we have a layer first.
+ if (mask != null && rippleRestoreCount < 0) {
+ rippleRestoreCount = canvas.saveLayer(bounds.left, bounds.top,
+ bounds.right, bounds.bottom, getMaskingPaint(SRC_ATOP), 0);
+ canvas.clipRect(bounds);
+ }
+
+ hasRipples |= ripple.draw(canvas, getRipplePaint());
}
}
- canvas.restoreToCount(restoreCount);
+ // If we have ripples, mask them.
+ if (mask != null && hasRipples) {
+ canvas.saveLayer(bounds.left, bounds.top, bounds.right,
+ bounds.bottom, getMaskingPaint(DST_IN), 0);
+ mask.draw(canvas);
+ }
+
+ // Composite the layers if needed:
+ // 1. Mask DST_IN
+ // 2. Ripples SRC_ATOP
+ // 3. Lower n/a
+ if (rippleRestoreCount > 0) {
+ canvas.restoreToCount(rippleRestoreCount);
+ }
+ }
+
+ private Paint getRipplePaint() {
+ if (mRipplePaint == null) {
+ mRipplePaint = new Paint();
+ mRipplePaint.setAntiAlias(true);
+
+ final int color = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
+ mRipplePaint.setColor(color);
+ }
+ return mRipplePaint;
+ }
+
+ private static final PorterDuffXfermode SRC_ATOP = new PorterDuffXfermode(Mode.SRC_ATOP);
+ private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
+
+ private Paint getMaskingPaint(PorterDuffXfermode mode) {
+ if (mMaskingPaint == null) {
+ mMaskingPaint = new Paint();
+ }
+ mMaskingPaint.setXfermode(mode);
+ return mMaskingPaint;
}
@Override
@@ -322,39 +431,46 @@
final Rect rippleBounds = mTempRect;
final ArrayList<Ripple> activeRipples = mActiveRipples;
if (activeRipples != null) {
- final int N = activeRipples.size();
- for (int i = 0; i < N; i++) {
- activeRipples.get(i).getBounds(rippleBounds);
- drawingBounds.union(rippleBounds);
- }
+ final int N = activeRipples.size();
+ for (int i = 0; i < N; i++) {
+ activeRipples.get(i).getBounds(rippleBounds);
+ drawingBounds.union(rippleBounds);
+ }
}
dirtyBounds.union(drawingBounds);
+ dirtyBounds.union(super.getDirtyBounds());
return dirtyBounds;
}
@Override
public ConstantState getConstantState() {
+ // TODO: Can we just rely on super.getConstantState()?
return mState;
}
- static class TouchFeedbackState extends ConstantState {
- ColorStateList mColorStateList;
+ static class TouchFeedbackState extends WrapperState {
+ ConstantState mMaskState;
+ ColorStateList mTint;
+ Mode mTintMode;
+ boolean mPinned;
+ boolean mProjected;
public TouchFeedbackState(TouchFeedbackState orig) {
+ super(orig);
+
if (orig != null) {
- mColorStateList = orig.mColorStateList;
+ mTint = orig.mTint;
+ mTintMode = orig.mTintMode;
+ mMaskState = orig.mMaskState;
+ mPinned = orig.mPinned;
+ mProjected = orig.mProjected;
}
}
@Override
- public int getChangingConfigurations() {
- return 0;
- }
-
- @Override
public Drawable newDrawable() {
- return newDrawable(null);
+ return new TouchFeedbackDrawable(this, null);
}
@Override
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 3759108..5611efb 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -16,8 +16,13 @@
package android.media;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.nio.NioUtils;
+import android.annotation.IntDef;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -145,6 +150,28 @@
private final static String TAG = "android.media.AudioTrack";
+ /** @hide */
+ @IntDef({
+ WRITE_BLOCKING,
+ WRITE_NON_BLOCKING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WriteMode {}
+
+ /**
+ * @hide CANDIDATE FOR PUBLIC API
+ * The write mode indicating the write operation will block until all data has been written,
+ * to be used in {@link #write(ByteBuffer, int, int, int)}.
+ */
+ public final static int WRITE_BLOCKING = 0;
+ /**
+ * @hide CANDIDATE FOR PUBLIC API
+ * The write mode indicating the write operation will return immediately after
+ * queuing as much audio data for playback as possible without blocking, to be used in
+ * {@link #write(ByteBuffer, int, int, int)}.
+ */
+ public final static int WRITE_NON_BLOCKING = 1;
+
//--------------------------------------------------------------------------
// Member variables
//--------------------
@@ -1084,7 +1111,8 @@
return ERROR_BAD_VALUE;
}
- int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat);
+ int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
+ true /*isBlocking*/);
if ((mDataLoadMode == MODE_STATIC)
&& (mState == STATE_NO_STATIC_DATA)
@@ -1141,6 +1169,75 @@
/**
+ * @hide CANDIDATE FOR PUBLIC API
+ * Writes the audio data to the audio sink for playback (streaming mode),
+ * or copies audio data for later playback (static buffer mode).
+ * In static buffer mode, copies the data to the buffer starting at its 0 offset, and the write
+ * mode is ignored.
+ * In streaming mode, the blocking behavior will depend on the write mode.
+ * @param audioData the buffer that holds the data to play, starting at the position reported
+ * by <code>audioData.position()</code>.
+ * <BR>Note that this method will not update the position in this buffer, therefore when
+ * writing a loop to write all the data in the buffer, you should increment the
+ * <code>offsetInBytes</code> parameter at each pass by the amount that was previously
+ * written for this buffer.
+ * @param offsetInBytes offset to read from in bytes (note this differs from
+ * <code>audioData.position()</code>).
+ * @param sizeInBytes number of bytes to read (note this differs from
+ * <code>audioData.remaining()</code>).
+ * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
+ * effect in static mode.
+ * <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
+ * to the audio sink.
+ * <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
+ * queuing as much audio data for playback as possible without blocking.
+ * @return 0 or a positive number of bytes that were written, or
+ * {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
+ */
+ public int write(ByteBuffer audioData, int offsetInBytes, int sizeInBytes,
+ @WriteMode int writeMode) {
+
+ if (mState == STATE_UNINITIALIZED) {
+ Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED");
+ return ERROR_INVALID_OPERATION;
+ }
+
+ if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) {
+ Log.e(TAG, "AudioTrack.write() called with invalid blocking mode");
+ return ERROR_BAD_VALUE;
+ }
+
+ if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
+ || (offsetInBytes + sizeInBytes < 0) // detect integer overflow
+ || (offsetInBytes + sizeInBytes > audioData.remaining())) {
+ Log.e(TAG, "AudioTrack.write() called with invalid size/offset values");
+ return ERROR_BAD_VALUE;
+ }
+
+ int ret = 0;
+ if (audioData.isDirect()) {
+ ret = native_write_native_bytes(audioData,
+ audioData.position(),
+ offsetInBytes, sizeInBytes, mAudioFormat,
+ writeMode == WRITE_BLOCKING);
+ } else {
+ ret = native_write_byte(NioUtils.unsafeArray(audioData),
+ NioUtils.unsafeArrayOffset(audioData) + audioData.position() + offsetInBytes,
+ sizeInBytes, mAudioFormat,
+ writeMode == WRITE_BLOCKING);
+ }
+
+ if ((mDataLoadMode == MODE_STATIC)
+ && (mState == STATE_NO_STATIC_DATA)
+ && (ret > 0)) {
+ // benign race with respect to other APIs that read mState
+ mState = STATE_INITIALIZED;
+ }
+
+ return ret;
+ }
+
+ /**
* Notifies the native resource to reuse the audio data already loaded in the native
* layer, that is to rewind to start of buffer.
* The track's creation mode must be {@link #MODE_STATIC}.
@@ -1339,11 +1436,15 @@
private native final void native_flush();
private native final int native_write_byte(byte[] audioData,
- int offsetInBytes, int sizeInBytes, int format);
+ int offsetInBytes, int sizeInBytes, int format,
+ boolean isBlocking);
private native final int native_write_short(short[] audioData,
int offsetInShorts, int sizeInShorts, int format);
+ private native final int native_write_native_bytes(Object audioData,
+ int positionInBytes, int offsetInBytes, int sizeInBytes, int format, boolean blocking);
+
private native final int native_reload_static();
private native final int native_get_native_frame_count();
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index e5c97e7..f518ab2 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -17,13 +17,11 @@
package android.media;
import android.media.MediaCodec.BufferInfo;
-
import dalvik.system.CloseGuard;
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.Map;
@@ -79,6 +77,7 @@
private OutputFormat() {}
/** MPEG4 media file format*/
public static final int MUXER_OUTPUT_MPEG_4 = 0;
+ public static final int MUXER_OUTPUT_WEBM = 1;
};
// All the native functions are listed here.
@@ -120,20 +119,22 @@
if (path == null) {
throw new IllegalArgumentException("path must not be null");
}
- if (format != OutputFormat.MUXER_OUTPUT_MPEG_4) {
+ if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 &&
+ format != OutputFormat.MUXER_OUTPUT_WEBM) {
throw new IllegalArgumentException("format is invalid");
}
- FileOutputStream fos = null;
+ // Use RandomAccessFile so we can open the file with RW access;
+ // RW access allows the native writer to memory map the output file.
+ RandomAccessFile file = null;
try {
- File file = new File(path);
- fos = new FileOutputStream(file);
- FileDescriptor fd = fos.getFD();
+ file = new RandomAccessFile(path, "rws");
+ FileDescriptor fd = file.getFD();
mNativeObject = nativeSetup(fd, format);
mState = MUXER_STATE_INITIALIZED;
mCloseGuard.open("release");
} finally {
- if (fos != null) {
- fos.close();
+ if (file != null) {
+ file.close();
}
}
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 2806bff..e20a4af 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -3069,7 +3069,7 @@
if (refreshTime ||
nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) {
try {
- mLastTimeUs = mPlayer.getCurrentPosition() * 1000;
+ mLastTimeUs = mPlayer.getCurrentPosition() * 1000L;
mPaused = !mPlayer.isPlaying();
if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs);
} catch (IllegalStateException e) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 0fa27c1..14de6dd 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -58,8 +58,8 @@
// how many cells the user has to cross before we poke the wakelock
private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
- private int mFailedPatternAttemptsSinceLastTimeout = 0;
- private int mTotalFailedPatternAttempts = 0;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
private CountDownTimer mCountdownTimer = null;
private LockPatternUtils mLockPatternUtils;
private LockPatternView mLockPatternView;
@@ -100,6 +100,7 @@
public KeyguardPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
}
public void setKeyguardCallback(KeyguardSecurityCallback callback) {
@@ -202,7 +203,8 @@
if (mCallback.isVerifyUnlockOnly()) {
updateFooter(FooterMode.VerifyUnlocked);
} else if (mEnableFallback &&
- (mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+ (mKeyguardUpdateMonitor.getFailedUnlockAttempts()
+ >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
updateFooter(FooterMode.ForgotLockPattern);
} else {
updateFooter(FooterMode.Normal);
@@ -211,7 +213,7 @@
}
private void displayDefaultSecurityMessage() {
- if (KeyguardUpdateMonitor.getInstance(mContext).getMaxBiometricUnlockAttemptsReached()) {
+ if (mKeyguardUpdateMonitor.getMaxBiometricUnlockAttemptsReached()) {
mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true);
} else {
mSecurityMessageDisplay.setMessage(R.string.kg_pattern_instructions, false);
@@ -262,20 +264,20 @@
if (mLockPatternUtils.checkPattern(pattern)) {
mCallback.reportUnlockAttempt(true);
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
- mTotalFailedPatternAttempts = 0;
mCallback.dismiss(true);
} else {
if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
}
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
- if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
- mTotalFailedPatternAttempts++;
- mFailedPatternAttemptsSinceLastTimeout++;
+ boolean registeredAttempt =
+ pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL;
+ if (registeredAttempt) {
mCallback.reportUnlockAttempt(false);
}
- if (mFailedPatternAttemptsSinceLastTimeout
- >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
+ int attempts = mKeyguardUpdateMonitor.getFailedUnlockAttempts();
+ if (registeredAttempt &&
+ 0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
} else {
@@ -363,7 +365,6 @@
mLockPatternView.setEnabled(true);
displayDefaultSecurityMessage();
// TODO mUnlockIcon.setVisibility(View.VISIBLE);
- mFailedPatternAttemptsSinceLastTimeout = 0;
if (mEnableFallback) {
updateFooter(FooterMode.ForgotLockPattern);
} else {
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 1e6011c..f0db449 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -198,7 +198,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ikke forbundet"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Intet netværk"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi slået fra"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast-skærm"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast skærm"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Lysstyrke"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="1666358784283020762">"Farveinverteringstilstand"</string>
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 9ecb917..15a68ef 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3988,7 +3988,7 @@
telephonyService.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
- && telephonyService.isOffhook()) {
+ && telephonyService.isOffhook() && isScreenOn) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telephonyService.endCall();
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 0b3eb12..6827b3f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -59,6 +59,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -78,6 +79,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
@@ -441,6 +443,10 @@
hideInputMethodMenu();
// No need to updateActive
return;
+ } else if (Intent.ACTION_USER_ADDED.equals(action)
+ || Intent.ACTION_USER_REMOVED.equals(action)) {
+ updateRelatedUserIds();
+ return;
} else {
Slog.w(TAG, "Unexpected intent " + intent);
}
@@ -642,6 +648,8 @@
broadcastFilter.addAction(Intent.ACTION_SCREEN_ON);
broadcastFilter.addAction(Intent.ACTION_SCREEN_OFF);
broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
+ broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
mNotificationShown = false;
@@ -675,6 +683,7 @@
// mSettings should be created before buildInputMethodListLocked
mSettings = new InputMethodSettings(
mRes, context.getContentResolver(), mMethodMap, mMethodList, userId);
+ updateRelatedUserIds();
mFileManager = new InputMethodFileManager(mMethodMap, userId);
mSwitchingController = new InputMethodSubtypeSwitchingController(mSettings);
mSwitchingController.resetCircularListLocked(context);
@@ -790,6 +799,7 @@
private void switchUserLocked(int newUserId) {
mSettings.setCurrentUserId(newUserId);
+ updateRelatedUserIds();
// InputMethodFileManager should be reset when the user is changed
mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
final String defaultImiId = mSettings.getSelectedInputMethod();
@@ -810,6 +820,16 @@
}
}
+ void updateRelatedUserIds() {
+ List<UserInfo> relatedUsers =
+ UserManager.get(mContext).getRelatedUsers(mSettings.getCurrentUserId());
+ int[] relatedUserIds = new int[relatedUsers.size()]; // relatedUsers will not be null
+ for (int i = 0; i < relatedUserIds.length; i++) {
+ relatedUserIds[i] = relatedUsers.get(i).id;
+ }
+ mSettings.setRelatedUserIds(relatedUserIds);
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -905,7 +925,7 @@
+ mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()
+ InputMethodUtils.getApiCallStack());
}
- if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) {
+ if (uid == Process.SYSTEM_UID || mSettings.isRelatedToOrCurrentUser(userId)) {
return true;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d910861..864211d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1015,6 +1015,7 @@
final ActivityThread mSystemThread;
int mCurrentUserId = 0;
+ int[] mRelatedUserIds = new int[0]; // Accessed by ActivityStack
private UserManagerService mUserManager;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -16127,6 +16128,20 @@
return startUser(userId, /* foreground */ false);
}
+ /**
+ * Refreshes the list of users related to the current user when either a
+ * user switch happens or when a new related user is started in the
+ * background.
+ */
+ private void updateRelatedUserIdsLocked() {
+ final List<UserInfo> relatedUsers = getUserManagerLocked().getRelatedUsers(mCurrentUserId);
+ int[] relatedUserIds = new int[relatedUsers.size()]; // relatedUsers will not be null
+ for (int i = 0; i < relatedUserIds.length; i++) {
+ relatedUserIds[i] = relatedUsers.get(i).id;
+ }
+ mRelatedUserIds = relatedUserIds;
+ }
+
@Override
public boolean switchUser(final int userId) {
return startUser(userId, /* foregound */ true);
@@ -16180,12 +16195,15 @@
if (foreground) {
mCurrentUserId = userId;
- mWindowManager.setCurrentUser(userId);
+ updateRelatedUserIdsLocked();
+ mWindowManager.setCurrentUser(userId, mRelatedUserIds);
// Once the internal notion of the active user has switched, we lock the device
// with the option to show the user switcher on the keyguard.
mWindowManager.lockNow(null);
} else {
final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId);
+ updateRelatedUserIdsLocked();
+ mWindowManager.updateRelatedUserIds(mRelatedUserIds);
mUserLru.remove(currentUserIdInt);
mUserLru.add(currentUserIdInt);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 922cef4..f3ccdd6 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -341,8 +341,19 @@
mCurrentUser = mService.mCurrentUserId;
}
- boolean okToShow(ActivityRecord r) {
- return r.userId == mCurrentUser
+ /**
+ * Checks whether the userid is either the current user or a related user.
+ */
+ private boolean isRelatedToOrCurrentUserLocked(int userId) {
+ if (mCurrentUser == userId) return true;
+ for (int i = 0; i < mService.mRelatedUserIds.length; i++) {
+ if (mService.mRelatedUserIds[i] == userId) return true;
+ }
+ return false;
+ }
+
+ boolean okToShowLocked(ActivityRecord r) {
+ return isRelatedToOrCurrentUserLocked(r.userId)
|| (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0;
}
@@ -362,7 +373,7 @@
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = activities.get(activityNdx);
- if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) {
+ if (!r.finishing && !r.delayedResume && r != notTop && okToShowLocked(r)) {
return r;
}
}
@@ -389,7 +400,7 @@
for (int i = activities.size() - 1; i >= 0; --i) {
final ActivityRecord r = activities.get(i);
// Note: the taskId check depends on real taskId fields being non-zero
- if (!r.finishing && (token != r.appToken) && okToShow(r)) {
+ if (!r.finishing && (token != r.appToken) && okToShowLocked(r)) {
return r;
}
}
@@ -542,7 +553,7 @@
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
TaskRecord task = mTaskHistory.get(taskNdx);
- if (task.userId != mCurrentUser) {
+ if (!isRelatedToOrCurrentUserLocked(task.userId)) {
return null;
}
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -573,7 +584,7 @@
int index = mTaskHistory.size();
for (int i = 0; i < index; ) {
TaskRecord task = mTaskHistory.get(i);
- if (task.userId == userId) {
+ if (isRelatedToOrCurrentUserLocked(task.userId)) {
if (DEBUG_TASKS) Slog.d(TAG, "switchUserLocked: stack=" + getStackId() +
" moving " + task + " to top");
mTaskHistory.remove(i);
@@ -1728,10 +1739,10 @@
mTaskHistory.remove(task);
// Now put task at top.
int stackNdx = mTaskHistory.size();
- if (task.userId != mCurrentUser) {
+ if (!isRelatedToOrCurrentUserLocked(task.userId)) {
// Put non-current user tasks below current user tasks.
while (--stackNdx >= 0) {
- if (mTaskHistory.get(stackNdx).userId != mCurrentUser) {
+ if (!isRelatedToOrCurrentUserLocked(mTaskHistory.get(stackNdx).userId)) {
break;
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 9740812..3a43521 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -153,7 +153,7 @@
ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = mActivities.get(activityNdx);
- if (!r.finishing && r != notTop && stack.okToShow(r)) {
+ if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
return r;
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e49382e..316bd57 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -52,6 +52,7 @@
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.hardware.input.KeyboardLayout;
+import android.hardware.input.TouchCalibration;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
@@ -76,6 +77,7 @@
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.PointerIcon;
+import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManagerPolicy;
import android.widget.Toast;
@@ -183,6 +185,7 @@
InputChannel fromChannel, InputChannel toChannel);
private static native void nativeSetPointerSpeed(long ptr, int speed);
private static native void nativeSetShowTouches(long ptr, boolean enabled);
+ private static native void nativeReloadCalibration(long ptr);
private static native void nativeVibrate(long ptr, int deviceId, long[] pattern,
int repeat, int token);
private static native void nativeCancelVibrate(long ptr, int deviceId, int token);
@@ -700,6 +703,47 @@
mTempFullKeyboards.clear();
}
+ @Override // Binder call & native callback
+ public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor,
+ int surfaceRotation) {
+ if (inputDeviceDescriptor == null) {
+ throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+ }
+
+ synchronized (mDataStore) {
+ return mDataStore.getTouchCalibration(inputDeviceDescriptor, surfaceRotation);
+ }
+ }
+
+ @Override // Binder call
+ public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation,
+ TouchCalibration calibration) {
+ if (!checkCallingPermission(android.Manifest.permission.SET_INPUT_CALIBRATION,
+ "setTouchCalibrationForInputDevice()")) {
+ throw new SecurityException("Requires SET_INPUT_CALIBRATION permission");
+ }
+ if (inputDeviceDescriptor == null) {
+ throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+ }
+ if (calibration == null) {
+ throw new IllegalArgumentException("calibration must not be null");
+ }
+ if (surfaceRotation < Surface.ROTATION_0 || surfaceRotation > Surface.ROTATION_270) {
+ throw new IllegalArgumentException("surfaceRotation value out of bounds");
+ }
+
+ synchronized (mDataStore) {
+ try {
+ if (mDataStore.setTouchCalibration(inputDeviceDescriptor, surfaceRotation,
+ calibration)) {
+ nativeReloadCalibration(mPtr);
+ }
+ } finally {
+ mDataStore.saveIfNeeded();
+ }
+ }
+ }
+
// Must be called on handler.
private void showMissingKeyboardLayoutNotification() {
if (!mKeyboardLayoutNotificationShown) {
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index 71de776..92fa813 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -24,6 +24,8 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.view.Surface;
+import android.hardware.input.TouchCalibration;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
@@ -82,6 +84,30 @@
}
}
+ public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
+ InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
+ if (state == null) {
+ return TouchCalibration.IDENTITY;
+ }
+
+ TouchCalibration cal = state.getTouchCalibration(surfaceRotation);
+ if (cal == null) {
+ return TouchCalibration.IDENTITY;
+ }
+ return cal;
+ }
+
+ public boolean setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration) {
+ InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
+
+ if (state.setTouchCalibration(surfaceRotation, calibration)) {
+ setDirty();
+ return true;
+ }
+
+ return false;
+ }
+
public String getCurrentKeyboardLayout(String inputDeviceDescriptor) {
InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
return state != null ? state.getCurrentKeyboardLayout() : null;
@@ -275,9 +301,35 @@
}
private static final class InputDeviceState {
+ private static final String[] CALIBRATION_NAME = { "x_scale",
+ "x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" };
+
+ private TouchCalibration[] mTouchCalibration = new TouchCalibration[4];
private String mCurrentKeyboardLayout;
private ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
+ public TouchCalibration getTouchCalibration(int surfaceRotation) {
+ try {
+ return mTouchCalibration[surfaceRotation];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ Slog.w(InputManagerService.TAG, "Cannot get touch calibration.", ex);
+ return null;
+ }
+ }
+
+ public boolean setTouchCalibration(int surfaceRotation, TouchCalibration calibration) {
+ try {
+ if (!calibration.equals(mTouchCalibration[surfaceRotation])) {
+ mTouchCalibration[surfaceRotation] = calibration;
+ return true;
+ }
+ return false;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ Slog.w(InputManagerService.TAG, "Cannot set touch calibration.", ex);
+ return false;
+ }
+ }
+
public String getCurrentKeyboardLayout() {
return mCurrentKeyboardLayout;
}
@@ -389,6 +441,52 @@
}
mCurrentKeyboardLayout = descriptor;
}
+ } else if (parser.getName().equals("calibration")) {
+ String format = parser.getAttributeValue(null, "format");
+ String rotation = parser.getAttributeValue(null, "rotation");
+ int r = -1;
+
+ if (format == null) {
+ throw new XmlPullParserException(
+ "Missing format attribute on calibration.");
+ }
+ if (!format.equals("affine")) {
+ throw new XmlPullParserException(
+ "Unsupported format for calibration.");
+ }
+ if (rotation != null) {
+ try {
+ r = stringToSurfaceRotation(rotation);
+ } catch (IllegalArgumentException e) {
+ throw new XmlPullParserException(
+ "Unsupported rotation for calibration.");
+ }
+ }
+
+ float[] matrix = TouchCalibration.IDENTITY.getAffineTransform();
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ String tag = parser.getName().toLowerCase();
+ String value = parser.nextText();
+
+ for (int i = 0; i < matrix.length && i < CALIBRATION_NAME.length; i++) {
+ if (tag.equals(CALIBRATION_NAME[i])) {
+ matrix[i] = Float.parseFloat(value);
+ break;
+ }
+ }
+ }
+
+ if (r == -1) {
+ // Assume calibration applies to all rotations
+ for (r = 0; r < mTouchCalibration.length; r++) {
+ mTouchCalibration[r] = new TouchCalibration(matrix[0],
+ matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+ }
+ } else {
+ mTouchCalibration[r] = new TouchCalibration(matrix[0],
+ matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+ }
}
}
@@ -411,6 +509,49 @@
}
serializer.endTag(null, "keyboard-layout");
}
+
+ for (int i = 0; i < mTouchCalibration.length; i++) {
+ if (mTouchCalibration[i] != null) {
+ String rotation = surfaceRotationToString(i);
+ float[] transform = mTouchCalibration[i].getAffineTransform();
+
+ serializer.startTag(null, "calibration");
+ serializer.attribute(null, "format", "affine");
+ serializer.attribute(null, "rotation", rotation);
+ for (int j = 0; j < transform.length && j < CALIBRATION_NAME.length; j++) {
+ serializer.startTag(null, CALIBRATION_NAME[j]);
+ serializer.text(Float.toString(transform[j]));
+ serializer.endTag(null, CALIBRATION_NAME[j]);
+ }
+ serializer.endTag(null, "calibration");
+ }
+ }
+ }
+
+ private static String surfaceRotationToString(int surfaceRotation) {
+ switch (surfaceRotation) {
+ case Surface.ROTATION_0: return "0";
+ case Surface.ROTATION_90: return "90";
+ case Surface.ROTATION_180: return "180";
+ case Surface.ROTATION_270: return "270";
+ }
+ throw new IllegalArgumentException("Unsupported surface rotation value" + surfaceRotation);
+ }
+
+ private static int stringToSurfaceRotation(String s) {
+ if ("0".equals(s)) {
+ return Surface.ROTATION_0;
+ }
+ if ("90".equals(s)) {
+ return Surface.ROTATION_90;
+ }
+ if ("180".equals(s)) {
+ return Surface.ROTATION_180;
+ }
+ if ("270".equals(s)) {
+ return Surface.ROTATION_270;
+ }
+ throw new IllegalArgumentException("Unsupported surface rotation string '" + s + "'");
}
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c006613..be94ca7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -19,7 +19,6 @@
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-
import android.app.AppOpsManager;
import android.util.ArraySet;
import android.util.TimeUtils;
@@ -302,8 +301,16 @@
}
};
- // Current user when multi-user is enabled. Don't show windows of non-current user.
+ /**
+ * Current user when multi-user is enabled. Don't show windows of
+ * non-current user. Also see mRelatedUserIds.
+ */
int mCurrentUserId;
+ /**
+ * Users related to the current user. These are also allowed to show windows
+ * on the current user.
+ */
+ int[] mRelatedUserIds = new int[0];
final Context mContext;
@@ -5284,10 +5291,16 @@
mPolicy.setTouchExplorationEnabled(enabled);
}
- public void setCurrentUser(final int newUserId) {
+ public void updateRelatedUserIds(final int[] relatedUserIds) {
synchronized (mWindowMap) {
- int oldUserId = mCurrentUserId;
+ mRelatedUserIds = relatedUserIds;
+ }
+ }
+
+ public void setCurrentUser(final int newUserId, final int[] relatedUserIds) {
+ synchronized (mWindowMap) {
mCurrentUserId = newUserId;
+ mRelatedUserIds = relatedUserIds;
mAppTransition.setCurrentUser(newUserId);
mPolicy.setCurrentUserLw(newUserId);
@@ -5302,6 +5315,15 @@
}
}
+ /* Called by WindowState */
+ boolean isRelatedToOrCurrentUserLocked(int userId) {
+ if (userId == mCurrentUserId) return true;
+ for (int i = 0; i < mRelatedUserIds.length; i++) {
+ if (mRelatedUserIds[i] == userId) return true;
+ }
+ return false;
+ }
+
public void enableScreenAfterBoot() {
synchronized(mWindowMap) {
if (DEBUG_BOOT) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a8e45c4..2c0e99e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1249,7 +1249,7 @@
}
return win.mShowToOwnerOnly
- && UserHandle.getUserId(win.mOwnerUid) != mService.mCurrentUserId;
+ && !mService.isRelatedToOrCurrentUserLocked(UserHandle.getUserId(win.mOwnerUid));
}
private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 0207c55..e1069ae 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -49,6 +49,7 @@
#include <android/graphics/GraphicsJNI.h>
#include <ScopedLocalRef.h>
+#include <ScopedPrimitiveArray.h>
#include <ScopedUtfChars.h>
#include "com_android_server_power_PowerManagerService.h"
@@ -86,6 +87,7 @@
jmethodID getPointerIcon;
jmethodID getKeyboardLayoutOverlay;
jmethodID getDeviceAlias;
+ jmethodID getTouchCalibrationForInputDevice;
} gServiceClassInfo;
static struct {
@@ -105,6 +107,11 @@
jmethodID constructor;
} gInputDeviceIdentifierInfo;
+static struct {
+ jclass clazz;
+ jmethodID getAffineTransform;
+} gTouchCalibrationClassInfo;
+
// --- Global functions ---
@@ -182,6 +189,7 @@
void setSystemUiVisibility(int32_t visibility);
void setPointerSpeed(int32_t speed);
void setShowTouches(bool enabled);
+ void reloadCalibration();
/* --- InputReaderPolicyInterface implementation --- */
@@ -190,6 +198,10 @@
virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices);
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier);
+ virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env,
+ jfloatArray matrixArr);
+ virtual TouchAffineTransformation getTouchAffineTransformation(
+ const String8& inputDeviceDescriptor, int32_t surfaceRotation);
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -741,6 +753,11 @@
InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
}
+void NativeInputManager::reloadCalibration() {
+ mInputManager->getReader()->requestRefreshConfiguration(
+ InputReaderConfiguration::TOUCH_AFFINE_TRANSFORMATION);
+}
+
bool NativeInputManager::isScreenOn() {
return android_server_PowerManagerService_isScreenOn();
}
@@ -749,6 +766,43 @@
return android_server_PowerManagerService_isScreenBright();
}
+TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
+ JNIEnv *env, jfloatArray matrixArr) {
+ ScopedFloatArrayRO matrix(env, matrixArr);
+ assert(matrix.size() == 6);
+
+ TouchAffineTransformation transform;
+ transform.x_scale = matrix[0];
+ transform.x_ymix = matrix[1];
+ transform.x_offset = matrix[2];
+ transform.y_xmix = matrix[3];
+ transform.y_scale = matrix[4];
+ transform.y_offset = matrix[5];
+
+ return transform;
+}
+
+TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
+ const String8& inputDeviceDescriptor, int32_t surfaceRotation) {
+ JNIEnv* env = jniEnv();
+
+ ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string()));
+
+ jobject cal = env->CallObjectMethod(mServiceObj,
+ gServiceClassInfo.getTouchCalibrationForInputDevice, descriptorObj.get(),
+ surfaceRotation);
+
+ jfloatArray matrixArr = jfloatArray(env->CallObjectMethod(cal,
+ gTouchCalibrationClassInfo.getAffineTransform));
+
+ TouchAffineTransformation transform = getTouchAffineTransformation(env, matrixArr);
+
+ env->DeleteLocalRef(matrixArr);
+ env->DeleteLocalRef(cal);
+
+ return transform;
+}
+
bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
jobject inputEventObj;
@@ -1231,6 +1285,11 @@
im->setShowTouches(enabled);
}
+static void nativeReloadCalibration(JNIEnv* env, jclass clazz, jlong ptr) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ im->reloadCalibration();
+}
+
static void nativeVibrate(JNIEnv* env,
jclass clazz, jlong ptr, jint deviceId, jlongArray patternObj,
jint repeat, jint token) {
@@ -1336,6 +1395,8 @@
(void*) nativeSetPointerSpeed },
{ "nativeSetShowTouches", "(JZ)V",
(void*) nativeSetShowTouches },
+ { "nativeReloadCalibration", "(J)V",
+ (void*) nativeReloadCalibration },
{ "nativeVibrate", "(JI[JII)V",
(void*) nativeVibrate },
{ "nativeCancelVibrate", "(JII)V",
@@ -1446,6 +1507,10 @@
GET_METHOD_ID(gServiceClassInfo.getDeviceAlias, clazz,
"getDeviceAlias", "(Ljava/lang/String;)Ljava/lang/String;");
+ GET_METHOD_ID(gServiceClassInfo.getTouchCalibrationForInputDevice, clazz,
+ "getTouchCalibrationForInputDevice",
+ "(Ljava/lang/String;I)Landroid/hardware/input/TouchCalibration;");
+
// InputDevice
FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
@@ -1468,6 +1533,14 @@
GET_METHOD_ID(gInputDeviceIdentifierInfo.constructor, gInputDeviceIdentifierInfo.clazz,
"<init>", "(Ljava/lang/String;II)V");
+ // TouchCalibration
+
+ FIND_CLASS(gTouchCalibrationClassInfo.clazz, "android/hardware/input/TouchCalibration");
+ gTouchCalibrationClassInfo.clazz = jclass(env->NewGlobalRef(gTouchCalibrationClassInfo.clazz));
+
+ GET_METHOD_ID(gTouchCalibrationClassInfo.getAffineTransform, gTouchCalibrationClassInfo.clazz,
+ "getAffineTransform", "()[F");
+
return 0;
}