auto import from //branches/cupcake/...@137197
diff --git a/api/current.xml b/api/current.xml
index 55c7756..96998ed 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -27761,6 +27761,17 @@
visibility="public"
>
</field>
+<field name="ACTION_USER_PRESENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.intent.action.USER_PRESENT""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_VIEW"
type="java.lang.String"
transient="false"
@@ -108917,6 +108928,21 @@
<parameter name="what" type="java.lang.Object">
</parameter>
</method>
+<method name="isSelectingMetaTracker"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="what" type="java.lang.Object">
+</parameter>
+</method>
<method name="onKeyDown"
return="boolean"
abstract="false"
@@ -123194,6 +123220,19 @@
visibility="public"
>
</method>
+<method name="checkInputConnectionProxy"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
<method name="clearAnimation"
return="void"
abstract="false"
@@ -135200,7 +135239,7 @@
type="int"
transient="false"
volatile="false"
- value="5"
+ value="6"
static="true"
final="true"
deprecated="not deprecated"
@@ -135211,7 +135250,7 @@
type="int"
transient="false"
volatile="false"
- value="1"
+ value="2"
static="true"
final="true"
deprecated="not deprecated"
@@ -135222,7 +135261,7 @@
type="int"
transient="false"
volatile="false"
- value="4"
+ value="5"
static="true"
final="true"
deprecated="not deprecated"
@@ -135233,7 +135272,7 @@
type="int"
transient="false"
volatile="false"
- value="0"
+ value="1"
static="true"
final="true"
deprecated="not deprecated"
@@ -135244,7 +135283,7 @@
type="int"
transient="false"
volatile="false"
- value="2"
+ value="3"
static="true"
final="true"
deprecated="not deprecated"
@@ -135255,7 +135294,18 @@
type="int"
transient="false"
volatile="false"
- value="3"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IME_ACTION_UNSPECIFIED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
static="true"
final="true"
deprecated="not deprecated"
@@ -135284,7 +135334,7 @@
visibility="public"
>
</field>
-<field name="IME_NORMAL"
+<field name="IME_NULL"
type="int"
transient="false"
volatile="false"
@@ -135295,17 +135345,6 @@
visibility="public"
>
</field>
-<field name="IME_UNDEFINED"
- type="int"
- transient="false"
- volatile="false"
- value="-2147483648"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="actionId"
type="int"
transient="false"
@@ -135501,6 +135540,17 @@
visibility="public"
>
</field>
+<field name="FLAG_SELECTING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_SINGLE_LINE"
type="int"
transient="false"
@@ -136071,6 +136121,271 @@
>
</field>
</interface>
+<class name="InputConnectionWrapper"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.view.inputmethod.InputConnection">
+</implements>
+<constructor name="InputConnectionWrapper"
+ type="android.view.inputmethod.InputConnectionWrapper"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="android.view.inputmethod.InputConnection">
+</parameter>
+</constructor>
+<method name="beginBatchEdit"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="clearMetaKeyStates"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="states" type="int">
+</parameter>
+</method>
+<method name="commitCompletion"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="android.view.inputmethod.CompletionInfo">
+</parameter>
+</method>
+<method name="commitText"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="newCursorPosition" type="int">
+</parameter>
+</method>
+<method name="deleteSurroundingText"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="leftLength" type="int">
+</parameter>
+<parameter name="rightLength" type="int">
+</parameter>
+</method>
+<method name="endBatchEdit"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="finishComposingText"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCursorCapsMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reqModes" type="int">
+</parameter>
+</method>
+<method name="getExtractedText"
+ return="android.view.inputmethod.ExtractedText"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="request" type="android.view.inputmethod.ExtractedTextRequest">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="getTextAfterCursor"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="n" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="getTextBeforeCursor"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="n" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="performContextMenuAction"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+<method name="performEditorAction"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="editorAction" type="int">
+</parameter>
+</method>
+<method name="performPrivateCommand"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="action" type="java.lang.String">
+</parameter>
+<parameter name="data" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="reportFullscreenMode"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
+<method name="sendKeyEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
+<method name="setComposingText"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="newCursorPosition" type="int">
+</parameter>
+</method>
+<method name="setSelection"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+</method>
+</class>
<interface name="InputMethod"
abstract="true"
static="false"
@@ -155853,7 +156168,7 @@
<parameter name="depth" type="int">
</parameter>
</method>
-<method name="didTouchFocusSelectAll"
+<method name="didTouchFocusSelect"
return="boolean"
abstract="false"
native="false"
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e1c1f64..824fd9b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1042,6 +1042,14 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON";
+
+ /**
+ * Broadcast Action: Sent when the user is present after device wakes up (e.g when the
+ * keyguard is gone).
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_USER_PRESENT= "android.intent.action.USER_PRESENT";
+
/**
* Broadcast Action: The current time has changed. Sent every
* minute. You can <em>not</em> receive this through components declared
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ae8b08..2dcb483 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -260,8 +260,9 @@
boolean assetError = true;
try {
assmgr = new AssetManager();
- if(assmgr.addAssetPath(mArchiveSourcePath) != 0) {
- parser = assmgr.openXmlResourceParser("AndroidManifest.xml");
+ int cookie = assmgr.addAssetPath(mArchiveSourcePath);
+ if(cookie != 0) {
+ parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
assetError = false;
} else {
Log.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index fadcb35..1c91736 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -567,8 +567,8 @@
/**
* Add an additional set of assets to the asset manager. This can be
- * either a directory or ZIP file. Not for use by applications. A
- * zero return value indicates failure.
+ * either a directory or ZIP file. Not for use by applications. Returns
+ * the cookie of the added asset, or 0 on failure.
* {@hide}
*/
public native final int addAssetPath(String path);
diff --git a/core/java/android/emoji/EmojiFactory.java b/core/java/android/emoji/EmojiFactory.java
new file mode 100644
index 0000000..389bd07
--- /dev/null
+++ b/core/java/android/emoji/EmojiFactory.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.emoji;
+
+import android.graphics.Bitmap;
+
+import java.lang.ref.WeakReference;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * A class for the factories which produce Emoji (pictgram) images.
+ * This is intended to be used by IME, Email app, etc.
+ * There's no plan to make this public for now.
+ * @hide
+ */
+public final class EmojiFactory {
+ // private static final String LOG_TAG = "EmojiFactory";
+
+ private int sCacheSize = 100;
+
+ // HashMap for caching Bitmap object. In order not to make an cache object
+ // blow up, we use LinkedHashMap with size limit.
+ private class CustomLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
+ public CustomLinkedHashMap() {
+ // These magic numbers are gotten from the source code of
+ // LinkedHashMap.java and HashMap.java.
+ super(16, 0.75f, true);
+ }
+
+ /*
+ * If size() becomes more than sCacheSize, least recently used cache
+ * is erased.
+ * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
+ */
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+ return size() > sCacheSize;
+ }
+ }
+
+ // A pointer to native EmojiFactory object.
+ private int mNativeEmojiFactory;
+ private String mName;
+ // Cache.
+ private Map<Integer, WeakReference<Bitmap>> mCache;
+
+ /**
+ * @noinspection UnusedDeclaration
+ */
+ /*
+ * Private constructor that must received an already allocated native
+ * EmojiFactory int (pointer).
+ *
+ * This can be called from JNI code.
+ */
+ private EmojiFactory(int nativeEmojiFactory, String name) {
+ mNativeEmojiFactory = nativeEmojiFactory;
+ mName = name;
+ mCache = new CustomLinkedHashMap<Integer, WeakReference<Bitmap>>();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ nativeDestructor(mNativeEmojiFactory);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ public String name() {
+ return mName;
+ }
+
+ /**
+ * Returns Bitmap object corresponding to the AndroidPua.
+ *
+ * Note that each Bitmap is cached by this class, which means that, if you modify a
+ * Bitmap object (using setPos() method), all same emoji Bitmap will be modified.
+ * If it is unacceptable, please copy the object before modifying it.
+ *
+ * @param pua A unicode codepoint.
+ * @return Bitmap object when this factory knows the Bitmap relevant to the codepoint.
+ * Otherwise null is returned.
+ */
+ public synchronized Bitmap getBitmapFromAndroidPua(int pua) {
+ WeakReference<Bitmap> cache = mCache.get(pua);
+ if (cache == null) {
+ Bitmap ret = nativeGetBitmapFromAndroidPua(mNativeEmojiFactory, pua);
+ // There is no need to cache returned null, since in most cases it means there
+ // is no map from the AndroidPua to a specific image. In other words, it usually does
+ // not include the cost of creating Bitmap object.
+ if (ret != null) {
+ mCache.put(pua, new WeakReference<Bitmap>(ret));
+ }
+ return ret;
+ } else {
+ Bitmap tmp = cache.get();
+ if (tmp == null) {
+ Bitmap ret = nativeGetBitmapFromAndroidPua(mNativeEmojiFactory, pua);
+ mCache.put(pua, new WeakReference<Bitmap>(ret));
+ return ret;
+ } else {
+ return tmp;
+ }
+ }
+ }
+
+ /**
+ * Returns Bitmap object corresponding to the vendor specified sjis.
+ *
+ * See comments in getBitmapFromAndroidPua().
+ *
+ * @param sjis sjis code specific to each career(vendor)
+ * @return Bitmap object when this factory knows the Bitmap relevant to the code. Otherwise
+ * null is returned.
+ */
+ public synchronized Bitmap getBitmapFromVendorSpecificSjis(char sjis) {
+ return getBitmapFromAndroidPua(getAndroidPuaFromVendorSpecificSjis(sjis));
+ }
+
+ /**
+ * Returns Bitmap object corresponding to the vendor specific Unicode.
+ *
+ * See comments in getBitmapFromAndroidPua().
+ *
+ * @param vsp vendor specific PUA.
+ * @return Bitmap object when this factory knows the Bitmap relevant to the code. Otherwise
+ * null is returned.
+ */
+ public synchronized Bitmap getBitmapFromVendorSpecificPua(int vsp) {
+ return getBitmapFromAndroidPua(getAndroidPuaFromVendorSpecificPua(vsp));
+ }
+
+ /**
+ * Returns Unicode PUA for Android corresponding to the vendor specific sjis.
+ *
+ * @param sjis vendor specific sjis
+ * @return Unicode PUA for Android, or -1 if there's no map for the sjis.
+ */
+ public int getAndroidPuaFromVendorSpecificSjis(char sjis) {
+ return nativeGetAndroidPuaFromVendorSpecificSjis(mNativeEmojiFactory, sjis);
+ }
+
+ /**
+ * Returns vendor specific sjis corresponding to the Unicode AndroidPua.
+ *
+ * @param pua Unicode PUA for Android,
+ * @return vendor specific sjis, or -1 if there's no map for the AndroidPua.
+ */
+ public int getVendorSpecificSjisFromAndroidPua(int pua) {
+ return nativeGetVendorSpecificSjisFromAndroidPua(mNativeEmojiFactory, pua);
+ }
+
+ /**
+ * Returns Unicode PUA for Android corresponding to the vendor specific Unicode.
+ *
+ * @param vsp vendor specific PUA.
+ * @return Unicode PUA for Android, or -1 if there's no map for the
+ * Unicode.
+ */
+ public int getAndroidPuaFromVendorSpecificPua(int vsp) {
+ return nativeGetAndroidPuaFromVendorSpecificPua(mNativeEmojiFactory, vsp);
+ }
+
+ public String getAndroidPuaFromVendorSpecificPua(String vspString) {
+ if (vspString == null) {
+ return null;
+ }
+ int minVsp = nativeGetMinimumVendorSpecificPua(mNativeEmojiFactory);
+ int maxVsp = nativeGetMaximumVendorSpecificPua(mNativeEmojiFactory);
+ int len = vspString.length();
+ int[] codePoints = new int[vspString.codePointCount(0, len)];
+
+ int new_len = 0;
+ for (int i = 0; i < len; i = vspString.offsetByCodePoints(i, 1), new_len++) {
+ int codePoint = vspString.codePointAt(i);
+ if (minVsp <= codePoint && codePoint <= maxVsp) {
+ int newCodePoint = getAndroidPuaFromVendorSpecificPua(codePoint);
+ if (newCodePoint > 0) {
+ codePoints[new_len] = newCodePoint;
+ continue;
+ }
+ }
+ codePoints[new_len] = codePoint;
+ }
+ return new String(codePoints, 0, new_len);
+ }
+
+ /**
+ * Returns vendor specific Unicode corresponding to the Unicode AndroidPua.
+ *
+ * @param pua Unicode PUA for Android,
+ * @return vendor specific sjis, or -1 if there's no map for the AndroidPua.
+ */
+ public int getVendorSpecificPuaFromAndroidPua(int pua) {
+ return nativeGetVendorSpecificPuaFromAndroidPua(mNativeEmojiFactory, pua);
+ }
+
+ public String getVendorSpecificPuaFromAndroidPua(String puaString) {
+ if (puaString == null) {
+ return null;
+ }
+ int minVsp = nativeGetMinimumAndroidPua(mNativeEmojiFactory);
+ int maxVsp = nativeGetMaximumAndroidPua(mNativeEmojiFactory);
+ int len = puaString.length();
+ int[] codePoints = new int[puaString.codePointCount(0, len)];
+
+ int new_len = 0;
+ for (int i = 0; i < len; i = puaString.offsetByCodePoints(i, 1), new_len++) {
+ int codePoint = puaString.codePointAt(i);
+ if (minVsp <= codePoint && codePoint <= maxVsp) {
+ int newCodePoint = getVendorSpecificPuaFromAndroidPua(codePoint);
+ if (newCodePoint > 0) {
+ codePoints[new_len] = newCodePoint;
+ continue;
+ }
+ }
+ codePoints[new_len] = codePoint;
+ }
+ return new String(codePoints, 0, new_len);
+ }
+
+ /**
+ * Constructs an instance of EmojiFactory corresponding to the name.
+ *
+ * @param class_name Name of the factory. This must include complete package name.
+ * @return A concrete EmojiFactory instance corresponding to factory_name.
+ * If factory_name is invalid, null is returned.
+ */
+ public static native EmojiFactory newInstance(String class_name);
+
+ /**
+ * Constructs an instance of available EmojiFactory.
+ *
+ * @return A concrete EmojiFactory instance. If there are several available
+ * EmojiFactory class, preferred one is chosen by the system. If there isn't, null
+ * is returned.
+ */
+ public static native EmojiFactory newAvailableInstance();
+
+ // native methods
+
+ private native void nativeDestructor(int factory);
+ private native Bitmap nativeGetBitmapFromAndroidPua(int nativeEmojiFactory, int AndroidPua);
+ private native int nativeGetAndroidPuaFromVendorSpecificSjis(int nativeEmojiFactory,
+ char sjis);
+ private native int nativeGetVendorSpecificSjisFromAndroidPua(int nativeEmojiFactory,
+ int pua);
+ private native int nativeGetAndroidPuaFromVendorSpecificPua(int nativeEmojiFactory,
+ int vsp);
+ private native int nativeGetVendorSpecificPuaFromAndroidPua(int nativeEmojiFactory,
+ int pua);
+ private native int nativeGetMaximumVendorSpecificPua(int nativeEmojiFactory);
+ private native int nativeGetMinimumVendorSpecificPua(int nativeEmojiFactory);
+ private native int nativeGetMaximumAndroidPua(int nativeEmojiFactory);
+ private native int nativeGetMinimumAndroidPua(int nativeEmojiFactory);
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f1e613e..34d5695 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1377,9 +1377,11 @@
if (mExtractedToken != token) {
return;
}
- if (mExtractEditText != null && text != null) {
- mExtractedText = text;
- mExtractEditText.setExtractedText(text);
+ if (text != null) {
+ if (mExtractEditText != null) {
+ mExtractedText = text;
+ mExtractEditText.setExtractedText(text);
+ }
}
}
diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java
index 1454089..d11a9c5 100644
--- a/core/java/android/provider/Checkin.java
+++ b/core/java/android/provider/Checkin.java
@@ -132,8 +132,6 @@
BROWSER_SNAP_CENTER,
BROWSER_TEXT_SIZE_CHANGE,
BROWSER_ZOOM_OVERVIEW,
- BROWSER_ZOOM_RING,
- BROWSER_ZOOM_RING_DRAG,
CRASHES_REPORTED,
CRASHES_TRUNCATED,
ELAPSED_REALTIME_SEC,
diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java
index 87153cf..6f5513a 100644
--- a/core/java/android/server/BluetoothDeviceService.java
+++ b/core/java/android/server/BluetoothDeviceService.java
@@ -237,7 +237,28 @@
public void run() {
boolean res = (enableNative() == 0);
if (res) {
- mEventLoop.start();
+ int retryCount = 2;
+ boolean running = false;
+ while ((retryCount-- > 0) && !running) {
+ mEventLoop.start();
+ // it may take a momement for the other thread to do its
+ // thing. Check periodically for a while.
+ int pollCount = 5;
+ while ((pollCount-- > 0) && !running) {
+ if (mEventLoop.isEventLoopRunning()) {
+ running = true;
+ break;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {}
+ }
+ }
+ if (!running) {
+ log("bt EnableThread giving up");
+ res = false;
+ disableNative();
+ }
}
if (mEnableCallback != null) {
@@ -254,14 +275,20 @@
persistBluetoothOnSetting(true);
}
mIsDiscovering = false;
- Intent intent = new Intent(BluetoothIntent.ENABLED_ACTION);
mBondState.loadBondState();
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mHandler.sendMessageDelayed(mHandler.obtainMessage(REGISTER_SDP_RECORDS), 3000);
// Update mode
mEventLoop.onModeChanged(getModeNative());
}
+ Intent intent = null;
+ if (res) {
+ intent = new Intent(BluetoothIntent.ENABLED_ACTION);
+ } else {
+ intent = new Intent(BluetoothIntent.DISABLED_ACTION);
+ }
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+
mEnableThread = null;
}
}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 8b09583..e4ebcca 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -44,6 +44,7 @@
private int mNativeData;
private Thread mThread;
+ private boolean mStarted;
private boolean mInterrupted;
private HashMap<String, Integer> mPasskeyAgentRequestData;
private HashMap<String, IBluetoothDeviceCallback> mGetRemoteServiceChannelCallbacks;
@@ -122,14 +123,18 @@
public void run() {
try {
if (setUpEventLoopNative()) {
+ mStarted = true;
while (!mInterrupted) {
waitForAndDispatchEvent(0);
sleep(500);
}
- tearDownEventLoopNative();
}
+ // tear down even in the error case to clean
+ // up anything we started to setup
+ tearDownEventLoopNative();
} catch (InterruptedException e) { }
if (DBG) log("Event Loop thread finished");
+ mThread = null;
}
};
if (DBG) log("Starting Event Loop thread");
@@ -152,7 +157,7 @@
}
public synchronized boolean isEventLoopRunning() {
- return mThread != null;
+ return mThread != null && mStarted;
}
/*package*/ void onModeChanged(String bluezMode) {
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 8aa49af..17c7a6c 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -204,7 +204,7 @@
MotionEvent event) {
boolean handled = Touch.onTouchEvent(widget, buffer, event);
- if (widget.isFocused() && !widget.didTouchFocusSelectAll()) {
+ if (widget.isFocused() && !widget.didTouchFocusSelect()) {
if (event.getAction() == MotionEvent.ACTION_UP) {
int x = (int) event.getX();
int y = (int) event.getY();
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index 39ad976..61ec67f 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -159,13 +159,21 @@
/**
* Returns true if this object is one that this class would use to
- * keep track of meta state in the specified text.
+ * keep track of any meta state in the specified text.
*/
public static boolean isMetaTracker(CharSequence text, Object what) {
return what == CAP || what == ALT || what == SYM ||
what == SELECTING;
}
+ /**
+ * Returns true if this object is one that this class would use to
+ * keep track of the selecting meta state in the specified text.
+ */
+ public static boolean isSelectingMetaTracker(CharSequence text, Object what) {
+ return what == SELECTING;
+ }
+
private static void adjust(Spannable content, Object what) {
int current = content.getSpanFlags(what);
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 88ff3c5..f7ac522 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -498,7 +498,6 @@
if (mIsDoubleTapping) {
// Finally, give the up event of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
- mIsDoubleTapping = false;
} else if (mInLongPress) {
mHandler.removeMessages(TAP);
mInLongPress = false;
@@ -520,6 +519,7 @@
mPreviousUpEvent = MotionEvent.obtain(ev);
mVelocityTracker.recycle();
mVelocityTracker = null;
+ mIsDoubleTapping = false;
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
break;
@@ -529,6 +529,7 @@
mHandler.removeMessages(TAP);
mVelocityTracker.recycle();
mVelocityTracker = null;
+ mIsDoubleTapping = false;
mStillDown = false;
if (mInLongPress) {
mInLongPress = false;
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index cc3563c..841066c 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -26,9 +26,6 @@
public static final int LONG_PRESS = 0;
- /** @hide pending API council */
- public static final int ZOOM_RING_TICK = 1;
-
/**
* Flag for {@link View#performHapticFeedback(int, int)
* View.performHapticFeedback(int, int)}: Ignore the setting in the
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3e762f5..d78320a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3663,8 +3663,15 @@
* would make sense to automatically display a soft input window for
* it. Subclasses should override this if they implement
* {@link #onCreateInputConnection(EditorInfo)} to return true if
- * a call on that method would return a non-null InputConnection. The
- * default implementation always returns false.
+ * a call on that method would return a non-null InputConnection, and
+ * they are really a first-class editor that the user would normally
+ * start typing on when the go into a window containing your view.
+ *
+ * <p>The default implementation always returns false. This does
+ * <em>not</em> mean that its {@link #onCreateInputConnection(EditorInfo)}
+ * will not be called or the user can not otherwise perform edits on your
+ * view; it is just a hint to the system that this is not the primary
+ * purpose of this view.
*
* @return Returns true if this view is a text editor, else false.
*/
@@ -3689,6 +3696,20 @@
}
/**
+ * Called by the {@link android.view.inputmethod.InputMethodManager}
+ * when a view who is not the current
+ * input connection target is trying to make a call on the manager. The
+ * default implementation returns false; you can override this to return
+ * true for certain views if you are performing InputConnection proxying
+ * to them.
+ * @param view The View that is making the InputMethodManager call.
+ * @return Return true to allow the call, false to reject.
+ */
+ public boolean checkInputConnectionProxy(View view) {
+ return false;
+ }
+
+ /**
* Show the context menu for this view. It is not safe to hold on to the
* menu after returning from this method.
*
@@ -5016,7 +5037,7 @@
(viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL ?
getVerticalScrollbarWidth() : 0;
- scrollBar.setBounds(scrollX + (mPaddingLeft & inside) + getScrollBarPaddingLeft(), top,
+ scrollBar.setBounds(scrollX + (mPaddingLeft & inside), top,
scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap, top + size);
scrollBar.setParameters(
computeHorizontalScrollRange(),
@@ -6503,32 +6524,6 @@
return mBGDrawable;
}
- private int getScrollBarPaddingLeft() {
- // TODO: Deal with RTL languages
- return 0;
- }
-
- /*
- * Returns the pixels occupied by the vertical scrollbar, if not overlaid
- */
- private int getScrollBarPaddingRight() {
- // TODO: Deal with RTL languages
- if ((mViewFlags & SCROLLBARS_VERTICAL) == 0) {
- return 0;
- }
- return (mViewFlags & SCROLLBARS_INSET_MASK) == 0 ? 0 : getVerticalScrollbarWidth();
- }
-
- /*
- * Returns the pixels occupied by the horizontal scrollbar, if not overlaid
- */
- private int getScrollBarPaddingBottom() {
- if ((mViewFlags & SCROLLBARS_HORIZONTAL) == 0) {
- return 0;
- }
- return (mViewFlags & SCROLLBARS_INSET_MASK) == 0 ? 0 : getHorizontalScrollbarHeight();
- }
-
/**
* Sets the padding. The view may add on the space required to display
* the scrollbars, depending on the style and visibility of the scrollbars.
@@ -6552,7 +6547,22 @@
mUserPaddingRight = right;
mUserPaddingBottom = bottom;
- if (mPaddingLeft != left + getScrollBarPaddingLeft()) {
+ final int viewFlags = mViewFlags;
+
+ // Common case is there are no scroll bars.
+ if ((viewFlags & (SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL)) != 0) {
+ // TODO: Deal with RTL languages to adjust left padding instead of right.
+ if ((viewFlags & SCROLLBARS_VERTICAL) != 0) {
+ right += (viewFlags & SCROLLBARS_INSET_MASK) == 0
+ ? 0 : getVerticalScrollbarWidth();
+ }
+ if ((viewFlags & SCROLLBARS_HORIZONTAL) == 0) {
+ bottom += (viewFlags & SCROLLBARS_INSET_MASK) == 0
+ ? 0 : getHorizontalScrollbarHeight();
+ }
+ }
+
+ if (mPaddingLeft != left) {
changed = true;
mPaddingLeft = left;
}
@@ -6560,13 +6570,13 @@
changed = true;
mPaddingTop = top;
}
- if (mPaddingRight != right + getScrollBarPaddingRight()) {
+ if (mPaddingRight != right) {
changed = true;
- mPaddingRight = right + getScrollBarPaddingRight();
+ mPaddingRight = right;
}
- if (mPaddingBottom != bottom + getScrollBarPaddingBottom()) {
+ if (mPaddingBottom != bottom) {
changed = true;
- mPaddingBottom = bottom + getScrollBarPaddingBottom();
+ mPaddingBottom = bottom;
}
if (changed) {
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 52b4107..deca910 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -386,7 +386,14 @@
// anyway.
return true;
}
- Selection.setSelection(content, start, end);
+ if (start == end && MetaKeyKeyListener.getMetaState(content,
+ MetaKeyKeyListener.META_SELECTING) != 0) {
+ // If we are in selection mode, then we want to extend the
+ // selection instead of replacing it.
+ Selection.extendSelection(content, start);
+ } else {
+ Selection.setSelection(content, start, end);
+ }
return true;
}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 1c0d42a..b00e565 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -33,43 +33,49 @@
public static final int IME_MASK_ACTION = 0x000000ff;
/**
- * Bits of {@link #IME_MASK_ACTION}: there is no special action
- * associated with this editor.
+ * Bits of {@link #IME_MASK_ACTION}: no specific action has been
+ * associated with this editor, let the editor come up with its own if
+ * it can.
*/
- public static final int IME_ACTION_NONE = 0x00000000;
+ public static final int IME_ACTION_UNSPECIFIED = 0x00000000;
+
+ /**
+ * Bits of {@link #IME_MASK_ACTION}: there is no available action.
+ */
+ public static final int IME_ACTION_NONE = 0x00000001;
/**
* Bits of {@link #IME_MASK_ACTION}: the action key performs a "go"
* operation to take the user to the target of the text they typed.
* Typically used, for example, when entering a URL.
*/
- public static final int IME_ACTION_GO = 0x00000001;
+ public static final int IME_ACTION_GO = 0x00000002;
/**
* Bits of {@link #IME_MASK_ACTION}: the action key performs a "search"
* operation, taking the user to the results of searching for the text
* the have typed (in whatever context is appropriate).
*/
- public static final int IME_ACTION_SEARCH = 0x00000002;
+ public static final int IME_ACTION_SEARCH = 0x00000003;
/**
* Bits of {@link #IME_MASK_ACTION}: the action key performs a "send"
* operation, delivering the text to its target. This is typically used
* when composing a message.
*/
- public static final int IME_ACTION_SEND = 0x00000003;
+ public static final int IME_ACTION_SEND = 0x00000004;
/**
* Bits of {@link #IME_MASK_ACTION}: the action key performs a "next"
* operation, taking the user to the next field that will accept text.
*/
- public static final int IME_ACTION_NEXT = 0x00000004;
+ public static final int IME_ACTION_NEXT = 0x00000005;
/**
* Bits of {@link #IME_MASK_ACTION}: the action key performs a "done"
* operation, typically meaning the IME will be closed.
*/
- public static final int IME_ACTION_DONE = 0x00000005;
+ public static final int IME_ACTION_DONE = 0x00000006;
/**
* Flag of {@link #imeOptions}: used in conjunction with
@@ -82,21 +88,15 @@
public static final int IME_FLAG_NO_ENTER_ACTION = 0x40000000;
/**
- * Generic non-special type for {@link #imeOptions}.
+ * Generic unspecified type for {@link #imeOptions}.
*/
- public static final int IME_NORMAL = 0x00000000;
-
- /**
- * Special code for when the ime option has been undefined. This is not
- * used with the EditorInfo structure, but can be used elsewhere.
- */
- public static final int IME_UNDEFINED = 0x80000000;
+ public static final int IME_NULL = 0x00000000;
/**
* Extended type information for the editor, to help the IME better
* integrate with it.
*/
- public int imeOptions = IME_NORMAL;
+ public int imeOptions = IME_NULL;
/**
* A string supplying additional information options that are
diff --git a/core/java/android/view/inputmethod/ExtractedText.java b/core/java/android/view/inputmethod/ExtractedText.java
index e5d3cae..c2851d6 100644
--- a/core/java/android/view/inputmethod/ExtractedText.java
+++ b/core/java/android/view/inputmethod/ExtractedText.java
@@ -55,6 +55,11 @@
public static final int FLAG_SINGLE_LINE = 0x0001;
/**
+ * Bit for {@link #flags}: set if the editor is currently in selection mode.
+ */
+ public static final int FLAG_SELECTING = 0x0002;
+
+ /**
* Additional bit flags of information about the edited text.
*/
public int flags;
@@ -72,7 +77,7 @@
dest.writeInt(partialEndOffset);
dest.writeInt(selectionStart);
dest.writeInt(selectionEnd);
- dest.writeInt(flags);
+ dest.writeInt(this.flags);
}
/**
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 32cce35..8b6831e 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -17,7 +17,6 @@
package android.view.inputmethod;
import android.os.Bundle;
-import android.text.Spanned;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -135,7 +134,7 @@
* @return Returns true on success, false if the input connection is no longer
* valid.
*/
- boolean deleteSurroundingText(int leftLength, int rightLength);
+ public boolean deleteSurroundingText(int leftLength, int rightLength);
/**
* Set composing text around the current cursor position with the given text,
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
new file mode 100644
index 0000000..e3d5e62
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view.inputmethod;
+
+import android.os.Bundle;
+import android.view.KeyEvent;
+
+/**
+ * <p>Wrapper class for proxying calls to another InputConnection. Subclass
+ * and have fun!
+ */
+public class InputConnectionWrapper implements InputConnection {
+ private final InputConnection mTarget;
+
+ public InputConnectionWrapper(InputConnection target) {
+ mTarget = target;
+ }
+
+ public CharSequence getTextBeforeCursor(int n, int flags) {
+ return mTarget.getTextBeforeCursor(n, flags);
+ }
+
+ public CharSequence getTextAfterCursor(int n, int flags) {
+ return mTarget.getTextAfterCursor(n, flags);
+ }
+
+ public int getCursorCapsMode(int reqModes) {
+ return mTarget.getCursorCapsMode(reqModes);
+ }
+
+ public ExtractedText getExtractedText(ExtractedTextRequest request,
+ int flags) {
+ return mTarget.getExtractedText(request, flags);
+ }
+
+ public boolean deleteSurroundingText(int leftLength, int rightLength) {
+ return mTarget.deleteSurroundingText(leftLength, rightLength);
+ }
+
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ return mTarget.setComposingText(text, newCursorPosition);
+ }
+
+ public boolean finishComposingText() {
+ return mTarget.finishComposingText();
+ }
+
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ return mTarget.commitText(text, newCursorPosition);
+ }
+
+ public boolean commitCompletion(CompletionInfo text) {
+ return mTarget.commitCompletion(text);
+ }
+
+ public boolean setSelection(int start, int end) {
+ return mTarget.setSelection(start, end);
+ }
+
+ public boolean performEditorAction(int editorAction) {
+ return mTarget.performEditorAction(editorAction);
+ }
+
+ public boolean performContextMenuAction(int id) {
+ return mTarget.performContextMenuAction(id);
+ }
+
+ public boolean beginBatchEdit() {
+ return mTarget.beginBatchEdit();
+ }
+
+ public boolean endBatchEdit() {
+ return mTarget.endBatchEdit();
+ }
+
+ public boolean sendKeyEvent(KeyEvent event) {
+ return mTarget.sendKeyEvent(event);
+ }
+
+ public boolean clearMetaKeyStates(int states) {
+ return mTarget.clearMetaKeyStates(states);
+ }
+
+ public boolean reportFullscreenMode(boolean enabled) {
+ return mTarget.reportFullscreenMode(enabled);
+ }
+
+ public boolean performPrivateCommand(String action, Bundle data) {
+ return mTarget.performPrivateCommand(action, data);
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 916ffea..7f2142e 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -529,7 +529,10 @@
public boolean isActive(View view) {
checkFocus();
synchronized (mH) {
- return mServedView == view && mCurrentTextBoxAttribute != null;
+ return (mServedView == view
+ || (mServedView != null
+ && mServedView.checkInputConnectionProxy(view)))
+ && mCurrentTextBoxAttribute != null;
}
}
@@ -620,7 +623,8 @@
public void displayCompletions(View view, CompletionInfo[] completions) {
checkFocus();
synchronized (mH) {
- if (mServedView != view) {
+ if (mServedView != view && (mServedView == null
+ || !mServedView.checkInputConnectionProxy(view))) {
return;
}
@@ -637,7 +641,8 @@
public void updateExtractedText(View view, int token, ExtractedText text) {
checkFocus();
synchronized (mH) {
- if (mServedView != view) {
+ if (mServedView != view && (mServedView == null
+ || !mServedView.checkInputConnectionProxy(view))) {
return;
}
@@ -730,7 +735,8 @@
ResultReceiver resultReceiver) {
checkFocus();
synchronized (mH) {
- if (mServedView != view) {
+ if (mServedView != view && (mServedView == null
+ || !mServedView.checkInputConnectionProxy(view))) {
return false;
}
@@ -871,7 +877,8 @@
public void restartInput(View view) {
checkFocus();
synchronized (mH) {
- if (mServedView != view) {
+ if (mServedView != view && (mServedView == null
+ || !mServedView.checkInputConnectionProxy(view))) {
return;
}
@@ -1032,7 +1039,7 @@
if (DEBUG) Log.v(TAG, "focusOut: " + view
+ " mServedView=" + mServedView
+ " winFocus=" + view.hasWindowFocus());
- if (mServedView == view) {
+ if (mServedView != view) {
// The following code would auto-hide the IME if we end up
// with no more views with focus. This can happen, however,
// whenever we go into touch mode, so it ends up hiding
@@ -1129,8 +1136,9 @@
try {
final boolean isTextEditor = focusedView != null &&
focusedView.onCheckIsTextEditor();
- mService.windowGainedFocus(mClient, focusedView != null,
- isTextEditor, softInputMode, first, windowFlags);
+ mService.windowGainedFocus(mClient, rootView.getWindowToken(),
+ focusedView != null, isTextEditor, softInputMode, first,
+ windowFlags);
} catch (RemoteException e) {
}
}
@@ -1150,8 +1158,9 @@
int candidatesStart, int candidatesEnd) {
checkFocus();
synchronized (mH) {
- if (mServedView != view || mCurrentTextBoxAttribute == null
- || mCurMethod == null) {
+ if ((mServedView != view && (mServedView == null
+ || !mServedView.checkInputConnectionProxy(view)))
+ || mCurrentTextBoxAttribute == null || mCurMethod == null) {
return;
}
@@ -1189,8 +1198,9 @@
public void updateCursor(View view, int left, int top, int right, int bottom) {
checkFocus();
synchronized (mH) {
- if (mServedView != view || mCurrentTextBoxAttribute == null
- || mCurMethod == null) {
+ if ((mServedView != view && (mServedView == null
+ || !mServedView.checkInputConnectionProxy(view)))
+ || mCurrentTextBoxAttribute == null || mCurMethod == null) {
return;
}
@@ -1223,7 +1233,8 @@
public void sendAppPrivateCommand(View view, String action, Bundle data) {
checkFocus();
synchronized (mH) {
- if ((view != null && mServedView != view)
+ if ((mServedView != view && (mServedView == null
+ || !mServedView.checkInputConnectionProxy(view)))
|| mCurrentTextBoxAttribute == null || mCurMethod == null) {
return;
}
diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java
index 39806dc..efc131f 100644
--- a/core/java/android/webkit/TextDialog.java
+++ b/core/java/android/webkit/TextDialog.java
@@ -32,8 +32,6 @@
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.MovementMethod;
-import android.text.method.PasswordTransformationMethod;
-import android.text.method.TextKeyListener;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.KeyCharacterMap;
@@ -475,15 +473,10 @@
* @param inPassword True if the textfield is a password field.
*/
/* package */ void setInPassword(boolean inPassword) {
- PasswordTransformationMethod method;
if (inPassword) {
- method = PasswordTransformationMethod.getInstance();
- setInputType(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.
+ setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.
TYPE_TEXT_VARIATION_PASSWORD);
- } else {
- method = null;
}
- setTransformationMethod(method);
}
/* package */ void setMaxLength(int maxLength) {
@@ -545,20 +538,13 @@
* removing the password input type.
*/
public void setSingleLine(boolean single) {
- if (mSingle != single) {
- TextKeyListener.Capitalize cap;
- int inputType = EditorInfo.TYPE_CLASS_TEXT;
- if (single) {
- cap = TextKeyListener.Capitalize.NONE;
- } else {
- cap = TextKeyListener.Capitalize.SENTENCES;
- inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
- }
- setKeyListener(TextKeyListener.getInstance(!single, cap));
- mSingle = single;
- setHorizontallyScrolling(single);
- setInputType(inputType);
+ int inputType = EditorInfo.TYPE_CLASS_TEXT;
+ if (!single) {
+ inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
}
+ mSingle = single;
+ setHorizontallyScrolling(single);
+ setInputType(inputType);
}
/**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 91795a3..dc39b90 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -73,7 +73,6 @@
import android.widget.Toast;
import android.widget.ZoomButtonsController;
import android.widget.ZoomControls;
-import android.widget.ZoomRingController;
import android.widget.FrameLayout;
import android.widget.AdapterView.OnItemClickListener;
@@ -239,8 +238,8 @@
*/
VelocityTracker mVelocityTracker;
- private static boolean mShowZoomRingTutorial = true;
- private static final int ZOOM_RING_TUTORIAL_DURATION = 3000;
+ private static boolean mShowZoomTutorial = true;
+ private static final int ZOOM_TUTORIAL_DURATION = 3000;
/**
* Touch mode
@@ -265,11 +264,6 @@
// Whether to forward the touch events to WebCore
private boolean mForwardTouchEvents = false;
- // Whether we are in the drag tap mode, which exists starting at the second
- // tap's down, through its move, and includes its up. These events should be
- // given to the method on the zoom controller.
- private boolean mInZoomTapDragMode = false;
-
// Whether to prevent drag during touch. The initial value depends on
// mForwardTouchEvents. If WebCore wants touch events, we assume it will
// take control of touch events unless it says no for touch down event.
@@ -361,7 +355,7 @@
private static final int UPDATE_TEXT_ENTRY_ADAPTER = 6;
private static final int SWITCH_TO_ENTER = 7;
private static final int RESUME_WEBCORE_UPDATE = 8;
- private static final int DISMISS_ZOOM_RING_TUTORIAL = 9;
+ private static final int DISMISS_ZOOM_TUTORIAL = 9;
//! arg1=x, arg2=y
static final int SCROLL_TO_MSG_ID = 10;
@@ -417,11 +411,6 @@
// width which view is considered to be fully zoomed out
static final int ZOOM_OUT_WIDTH = 1024;
- private static final float MAX_ZOOM_RING_ANGLE = (float) (Math.PI * 2 / 3);
- private static final int ZOOM_RING_STEPS = 4;
- private static final float ZOOM_RING_ANGLE_UNIT = MAX_ZOOM_RING_ANGLE
- / ZOOM_RING_STEPS;
-
private static final float DEFAULT_MAX_ZOOM_SCALE = 2;
private static final float DEFAULT_MIN_ZOOM_SCALE = (float) 1/3;
// scale limit, which can be set through viewport meta tag in the web page
@@ -560,146 +549,52 @@
}
private ZoomButtonsController mZoomButtonsController;
+ private ImageView mZoomOverviewButton;
- private ZoomRingController mZoomRingController;
- private ImageView mZoomRingOverview;
- private Animation mZoomRingOverviewExitAnimation;
-
- // These keep track of the center point of the zoom ring. They are used to
+ // These keep track of the center point of the zoom. They are used to
// determine the point around which we should zoom.
private float mZoomCenterX;
private float mZoomCenterY;
- private ZoomRingController.OnZoomListener mZoomListener =
- new ZoomRingController.OnZoomListener() {
-
- private float mClockwiseBound;
- private float mCounterClockwiseBound;
- private float mStartScale;
+ private ZoomButtonsController.OnZoomListener mZoomListener =
+ new ZoomButtonsController.OnZoomListener() {
public void onCenter(int x, int y) {
// Don't translate when the control is invoked, hence we do nothing
// in this callback
}
- public void onBeginPan() {
- setZoomOverviewVisible(false);
+ public void onOverview() {
+ mZoomButtonsController.setVisible(false);
+ zoomScrollOut();
if (mLogEvent) {
Checkin.updateStats(mContext.getContentResolver(),
- Checkin.Stats.Tag.BROWSER_ZOOM_RING_DRAG, 1, 0.0);
+ Checkin.Stats.Tag.BROWSER_ZOOM_OVERVIEW, 1, 0.0);
}
}
- public boolean onPan(int deltaX, int deltaY) {
- return pinScrollBy(deltaX, deltaY, false, 0);
- }
-
- public void onEndPan() {
- }
-
public void onVisibilityChanged(boolean visible) {
if (visible) {
switchOutDrawHistory();
- if (mMaxZoomScale - 1 > ZOOM_RING_STEPS * 0.01f) {
- mClockwiseBound = (float) (2 * Math.PI - MAX_ZOOM_RING_ANGLE);
- } else {
- mClockwiseBound = (float) (2 * Math.PI);
- }
- mZoomRingController.setThumbClockwiseBound(mClockwiseBound);
- if (1 - mMinZoomScale > ZOOM_RING_STEPS * 0.01f) {
- mCounterClockwiseBound = MAX_ZOOM_RING_ANGLE;
- } else {
- mCounterClockwiseBound = 0;
- }
- mZoomRingController
- .setThumbCounterclockwiseBound(mCounterClockwiseBound);
- float angle = 0f;
- if (mActualScale > 1 && mClockwiseBound < (float) (2 * Math.PI)) {
- angle = -(float) Math.round(ZOOM_RING_STEPS
- * (mActualScale - 1) / (mMaxZoomScale - 1))
- / ZOOM_RING_STEPS;
- } else if (mActualScale < 1 && mCounterClockwiseBound > 0) {
- angle = (float) Math.round(ZOOM_RING_STEPS
- * (1 - mActualScale) / (1 - mMinZoomScale))
- / ZOOM_RING_STEPS;
- }
- mZoomRingController.setThumbAngle(angle * MAX_ZOOM_RING_ANGLE);
-
- // Don't show a thumb if the user cannot zoom
- mZoomRingController.setThumbVisible(mMinZoomScale != mMaxZoomScale);
-
- // Show the zoom overview tab on the ring
- setZoomOverviewVisible(true);
- if (mLogEvent) {
- Checkin.updateStats(mContext.getContentResolver(),
- Checkin.Stats.Tag.BROWSER_ZOOM_RING, 1, 0.0);
- }
+ mZoomButtonsController.setOverviewVisible(true);
+ updateButtonsEnabled();
}
}
- public void onBeginDrag() {
- mPreviewZoomOnly = true;
- mStartScale = mActualScale;
- setZoomOverviewVisible(false);
- }
-
- public void onEndDrag() {
- mPreviewZoomOnly = false;
- if (mLogEvent) {
- EventLog.writeEvent(EVENT_LOG_ZOOM_LEVEL_CHANGE,
- (int) mStartScale * 100, (int) mActualScale * 100,
- System.currentTimeMillis());
- }
- setNewZoomScale(mActualScale, true);
+ private void updateButtonsEnabled() {
+ mZoomButtonsController.setZoomInEnabled(mActualScale < mMaxZoomScale);
+ mZoomButtonsController.setZoomOutEnabled(mActualScale > mMinZoomScale);
}
- public boolean onDragZoom(int deltaZoomLevel, int centerX,
- int centerY, float startAngle, float curAngle) {
- if (deltaZoomLevel < 0
- && Math.abs(mActualScale - mMinZoomScale) < 0.01f
- || deltaZoomLevel > 0
- && Math.abs(mActualScale - mMaxZoomScale) < 0.01f
- || deltaZoomLevel == 0) {
- return false;
- }
- mZoomCenterX = (float) centerX;
- mZoomCenterY = (float) centerY;
-
- float scale = 1.0f;
- // curAngle is [0, 2 * Math.PI)
- if (curAngle < (float) Math.PI) {
- if (curAngle >= mCounterClockwiseBound) {
- scale = mMinZoomScale;
- } else {
- scale = 1 - (float) Math.round(curAngle
- / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS
- * (1 - mMinZoomScale);
- }
- } else {
- if (curAngle <= mClockwiseBound) {
- scale = mMaxZoomScale;
- } else {
- scale = 1 + (float) Math.round(
- ((float) 2 * Math.PI - curAngle)
- / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS
- * (mMaxZoomScale - 1);
- }
- }
- zoomWithPreview(scale);
- return true;
- }
-
- public void onSimpleZoom(boolean zoomIn, int centerX, int centerY) {
- mZoomCenterX = (float) centerX;
- mZoomCenterY = (float) centerY;
-
+ public void onZoom(boolean zoomIn) {
if (zoomIn) {
zoomIn();
} else {
zoomOut();
}
+
+ updateButtonsEnabled();
}
-
};
/**
@@ -738,34 +633,8 @@
mFocusData.mX = 0;
mFocusData.mY = 0;
mScroller = new Scroller(context);
- mZoomRingController = new ZoomRingController(context, this);
- mZoomRingController.setCallback(mZoomListener);
- mZoomRingController.setTrackDrawable(
- com.android.internal.R.drawable.zoom_ring_track_absolute);
- float density = context.getResources().getDisplayMetrics().density;
- mZoomRingController.setPannerAcceleration((int) (160 * density));
- mZoomRingController.setPannerStartAcceleratingDuration((int) (700 * density));
- createZoomRingOverviewTab();
mZoomButtonsController = new ZoomButtonsController(context, this);
- mZoomButtonsController.setOverviewVisible(true);
- mZoomButtonsController.setCallback(new ZoomButtonsController.OnZoomListener() {
- public void onCenter(int x, int y) {
- mZoomListener.onCenter(x, y);
- }
-
- public void onOverview() {
- mZoomButtonsController.setVisible(false);
- zoomScrollOut();
- }
-
- public void onVisibilityChanged(boolean visible) {
- mZoomListener.onVisibilityChanged(visible);
- }
-
- public void onZoom(boolean zoomIn) {
- mZoomListener.onSimpleZoom(zoomIn, getWidth() / 2, getHeight() / 2);
- }
- });
+ mZoomButtonsController.setCallback(mZoomListener);
}
private void init() {
@@ -783,67 +652,6 @@
mDoubleTapSlopSquare = doubleTapslop * doubleTapslop;
}
- private void createZoomRingOverviewTab() {
- Context context = getContext();
-
- mZoomRingOverviewExitAnimation = AnimationUtils.loadAnimation(context,
- com.android.internal.R.anim.fade_out);
-
- mZoomRingOverview = new ImageView(context);
- mZoomRingOverview.setBackgroundResource(
- com.android.internal.R.drawable.zoom_ring_overview_tab);
- mZoomRingOverview.setImageResource(com.android.internal.R.drawable.btn_zoom_page);
-
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT,
- FrameLayout.LayoutParams.WRAP_CONTENT,
- Gravity.CENTER);
- // TODO: magic constant that's based on the zoom ring radius + some offset
- lp.topMargin = 200;
- mZoomRingOverview.setLayoutParams(lp);
- mZoomRingOverview.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- // Hide the zoom ring
- mZoomRingController.setVisible(false);
- if (mLogEvent) {
- Checkin.updateStats(mContext.getContentResolver(),
- Checkin.Stats.Tag.BROWSER_ZOOM_OVERVIEW, 1, 0.0);
- }
- zoomScrollOut();
- }});
-
- // Measure the overview View to figure out its height
- mZoomRingOverview.forceLayout();
- mZoomRingOverview.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-
- ViewGroup container = mZoomRingController.getContainer();
- // Find the index of the zoom ring in the container
- View zoomRing = container.findViewById(mZoomRingController.getZoomRingId());
- int zoomRingIndex;
- for (zoomRingIndex = container.getChildCount() - 1; zoomRingIndex >= 0; zoomRingIndex--) {
- if (container.getChildAt(zoomRingIndex) == zoomRing) break;
- }
- // Add the overview tab below the zoom ring (so we don't steal its events)
- container.addView(mZoomRingOverview, zoomRingIndex);
- // Since we use margins to adjust the vertical placement of the tab, the widget
- // ends up getting clipped off. Ensure the container is big enough for
- // us.
- int myHeight = mZoomRingOverview.getMeasuredHeight() + lp.topMargin / 2;
- // Multiplied by 2 b/c the zoom ring needs to be centered on the screen
- container.setMinimumHeight(myHeight * 2);
- }
-
- private void setZoomOverviewVisible(boolean visible) {
- int newVisibility = visible ? View.VISIBLE : View.INVISIBLE;
- if (mZoomRingOverview.getVisibility() == newVisibility) return;
-
- if (!visible) {
- mZoomRingOverview.startAnimation(mZoomRingOverviewExitAnimation);
- }
- mZoomRingOverview.setVisibility(newVisibility);
- }
-
/* package */ boolean onSavePassword(String schemePlusHost, String username,
String password, final Message resumeMsg) {
boolean rVal = false;
@@ -2988,6 +2796,7 @@
* @param end End of selection to delete.
*/
/* package */ void deleteSelection(int start, int end) {
+ mTextGeneration++;
mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, start, end,
new WebViewCore.FocusData(mFocusData));
}
@@ -3452,8 +3261,7 @@
p.setOnHierarchyChangeListener(null);
}
- // Clean up the zoom ring
- mZoomRingController.setVisible(false);
+ // Clean up the zoom controller
mZoomButtonsController.setVisible(false);
}
@@ -3565,9 +3373,7 @@
@Override
protected void onSizeChanged(int w, int h, int ow, int oh) {
super.onSizeChanged(w, h, ow, oh);
- // Center zooming to the center of the screen. This is appropriate for
- // this case of zooming, and it also sets us up properly if we remove
- // the new zoom ring controller
+ // Center zooming to the center of the screen.
mZoomCenterX = getViewWidth() * .5f;
mZoomCenterY = getViewHeight() * .5f;
@@ -3641,13 +3447,13 @@
return false;
}
- if (mShowZoomRingTutorial && getSettings().supportZoom()
- && (mMaxZoomScale - mMinZoomScale) > ZOOM_RING_STEPS * 0.01f) {
- ZoomRingController.showZoomTutorialOnce(mContext);
- mShowZoomRingTutorial = false;
+ if (mShowZoomTutorial && getSettings().supportZoom()
+ && (mMaxZoomScale != mMinZoomScale)) {
+ ZoomButtonsController.showZoomTutorialOnce(mContext);
+ mShowZoomTutorial = false;
mPrivateHandler.sendMessageDelayed(mPrivateHandler
- .obtainMessage(DISMISS_ZOOM_RING_TUTORIAL),
- ZOOM_RING_TUTORIAL_DURATION);
+ .obtainMessage(DISMISS_ZOOM_TUTORIAL),
+ ZOOM_TUTORIAL_DURATION);
}
if (LOGV_ENABLED) {
@@ -3655,15 +3461,6 @@
+ mTouchMode);
}
- if ((mZoomRingController.isVisible() || mZoomButtonsController.isVisible())
- && mInZoomTapDragMode) {
- if (ev.getAction() == MotionEvent.ACTION_UP) {
- // Just released the second tap, no longer in tap-drag mode
- mInZoomTapDragMode = false;
- }
- return mZoomRingController.handleDoubleTapEvent(ev);
- }
-
int action = ev.getAction();
float x = ev.getX();
float y = ev.getY();
@@ -3689,7 +3486,7 @@
WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
ted.mAction = action;
ted.mX = viewToContent((int) x + mScrollX);
- ted.mY = viewToContent((int) y + mScrollY);;
+ ted.mY = viewToContent((int) y + mScrollY);
mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
mLastSentTouchTime = eventTime;
}
@@ -3721,7 +3518,7 @@
nativeMoveSelection(viewToContent(mSelectX)
, viewToContent(mSelectY), false);
mTouchSelection = mExtendSelection = true;
- } else if (!ZoomRingController.useOldZoom(mContext) &&
+ } else if (!ZoomButtonsController.useOldZoom(mContext) &&
mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP) &&
(deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare)) {
// Found doubletap, invoke the zoom controller
@@ -3732,13 +3529,11 @@
mTextEntry.updateCachedTextfield();
}
nativeClearFocus(contentX, contentY);
- mInZoomTapDragMode = true;
if (mLogEvent) {
EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION,
(eventTime - mLastTouchUpTime), eventTime);
}
- return mZoomRingController.handleDoubleTapEvent(ev) ||
- mZoomButtonsController.handleDoubleTapEvent(ev);
+ return mZoomButtonsController.handleDoubleTapEvent(ev);
} else {
mTouchMode = TOUCH_INIT_MODE;
mPreventDrag = mForwardTouchEvents;
@@ -3747,9 +3542,8 @@
(eventTime - mLastTouchUpTime), eventTime);
}
}
- // don't trigger the link if zoom ring is visible
- if (mTouchMode == TOUCH_INIT_MODE
- && !mZoomRingController.isVisible()) {
+ // Trigger the link
+ if (mTouchMode == TOUCH_INIT_MODE) {
mPrivateHandler.sendMessageDelayed(mPrivateHandler
.obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
}
@@ -3885,7 +3679,7 @@
mUserScroll = true;
}
- if (ZoomRingController.useOldZoom(mContext)) {
+ if (ZoomButtonsController.useOldZoom(mContext)) {
boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
boolean showMagnify = canZoomScrollOut();
if (mZoomControls != null && (showPlusMinus || showMagnify)) {
@@ -3909,15 +3703,6 @@
mLastTouchUpTime = eventTime;
switch (mTouchMode) {
case TOUCH_INIT_MODE: // tap
- if (mZoomRingController.isVisible()) {
- // don't trigger the link if zoom ring is visible,
- // but still allow the double tap
- mPrivateHandler.sendMessageDelayed(mPrivateHandler
- .obtainMessage(RELEASE_SINGLE_TAP,
- new Boolean(false)),
- DOUBLE_TAP_TIMEOUT);
- break;
- }
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
if (getSettings().supportZoom()) {
mPrivateHandler.sendMessageDelayed(mPrivateHandler
@@ -4432,14 +4217,6 @@
}
/**
- * @hide pending API council? Assuming we make ZoomRingController itself
- * public, which I think we will.
- */
- public ZoomRingController getZoomRingController() {
- return mZoomRingController;
- }
-
- /**
* Perform zoom in in the webview
* @return TRUE if zoom in succeeds. FALSE if no zoom changes.
*/
@@ -4482,9 +4259,6 @@
});
zoomControls.setOnZoomMagnifyClickListener(new OnClickListener() {
public void onClick(View v) {
- // Hide the zoom ring
- mZoomRingController.setVisible(false);
-
mPrivateHandler.removeCallbacks(mZoomControlRunnable);
mPrivateHandler.postDelayed(mZoomControlRunnable,
ZOOM_CONTROLS_TIMEOUT);
@@ -4693,6 +4467,7 @@
arg.put("replace", replace);
arg.put("start", new Integer(newStart));
arg.put("end", new Integer(newEnd));
+ mTextGeneration++;
mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
}
@@ -5006,8 +4781,8 @@
}
break;
- case DISMISS_ZOOM_RING_TUTORIAL:
- mZoomRingController.finishZoomTutorial();
+ case DISMISS_ZOOM_TUTORIAL:
+ mZoomButtonsController.finishZoomTutorial();
break;
default:
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d72570a..f517dc9 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -41,6 +41,10 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputMethodManager;
import android.view.ContextMenu.ContextMenuInfo;
@@ -899,6 +903,10 @@
}
private boolean acceptFilter() {
+ if (!mTextFilterEnabled || !(getAdapter() instanceof Filterable) ||
+ ((Filterable) getAdapter()).getFilter() == null) {
+ return false;
+ }
final Context context = mContext;
final InputMethodManager inputManager = (InputMethodManager)
context.getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -1107,7 +1115,7 @@
// filter popup needs to follow the widget.
if (mFiltered && changed && getWindowVisibility() == View.VISIBLE && mPopup != null &&
mPopup.isShowing()) {
- positionPopup(true);
+ positionPopup();
}
return changed;
@@ -2767,20 +2775,20 @@
// Make sure we have a window before showing the popup
if (getWindowVisibility() == View.VISIBLE) {
createTextFilter(true);
- positionPopup(false);
+ positionPopup();
// Make sure we get focus if we are showing the popup
checkFocus();
}
}
- private void positionPopup(boolean update) {
+ private void positionPopup() {
int screenHeight = getResources().getDisplayMetrics().heightPixels;
final int[] xy = new int[2];
getLocationOnScreen(xy);
// TODO: The 20 below should come from the theme and be expressed in dip
// TODO: And the gravity should be defined in the theme as well
final int bottomGap = screenHeight - xy[1] - getHeight() + (int) (mDensityScale * 20);
- if (!update) {
+ if (!mPopup.isShowing()) {
mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL,
xy[0], bottomGap);
} else {
@@ -2849,8 +2857,7 @@
* @return True if the text filter handled the event, false otherwise.
*/
boolean sendToTextFilter(int keyCode, int count, KeyEvent event) {
- if (!mTextFilterEnabled || !(getAdapter() instanceof Filterable) ||
- ((Filterable) getAdapter()).getFilter() == null) {
+ if (!acceptFilter()) {
return false;
}
@@ -2879,7 +2886,7 @@
break;
}
- if (okToSend && acceptFilter()) {
+ if (okToSend) {
createTextFilter(true);
KeyEvent forwardEvent = event;
@@ -2906,6 +2913,27 @@
}
/**
+ * Return an InputConnection for editing of the filter text.
+ */
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ // XXX we need to have the text filter created, so we can get an
+ // InputConnection to proxy to. Unfortunately this means we pretty
+ // much need to make it as soon as a list view gets focus.
+ createTextFilter(false);
+ return mTextFilter.onCreateInputConnection(outAttrs);
+ }
+
+ /**
+ * For filtering we proxy an input connection to an internal text editor,
+ * and this allows the proxying to happen.
+ */
+ @Override
+ public boolean checkInputConnectionProxy(View view) {
+ return view == mTextFilter;
+ }
+
+ /**
* Creates the window for the text filter and populates it with an EditText field;
*
* @param animateEntrance true if the window should appear with an animation
@@ -2918,6 +2946,11 @@
c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mTextFilter = (EditText) layoutInflater.inflate(
com.android.internal.R.layout.typing_filter, null);
+ // For some reason setting this as the "real" input type changes
+ // the text view in some way that it doesn't work, and I don't
+ // want to figure out why this is.
+ mTextFilter.setRawInputType(EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_FILTER);
mTextFilter.addTextChangedListener(this);
p.setFocusable(false);
p.setTouchable(false);
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 0a552e8..3368477 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -26,7 +26,6 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.SystemClock;
-import android.util.TypedValue;
import android.view.MotionEvent;
/**
@@ -313,12 +312,17 @@
// Non-existent letter
while (section > 0) {
section--;
- prevIndex = mSectionIndexer.getPositionForSection(section);
- if (prevIndex != index) {
- prevSection = section;
- sectionIndex = section;
- break;
- }
+ prevIndex = mSectionIndexer.getPositionForSection(section);
+ if (prevIndex != index) {
+ prevSection = section;
+ sectionIndex = section;
+ break;
+ } else if (section == 0) {
+ // When section reaches 0 here, sectionIndex must follow it.
+ // Assuming mSectionIndexer.getPositionForSection(0) == 0.
+ sectionIndex = 0;
+ break;
+ }
}
}
// Find the next index, in case the assumed next index is not
diff --git a/core/java/android/widget/Filter.java b/core/java/android/widget/Filter.java
index 1d0fd5e..7e55c78 100644
--- a/core/java/android/widget/Filter.java
+++ b/core/java/android/widget/Filter.java
@@ -42,10 +42,12 @@
private static final String THREAD_NAME = "Filter";
private static final int FILTER_TOKEN = 0xD0D0F00D;
private static final int FINISH_TOKEN = 0xDEADBEEF;
-
+
private Handler mThreadHandler;
private Handler mResultHandler;
+ private final Object mLock = new Object();
+
/**
* <p>Creates a new asynchronous filter.</p>
*/
@@ -81,8 +83,7 @@
* @see #publishResults(CharSequence, android.widget.Filter.FilterResults)
*/
public final void filter(CharSequence constraint, FilterListener listener) {
- synchronized (this) {
-
+ synchronized (mLock) {
if (mThreadHandler == null) {
HandlerThread thread = new HandlerThread(THREAD_NAME);
thread.start();
@@ -221,7 +222,7 @@
message.sendToTarget();
}
- synchronized (this) {
+ synchronized (mLock) {
if (mThreadHandler != null) {
Message finishMessage = mThreadHandler.obtainMessage(FINISH_TOKEN);
mThreadHandler.sendMessageDelayed(finishMessage, 3000);
@@ -229,7 +230,7 @@
}
break;
case FINISH_TOKEN:
- synchronized (this) {
+ synchronized (mLock) {
if (mThreadHandler != null) {
mThreadHandler.getLooper().quit();
mThreadHandler = null;
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index f2cec92..227fb95 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -451,6 +451,7 @@
public void onProgressChanged(SeekBar bar, int progress, boolean fromtouch) {
if (fromtouch) {
mDragging = true;
+ duration = mPlayer.getDuration();
long newposition = (duration * progress) / 1000L;
mPlayer.seekTo( (int) newposition);
if (mCurrentTime != null)
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3f4912f..8271a9a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -43,6 +43,7 @@
import android.text.GraphicsOperations;
import android.text.ClipboardManager;
import android.text.InputFilter;
+import android.text.InputType;
import android.text.Layout;
import android.text.ParcelableSpan;
import android.text.Selection;
@@ -235,7 +236,7 @@
private CharWrapper mCharWrapper = null;
private boolean mSelectionMoved = false;
- private boolean mTouchFocusSelectedAll = false;
+ private boolean mTouchFocusSelected = false;
private Marquee mMarquee;
private boolean mRestartMarquee;
@@ -243,7 +244,7 @@
private int mMarqueeRepeatLimit = 3;
class InputContentType {
- int imeOptions = EditorInfo.IME_UNDEFINED;
+ int imeOptions = EditorInfo.IME_NULL;
String privateImeOptions;
CharSequence imeActionLabel;
int imeActionId;
@@ -261,6 +262,7 @@
final ExtractedText mTmpExtracted = new ExtractedText();
int mBatchEditNesting;
boolean mCursorChanged;
+ boolean mSelectionModeChanged;
boolean mContentChanged;
int mChangedStart, mChangedEnd, mChangedDelta;
}
@@ -287,8 +289,8 @@
*
* @param v The view that was clicked.
* @param actionId Identifier of the action. This will be either the
- * identifier you supplied, or {@link EditorInfo#IME_UNDEFINED
- * EditorInfo.IME_UNDEFINED} if being called due to the enter key
+ * identifier you supplied, or {@link EditorInfo#IME_NULL
+ * EditorInfo.IME_NULL} if being called due to the enter key
* being pressed.
* @param event If triggered by an enter key, this is the event;
* otherwise, this is null.
@@ -1492,10 +1494,10 @@
@Override
public void setPadding(int left, int top, int right, int bottom) {
- if (left != getPaddingLeft() ||
- right != getPaddingRight() ||
- top != getPaddingTop() ||
- bottom != getPaddingBottom()) {
+ if (left != mPaddingLeft ||
+ right != mPaddingRight ||
+ top != mPaddingTop ||
+ bottom != mPaddingBottom) {
nullLayouts();
}
@@ -2951,7 +2953,7 @@
*/
public int getImeOptions() {
return mInputContentType != null
- ? mInputContentType.imeOptions : EditorInfo.IME_UNDEFINED;
+ ? mInputContentType.imeOptions : EditorInfo.IME_NULL;
}
/**
@@ -3013,9 +3015,16 @@
* Called when an attached input method calls
* {@link InputConnection#performEditorAction(int)
* InputConnection.performEditorAction()}
- * for this text view. The default implementation will call your click
- * listener supplied to {@link #setOnEditorActionListener},
- * or generate an enter key down/up pair to invoke the action if not.
+ * for this text view. The default implementation will call your action
+ * listener supplied to {@link #setOnEditorActionListener}, or perform
+ * a standard operation for {@link EditorInfo#IME_ACTION_NEXT
+ * EditorInfo.IME_ACTION_NEXT} or {@link EditorInfo#IME_ACTION_DONE
+ * EditorInfo.IME_ACTION_DONE}.
+ *
+ * <p>For backwards compatibility, if no IME options have been set and the
+ * text view would not normally advance focus on enter, then
+ * the NEXT and DONE actions received here will be turned into an enter
+ * key down/up pair to go through the normal key handling.
*
* @param actionCode The code of the action being performed.
*
@@ -3052,6 +3061,7 @@
if (imm != null) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
+ return;
}
}
@@ -3844,7 +3854,7 @@
if (imm != null) {
if (imm.isActive(this)) {
boolean reported = false;
- if (ims.mContentChanged) {
+ if (ims.mContentChanged || ims.mSelectionModeChanged) {
// We are in extract mode and the content has changed
// in some way... just report complete new text to the
// input method.
@@ -4197,7 +4207,7 @@
&& mInputContentType.enterDown) {
mInputContentType.enterDown = false;
if (mInputContentType.onEditorActionListener.onEditorAction(
- this, EditorInfo.IME_UNDEFINED, event)) {
+ this, EditorInfo.IME_NULL, event)) {
return true;
}
}
@@ -4272,17 +4282,18 @@
outAttrs.actionId = mInputContentType.imeActionId;
outAttrs.extras = mInputContentType.extras;
} else {
- outAttrs.imeOptions = EditorInfo.IME_UNDEFINED;
+ outAttrs.imeOptions = EditorInfo.IME_NULL;
}
- if (outAttrs.imeOptions == EditorInfo.IME_UNDEFINED) {
+ if ((outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION)
+ == EditorInfo.IME_ACTION_UNSPECIFIED) {
if (focusSearch(FOCUS_DOWN) != null) {
// An action has not been set, but the enter key will move to
// the next focus, so set the action to that.
- outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
+ outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
} else {
// An action has not been set, and there is no focus to move
// to, so let's just supply a "done" action.
- outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
+ outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE;
}
if (!shouldAdvanceFocusOnEnter()) {
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
@@ -4307,51 +4318,64 @@
*/
public boolean extractText(ExtractedTextRequest request,
ExtractedText outText) {
- return extractTextInternal(request, -1, -1, -1, outText);
+ return extractTextInternal(request, EXTRACT_UNKNOWN, EXTRACT_UNKNOWN,
+ EXTRACT_UNKNOWN, outText);
}
+ static final int EXTRACT_NOTHING = -2;
+ static final int EXTRACT_UNKNOWN = -1;
+
boolean extractTextInternal(ExtractedTextRequest request,
int partialStartOffset, int partialEndOffset, int delta,
ExtractedText outText) {
final CharSequence content = mText;
if (content != null) {
- final int N = content.length();
- if (partialStartOffset < 0) {
- outText.partialStartOffset = outText.partialEndOffset = -1;
- partialStartOffset = 0;
- partialEndOffset = N;
- } else {
- // Adjust offsets to ensure we contain full spans.
- if (content instanceof Spanned) {
- Spanned spanned = (Spanned)content;
- Object[] spans = spanned.getSpans(partialStartOffset,
- partialEndOffset, ParcelableSpan.class);
- int i = spans.length;
- while (i > 0) {
- i--;
- int j = spanned.getSpanStart(spans[i]);
- if (j < partialStartOffset) partialStartOffset = j;
- j = spanned.getSpanEnd(spans[i]);
- if (j > partialEndOffset) partialEndOffset = j;
+ if (partialStartOffset != EXTRACT_NOTHING) {
+ final int N = content.length();
+ if (partialStartOffset < 0) {
+ outText.partialStartOffset = outText.partialEndOffset = -1;
+ partialStartOffset = 0;
+ partialEndOffset = N;
+ } else {
+ // Adjust offsets to ensure we contain full spans.
+ if (content instanceof Spanned) {
+ Spanned spanned = (Spanned)content;
+ Object[] spans = spanned.getSpans(partialStartOffset,
+ partialEndOffset, ParcelableSpan.class);
+ int i = spans.length;
+ while (i > 0) {
+ i--;
+ int j = spanned.getSpanStart(spans[i]);
+ if (j < partialStartOffset) partialStartOffset = j;
+ j = spanned.getSpanEnd(spans[i]);
+ if (j > partialEndOffset) partialEndOffset = j;
+ }
+ }
+ outText.partialStartOffset = partialStartOffset;
+ outText.partialEndOffset = partialEndOffset;
+ // Now use the delta to determine the actual amount of text
+ // we need.
+ partialEndOffset += delta;
+ if (partialEndOffset > N) {
+ partialEndOffset = N;
+ } else if (partialEndOffset < 0) {
+ partialEndOffset = 0;
}
}
- outText.partialStartOffset = partialStartOffset;
- outText.partialEndOffset = partialEndOffset;
- // Now use the delta to determine the actual amount of text
- // we need.
- partialEndOffset += delta;
- if (partialEndOffset > N) {
- partialEndOffset = N;
- } else if (partialEndOffset < 0) {
- partialEndOffset = 0;
+ if ((request.flags&InputConnection.GET_TEXT_WITH_STYLES) != 0) {
+ outText.text = content.subSequence(partialStartOffset,
+ partialEndOffset);
+ } else {
+ outText.text = TextUtils.substring(content, partialStartOffset,
+ partialEndOffset);
}
}
- if ((request.flags&InputConnection.GET_TEXT_WITH_STYLES) != 0) {
- outText.text = content.subSequence(partialStartOffset,
- partialEndOffset);
- } else {
- outText.text = TextUtils.substring(content, partialStartOffset,
- partialEndOffset);
+ outText.flags = 0;
+ if (MetaKeyKeyListener.getMetaState(mText, MetaKeyKeyListener.META_SELECTING) != 0) {
+ outText.flags |= ExtractedText.FLAG_SELECTING;
+ }
+ if (mSingleLine) {
+ outText.flags |= ExtractedText.FLAG_SINGLE_LINE;
}
outText.startOffset = 0;
outText.selectionStart = Selection.getSelectionStart(content);
@@ -4363,8 +4387,10 @@
boolean reportExtractedText() {
final InputMethodState ims = mInputMethodState;
- if (ims != null && ims.mContentChanged) {
+ final boolean contentChanged = ims.mContentChanged;
+ if (ims != null && (contentChanged || ims.mSelectionModeChanged)) {
ims.mContentChanged = false;
+ ims.mSelectionModeChanged = false;
final ExtractedTextRequest req = mInputMethodState.mExtracting;
if (req != null) {
InputMethodManager imm = InputMethodManager.peekInstance();
@@ -4372,6 +4398,9 @@
if (DEBUG_EXTRACT) Log.v(TAG, "Retrieving extracted start="
+ ims.mChangedStart + " end=" + ims.mChangedEnd
+ " delta=" + ims.mChangedDelta);
+ if (ims.mChangedStart < 0 && !contentChanged) {
+ ims.mChangedStart = EXTRACT_NOTHING;
+ }
if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd,
ims.mChangedDelta, ims.mTmpExtracted)) {
if (DEBUG_EXTRACT) Log.v(TAG, "Reporting extracted start="
@@ -4408,19 +4437,21 @@
*/
public void setExtractedText(ExtractedText text) {
Editable content = getEditableText();
- if (content == null) {
- setText(text.text, TextView.BufferType.EDITABLE);
- } else if (text.partialStartOffset < 0) {
- removeParcelableSpans(content, 0, content.length());
- content.replace(0, content.length(), text.text);
- } else {
- final int N = content.length();
- int start = text.partialStartOffset;
- if (start > N) start = N;
- int end = text.partialEndOffset;
- if (end > N) end = N;
- removeParcelableSpans(content, start, end);
- content.replace(start, end, text.text);
+ if (text.text != null) {
+ if (content == null) {
+ setText(text.text, TextView.BufferType.EDITABLE);
+ } else if (text.partialStartOffset < 0) {
+ removeParcelableSpans(content, 0, content.length());
+ content.replace(0, content.length(), text.text);
+ } else {
+ final int N = content.length();
+ int start = text.partialStartOffset;
+ if (start > N) start = N;
+ int end = text.partialEndOffset;
+ if (end > N) end = N;
+ removeParcelableSpans(content, start, end);
+ content.replace(start, end, text.text);
+ }
}
// Now set the selection position... make sure it is in range, to
@@ -4436,6 +4467,13 @@
if (end < 0) end = 0;
else if (end > N) end = N;
Selection.setSelection(sp, start, end);
+
+ // Finally, update the selection mode.
+ if ((text.flags&ExtractedText.FLAG_SELECTING) != 0) {
+ MetaKeyKeyListener.startSelecting(this, sp);
+ } else {
+ MetaKeyKeyListener.stopSelecting(this, sp);
+ }
}
/**
@@ -4473,8 +4511,8 @@
ims.mChangedStart = 0;
ims.mChangedEnd = mText.length();
} else {
- ims.mChangedStart = -1;
- ims.mChangedEnd = -1;
+ ims.mChangedStart = EXTRACT_UNKNOWN;
+ ims.mChangedEnd = EXTRACT_UNKNOWN;
ims.mContentChanged = false;
}
onBeginBatchEdit();
@@ -4503,7 +4541,7 @@
void finishBatchEdit(final InputMethodState ims) {
onEndBatchEdit();
- if (ims.mContentChanged) {
+ if (ims.mContentChanged || ims.mSelectionModeChanged) {
updateAfterEdit();
reportExtractedText();
} else if (ims.mCursorChanged) {
@@ -5888,6 +5926,9 @@
if (MetaKeyKeyListener.isMetaTracker(buf, what)) {
mHighlightPathBogus = true;
+ if (ims != null && MetaKeyKeyListener.isSelectingMetaTracker(buf, what)) {
+ ims.mSelectionModeChanged = true;
+ }
if (Selection.getSelectionStart(buf) >= 0) {
if (ims == null || ims.mBatchEditNesting == 0) {
@@ -6026,7 +6067,6 @@
if (mSelectAllOnFocus) {
Selection.setSelection((Spannable) mText, 0, mText.length());
- mTouchFocusSelectedAll = true;
}
if (selMoved && selStart >= 0 && selEnd >= 0) {
@@ -6042,6 +6082,7 @@
Selection.setSelection((Spannable) mText, selStart, selEnd);
}
+ mTouchFocusSelected = true;
}
mFrozenWithFocus = false;
@@ -6149,7 +6190,7 @@
if (action == MotionEvent.ACTION_DOWN) {
// Reset this state; it will be re-set if super.onTouchEvent
// causes focus to move to the view.
- mTouchFocusSelectedAll = false;
+ mTouchFocusSelected = false;
}
final boolean superResult = super.onTouchEvent(event);
@@ -6218,10 +6259,11 @@
/**
* Returns true, only while processing a touch gesture, if the initial
* touch down event caused focus to move to the text view and as a result
- * it selected all of its text.
+ * its selection changed. Only valid while processing the touch gesture
+ * of interest.
*/
- public boolean didTouchFocusSelectAll() {
- return mTouchFocusSelectedAll;
+ public boolean didTouchFocusSelect() {
+ return mTouchFocusSelected;
}
@Override
@@ -6351,6 +6393,11 @@
return super.computeVerticalScrollRange();
}
+ @Override
+ protected int computeVerticalScrollExtent() {
+ return getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
+ }
+
public enum BufferType {
NORMAL, SPANNABLE, EDITABLE,
}
@@ -6497,6 +6544,26 @@
* or null if there is no cursor or no word at the cursor.
*/
private String getWordForDictionary() {
+ /*
+ * Quick return if the input type is one where adding words
+ * to the dictionary doesn't make any sense.
+ */
+ int klass = mInputType & InputType.TYPE_MASK_CLASS;
+ if (klass == InputType.TYPE_CLASS_NUMBER ||
+ klass == InputType.TYPE_CLASS_PHONE ||
+ klass == InputType.TYPE_CLASS_DATETIME) {
+ return null;
+ }
+
+ int variation = mInputType & InputType.TYPE_MASK_VARIATION;
+ if (variation == InputType.TYPE_TEXT_VARIATION_URI ||
+ variation == InputType.TYPE_TEXT_VARIATION_PASSWORD ||
+ variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ||
+ variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ||
+ variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
+ return null;
+ }
+
int end = getSelectionEnd();
if (end < 0) {
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 1227afd..ec6f88b 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -46,8 +46,10 @@
* such as scaling and tinting.
*/
public class VideoView extends SurfaceView implements MediaPlayerControl {
+ private String TAG = "VideoView";
// settable by the client
private Uri mUri;
+ private int mDuration;
// All the stuff we need for playing and showing a video
private SurfaceHolder mSurfaceHolder = null;
@@ -184,6 +186,8 @@
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mIsPrepared = false;
+ Log.v(TAG, "reset duration to -1 in openVideo");
+ mDuration = -1;
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
@@ -195,10 +199,10 @@
mMediaPlayer.prepareAsync();
attachMediaController();
} catch (IOException ex) {
- Log.w("VideoView", "Unable to open content: " + mUri, ex);
+ Log.w(TAG, "Unable to open content: " + mUri, ex);
return;
} catch (IllegalArgumentException ex) {
- Log.w("VideoView", "Unable to open content: " + mUri, ex);
+ Log.w(TAG, "Unable to open content: " + mUri, ex);
return;
}
}
@@ -299,7 +303,7 @@
private MediaPlayer.OnErrorListener mErrorListener =
new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer mp, int a, int b) {
- Log.d("VideoView", "Error: " + a + "," + b);
+ Log.d(TAG, "Error: " + a + "," + b);
if (mMediaController != null) {
mMediaController.hide();
}
@@ -497,9 +501,14 @@
public int getDuration() {
if (mMediaPlayer != null && mIsPrepared) {
- return mMediaPlayer.getDuration();
+ if (mDuration > 0) {
+ return mDuration;
+ }
+ mDuration = mMediaPlayer.getDuration();
+ return mDuration;
}
- return -1;
+ mDuration = -1;
+ return mDuration;
}
public int getCurrentPosition() {
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index ec45e23..1ba2dce 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -16,14 +16,19 @@
package android.widget;
+import android.app.AlertDialog;
+import android.app.Dialog;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
+import android.os.SystemClock;
import android.provider.Settings;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -31,6 +36,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.WindowManager.LayoutParams;
@@ -46,7 +52,7 @@
*
* @hide
*/
-public class ZoomButtonsController implements View.OnTouchListener {
+public class ZoomButtonsController implements View.OnTouchListener, View.OnKeyListener {
private static final String TAG = "ZoomButtonsController";
@@ -60,13 +66,13 @@
private WindowManager mWindowManager;
/**
- * The view that is being zoomed by this zoom ring.
+ * The view that is being zoomed by this zoom controller.
*/
private View mOwnerView;
/**
* The bounds of the owner view in global coordinates. This is recalculated
- * each time the zoom ring is shown.
+ * each time the zoom controller is shown.
*/
private Rect mOwnerViewBounds = new Rect();
@@ -89,11 +95,13 @@
*/
private int[] mTouchTargetLocationInWindow = new int[2];
/**
- * If the zoom ring is dismissed but the user is still in a touch
+ * If the zoom controller is dismissed but the user is still in a touch
* interaction, we set this to true. This will ignore all touch events until
* up/cancel, and then set the owner's touch listener to null.
*/
private boolean mReleaseTouchListenerOnUp;
+
+ private boolean mIsSecondTapDown;
private boolean mIsVisible;
@@ -122,10 +130,16 @@
}
};
+ /**
+ * The setting name that tracks whether we've shown the zoom tutorial.
+ */
+ private static final String SETTING_NAME_SHOWN_TUTORIAL = "shown_zoom_tutorial";
+ private static Dialog sTutorialDialog;
+
/** When configuration changes, this is called after the UI thread is idle. */
private static final int MSG_POST_CONFIGURATION_CHANGED = 2;
- /** Used to delay the zoom ring dismissal. */
- private static final int MSG_DISMISS_ZOOM_RING = 3;
+ /** Used to delay the zoom controller dismissal. */
+ private static final int MSG_DISMISS_ZOOM_CONTROLS = 3;
/**
* If setVisible(true) is called and the owner view's window token is null,
* we delay the setVisible(true) call until it is not null.
@@ -140,7 +154,7 @@
onPostConfigurationChanged();
break;
- case MSG_DISMISS_ZOOM_RING:
+ case MSG_DISMISS_ZOOM_CONTROLS:
setVisible(false);
break;
@@ -148,7 +162,7 @@
if (mOwnerView.getWindowToken() == null) {
// Doh, it is still null, throw an exception
throw new IllegalArgumentException(
- "Cannot make the zoom ring visible if the owner view is " +
+ "Cannot make the zoom controller visible if the owner view is " +
"not attached to a window.");
}
setVisible(true);
@@ -165,11 +179,24 @@
mContainer = createContainer();
}
-
+
+ public void setZoomInEnabled(boolean enabled) {
+ mControls.setIsZoomInEnabled(enabled);
+ }
+
+ public void setZoomOutEnabled(boolean enabled) {
+ mControls.setIsZoomOutEnabled(enabled);
+ }
+
+ public void setZoomSpeed(long speed) {
+ mControls.setZoomSpeed(speed);
+ }
+
private FrameLayout createContainer() {
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.BOTTOM | Gravity.CENTER;
lp.flags = LayoutParams.FLAG_NOT_TOUCHABLE |
+ LayoutParams.FLAG_NOT_FOCUSABLE |
LayoutParams.FLAG_LAYOUT_NO_LIMITS;
lp.height = LayoutParams.WRAP_CONTENT;
lp.width = LayoutParams.FILL_PARENT;
@@ -182,6 +209,7 @@
FrameLayout container = new FrameLayout(mContext);
container.setLayoutParams(lp);
container.setMeasureAllChildren(true);
+ container.setOnKeyListener(this);
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -326,13 +354,13 @@
return mContainer;
}
- public int getZoomRingId() {
+ public int getZoomControlsId() {
return mControls.getId();
}
private void dismissControlsDelayed(int delay) {
- mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
- mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_RING, delay);
+ mHandler.removeMessages(MSG_DISMISS_ZOOM_CONTROLS);
+ mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_CONTROLS, delay);
}
/**
@@ -351,8 +379,21 @@
int x = (int) event.getX();
int y = (int) event.getY();
+ /*
+ * This class will consume all events in the second tap (down,
+ * move(s), up). But, the owner already got the second tap's down,
+ * so cancel that. Do this before setVisible, since that call
+ * will set us as a touch listener.
+ */
+ MotionEvent cancelEvent = MotionEvent.obtain(event.getDownTime(),
+ SystemClock.elapsedRealtime(),
+ MotionEvent.ACTION_CANCEL, 0, 0, 0);
+ mOwnerView.dispatchTouchEvent(cancelEvent);
+ cancelEvent.recycle();
+
setVisible(true);
centerPoint(x, y);
+ mIsSecondTapDown = true;
}
return true;
@@ -373,11 +414,23 @@
}
}
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
+ return false;
+ }
+
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
+ // Consume all events during the second-tap interaction (down, move, up/cancel)
+ boolean consumeEvent = mIsSecondTapDown;
+ if ((action == MotionEvent.ACTION_UP) || (action == MotionEvent.ACTION_CANCEL)) {
+ // The second tap can no longer be down
+ mIsSecondTapDown = false;
+ }
+
if (mReleaseTouchListenerOnUp) {
- // The ring was dismissed but we need to throw away all events until the up
+ // The controls were dismissed but we need to throw away all events until the up
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
mOwnerView.setOnTouchListener(null);
setTouchTargetView(null);
@@ -417,10 +470,10 @@
mOwnerViewBounds.top - targetViewRawY);
boolean retValue = targetView.dispatchTouchEvent(containerEvent);
containerEvent.recycle();
- return retValue;
+ return retValue || consumeEvent;
} else {
- return false;
+ return consumeEvent;
}
}
@@ -465,10 +518,102 @@
refreshPositioningVariables();
}
- public static boolean useThisZoom(Context context) {
- return ZoomRingController.getZoomType(context) == 2;
+ /*
+ * This is static so Activities can call this instead of the Views
+ * (Activities usually do not have a reference to the ZoomButtonsController
+ * instance.)
+ */
+ /**
+ * Shows a "tutorial" (some text) to the user teaching her the new zoom
+ * invocation method. Must call from the main thread.
+ * <p>
+ * It checks the global system setting to ensure this has not been seen
+ * before. Furthermore, if the application does not have privilege to write
+ * to the system settings, it will store this bit locally in a shared
+ * preference.
+ *
+ * @hide This should only be used by our main apps--browser, maps, and
+ * gallery
+ */
+ public static void showZoomTutorialOnce(Context context) {
+ ContentResolver cr = context.getContentResolver();
+ if (Settings.System.getInt(cr, SETTING_NAME_SHOWN_TUTORIAL, 0) == 1) {
+ return;
+ }
+
+ SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE);
+ if (sp.getInt(SETTING_NAME_SHOWN_TUTORIAL, 0) == 1) {
+ return;
+ }
+
+ if (sTutorialDialog != null && sTutorialDialog.isShowing()) {
+ sTutorialDialog.dismiss();
+ }
+
+ LayoutInflater layoutInflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ TextView textView = (TextView) layoutInflater.inflate(
+ com.android.internal.R.layout.alert_dialog_simple_text, null)
+ .findViewById(android.R.id.text1);
+ textView.setText(com.android.internal.R.string.tutorial_double_tap_to_zoom_message_short);
+
+ sTutorialDialog = new AlertDialog.Builder(context)
+ .setView(textView)
+ .setIcon(0)
+ .create();
+
+ Window window = sTutorialDialog.getWindow();
+ window.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND |
+ WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+
+ sTutorialDialog.show();
}
-
+
+ /** @hide Should only be used by Android platform apps */
+ public static void finishZoomTutorial(Context context, boolean userNotified) {
+ if (sTutorialDialog == null) return;
+
+ sTutorialDialog.dismiss();
+ sTutorialDialog = null;
+
+ // Record that they have seen the tutorial
+ if (userNotified) {
+ try {
+ Settings.System.putInt(context.getContentResolver(), SETTING_NAME_SHOWN_TUTORIAL,
+ 1);
+ } catch (SecurityException e) {
+ /*
+ * The app does not have permission to clear this global flag, make
+ * sure the user does not see the message when he comes back to this
+ * same app at least.
+ */
+ SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE);
+ sp.edit().putInt(SETTING_NAME_SHOWN_TUTORIAL, 1).commit();
+ }
+ }
+ }
+
+ /** @hide Should only be used by Android platform apps */
+ public void finishZoomTutorial() {
+ finishZoomTutorial(mContext, true);
+ }
+
+ // Temporary methods for different zoom types
+ static int getZoomType(Context context) {
+ return Settings.System.getInt(context.getContentResolver(), "zoom", 1);
+ }
+
+ public static boolean useOldZoom(Context context) {
+ return getZoomType(context) == 0;
+ }
+
+ public static boolean useThisZoom(Context context) {
+ return getZoomType(context) == 2;
+ }
+
public interface OnZoomListener {
void onCenter(int x, int y);
void onVisibilityChanged(boolean visible);
diff --git a/core/java/android/widget/ZoomControls.java b/core/java/android/widget/ZoomControls.java
index fdc05b6..e978db8 100644
--- a/core/java/android/widget/ZoomControls.java
+++ b/core/java/android/widget/ZoomControls.java
@@ -81,7 +81,7 @@
}
public void show() {
- if (ZoomRingController.useOldZoom(mContext)) {
+ if (ZoomButtonsController.useOldZoom(mContext)) {
fade(View.VISIBLE, 0.0f, 1.0f);
}
}
diff --git a/core/java/android/widget/ZoomRing.java b/core/java/android/widget/ZoomRing.java
deleted file mode 100644
index 83a1225..0000000
--- a/core/java/android/widget/ZoomRing.java
+++ /dev/null
@@ -1,1274 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget;
-
-import com.android.internal.R;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RotateDrawable;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-/**
- * A view that has a draggable thumb on a circle.
- *
- * @hide
- */
-public class ZoomRing extends View {
- private static final String TAG = "ZoomRing";
-
- // TODO: Temporary until the trail is done
- private static final boolean DRAW_TRAIL = false;
-
- /**
- * To avoid floating point calculations and int round-offs, we multiply
- * radians by this value.
- */
- public static final int RADIAN_INT_MULTIPLIER = 10000;
- /** The allowable margin of error when comparing two angles. */
- public static final int RADIAN_INT_ERROR = 100;
- public static final int PI_INT_MULTIPLIED = (int) (Math.PI * RADIAN_INT_MULTIPLIER);
- public static final int TWO_PI_INT_MULTIPLIED = PI_INT_MULTIPLIED * 2;
- private static final int HALF_PI_INT_MULTIPLIED = PI_INT_MULTIPLIED / 2;
-
- private static final int DOUBLE_TAP_DISMISS_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
- private final int mTouchSlop;
-
- /** The slop when the user is grabbing the thumb. */
- private static final int THUMB_GRAB_SLOP = PI_INT_MULTIPLIED / 8;
- /** The slop until a user starts dragging the thumb. */
- private static final int THUMB_DRAG_SLOP = PI_INT_MULTIPLIED / 12;
-
- /** The distance (in px) from the center of the ring to the center of the thumb. */
- private int mThumbDistance;
-
- /** The angle on a unit circle that is considered to be the zoom ring's 0 degree. */
- private int mZeroAngle = HALF_PI_INT_MULTIPLIED * 3;
-
- /**
- * The maximum delta angle that the thumb can move. The primary use is to
- * ensure that when a user taps on the ring, the movement to reach that
- * target angle is not ambiguous (for example, if the thumb is at 0 and he
- * taps 180, should the thumb go clockwise or counterclockwise?
- * <p>
- * Includes error because we compare this to the result of
- * getDelta(getClosestTickeAngle(..), oldAngle) which ends up having some
- * rounding error.
- */
- private static final int MAX_ABS_JUMP_DELTA_ANGLE = (2 * PI_INT_MULTIPLIED / 3) +
- RADIAN_INT_ERROR;
-
- /** The cached X of the zoom ring's center (in zoom ring coordinates). */
- private int mCenterX;
- /** The cached Y of the zoom ring's center (in zoom ring coordinates). */
- private int mCenterY;
-
- /** The angle of the thumb (in int radians) */
- private int mThumbAngle;
- /** The cached width/2 of the zoom ring. */
- private int mThumbHalfWidth;
- /** The cached height/2 of the zoom ring. */
- private int mThumbHalfHeight;
-
- /**
- * The bound for the thumb's movement when it is being dragged clockwise.
- * Can be Integer.MIN_VALUE if there is no bound in this direction.
- */
- private int mThumbCwBound = Integer.MIN_VALUE;
- /**
- * The bound for the thumb's movement when it is being dragged
- * counterclockwise. Can be Integer.MIN_VALUE if there is no bound in this
- * direction.
- */
- private int mThumbCcwBound = Integer.MIN_VALUE;
-
- /**
- * Whether to enforce the maximum absolute jump delta. See
- * {@link #MAX_ABS_JUMP_DELTA_ANGLE}.
- */
- private boolean mEnforceMaxAbsJump = true;
-
- /** The inner radius of the track. */
- private int mTrackInnerRadius;
- /** Cached square of the inner radius of the track. */
- private int mTrackInnerRadiusSquared;
- /** The outer radius of the track. */
- private int mTrackOuterRadius;
- /** Cached square of the outer radius of the track. */
- private int mTrackOuterRadiusSquared;
-
- /** The raw X of where the widget previously was located. */
- private int mPreviousWidgetDragX;
- /** The raw Y of where the widget previously was located. */
- private int mPreviousWidgetDragY;
-
- /** Whether the thumb should be visible. */
- private boolean mThumbVisible = true;
-
- /** The drawable for the thumb. */
- private Drawable mThumbDrawable;
-
- /** Shown beneath the thumb if we can still zoom in. */
- private Drawable mZoomInArrowDrawable;
- /** Shown beneath the thumb if we can still zoom out. */
- private Drawable mZoomOutArrowDrawable;
-
- /** @see #mThumbArrowsToDraw */
- private static final int THUMB_ARROW_PLUS = 1 << 0;
- /** @see #mThumbArrowsToDraw */
- private static final int THUMB_ARROW_MINUS = 1 << 1;
- /** Bitwise-OR of {@link #THUMB_ARROW_MINUS} and {@link #THUMB_ARROW_PLUS} */
- private int mThumbArrowsToDraw;
-
- /** The duration for the thumb arrows fading out */
- private static final int THUMB_ARROWS_FADE_DURATION = 300;
- /** The time when the fade out started. */
- private long mThumbArrowsFadeStartTime;
- /** The current alpha for the thumb arrows. */
- private int mThumbArrowsAlpha = 255;
-
- /** The distance from the center to the zoom arrow hints (usually plus and minus). */
- private int mZoomArrowHintDistance;
- /** The offset angle from the thumb angle to draw the zoom arrow hints. */
- private int mZoomArrowHintOffsetAngle = TWO_PI_INT_MULTIPLIED / 11;
- /** Drawn (without rotation) on top of the arrow. */
- private Drawable mZoomInArrowHintDrawable;
- /** Drawn (without rotation) on top of the arrow. */
- private Drawable mZoomOutArrowHintDrawable;
-
- /** Zoom ring is just chillin' */
- private static final int MODE_IDLE = 0;
- /**
- * User has his finger down somewhere on the ring (besides the thumb) and we
- * are waiting for him to move the slop amount before considering him in the
- * drag thumb state.
- */
- private static final int MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP = 5;
- /** User is dragging the thumb. */
- private static final int MODE_DRAG_THUMB = 1;
- /**
- * User has his finger down, but we are waiting for him to pass the touch
- * slop before going into the #MODE_MOVE_ZOOM_RING. This is a good time to
- * show the movable hint.
- */
- private static final int MODE_WAITING_FOR_MOVE_ZOOM_RING = 4;
- /** User is moving the zoom ring. */
- private static final int MODE_MOVE_ZOOM_RING = 2;
- /** User is dragging the thumb via tap-drag. */
- private static final int MODE_TAP_DRAG = 3;
- /** Ignore the touch interaction until the user touches the thumb again. */
- private static final int MODE_IGNORE_UNTIL_TOUCHES_THUMB = 6;
- /** The current mode of interaction. */
- private int mMode;
- /** Records the last mode the user was in. */
- private int mPreviousMode;
-
- /** The previous time of the up-touch on the center. */
- private long mPreviousCenterUpTime;
- /** The previous X of down-touch. */
- private int mPreviousDownX;
- /** The previous Y of down-touch. */
- private int mPreviousDownY;
-
- /** The angle where the user first grabbed the thumb. */
- private int mInitialGrabThumbAngle;
-
- /** The callback. */
- private OnZoomRingCallback mCallback;
- /** The tick angle that we previously called back with. */
- private int mPreviousCallbackTickAngle;
- /** The delta angle between ticks. A tick is a callback point. */
- private int mTickDelta = Integer.MAX_VALUE;
- /** If the user drags to within __% of a tick, snap to that tick. */
- private int mFuzzyTickDelta = Integer.MAX_VALUE;
-
- /** The angle where the thumb is officially starting to be dragged. */
- private int mThumbDragStartAngle;
-
- /** The drawable for the zoom trail. */
- private Drawable mTrail;
- /** The accumulated angle for the trail. */
- private double mAcculumalatedTrailAngle;
-
- /** The animation-step tracker for scrolling the thumb to a particular position. */
- private Scroller mThumbScroller;
-
- /** Whether to ever vibrate when passing a tick. */
- private boolean mVibration = true;
-
- /** The drawable used to hint that this can pan its owner. */
- private Drawable mPanningArrowsDrawable;
-
- private static final int MSG_THUMB_SCROLLER_STEP = 1;
- private static final int MSG_THUMB_ARROWS_FADE_STEP = 2;
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_THUMB_SCROLLER_STEP:
- onThumbScrollerStep();
- break;
-
- case MSG_THUMB_ARROWS_FADE_STEP:
- onThumbArrowsFadeStep();
- break;
- }
- }
- };
-
- public ZoomRing(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
- mTouchSlop = viewConfiguration.getScaledTouchSlop();
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ZoomRing, defStyle, 0);
- mThumbDistance = (int) a.getDimension(R.styleable.ZoomRing_thumbDistance, 0);
- setTrackRadii(
- (int) a.getDimension(R.styleable.ZoomRing_trackInnerRadius, 0),
- (int) a.getDimension(R.styleable.ZoomRing_trackOuterRadius, Integer.MAX_VALUE));
- mThumbDrawable = a.getDrawable(R.styleable.ZoomRing_thumbDrawable);
- mZoomInArrowDrawable = a.getDrawable(R.styleable.ZoomRing_zoomInArrowDrawable);
- mZoomOutArrowDrawable = a.getDrawable(R.styleable.ZoomRing_zoomOutArrowDrawable);
- mZoomInArrowHintDrawable = a.getDrawable(R.styleable.ZoomRing_zoomInArrowHintDrawable);
- mZoomOutArrowHintDrawable = a.getDrawable(R.styleable.ZoomRing_zoomOutArrowHintDrawable);
- mZoomArrowHintDistance =
- (int) a.getDimension(R.styleable.ZoomRing_zoomArrowHintDistance, 0);
- mZoomArrowHintOffsetAngle =
- (int) (a.getInteger(R.styleable.ZoomRing_zoomArrowHintOffsetAngle, 0)
- * TWO_PI_INT_MULTIPLIED / 360);
- mPanningArrowsDrawable = a.getDrawable(R.styleable.ZoomRing_panningArrowsDrawable);
- a.recycle();
-
- Resources res = context.getResources();
- if (DRAW_TRAIL) {
- // TODO get drawables from style instead
- mTrail = res.getDrawable(R.drawable.zoom_ring_trail).mutate();
- }
-
- mThumbHalfHeight = mThumbDrawable.getIntrinsicHeight() / 2;
- mThumbHalfWidth = mThumbDrawable.getIntrinsicWidth() / 2;
-
- setTickDelta(PI_INT_MULTIPLIED / 6);
- }
-
- public ZoomRing(Context context, AttributeSet attrs) {
- this(context, attrs, com.android.internal.R.attr.zoomRingStyle);
- }
-
- public ZoomRing(Context context) {
- this(context, null);
- }
-
- public void setTrackDrawable(Drawable drawable) {
- setBackgroundDrawable(drawable);
- }
-
- public void setCallback(OnZoomRingCallback callback) {
- mCallback = callback;
- }
-
- /**
- * Sets the distance between ticks. This will be used as a callback threshold.
- *
- * @param angle The angle between ticks.
- */
- public void setTickDelta(int angle) {
- mTickDelta = angle;
- mFuzzyTickDelta = (int) (angle * 0.65f);
- }
-
- public void setVibration(boolean vibration) {
- mVibration = vibration;
- }
-
- public void setThumbVisible(boolean thumbVisible) {
- if (mThumbVisible != thumbVisible) {
- mThumbVisible = thumbVisible;
- invalidate();
- }
- }
-
- public Drawable getPanningArrowsDrawable() {
- return mPanningArrowsDrawable;
- }
-
- public void setTrackRadii(int innerRadius, int outerRadius) {
- mTrackInnerRadius = innerRadius;
- mTrackOuterRadius = outerRadius;
-
- mTrackInnerRadiusSquared = innerRadius * innerRadius;
- if (mTrackInnerRadiusSquared < innerRadius) {
- // Prevent overflow
- mTrackInnerRadiusSquared = Integer.MAX_VALUE;
- }
-
- mTrackOuterRadiusSquared = outerRadius * outerRadius;
- if (mTrackOuterRadiusSquared < outerRadius) {
- // Prevent overflow
- mTrackOuterRadiusSquared = Integer.MAX_VALUE;
- }
- }
-
- public int getTrackInnerRadius() {
- return mTrackInnerRadius;
- }
-
- public int getTrackOuterRadius() {
- return mTrackOuterRadius;
- }
-
- public void setThumbClockwiseBound(int angle) {
- if (angle < 0) {
- mThumbCwBound = Integer.MIN_VALUE;
- } else {
- mThumbCwBound = getClosestTickAngle(angle);
- }
- updateEnforceMaxAbsJump();
- }
-
- public void setThumbCounterclockwiseBound(int angle) {
- if (angle < 0) {
- mThumbCcwBound = Integer.MIN_VALUE;
- } else {
- mThumbCcwBound = getClosestTickAngle(angle);
- }
- updateEnforceMaxAbsJump();
- }
-
- private void updateEnforceMaxAbsJump() {
- // If there are bounds in both direction, there is no reason to restrict
- // the amount that a user can absolute jump to
- mEnforceMaxAbsJump =
- mThumbCcwBound == Integer.MIN_VALUE || mThumbCwBound == Integer.MIN_VALUE;
- }
-
- public int getThumbAngle() {
- return mThumbAngle;
- }
-
- public void setThumbAngle(int angle) {
- angle = getValidAngle(angle);
- mPreviousCallbackTickAngle = getClosestTickAngle(angle);
- setThumbAngleAuto(angle, false, false);
- }
-
- /**
- * Sets the thumb angle. If already animating, will continue the animation,
- * otherwise it will do a direct jump.
- *
- * @param angle
- * @param useDirection Whether to use the ccw parameter
- * @param ccw Whether going counterclockwise (only used if useDirection is true)
- */
- private void setThumbAngleAuto(int angle, boolean useDirection, boolean ccw) {
- if (mThumbScroller == null
- || mThumbScroller.isFinished()
- || Math.abs(getDelta(angle, getThumbScrollerAngle())) < THUMB_GRAB_SLOP) {
- setThumbAngleInt(angle);
- } else {
- if (useDirection) {
- setThumbAngleAnimated(angle, 0, ccw);
- } else {
- setThumbAngleAnimated(angle, 0);
- }
- }
- }
-
- private void setThumbAngleInt(int angle) {
- mThumbAngle = angle;
- int unoffsetAngle = angle + mZeroAngle;
- int thumbCenterX = (int) (Math.cos(1f * unoffsetAngle / RADIAN_INT_MULTIPLIER) *
- mThumbDistance) + mCenterX;
- int thumbCenterY = (int) (Math.sin(1f * unoffsetAngle / RADIAN_INT_MULTIPLIER) *
- mThumbDistance) * -1 + mCenterY;
-
- mThumbDrawable.setBounds(thumbCenterX - mThumbHalfWidth,
- thumbCenterY - mThumbHalfHeight,
- thumbCenterX + mThumbHalfWidth,
- thumbCenterY + mThumbHalfHeight);
-
- if (mThumbArrowsToDraw > 0) {
- setThumbArrowsAngle(angle);
- }
-
- if (DRAW_TRAIL) {
- double degrees;
- degrees = Math.min(359.0, Math.abs(mAcculumalatedTrailAngle));
- int level = (int) (10000.0 * degrees / 360.0);
-
- mTrail.setLevel((int) (10000.0 *
- (-Math.toDegrees(angle / (double) RADIAN_INT_MULTIPLIER) -
- degrees + 90) / 360.0));
- ((RotateDrawable) mTrail).getDrawable().setLevel(level);
- }
-
- invalidate();
- }
-
- /**
- *
- * @param angle
- * @param duration The animation duration, or 0 for the default duration.
- */
- public void setThumbAngleAnimated(int angle, int duration) {
- // The angle when going from the current angle to the new angle
- int deltaAngle = getDelta(mThumbAngle, angle);
- setThumbAngleAnimated(angle, duration, deltaAngle > 0);
- }
-
- public void setThumbAngleAnimated(int angle, int duration, boolean counterClockwise) {
- if (mThumbScroller == null) {
- mThumbScroller = new Scroller(mContext);
- }
-
- int startAngle = mThumbAngle;
- int endAngle = getValidAngle(angle);
- int deltaAngle = getDelta(startAngle, endAngle, counterClockwise);
- if (startAngle + deltaAngle < 0) {
- // Keep our angles positive
- startAngle += TWO_PI_INT_MULTIPLIED;
- }
-
- if (!mThumbScroller.isFinished()) {
- duration = mThumbScroller.getDuration() - mThumbScroller.timePassed();
- } else if (duration == 0) {
- duration = getAnimationDuration(deltaAngle);
- }
- mThumbScroller.startScroll(startAngle, 0, deltaAngle, 0, duration);
- onThumbScrollerStep();
- }
-
- private int getAnimationDuration(int deltaAngle) {
- if (deltaAngle < 0) deltaAngle *= -1;
- return 300 + deltaAngle * 300 / RADIAN_INT_MULTIPLIER;
- }
-
- private void onThumbScrollerStep() {
- if (!mThumbScroller.computeScrollOffset()) return;
- setThumbAngleInt(getThumbScrollerAngle());
- mHandler.sendEmptyMessage(MSG_THUMB_SCROLLER_STEP);
- }
-
- private int getThumbScrollerAngle() {
- return mThumbScroller.getCurrX() % TWO_PI_INT_MULTIPLIED;
- }
-
- public void resetThumbAngle() {
- mPreviousCallbackTickAngle = 0;
- setThumbAngleInt(0);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
- resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec));
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right,
- int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- // Cache the center point
- mCenterX = (right - left) / 2;
- mCenterY = (bottom - top) / 2;
-
- // Done here since we now have center, which is needed to calculate some
- // aux info for thumb angle
- if (mThumbAngle == Integer.MIN_VALUE) {
- resetThumbAngle();
- }
-
- if (DRAW_TRAIL) {
- mTrail.setBounds(0, 0, right - left, bottom - top);
- }
-
- // These drawables are the same size as the track
- mZoomInArrowDrawable.setBounds(0, 0, right - left, bottom - top);
- mZoomOutArrowDrawable.setBounds(0, 0, right - left, bottom - top);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return handleTouch(event.getAction(), event.getEventTime(),
- (int) event.getX(), (int) event.getY(), (int) event.getRawX(),
- (int) event.getRawY());
- }
-
- private void resetToIdle() {
- setMode(MODE_IDLE);
- mPreviousWidgetDragX = mPreviousWidgetDragY = Integer.MIN_VALUE;
- mAcculumalatedTrailAngle = 0.0;
- }
-
- public void setTapDragMode(boolean tapDragMode, int x, int y) {
- resetToIdle();
- if (tapDragMode) {
- setMode(MODE_TAP_DRAG);
- mCallback.onUserInteractionStarted();
- onThumbDragStarted(getAngle(x - mCenterX, y - mCenterY));
- } else {
- onTouchUp(SystemClock.elapsedRealtime(), true);
- }
- }
-
- public boolean handleTouch(int action, long time, int x, int y, int rawX, int rawY) {
- // local{X,Y} will be where the center of the widget is (0,0)
- int localX = x - mCenterX;
- int localY = y - mCenterY;
-
- /*
- * If we are not drawing the thumb, there is no way for the user to be
- * touching the thumb. Also, if this is the case, assume they are not
- * touching the ring (so the user cannot absolute set the thumb, and
- * there will be a larger touch region for going into the move-ring
- * mode).
- */
- boolean isTouchingThumb = mThumbVisible;
- boolean isTouchingRing = mThumbVisible;
-
- int touchAngle = getAngle(localX, localY);
-
- int radiusSquared = localX * localX + localY * localY;
- if (radiusSquared < mTrackInnerRadiusSquared ||
- radiusSquared > mTrackOuterRadiusSquared) {
- // Out-of-bounds
- isTouchingThumb = false;
- isTouchingRing = false;
- }
-
- if (isTouchingThumb) {
- int deltaThumbAndTouch = getDelta(mThumbAngle, touchAngle);
- int absoluteDeltaThumbAndTouch = deltaThumbAndTouch >= 0 ?
- deltaThumbAndTouch : -deltaThumbAndTouch;
- if (absoluteDeltaThumbAndTouch > THUMB_GRAB_SLOP) {
- // Didn't grab close enough to the thumb
- isTouchingThumb = false;
- }
- }
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- if (!isTouchingRing &&
- (time - mPreviousCenterUpTime <= DOUBLE_TAP_DISMISS_TIMEOUT)) {
- // Make sure the double-tap is in the center of the widget (and not on the ring)
- mCallback.onZoomRingDismissed();
- onTouchUp(time, isTouchingRing);
-
- // Dismissing, so halt here
- return true;
- }
-
- resetToIdle();
- mCallback.onUserInteractionStarted();
- mPreviousDownX = x;
- mPreviousDownY = y;
- // Fall through to code below switch (since the down is used for
- // jumping to the touched tick)
- break;
-
- case MotionEvent.ACTION_MOVE:
- // Fall through to code below switch
- break;
-
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- onTouchUp(time, isTouchingRing);
- return true;
-
- default:
- return false;
- }
-
- if (mMode == MODE_IDLE) {
- if (isTouchingThumb) {
- // They grabbed the thumb
- setMode(MODE_DRAG_THUMB);
- onThumbDragStarted(touchAngle);
-
- } else if (isTouchingRing) {
- // They tapped somewhere else on the ring
- int tickAngle = getClosestTickAngle(touchAngle);
- int deltaThumbAndTick = getDelta(mThumbAngle, tickAngle);
- int boundAngle = getBoundIfExceeds(mThumbAngle, deltaThumbAndTick);
-
- if (mEnforceMaxAbsJump) {
- // Enforcing the max jump
- if (deltaThumbAndTick > MAX_ABS_JUMP_DELTA_ANGLE ||
- deltaThumbAndTick < -MAX_ABS_JUMP_DELTA_ANGLE) {
- // Trying to jump too far, ignore this touch interaction
- setMode(MODE_IGNORE_UNTIL_TOUCHES_THUMB);
- return true;
- }
-
- if (boundAngle != Integer.MIN_VALUE) {
- // Cap the user's jump to the bound
- tickAngle = boundAngle;
- }
- } else {
- // Not enforcing the max jump, but we have to make sure
- // we're getting to the tapped angle by going through the
- // in-bounds region
- if (boundAngle != Integer.MIN_VALUE) {
- // Going this direction hits a bound, let's go the opposite direction
- boolean oldDirectionIsCcw = deltaThumbAndTick > 0;
- deltaThumbAndTick = getDelta(mThumbAngle, tickAngle, !oldDirectionIsCcw);
- boundAngle = getBoundIfExceeds(mThumbAngle, deltaThumbAndTick);
- if (boundAngle != Integer.MIN_VALUE) {
- // Cannot get to the tapped location because it is out-of-bounds
- setMode(MODE_IGNORE_UNTIL_TOUCHES_THUMB);
- return true;
- }
- }
- }
-
- setMode(MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP);
- mInitialGrabThumbAngle = touchAngle;
- boolean ccw = deltaThumbAndTick > 0;
- setThumbAngleAnimated(tickAngle, 0, ccw);
-
- /*
- * Our thumb scrolling animation takes us from mThumbAngle to
- * tickAngle, so manifest that as the user dragging the thumb
- * there.
- */
- onThumbDragStarted(mThumbAngle);
- // We know which direction we want to go
- onThumbDragged(tickAngle, true, ccw);
-
- } else {
- // They tapped somewhere else on the widget
- setMode(MODE_WAITING_FOR_MOVE_ZOOM_RING);
- mCallback.onZoomRingSetMovableHintVisible(true);
- }
-
- } else if (mMode == MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP) {
- int deltaDownAngle = getDelta(mInitialGrabThumbAngle, touchAngle);
- if ((deltaDownAngle < -THUMB_DRAG_SLOP || deltaDownAngle > THUMB_DRAG_SLOP) &&
- isDeltaInBounds(mInitialGrabThumbAngle, deltaDownAngle)) {
- setMode(MODE_DRAG_THUMB);
-
- // No need to call onThumbDragStarted, since that was done when they tapped-to-jump
- }
-
- } else if (mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) {
- if (Math.abs(x - mPreviousDownX) > mTouchSlop ||
- Math.abs(y - mPreviousDownY) > mTouchSlop) {
- /* Make sure the user has moved the slop amount before going into that mode. */
- setMode(MODE_MOVE_ZOOM_RING);
- mCallback.onZoomRingMovingStarted();
- // Move the zoom ring so it is under the finger where the user first touched
- mCallback.onZoomRingMoved(x - mPreviousDownX, y - mPreviousDownY, rawX, rawY);
- }
- } else if (mMode == MODE_IGNORE_UNTIL_TOUCHES_THUMB) {
- if (isTouchingThumb) {
- // The user is back on the thumb, let's go back to the previous mode
- setMode(mPreviousMode);
- }
- }
-
- // Purposefully not an "else if"
- if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) {
- if (isTouchingRing) {
- onThumbDragged(touchAngle, false, false);
- }
- } else if (mMode == MODE_MOVE_ZOOM_RING) {
- onZoomRingMoved(rawX, rawY);
- }
-
- return true;
- }
-
- private void onTouchUp(long time, boolean isTouchingRing) {
- int mode = mMode;
- if (mode == MODE_IGNORE_UNTIL_TOUCHES_THUMB) {
- // For cleaning up, pretend like the user was still in the previous mode
- mode = mPreviousMode;
- }
-
- if (mode == MODE_MOVE_ZOOM_RING || mode == MODE_WAITING_FOR_MOVE_ZOOM_RING) {
- mCallback.onZoomRingSetMovableHintVisible(false);
- if (mode == MODE_MOVE_ZOOM_RING) {
- mCallback.onZoomRingMovingStopped();
- }
- } else if (mode == MODE_DRAG_THUMB || mode == MODE_TAP_DRAG ||
- mode == MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP) {
- onThumbDragStopped();
-
- if (mode == MODE_DRAG_THUMB || mode == MODE_TAP_DRAG) {
- // Animate back to a tick
- setThumbAngleAnimated(mPreviousCallbackTickAngle, 0);
- }
- }
- mCallback.onUserInteractionStopped();
-
- if (!isTouchingRing) {
- mPreviousCenterUpTime = time;
- }
- }
-
- private void setMode(int mode) {
- if (mode != mMode) {
- mPreviousMode = mMode;
- mMode = mode;
- }
- }
-
- private boolean isDeltaInBounds(int startAngle, int deltaAngle) {
- return getBoundIfExceeds(startAngle, deltaAngle) == Integer.MIN_VALUE;
- }
-
- private int getBoundIfExceeds(int startAngle, int deltaAngle) {
- if (deltaAngle > 0) {
- // Counterclockwise movement
- if (mThumbCcwBound != Integer.MIN_VALUE &&
- getDelta(startAngle, mThumbCcwBound, true) < deltaAngle) {
- return mThumbCcwBound;
- }
- } else if (deltaAngle < 0) {
- // Clockwise movement, both of these will be negative
- int deltaThumbAndBound = getDelta(startAngle, mThumbCwBound, false);
- if (mThumbCwBound != Integer.MIN_VALUE &&
- deltaThumbAndBound > deltaAngle) {
- // Tapped outside of the bound in that direction
- return mThumbCwBound;
- }
- }
-
- return Integer.MIN_VALUE;
- }
-
- private int getDelta(int startAngle, int endAngle, boolean useDirection, boolean ccw) {
- return useDirection ? getDelta(startAngle, endAngle, ccw) : getDelta(startAngle, endAngle);
- }
-
- /**
- * Gets the smallest delta between two angles, and infers the direction
- * based on the shortest path between the two angles. If going from
- * startAngle to endAngle is counterclockwise, the result will be positive.
- * If it is clockwise, the result will be negative.
- *
- * @param startAngle The start angle.
- * @param endAngle The end angle.
- * @return The difference in angles.
- */
- private int getDelta(int startAngle, int endAngle) {
- int largerAngle, smallerAngle;
- if (endAngle > startAngle) {
- largerAngle = endAngle;
- smallerAngle = startAngle;
- } else {
- largerAngle = startAngle;
- smallerAngle = endAngle;
- }
-
- int delta = largerAngle - smallerAngle;
- if (delta <= PI_INT_MULTIPLIED) {
- // If going clockwise, negate the delta
- return startAngle == largerAngle ? -delta : delta;
- } else {
- // The other direction is the delta we want (it includes the
- // discontinuous 0-2PI angle)
- delta = TWO_PI_INT_MULTIPLIED - delta;
- // If going clockwise, negate the delta
- return startAngle == smallerAngle ? -delta : delta;
- }
- }
-
- /**
- * Gets the delta between two angles in the direction specified.
- *
- * @param startAngle The start angle.
- * @param endAngle The end angle.
- * @param counterClockwise The direction to take when computing the delta.
- * @return The difference in angles in the given direction.
- */
- private int getDelta(int startAngle, int endAngle, boolean counterClockwise) {
- int delta = endAngle - startAngle;
-
- if (!counterClockwise && delta > 0) {
- // Crossed the discontinuous 0/2PI angle, take the leftover slice of
- // the pie and negate it
- return -TWO_PI_INT_MULTIPLIED + delta;
- } else if (counterClockwise && delta < 0) {
- // Crossed the discontinuous 0/2PI angle, take the leftover slice of
- // the pie (and ensure it is positive)
- return TWO_PI_INT_MULTIPLIED + delta;
- } else {
- return delta;
- }
- }
-
- private void onThumbDragStarted(int startAngle) {
- setThumbArrowsVisible(false);
- mThumbDragStartAngle = startAngle;
- mCallback.onZoomRingThumbDraggingStarted();
- }
-
- private void onThumbDragged(int touchAngle, boolean useDirection, boolean ccw) {
- boolean animateThumbToNewAngle = false;
-
- int totalDeltaAngle;
- totalDeltaAngle = getDelta(mPreviousCallbackTickAngle, touchAngle, useDirection, ccw);
- if (totalDeltaAngle >= mFuzzyTickDelta
- || totalDeltaAngle <= -mFuzzyTickDelta) {
-
- if (!useDirection) {
- // Set ccw to match the direction found by getDelta
- ccw = totalDeltaAngle > 0;
- }
-
- /*
- * When the user slides the thumb through the tick that corresponds
- * to a zoom bound, we don't want to abruptly stop there. Instead,
- * let the user slide it to the next tick, and then animate it back
- * to the original zoom bound tick. Because of this, we make sure
- * the delta from the bound is more than halfway to the next tick.
- * We make sure the bound is between the touch and the previous
- * callback to ensure we just passed the bound.
- */
- int oldTouchAngle = touchAngle;
- if (ccw && mThumbCcwBound != Integer.MIN_VALUE) {
- int deltaCcwBoundAndTouch =
- getDelta(mThumbCcwBound, touchAngle, useDirection, true);
- if (deltaCcwBoundAndTouch >= mTickDelta / 2) {
- // The touch has past a bound
- int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackTickAngle,
- touchAngle, useDirection, true);
- if (deltaPreviousCbAndTouch >= deltaCcwBoundAndTouch) {
- // The bound is between the previous callback angle and the touch
- touchAngle = mThumbCcwBound;
- // We're moving the touch BACK to the bound, so opposite direction
- ccw = false;
- }
- }
- } else if (!ccw && mThumbCwBound != Integer.MIN_VALUE) {
- // See block above for general comments
- int deltaCwBoundAndTouch =
- getDelta(mThumbCwBound, touchAngle, useDirection, false);
- if (deltaCwBoundAndTouch <= -mTickDelta / 2) {
- int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackTickAngle,
- touchAngle, useDirection, false);
- /*
- * Both of these will be negative since we got delta in
- * clockwise direction, and we want the magnitude of
- * deltaPreviousCbAndTouch to be greater than the magnitude
- * of deltaCwBoundAndTouch
- */
- if (deltaPreviousCbAndTouch <= deltaCwBoundAndTouch) {
- touchAngle = mThumbCwBound;
- ccw = true;
- }
- }
- }
- if (touchAngle != oldTouchAngle) {
- // We bounded the touch angle
- totalDeltaAngle = getDelta(mPreviousCallbackTickAngle, touchAngle, useDirection, ccw);
- animateThumbToNewAngle = true;
- setMode(MODE_IGNORE_UNTIL_TOUCHES_THUMB);
- }
-
-
- // Prevent it from jumping too far
- if (mEnforceMaxAbsJump) {
- if (totalDeltaAngle <= -MAX_ABS_JUMP_DELTA_ANGLE) {
- totalDeltaAngle = -MAX_ABS_JUMP_DELTA_ANGLE;
- animateThumbToNewAngle = true;
- } else if (totalDeltaAngle >= MAX_ABS_JUMP_DELTA_ANGLE) {
- totalDeltaAngle = MAX_ABS_JUMP_DELTA_ANGLE;
- animateThumbToNewAngle = true;
- }
- }
-
- /*
- * We need to cover the edge case of a user grabbing the thumb,
- * going into the center of the widget, and then coming out from the
- * center to an angle that's slightly below the angle he's trying to
- * hit. If we do int division, we'll end up with one level lower
- * than the one he was going for.
- */
- int deltaLevels = Math.round((float) totalDeltaAngle / mTickDelta);
- if (deltaLevels != 0) {
- boolean canStillZoom = mCallback.onZoomRingThumbDragged(
- deltaLevels, mThumbDragStartAngle, touchAngle);
-
- if (mVibration) {
- // TODO: we're trying the haptics to see how it goes with
- // users, so we're ignoring the settings (for now)
- performHapticFeedback(HapticFeedbackConstants.ZOOM_RING_TICK,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING |
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- }
-
- // Set the callback angle to the actual angle based on how many delta levels we gave
- mPreviousCallbackTickAngle = getValidAngle(
- mPreviousCallbackTickAngle + (deltaLevels * mTickDelta));
- }
- }
-
- if (DRAW_TRAIL) {
- int deltaAngle = getDelta(mThumbAngle, touchAngle, useDirection, ccw);
- mAcculumalatedTrailAngle += Math.toDegrees(deltaAngle / (double) RADIAN_INT_MULTIPLIER);
- }
-
- if (animateThumbToNewAngle) {
- if (useDirection) {
- setThumbAngleAnimated(touchAngle, 0, ccw);
- } else {
- setThumbAngleAnimated(touchAngle, 0);
- }
- } else {
- setThumbAngleAuto(touchAngle, useDirection, ccw);
- }
- }
-// private void onThumbDragged(int touchAngle, boolean useDirection, boolean ccw) {
-// int deltaPrevCbAndTouch = getDelta(mPreviousCallbackAngle, touchAngle, useDirection, ccw);
-//
-// if (!useDirection) {
-// // Set ccw to match the direction found by getDelta
-// ccw = deltaPrevCbAndTouch > 0;
-// useDirection = true;
-// }
-//
-// boolean animateThumbToNewAngle = false;
-// boolean animationCcw = ccw;
-//
-// if (deltaPrevCbAndTouch >= mFuzzyCallbackThreshold
-// || deltaPrevCbAndTouch <= -mFuzzyCallbackThreshold) {
-//
-// /*
-// * When the user slides the thumb through the tick that corresponds
-// * to a zoom bound, we don't want to abruptly stop there. Instead,
-// * let the user slide it to the next tick, and then animate it back
-// * to the original zoom bound tick. Because of this, we make sure
-// * the delta from the bound is more than halfway to the next tick.
-// * We make sure the bound is between the touch and the previous
-// * callback to ensure we JUST passed the bound.
-// */
-// int oldTouchAngle = touchAngle;
-// if (ccw && mThumbCcwBound != Integer.MIN_VALUE) {
-// int deltaCcwBoundAndTouch =
-// getDelta(mThumbCcwBound, touchAngle, true, ccw);
-// if (deltaCcwBoundAndTouch >= mCallbackThreshold / 2) {
-// // The touch has past far enough from the bound
-// int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackAngle,
-// touchAngle, true, ccw);
-// if (deltaPreviousCbAndTouch >= deltaCcwBoundAndTouch) {
-// // The bound is between the previous callback angle and the touch
-// // Cap to the bound
-// touchAngle = mThumbCcwBound;
-// /*
-// * We're moving the touch BACK to the bound, so animate
-// * back in the opposite direction that passed the bound.
-// */
-// animationCcw = false;
-// }
-// }
-// } else if (!ccw && mThumbCwBound != Integer.MIN_VALUE) {
-// // See block above for general comments
-// int deltaCwBoundAndTouch =
-// getDelta(mThumbCwBound, touchAngle, true, ccw);
-// if (deltaCwBoundAndTouch <= -mCallbackThreshold / 2) {
-// int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackAngle,
-// touchAngle, true, ccw);
-// /*
-// * Both of these will be negative since we got delta in
-// * clockwise direction, and we want the magnitude of
-// * deltaPreviousCbAndTouch to be greater than the magnitude
-// * of deltaCwBoundAndTouch
-// */
-// if (deltaPreviousCbAndTouch <= deltaCwBoundAndTouch) {
-// touchAngle = mThumbCwBound;
-// animationCcw = true;
-// }
-// }
-// }
-// if (touchAngle != oldTouchAngle) {
-// // We bounded the touch angle
-// deltaPrevCbAndTouch = getDelta(mPreviousCallbackAngle, touchAngle, true, ccw);
-// // Animate back to the bound
-// animateThumbToNewAngle = true;
-// // Disallow movement now
-// setMode(MODE_IGNORE_UNTIL_UP);
-// }
-//
-//
-// /*
-// * Prevent it from jumping too far (this could happen if the user
-// * goes through the center)
-// */
-//
-// if (mEnforceMaxAbsJump) {
-// if (deltaPrevCbAndTouch <= -MAX_ABS_JUMP_DELTA_ANGLE) {
-// deltaPrevCbAndTouch = -MAX_ABS_JUMP_DELTA_ANGLE;
-// animateThumbToNewAngle = true;
-// } else if (deltaPrevCbAndTouch >= MAX_ABS_JUMP_DELTA_ANGLE) {
-// deltaPrevCbAndTouch = MAX_ABS_JUMP_DELTA_ANGLE;
-// animateThumbToNewAngle = true;
-// }
-// }
-//
-// /*
-// * We need to cover the edge case of a user grabbing the thumb,
-// * going into the center of the widget, and then coming out from the
-// * center to an angle that's slightly below the angle he's trying to
-// * hit. If we do int division, we'll end up with one level lower
-// * than the one he was going for.
-// */
-// int deltaLevels = Math.round((float) deltaPrevCbAndTouch / mCallbackThreshold);
-// if (deltaLevels != 0) {
-// boolean canStillZoom = mCallback.onZoomRingThumbDragged(
-// deltaLevels, mThumbDragStartAngle, touchAngle);
-//
-// if (mVibration) {
-// // TODO: we're trying the haptics to see how it goes with
-// // users, so we're ignoring the settings (for now)
-// performHapticFeedback(HapticFeedbackConstants.ZOOM_RING_TICK,
-// HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING |
-// HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-//
-// }
-// // Set the callback angle to the actual angle based on how many delta levels we gave
-// mPreviousCallbackAngle = getValidAngle(
-// mPreviousCallbackAngle + (deltaLevels * mCallbackThreshold));
-// }
-// }
-//
-// if (DRAW_TRAIL) {
-// int deltaAngle = getDelta(mThumbAngle, touchAngle, true, ccw);
-// mAcculumalatedTrailAngle += Math.toDegrees(deltaAngle / (double) RADIAN_INT_MULTIPLIER);
-// }
-//
-// if (animateThumbToNewAngle) {
-// setThumbAngleAnimated(touchAngle, 0, animationCcw);
-// } else {
-// /*
-// * Use regular ccw here because animationCcw will never have been
-// * changed if animateThumbToNewAngle is false
-// */
-// setThumbAngleAuto(touchAngle, true, ccw);
-// }
-// }
-
- private int getValidAngle(int invalidAngle) {
- if (invalidAngle < 0) {
- return (invalidAngle % TWO_PI_INT_MULTIPLIED) + TWO_PI_INT_MULTIPLIED;
- } else if (invalidAngle >= TWO_PI_INT_MULTIPLIED) {
- return invalidAngle % TWO_PI_INT_MULTIPLIED;
- } else {
- return invalidAngle;
- }
- }
-
- private int getClosestTickAngle(int angle) {
- int smallerAngleDistance = angle % mTickDelta;
- int smallerAngle = angle - smallerAngleDistance;
- if (smallerAngleDistance < mTickDelta / 2) {
- // Closer to the smaller angle
- return smallerAngle;
- } else {
- // Closer to the bigger angle (premodding)
- return (smallerAngle + mTickDelta) % TWO_PI_INT_MULTIPLIED;
- }
- }
-
- private void onThumbDragStopped() {
- mCallback.onZoomRingThumbDraggingStopped();
- }
-
- private void onZoomRingMoved(int rawX, int rawY) {
- if (mPreviousWidgetDragX != Integer.MIN_VALUE) {
- int deltaX = rawX - mPreviousWidgetDragX;
- int deltaY = rawY - mPreviousWidgetDragY;
-
- mCallback.onZoomRingMoved(deltaX, deltaY, rawX, rawY);
- }
-
- mPreviousWidgetDragX = rawX;
- mPreviousWidgetDragY = rawY;
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- super.onWindowFocusChanged(hasWindowFocus);
-
- if (!hasWindowFocus) {
- mCallback.onZoomRingDismissed();
- }
- }
-
- private int getAngle(int localX, int localY) {
- int radians = (int) (Math.atan2(localY, localX) * RADIAN_INT_MULTIPLIER);
-
- // Convert from [-pi,pi] to {0,2pi]
- if (radians < 0) {
- radians = -radians;
- } else if (radians > 0) {
- radians = 2 * PI_INT_MULTIPLIED - radians;
- } else {
- radians = 0;
- }
-
- radians = radians - mZeroAngle;
- return radians >= 0 ? radians : radians + 2 * PI_INT_MULTIPLIED;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- if (mThumbVisible) {
- if (DRAW_TRAIL) {
- mTrail.draw(canvas);
- }
- if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) {
- mZoomInArrowDrawable.draw(canvas);
- mZoomInArrowHintDrawable.draw(canvas);
- }
- if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) {
- mZoomOutArrowDrawable.draw(canvas);
- mZoomOutArrowHintDrawable.draw(canvas);
- }
- mThumbDrawable.draw(canvas);
- }
- }
-
- private void setThumbArrowsAngle(int angle) {
- int level = -angle * 10000 / ZoomRing.TWO_PI_INT_MULTIPLIED;
- mZoomInArrowDrawable.setLevel(level);
- mZoomOutArrowDrawable.setLevel(level);
-
- // Assume it is a square
- int halfSideLength = mZoomInArrowHintDrawable.getIntrinsicHeight() / 2;
- int unoffsetAngle = angle + mZeroAngle;
-
- int plusCenterX = (int) (Math.cos(1f * (unoffsetAngle - mZoomArrowHintOffsetAngle)
- / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) + mCenterX;
- int plusCenterY = (int) (Math.sin(1f * (unoffsetAngle - mZoomArrowHintOffsetAngle)
- / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) * -1 + mCenterY;
- mZoomInArrowHintDrawable.setBounds(plusCenterX - halfSideLength,
- plusCenterY - halfSideLength,
- plusCenterX + halfSideLength,
- plusCenterY + halfSideLength);
-
- int minusCenterX = (int) (Math.cos(1f * (unoffsetAngle + mZoomArrowHintOffsetAngle)
- / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) + mCenterX;
- int minusCenterY = (int) (Math.sin(1f * (unoffsetAngle + mZoomArrowHintOffsetAngle)
- / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) * -1 + mCenterY;
- mZoomOutArrowHintDrawable.setBounds(minusCenterX - halfSideLength,
- minusCenterY - halfSideLength,
- minusCenterX + halfSideLength,
- minusCenterY + halfSideLength);
- }
-
- void setThumbArrowsVisible(boolean visible) {
- if (visible) {
- mThumbArrowsAlpha = 255;
- int callbackAngle = mPreviousCallbackTickAngle;
- if (callbackAngle < mThumbCwBound - RADIAN_INT_ERROR ||
- callbackAngle > mThumbCwBound + RADIAN_INT_ERROR) {
- mZoomInArrowDrawable.setAlpha(255);
- mZoomInArrowHintDrawable.setAlpha(255);
- mThumbArrowsToDraw |= THUMB_ARROW_PLUS;
- } else {
- mThumbArrowsToDraw &= ~THUMB_ARROW_PLUS;
- }
- if (callbackAngle < mThumbCcwBound - RADIAN_INT_ERROR ||
- callbackAngle > mThumbCcwBound + RADIAN_INT_ERROR) {
- mZoomOutArrowDrawable.setAlpha(255);
- mZoomOutArrowHintDrawable.setAlpha(255);
- mThumbArrowsToDraw |= THUMB_ARROW_MINUS;
- } else {
- mThumbArrowsToDraw &= ~THUMB_ARROW_MINUS;
- }
- invalidate();
- } else if (mThumbArrowsAlpha == 255) {
- // Only start fade if we're fully visible (otherwise another fade is happening already)
- mThumbArrowsFadeStartTime = SystemClock.elapsedRealtime();
- onThumbArrowsFadeStep();
- }
- }
-
- private void onThumbArrowsFadeStep() {
- if (mThumbArrowsAlpha <= 0) {
- mThumbArrowsToDraw = 0;
- return;
- }
-
- mThumbArrowsAlpha = (int)
- (255 - (255 * (SystemClock.elapsedRealtime() - mThumbArrowsFadeStartTime)
- / THUMB_ARROWS_FADE_DURATION));
- if (mThumbArrowsAlpha < 0) mThumbArrowsAlpha = 0;
- if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) {
- mZoomInArrowDrawable.setAlpha(mThumbArrowsAlpha);
- mZoomInArrowHintDrawable.setAlpha(mThumbArrowsAlpha);
- invalidateDrawable(mZoomInArrowHintDrawable);
- invalidateDrawable(mZoomInArrowDrawable);
- }
- if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) {
- mZoomOutArrowDrawable.setAlpha(mThumbArrowsAlpha);
- mZoomOutArrowHintDrawable.setAlpha(mThumbArrowsAlpha);
- invalidateDrawable(mZoomOutArrowHintDrawable);
- invalidateDrawable(mZoomOutArrowDrawable);
- }
-
- if (!mHandler.hasMessages(MSG_THUMB_ARROWS_FADE_STEP)) {
- mHandler.sendEmptyMessage(MSG_THUMB_ARROWS_FADE_STEP);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- setThumbArrowsAngle(mThumbAngle);
- setThumbArrowsVisible(true);
- }
-
- public interface OnZoomRingCallback {
- void onZoomRingSetMovableHintVisible(boolean visible);
-
- void onZoomRingMovingStarted();
- boolean onZoomRingMoved(int deltaX, int deltaY, int rawX, int rawY);
- void onZoomRingMovingStopped();
-
- void onZoomRingThumbDraggingStarted();
- boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle);
- void onZoomRingThumbDraggingStopped();
-
- void onZoomRingDismissed();
-
- void onUserInteractionStarted();
- void onUserInteractionStopped();
- }
-
- private static void printAngle(String angleName, int angle) {
- Log.d(TAG, angleName + ": " + (long) angle * 180 / PI_INT_MULTIPLIED);
- }
-}
diff --git a/core/java/android/widget/ZoomRingController.java b/core/java/android/widget/ZoomRingController.java
deleted file mode 100644
index 3bf3b22..0000000
--- a/core/java/android/widget/ZoomRingController.java
+++ /dev/null
@@ -1,1383 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.view.GestureDetector;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-
-/**
- * A controller to simplify the use of the zoom ring widget.
- * <p>
- * If you are using this with a custom View, please call
- * {@link #setVisible(boolean) setVisible(false)} from the
- * {@link View#onDetachedFromWindow}.
- *
- * @hide
- */
-public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
- View.OnTouchListener, View.OnKeyListener {
-
- // Temporary methods for different zoom types
- static int getZoomType(Context context) {
- return Settings.System.getInt(context.getContentResolver(), "zoom", 1);
- }
- public static boolean useOldZoom(Context context) {
- return getZoomType(context) == 0;
- }
- private static boolean useThisZoom(Context context) {
- return getZoomType(context) == 1;
- }
-
- /** The duration for the animation to re-center the zoom ring. */
- private static final int RECENTERING_DURATION = 500;
-
- /** The inactivity timeout for the zoom ring. */
- private static final int INACTIVITY_TIMEOUT =
- (int) ViewConfiguration.getZoomControlsTimeout();
-
- /**
- * The delay when the user taps outside to dismiss the zoom ring. This is
- * because the user can do a second-tap to recenter the owner view instead
- * of dismissing the zoom ring.
- */
- private static final int OUTSIDE_TAP_DISMISS_DELAY =
- ViewConfiguration.getDoubleTapTimeout() / 2;
-
- /**
- * When the zoom ring is on the edge, this is the delay before we actually
- * start panning the owner.
- * @see #mInitiatePanGap
- */
- private static final int INITIATE_PAN_DELAY = 300;
-
- /**
- * While already panning, if the zoom ring remains this close to an edge,
- * the owner will continue to be panned.
- */
- private int mPanGap;
-
- /** To begin a pan, the zoom ring must be this close to an edge. */
- private int mInitiatePanGap;
-
- /** Initialized from ViewConfiguration. */
- private int mScaledTouchSlop;
-
- /**
- * The setting name that tracks whether we've shown the zoom ring toast.
- */
- private static final String SETTING_NAME_SHOWN_TOAST = "shown_zoom_ring_toast";
-
- private Context mContext;
- private WindowManager mWindowManager;
-
- /**
- * The view that is being zoomed by this zoom ring.
- */
- private View mOwnerView;
-
- /**
- * The bounds of the owner view in global coordinates. This is recalculated
- * each time the zoom ring is shown.
- */
- private Rect mOwnerViewBounds = new Rect();
-
- /**
- * The container that is added as a window.
- */
- private FrameLayout mContainer;
- private LayoutParams mContainerLayoutParams;
-
- /**
- * The view (or null) that should receive touch events. This will get set if
- * the touch down hits the container. It will be reset on the touch up.
- */
- private View mTouchTargetView;
- /**
- * The {@link #mTouchTargetView}'s location in window, set on touch down.
- */
- private int[] mTouchTargetLocationInWindow = new int[2];
- /**
- * If the zoom ring is dismissed but the user is still in a touch
- * interaction, we set this to true. This will ignore all touch events until
- * up/cancel, and then set the owner's touch listener to null.
- */
- private boolean mReleaseTouchListenerOnUp;
-
-
- /*
- * Tap-drag is an interaction where the user first taps and then (quickly)
- * does the clockwise or counter-clockwise drag. In reality, this is: (down,
- * up, down, move in circles, up). This differs from the usual events of:
- * (down, up, down, up, down, move in circles, up). While the only
- * difference is the omission of an (up, down), for power-users this is a
- * pretty big improvement as it now only requires them to focus on the
- * screen once (for the first tap down) instead of twice (for the first tap
- * down and then to grab the thumb).
- */
- /** The X where the tap-drag started. */
- private int mTapDragStartX;
- /** The Y where the tap-drag started. */
- private int mTapDragStartY;
-
- /** The controller is idle */
- private static final int TOUCH_MODE_IDLE = 0;
- /**
- * In the middle of a second-tap interaction, waiting for either an up-touch
- * or the user to start dragging to go into tap-drag mode.
- */
- private static final int TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT = 2;
- /** In the middle of a tap-drag. */
- private static final int TOUCH_MODE_FORWARDING_FOR_TAP_DRAG = 3;
- private int mTouchMode;
-
- /** Whether the zoom ring is visible. */
- private boolean mIsZoomRingVisible;
-
- private ZoomRing mZoomRing;
- /** Cached width of the zoom ring. */
- private int mZoomRingWidth;
- /** Cached height of the zoom ring. */
- private int mZoomRingHeight;
-
- /** Invokes panning of owner view if the zoom ring is touching an edge. */
- private Panner mPanner;
- /** The time when the zoom ring first touched the edge. */
- private long mTouchingEdgeStartTime;
- /** Whether the user has already initiated the panning. */
- private boolean mPanningInitiated;
-
- /**
- * When the finger moves the zoom ring to an edge, this is the horizontal
- * accumulator for how much the finger has moved off of its original touch
- * point on the zoom ring (OOB = out-of-bounds). If < 0, the finger has
- * moved that many px to the left of its original touch point on the ring.
- */
- private int mMovingZoomRingOobX;
- /** Vertical accumulator, see {@link #mMovingZoomRingOobX} */
- private int mMovingZoomRingOobY;
-
- /** Arrows that hint that the zoom ring is movable. */
- private ImageView mPanningArrows;
- /** The animation shown when the panning arrows are being shown. */
- private Animation mPanningArrowsEnterAnimation;
- /** The animation shown when the panning arrows are being hidden. */
- private Animation mPanningArrowsExitAnimation;
-
- /**
- * Temporary rectangle, only use from the UI thread (and ideally don't rely
- * on it being unused across many method calls.)
- */
- private Rect mTempRect = new Rect();
-
- private OnZoomListener mCallback;
-
- /**
- * When the zoom ring is centered on screen, this will be the x value used
- * for the container's layout params.
- */
- private int mCenteredContainerX = Integer.MIN_VALUE;
-
- /**
- * When the zoom ring is centered on screen, this will be the y value used
- * for the container's layout params.
- */
- private int mCenteredContainerY = Integer.MIN_VALUE;
-
- /**
- * Scroller used to re-center the zoom ring if the user had dragged it to a
- * corner and then double-taps any point on the owner view (the owner view
- * will center the double-tapped point, but we should re-center the zoom
- * ring).
- * <p>
- * The (x,y) of the scroller is the (x,y) of the container's layout params.
- */
- private Scroller mScroller;
-
- /**
- * When showing the zoom ring, we add the view as a new window. However,
- * there is logic that needs to know the size of the zoom ring which is
- * determined after it's laid out. Therefore, we must post this logic onto
- * the UI thread so it will be exceuted AFTER the layout. This is the logic.
- */
- private Runnable mPostedVisibleInitializer;
-
- /**
- * Only touch from the main thread.
- */
- private static Dialog sTutorialDialog;
- private static long sTutorialShowTime;
- private static final int TUTORIAL_MIN_DISPLAY_TIME = 2000;
-
- private IntentFilter mConfigurationChangedFilter =
- new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
-
- /** Listens for configuration changes so we can make sure we're still in a reasonable state. */
- private BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!mIsZoomRingVisible) return;
-
- mHandler.removeMessages(MSG_POST_CONFIGURATION_CHANGED);
- mHandler.sendEmptyMessage(MSG_POST_CONFIGURATION_CHANGED);
- }
- };
-
- /** Keeps the scroller going (or starts it). */
- private static final int MSG_SCROLLER_STEP = 1;
- /** When configuration changes, this is called after the UI thread is idle. */
- private static final int MSG_POST_CONFIGURATION_CHANGED = 2;
- /** Used to delay the zoom ring dismissal. */
- private static final int MSG_DISMISS_ZOOM_RING = 3;
-
- /**
- * If setVisible(true) is called and the owner view's window token is null,
- * we delay the setVisible(true) call until it is not null.
- */
- private static final int MSG_POST_SET_VISIBLE = 4;
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SCROLLER_STEP:
- onScrollerStep();
- break;
-
- case MSG_POST_CONFIGURATION_CHANGED:
- onPostConfigurationChanged();
- break;
-
- case MSG_DISMISS_ZOOM_RING:
- setVisible(false);
- break;
-
- case MSG_POST_SET_VISIBLE:
- if (mOwnerView.getWindowToken() == null) {
- // Doh, it is still null, throw an exception
- throw new IllegalArgumentException(
- "Cannot make the zoom ring visible if the owner view is " +
- "not attached to a window.");
- }
- setVisible(true);
- break;
- }
-
- }
- };
-
- public ZoomRingController(Context context, View ownerView) {
- mContext = context;
- mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-
- mPanner = new Panner();
- mOwnerView = ownerView;
-
- mZoomRing = new ZoomRing(context);
- mZoomRing.setId(com.android.internal.R.id.zoomControls);
- mZoomRing.setLayoutParams(new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT,
- FrameLayout.LayoutParams.WRAP_CONTENT,
- Gravity.CENTER));
- mZoomRing.setCallback(this);
-
- createPanningArrows();
-
- mContainerLayoutParams = new LayoutParams();
- mContainerLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
- mContainerLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCHABLE |
- LayoutParams.FLAG_NOT_FOCUSABLE |
- LayoutParams.FLAG_LAYOUT_NO_LIMITS;
- mContainerLayoutParams.height = LayoutParams.WRAP_CONTENT;
- mContainerLayoutParams.width = LayoutParams.WRAP_CONTENT;
- mContainerLayoutParams.type = LayoutParams.TYPE_APPLICATION_PANEL;
- mContainerLayoutParams.format = PixelFormat.TRANSPARENT;
- mContainerLayoutParams.windowAnimations = com.android.internal.R.style.Animation_ZoomRing;
-
- mContainer = new FrameLayout(context);
- mContainer.setLayoutParams(mContainerLayoutParams);
- mContainer.setMeasureAllChildren(true);
-
- mContainer.addView(mZoomRing);
- mContainer.addView(mPanningArrows);
-
- mScroller = new Scroller(context, new DecelerateInterpolator());
-
- ViewConfiguration vc = ViewConfiguration.get(context);
- mScaledTouchSlop = vc.getScaledTouchSlop();
-
- float density = context.getResources().getDisplayMetrics().density;
- mPanGap = (int) (20 * density);
- mInitiatePanGap = (int) (10 * density);
- }
-
- private void createPanningArrows() {
- mPanningArrows = new ImageView(mContext);
- mPanningArrows.setImageDrawable(mZoomRing.getPanningArrowsDrawable());
- mPanningArrows.setLayoutParams(new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT,
- FrameLayout.LayoutParams.WRAP_CONTENT,
- Gravity.CENTER));
- mPanningArrows.setVisibility(View.INVISIBLE);
-
- mPanningArrowsEnterAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.fade_in);
- mPanningArrowsExitAnimation = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.fade_out);
- }
-
- /**
- * Sets the angle (in radians) between ticks. This is also the angle a user
- * must move the thumb in order for the client to get a callback. Once there
- * is a callback, the accumulator resets. For example, if you set this to
- * PI/6, it will give a callback every time the user moves PI/6 amount on
- * the ring.
- *
- * @param angle The angle for the callback threshold, in radians
- */
- public void setTickDelta(float angle) {
- mZoomRing.setTickDelta((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER));
- }
-
- /**
- * Sets a drawable for the zoom ring track.
- *
- * @param drawable The drawable to use for the track.
- * @hide Need a better way of doing this, but this one-off for browser so it
- * can have its final look for the usability study
- */
- public void setTrackDrawable(int drawable) {
- mZoomRing.setBackgroundResource(drawable);
- }
-
- /**
- * Sets the callback for the zoom ring controller.
- *
- * @param callback The callback.
- */
- public void setCallback(OnZoomListener callback) {
- mCallback = callback;
- }
-
- public void setVibration(boolean vibrate) {
- mZoomRing.setVibration(vibrate);
- }
-
- public void setThumbAngle(float angle) {
- mZoomRing.setThumbAngle((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER));
- }
-
- public void setThumbAngleAnimated(float angle) {
- mZoomRing.setThumbAngleAnimated((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER), 0);
- }
-
- public void setThumbVisible(boolean thumbVisible) {
- mZoomRing.setThumbVisible(thumbVisible);
- }
-
- public void setThumbClockwiseBound(float angle) {
- mZoomRing.setThumbClockwiseBound(angle >= 0 ?
- (int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER) :
- Integer.MIN_VALUE);
- }
-
- public void setThumbCounterclockwiseBound(float angle) {
- mZoomRing.setThumbCounterclockwiseBound(angle >= 0 ?
- (int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER) :
- Integer.MIN_VALUE);
- }
-
- public boolean isVisible() {
- return mIsZoomRingVisible;
- }
-
- public void setVisible(boolean visible) {
-
- if (!useThisZoom(mContext)) return;
-
- if (visible) {
- if (mOwnerView.getWindowToken() == null) {
- /*
- * We need a window token to show ourselves, maybe the owner's
- * window hasn't been created yet but it will have been by the
- * time the looper is idle, so post the setVisible(true) call.
- */
- if (!mHandler.hasMessages(MSG_POST_SET_VISIBLE)) {
- mHandler.sendEmptyMessage(MSG_POST_SET_VISIBLE);
- }
- return;
- }
-
- dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
- } else {
- mPanner.stop();
- }
-
- if (mIsZoomRingVisible == visible) {
- return;
- }
- mIsZoomRingVisible = visible;
-
- if (visible) {
- if (mContainerLayoutParams.token == null) {
- mContainerLayoutParams.token = mOwnerView.getWindowToken();
- }
-
- mWindowManager.addView(mContainer, mContainerLayoutParams);
-
- if (mPostedVisibleInitializer == null) {
- mPostedVisibleInitializer = new Runnable() {
- public void run() {
- refreshPositioningVariables();
- resetZoomRing();
- refreshContainerLayout();
-
- if (mCallback != null) {
- mCallback.onVisibilityChanged(true);
- }
- }
- };
- }
-
- mPanningArrows.setAnimation(null);
-
- mHandler.post(mPostedVisibleInitializer);
-
- // Handle configuration changes when visible
- mContext.registerReceiver(mConfigurationChangedReceiver, mConfigurationChangedFilter);
-
- // Steal key/touches events from the owner
- mOwnerView.setOnKeyListener(this);
- mOwnerView.setOnTouchListener(this);
- mReleaseTouchListenerOnUp = false;
-
- } else {
- // Don't want to steal any more keys/touches
- mOwnerView.setOnKeyListener(null);
- if (mTouchTargetView != null) {
- // We are still stealing the touch events for this touch
- // sequence, so release the touch listener later
- mReleaseTouchListenerOnUp = true;
- } else {
- mOwnerView.setOnTouchListener(null);
- }
-
- // No longer care about configuration changes
- mContext.unregisterReceiver(mConfigurationChangedReceiver);
-
- mWindowManager.removeView(mContainer);
- mHandler.removeCallbacks(mPostedVisibleInitializer);
-
- if (mCallback != null) {
- mCallback.onVisibilityChanged(false);
- }
- }
-
- }
-
- private void refreshContainerLayout() {
- if (mIsZoomRingVisible) {
- mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams);
- }
- }
-
- /**
- * Returns the container of the zoom ring widget. The client can add views
- * here to be shown alongside the zoom ring. See {@link #getZoomRingId()}.
- * <p>
- * Notes:
- * <ul>
- * <li> The controller dispatches touch events differently than the regular view
- * framework.
- * <li> Please ensure you set your view to INVISIBLE not GONE when hiding it.
- * </ul>
- *
- * @return The layout used by the container.
- */
- public FrameLayout getContainer() {
- return mContainer;
- }
-
- /**
- * Returns the id of the zoom ring widget.
- *
- * @return The id of the zoom ring widget.
- */
- public int getZoomRingId() {
- return mZoomRing.getId();
- }
-
- private void dismissZoomRingDelayed(int delay) {
- mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
- mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_RING, delay);
- }
-
- private void resetZoomRing() {
- mScroller.abortAnimation();
-
- mContainerLayoutParams.x = mCenteredContainerX;
- mContainerLayoutParams.y = mCenteredContainerY;
-
- // Reset the thumb
- mZoomRing.resetThumbAngle();
- }
-
- /**
- * Should be called by the client for each event belonging to the second tap
- * (the down, move, up, and cancel events).
- * <p>
- * In most cases, the client can use a {@link GestureDetector} and forward events from
- * {@link GestureDetector.OnDoubleTapListener#onDoubleTapEvent(MotionEvent)}.
- *
- * @param event The event belonging to the second tap.
- * @return Whether the event was consumed.
- */
- public boolean handleDoubleTapEvent(MotionEvent event) {
- if (!useThisZoom(mContext)) return false;
-
- int action = event.getAction();
-
- // TODO: make sure this works well with the
- // ownerView.setOnTouchListener(this) instead of window receiving
- // touches
- if (action == MotionEvent.ACTION_DOWN) {
- mTouchMode = TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT;
- int x = (int) event.getX();
- int y = (int) event.getY();
-
- refreshPositioningVariables();
- setVisible(true);
- centerPoint(x, y);
- ensureZoomRingIsCentered();
-
- // Tap drag mode stuff
- mTapDragStartX = x;
- mTapDragStartY = y;
-
- } else if (action == MotionEvent.ACTION_CANCEL) {
- mTouchMode = TOUCH_MODE_IDLE;
-
- } else { // action is move or up
- switch (mTouchMode) {
- case TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT: {
- switch (action) {
- case MotionEvent.ACTION_MOVE:
- int x = (int) event.getX();
- int y = (int) event.getY();
- if (Math.abs(x - mTapDragStartX) > mScaledTouchSlop ||
- Math.abs(y - mTapDragStartY) >
- mScaledTouchSlop) {
- mZoomRing.setTapDragMode(true, x, y);
- mTouchMode = TOUCH_MODE_FORWARDING_FOR_TAP_DRAG;
- setTouchTargetView(mZoomRing);
- }
- return true;
-
- case MotionEvent.ACTION_UP:
- mTouchMode = TOUCH_MODE_IDLE;
- break;
- }
- break;
- }
-
- case TOUCH_MODE_FORWARDING_FOR_TAP_DRAG: {
- switch (action) {
- case MotionEvent.ACTION_MOVE:
- giveTouchToZoomRing(event);
- return true;
-
- case MotionEvent.ACTION_UP:
- mTouchMode = TOUCH_MODE_IDLE;
-
- /*
- * This is a power-user feature that only shows the
- * zoom while the user is performing the tap-drag.
- * That means once it is released, the zoom ring
- * should disappear.
- */
- mZoomRing.setTapDragMode(false, (int) event.getX(), (int) event.getY());
- dismissZoomRingDelayed(0);
- break;
- }
- break;
- }
- }
- }
-
- return true;
- }
-
- private void ensureZoomRingIsCentered() {
- LayoutParams lp = mContainerLayoutParams;
-
- if (lp.x != mCenteredContainerX || lp.y != mCenteredContainerY) {
- int width = mContainer.getWidth();
- int height = mContainer.getHeight();
- mScroller.startScroll(lp.x, lp.y, mCenteredContainerX - lp.x,
- mCenteredContainerY - lp.y, RECENTERING_DURATION);
- mHandler.sendEmptyMessage(MSG_SCROLLER_STEP);
- }
- }
-
- private void refreshPositioningVariables() {
- mZoomRingWidth = mZoomRing.getWidth();
- mZoomRingHeight = mZoomRing.getHeight();
-
- // Calculate the owner view's bounds
- mOwnerView.getGlobalVisibleRect(mOwnerViewBounds);
-
- // Get the center
- Gravity.apply(Gravity.CENTER, mContainer.getWidth(), mContainer.getHeight(),
- mOwnerViewBounds, mTempRect);
- mCenteredContainerX = mTempRect.left;
- mCenteredContainerY = mTempRect.top;
- }
-
- /**
- * Centers the point (in owner view's coordinates).
- */
- private void centerPoint(int x, int y) {
- if (mCallback != null) {
- mCallback.onCenter(x, y);
- }
- }
-
- private void giveTouchToZoomRing(MotionEvent event) {
- int rawX = (int) event.getRawX();
- int rawY = (int) event.getRawY();
- int x = rawX - mContainerLayoutParams.x - mZoomRing.getLeft();
- int y = rawY - mContainerLayoutParams.y - mZoomRing.getTop();
- mZoomRing.handleTouch(event.getAction(), event.getEventTime(), x, y, rawX, rawY);
- }
-
- /** @hide */
- public void onZoomRingSetMovableHintVisible(boolean visible) {
- setPanningArrowsVisible(visible);
- }
-
- /** @hide */
- public void onUserInteractionStarted() {
- mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
- }
-
- /** @hide */
- public void onUserInteractionStopped() {
- dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
- }
-
- /** @hide */
- public void onZoomRingMovingStarted() {
- mScroller.abortAnimation();
- mTouchingEdgeStartTime = 0;
- mMovingZoomRingOobX = 0;
- mMovingZoomRingOobY = 0;
- if (mCallback != null) {
- mCallback.onBeginPan();
- }
- }
-
- private void setPanningArrowsVisible(boolean visible) {
- mPanningArrows.startAnimation(visible ? mPanningArrowsEnterAnimation
- : mPanningArrowsExitAnimation);
- mPanningArrows.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- }
-
- /** @hide */
- public boolean onZoomRingMoved(int deltaX, int deltaY, int rawX, int rawY) {
-
- if (mMovingZoomRingOobX != 0) {
- /*
- * The finger has moved off the point where it originally touched
- * the zidget.
- */
- boolean wasOobLeft = mMovingZoomRingOobX < 0;
- mMovingZoomRingOobX += deltaX;
- if ((wasOobLeft && mMovingZoomRingOobX > 0) ||
- (!wasOobLeft && mMovingZoomRingOobX < 0)) {
- /*
- * Woot, the finger is back on the original point. Infact, it
- * went PAST its original point, so take the amount it passed
- * and use that as the delta to move the zoom ring.
- */
- deltaX = mMovingZoomRingOobX;
- // No longer out-of-bounds, reset
- mMovingZoomRingOobX = 0;
- } else {
- // The finger is still not back, eat this movement
- deltaX = 0;
- }
- }
-
- if (mMovingZoomRingOobY != 0) {
- // See above for comments
- boolean wasOobUp = mMovingZoomRingOobY < 0;
- mMovingZoomRingOobY += deltaY;
- if ((wasOobUp && mMovingZoomRingOobY > 0) || (!wasOobUp && mMovingZoomRingOobY < 0)) {
- deltaY = mMovingZoomRingOobY;
- mMovingZoomRingOobY = 0;
- } else {
- deltaY = 0;
- }
- }
-
- WindowManager.LayoutParams lp = mContainerLayoutParams;
- Rect ownerBounds = mOwnerViewBounds;
-
- int zoomRingLeft = mZoomRing.getLeft();
- int zoomRingTop = mZoomRing.getTop();
-
- int newX = lp.x + deltaX;
- int newZoomRingX = newX + zoomRingLeft;
- newZoomRingX = (newZoomRingX <= ownerBounds.left) ? ownerBounds.left :
- (newZoomRingX + mZoomRingWidth > ownerBounds.right) ?
- ownerBounds.right - mZoomRingWidth : newZoomRingX;
- lp.x = newZoomRingX - zoomRingLeft;
-
- int newY = lp.y + deltaY;
- int newZoomRingY = newY + zoomRingTop;
- newZoomRingY = (newZoomRingY <= ownerBounds.top) ? ownerBounds.top :
- (newZoomRingY + mZoomRingHeight > ownerBounds.bottom) ?
- ownerBounds.bottom - mZoomRingHeight : newZoomRingY;
- lp.y = newZoomRingY - zoomRingTop;
-
- refreshContainerLayout();
-
- // Check for pan
- boolean horizontalPanning = true;
- int leftGap = newZoomRingX - ownerBounds.left;
- if (leftGap < mPanGap) {
- if (leftGap == 0 && deltaX != 0 && mMovingZoomRingOobX == 0) {
- // Future moves in this direction should be accumulated in mMovingZoomRingOobX
- mMovingZoomRingOobX = deltaX / Math.abs(deltaX);
- }
- if (shouldPan(leftGap)) {
- mPanner.setHorizontalStrength(-getStrengthFromGap(leftGap));
- }
- } else {
- int rightGap = ownerBounds.right - (lp.x + mZoomRingWidth + zoomRingLeft);
- if (rightGap < mPanGap) {
- if (rightGap == 0 && deltaX != 0 && mMovingZoomRingOobX == 0) {
- mMovingZoomRingOobX = deltaX / Math.abs(deltaX);
- }
- if (shouldPan(rightGap)) {
- mPanner.setHorizontalStrength(getStrengthFromGap(rightGap));
- }
- } else {
- mPanner.setHorizontalStrength(0);
- horizontalPanning = false;
- }
- }
-
- int topGap = newZoomRingY - ownerBounds.top;
- if (topGap < mPanGap) {
- if (topGap == 0 && deltaY != 0 && mMovingZoomRingOobY == 0) {
- mMovingZoomRingOobY = deltaY / Math.abs(deltaY);
- }
- if (shouldPan(topGap)) {
- mPanner.setVerticalStrength(-getStrengthFromGap(topGap));
- }
- } else {
- int bottomGap = ownerBounds.bottom - (lp.y + mZoomRingHeight + zoomRingTop);
- if (bottomGap < mPanGap) {
- if (bottomGap == 0 && deltaY != 0 && mMovingZoomRingOobY == 0) {
- mMovingZoomRingOobY = deltaY / Math.abs(deltaY);
- }
- if (shouldPan(bottomGap)) {
- mPanner.setVerticalStrength(getStrengthFromGap(bottomGap));
- }
- } else {
- mPanner.setVerticalStrength(0);
- if (!horizontalPanning) {
- // Neither are panning, reset any timer to start pan mode
- mTouchingEdgeStartTime = 0;
- mPanningInitiated = false;
- mPanner.stop();
- }
- }
- }
-
- return true;
- }
-
- private boolean shouldPan(int gap) {
- if (mPanningInitiated) return true;
-
- if (gap < mInitiatePanGap) {
- long time = SystemClock.elapsedRealtime();
- if (mTouchingEdgeStartTime != 0 &&
- mTouchingEdgeStartTime + INITIATE_PAN_DELAY < time) {
- mPanningInitiated = true;
- return true;
- } else if (mTouchingEdgeStartTime == 0) {
- mTouchingEdgeStartTime = time;
- } else {
- }
- } else {
- // Moved away from the initiate pan gap, so reset the timer
- mTouchingEdgeStartTime = 0;
- }
- return false;
- }
-
- /** @hide */
- public void onZoomRingMovingStopped() {
- mPanner.stop();
- setPanningArrowsVisible(false);
- if (mCallback != null) {
- mCallback.onEndPan();
- }
- }
-
- private int getStrengthFromGap(int gap) {
- return gap > mPanGap ? 0 :
- (mPanGap - gap) * 100 / mPanGap;
- }
-
- /** @hide */
- public void onZoomRingThumbDraggingStarted() {
- if (mCallback != null) {
- mCallback.onBeginDrag();
- }
- }
-
- /** @hide */
- public boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle) {
- if (mCallback != null) {
- int deltaZoomLevel = -numLevels;
-
- return mCallback.onDragZoom(deltaZoomLevel,
- getZoomRingCenterXInOwnerCoordinates(),
- getZoomRingCenterYInOwnerCoordinates(),
- (float) startAngle / ZoomRing.RADIAN_INT_MULTIPLIER,
- (float) curAngle / ZoomRing.RADIAN_INT_MULTIPLIER);
- }
-
- return false;
- }
-
- private int getZoomRingCenterXInOwnerCoordinates() {
- int globalZoomCenterX = mContainerLayoutParams.x + mZoomRing.getLeft() +
- mZoomRingWidth / 2;
- return globalZoomCenterX - mOwnerViewBounds.left;
- }
-
- private int getZoomRingCenterYInOwnerCoordinates() {
- int globalZoomCenterY = mContainerLayoutParams.y + mZoomRing.getTop() +
- mZoomRingHeight / 2;
- return globalZoomCenterY - mOwnerViewBounds.top;
- }
-
- /** @hide */
- public void onZoomRingThumbDraggingStopped() {
- if (mCallback != null) {
- mCallback.onEndDrag();
- }
- }
-
- /** @hide */
- public void onZoomRingDismissed() {
- mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
- setVisible(false);
- }
-
- /** @hide */
- public void onRingDown(int tickAngle, int touchAngle) {
- }
-
- /** @hide */
- public boolean onTouch(View v, MotionEvent event) {
- if (sTutorialDialog != null && sTutorialDialog.isShowing() &&
- SystemClock.elapsedRealtime() - sTutorialShowTime >= TUTORIAL_MIN_DISPLAY_TIME) {
- finishZoomTutorial();
- }
-
- int action = event.getAction();
-
- if (mReleaseTouchListenerOnUp) {
- // The ring was dismissed but we need to throw away all events until the up
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- mOwnerView.setOnTouchListener(null);
- setTouchTargetView(null);
- mReleaseTouchListenerOnUp = false;
- }
-
- // Eat this event
- return true;
- }
-
- View targetView = mTouchTargetView;
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- targetView = getViewForTouch((int) event.getRawX(), (int) event.getRawY());
- setTouchTargetView(targetView);
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- setTouchTargetView(null);
- break;
- }
-
- if (targetView != null) {
- // The upperleft corner of the target view in raw coordinates
- int targetViewRawX = mContainerLayoutParams.x + mTouchTargetLocationInWindow[0];
- int targetViewRawY = mContainerLayoutParams.y + mTouchTargetLocationInWindow[1];
-
- MotionEvent containerEvent = MotionEvent.obtain(event);
- // Convert the motion event into the target view's coordinates (from
- // owner view's coordinates)
- containerEvent.offsetLocation(mOwnerViewBounds.left - targetViewRawX,
- mOwnerViewBounds.top - targetViewRawY);
- boolean retValue = targetView.dispatchTouchEvent(containerEvent);
- containerEvent.recycle();
- return retValue;
-
- } else {
-// dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT);
- if (action == MotionEvent.ACTION_DOWN) {
- dismissZoomRingDelayed(OUTSIDE_TAP_DISMISS_DELAY);
- }
-
- return false;
- }
- }
-
- private void setTouchTargetView(View view) {
- mTouchTargetView = view;
- if (view != null) {
- view.getLocationInWindow(mTouchTargetLocationInWindow);
- }
- }
-
- /**
- * Returns the View that should receive a touch at the given coordinates.
- *
- * @param rawX The raw X.
- * @param rawY The raw Y.
- * @return The view that should receive the touches, or null if there is not one.
- */
- private View getViewForTouch(int rawX, int rawY) {
- // Check to see if it is touching the ring
- int containerCenterX = mContainerLayoutParams.x + mContainer.getWidth() / 2;
- int containerCenterY = mContainerLayoutParams.y + mContainer.getHeight() / 2;
- int distanceFromCenterX = rawX - containerCenterX;
- int distanceFromCenterY = rawY - containerCenterY;
- int zoomRingRadius = mZoomRing.getTrackOuterRadius();
- if (distanceFromCenterX * distanceFromCenterX +
- distanceFromCenterY * distanceFromCenterY <=
- zoomRingRadius * zoomRingRadius) {
- return mZoomRing;
- }
-
- // Check to see if it is touching any other clickable View.
- // Reverse order so the child drawn on top gets first dibs.
- int containerCoordsX = rawX - mContainerLayoutParams.x;
- int containerCoordsY = rawY - mContainerLayoutParams.y;
- Rect frame = mTempRect;
- for (int i = mContainer.getChildCount() - 1; i >= 0; i--) {
- View child = mContainer.getChildAt(i);
- if (child == mZoomRing || child.getVisibility() != View.VISIBLE ||
- !child.isClickable()) {
- continue;
- }
-
- child.getHitRect(frame);
- if (frame.contains(containerCoordsX, containerCoordsY)) {
- return child;
- }
- }
-
- return null;
- }
-
- /**
- * Steals key events from the owner view.
- *
- * @hide
- */
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- // Eat these
- return true;
-
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- // Keep the zoom alive a little longer
- dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
- // They started zooming, hide the thumb arrows
- mZoomRing.setThumbArrowsVisible(false);
-
- if (mCallback != null && event.getAction() == KeyEvent.ACTION_DOWN) {
- mCallback.onSimpleZoom(keyCode == KeyEvent.KEYCODE_DPAD_UP,
- getZoomRingCenterXInOwnerCoordinates(),
- getZoomRingCenterYInOwnerCoordinates());
- }
-
- return true;
- }
-
- return false;
- }
-
- private void onScrollerStep() {
- if (!mScroller.computeScrollOffset() || !mIsZoomRingVisible) return;
-
- mContainerLayoutParams.x = mScroller.getCurrX();
- mContainerLayoutParams.y = mScroller.getCurrY();
- refreshContainerLayout();
-
- mHandler.sendEmptyMessage(MSG_SCROLLER_STEP);
- }
-
- private void onPostConfigurationChanged() {
- dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
- refreshPositioningVariables();
- ensureZoomRingIsCentered();
- }
-
- /*
- * This is static so Activities can call this instead of the Views
- * (Activities usually do not have a reference to the ZoomRingController
- * instance.)
- */
- /**
- * Shows a "tutorial" (some text) to the user teaching her the new zoom
- * invocation method. Must call from the main thread.
- * <p>
- * It checks the global system setting to ensure this has not been seen
- * before. Furthermore, if the application does not have privilege to write
- * to the system settings, it will store this bit locally in a shared
- * preference.
- *
- * @hide This should only be used by our main apps--browser, maps, and
- * gallery
- */
- public static void showZoomTutorialOnce(Context context) {
- ContentResolver cr = context.getContentResolver();
- if (Settings.System.getInt(cr, SETTING_NAME_SHOWN_TOAST, 0) == 1) {
- return;
- }
-
- SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE);
- if (sp.getInt(SETTING_NAME_SHOWN_TOAST, 0) == 1) {
- return;
- }
-
- if (sTutorialDialog != null && sTutorialDialog.isShowing()) {
- sTutorialDialog.dismiss();
- }
-
- LayoutInflater layoutInflater =
- (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- TextView textView = (TextView) layoutInflater.inflate(
- com.android.internal.R.layout.alert_dialog_simple_text, null)
- .findViewById(android.R.id.text1);
- textView.setText(com.android.internal.R.string.tutorial_double_tap_to_zoom_message_short);
-
- sTutorialDialog = new AlertDialog.Builder(context)
- .setView(textView)
- .setIcon(0)
- .create();
-
- Window window = sTutorialDialog.getWindow();
- window.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
- window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND |
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
-
- sTutorialDialog.show();
- sTutorialShowTime = SystemClock.elapsedRealtime();
- }
-
- /** @hide Should only be used by Android platform apps */
- public static void finishZoomTutorial(Context context, boolean userNotified) {
- if (sTutorialDialog == null) return;
-
- sTutorialDialog.dismiss();
- sTutorialDialog = null;
-
- // Record that they have seen the tutorial
- if (userNotified) {
- try {
- Settings.System.putInt(context.getContentResolver(), SETTING_NAME_SHOWN_TOAST, 1);
- } catch (SecurityException e) {
- /*
- * The app does not have permission to clear this global flag, make
- * sure the user does not see the message when he comes back to this
- * same app at least.
- */
- SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE);
- sp.edit().putInt(SETTING_NAME_SHOWN_TOAST, 1).commit();
- }
- }
- }
-
- /** @hide Should only be used by Android platform apps */
- public void finishZoomTutorial() {
- finishZoomTutorial(mContext, true);
- }
-
- /**
- * Sets the initial velocity of a pan.
- *
- * @param startVelocity The initial velocity to move the owner view, in
- * pixels per second.
- */
- public void setPannerStartVelocity(float startVelocity) {
- mPanner.mStartVelocity = startVelocity;
- }
-
- /**
- * Sets the accelartion of the pan.
- *
- * @param acceleration The acceleration, in pixels per second squared.
- */
- public void setPannerAcceleration(float acceleration) {
- mPanner.mAcceleration = acceleration;
- }
-
- /**
- * Sets the maximum velocity of a pan.
- *
- * @param maxVelocity The max velocity to move the owner view, in pixels per
- * second.
- */
- public void setPannerMaxVelocity(float maxVelocity) {
- mPanner.mMaxVelocity = maxVelocity;
- }
-
- /**
- * Sets the duration before acceleration will be applied.
- *
- * @param duration The duration, in milliseconds.
- */
- public void setPannerStartAcceleratingDuration(int duration) {
- mPanner.mStartAcceleratingDuration = duration;
- }
-
- private class Panner implements Runnable {
- private static final int RUN_DELAY = 15;
- private static final float STOP_SLOWDOWN = 0.8f;
-
- private final Handler mUiHandler = new Handler();
-
- private int mVerticalStrength;
- private int mHorizontalStrength;
-
- private boolean mStopping;
-
- /** The time this current pan started. */
- private long mStartTime;
-
- /** The time of the last callback to pan the map/browser/etc. */
- private long mPreviousCallbackTime;
-
- // TODO Adjust to be DPI safe
- private float mStartVelocity = 135;
- private float mAcceleration = 160;
- private float mMaxVelocity = 1000;
- private int mStartAcceleratingDuration = 700;
- private float mVelocity;
-
- /** -100 (full left) to 0 (none) to 100 (full right) */
- public void setHorizontalStrength(int horizontalStrength) {
- if (mHorizontalStrength == 0 && mVerticalStrength == 0 && horizontalStrength != 0) {
- start();
- } else if (mVerticalStrength == 0 && horizontalStrength == 0) {
- stop();
- }
-
- mHorizontalStrength = horizontalStrength;
- mStopping = false;
- }
-
- /** -100 (full up) to 0 (none) to 100 (full down) */
- public void setVerticalStrength(int verticalStrength) {
- if (mHorizontalStrength == 0 && mVerticalStrength == 0 && verticalStrength != 0) {
- start();
- } else if (mHorizontalStrength == 0 && verticalStrength == 0) {
- stop();
- }
-
- mVerticalStrength = verticalStrength;
- mStopping = false;
- }
-
- private void start() {
- mUiHandler.post(this);
- mPreviousCallbackTime = 0;
- mStartTime = 0;
- }
-
- public void stop() {
- mStopping = true;
- }
-
- public void run() {
- if (mStopping) {
- mHorizontalStrength *= STOP_SLOWDOWN;
- mVerticalStrength *= STOP_SLOWDOWN;
- }
-
- if (mHorizontalStrength == 0 && mVerticalStrength == 0) {
- return;
- }
-
- boolean firstRun = mPreviousCallbackTime == 0;
- long curTime = SystemClock.elapsedRealtime();
- int panAmount = getPanAmount(mPreviousCallbackTime, curTime);
- mPreviousCallbackTime = curTime;
-
- if (firstRun) {
- mStartTime = curTime;
- mVelocity = mStartVelocity;
- } else {
- int panX = panAmount * mHorizontalStrength / 100;
- int panY = panAmount * mVerticalStrength / 100;
-
- if (mCallback != null) {
- mCallback.onPan(panX, panY);
- }
- }
-
- mUiHandler.postDelayed(this, RUN_DELAY);
- }
-
- private int getPanAmount(long previousTime, long currentTime) {
- if (mVelocity > mMaxVelocity) {
- mVelocity = mMaxVelocity;
- } else if (mVelocity < mMaxVelocity) {
- // See if it's time to add in some acceleration
- if (currentTime - mStartTime > mStartAcceleratingDuration) {
- mVelocity += (currentTime - previousTime) * mAcceleration / 1000;
- }
- }
-
- return (int) ((currentTime - previousTime) * mVelocity) / 1000;
- }
-
- }
-
- /**
- * Interface used to inform the client of zoom events that the user
- * triggers.
- */
- public interface OnZoomListener {
- /**
- * Called when the user begins dragging the thumb on the zoom ring.
- */
- void onBeginDrag();
-
- /**
- * Called when the user drags the thumb and passes a tick causing a
- * zoom.
- *
- * @param deltaZoomLevel The number of levels to be zoomed. Positive to
- * zoom in, negative to zoom out.
- * @param centerX The point about which to zoom. The zoom should pin
- * this point, leaving it at the same coordinate. This is
- * relative to the owner view's upper-left.
- * @param centerY The point about which to zoom. The zoom should pin
- * this point, leaving it at the same coordinate. This is
- * relative to the owner view's upper-left.
- * @param startAngle The angle where the user started dragging the thumb.
- * @param curAngle The current angle of the thumb.
- * @return Whether the owner was zoomed.
- */
- boolean onDragZoom(int deltaZoomLevel, int centerX, int centerY, float startAngle,
- float curAngle);
-
- /**
- * Called when the user releases the thumb.
- */
- void onEndDrag();
-
- /**
- * Called when the user zooms via some other mechanism, for example
- * arrow keys or a trackball.
- *
- * @param zoomIn Whether to zoom in (true) or out (false).
- * @param centerX See {@link #onDragZoom(int, int, int, float, float)}.
- * @param centerY See {@link #onDragZoom(int, int, int, float, float)}.
- */
- void onSimpleZoom(boolean zoomIn, int centerX, int centerY);
-
- /**
- * Called when the user begins moving the zoom ring in order to pan the
- * owner.
- */
- void onBeginPan();
-
- /**
- * Called when the owner should pan as a result of the user moving the zoom ring.
- *
- * @param deltaX The amount to pan horizontally.
- * @param deltaY The amount to pan vertically.
- * @return Whether the owner was panned.
- */
- boolean onPan(int deltaX, int deltaY);
-
- /**
- * Called when the user releases the zoom ring.
- */
- void onEndPan();
-
- /**
- * Called when the client should center the owner on the given point.
- *
- * @param x The x to center on, relative to the owner view's upper-left.
- * @param y The y to center on, relative to the owner view's upper-left.
- */
- void onCenter(int x, int y);
-
- /**
- * Called when the zoom ring's visibility changes.
- *
- * @param visible Whether the zoom ring is visible (true) or not (false).
- */
- void onVisibilityChanged(boolean visible);
- }
-}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 9030a3e..adec0a7 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -43,7 +43,7 @@
in ResultReceiver resultReceiver);
boolean hideSoftInput(in IInputMethodClient client, int flags,
in ResultReceiver resultReceiver);
- void windowGainedFocus(in IInputMethodClient client,
+ void windowGainedFocus(in IInputMethodClient client, in IBinder windowToken,
boolean viewHasFocus, boolean isTextEditor,
int softInputMode, boolean first, int windowFlags);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 6e5c4e0..18f2878 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -27,6 +27,7 @@
android_database_SQLiteProgram.cpp \
android_database_SQLiteQuery.cpp \
android_database_SQLiteStatement.cpp \
+ android_emoji_EmojiFactory.cpp \
android_view_Display.cpp \
android_view_Surface.cpp \
android_view_ViewRoot.cpp \
@@ -132,6 +133,7 @@
external/tremor/Tremor \
external/icu4c/i18n \
external/icu4c/common \
+ frameworks/opt/emoji
LOCAL_SHARED_LIBRARIES := \
libexpat \
@@ -156,12 +158,13 @@
libicui18n \
libicudata \
libmedia \
- libwpa_client
+ libwpa_client \
+ libemoji
ifeq ($(BOARD_HAVE_BLUETOOTH),true)
LOCAL_C_INCLUDES += \
external/dbus \
- external/bluez/libs/include
+ system/bluetooth/bluez-clean-headers
LOCAL_CFLAGS += -DHAVE_BLUETOOTH
LOCAL_SHARED_LIBRARIES += libbluedroid libdbus
endif
@@ -190,5 +193,4 @@
include $(BUILD_SHARED_LIBRARY)
-
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 40dc2a1..28c602b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -92,6 +92,7 @@
extern int register_android_util_Log(JNIEnv* env);
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
+extern int register_android_emoji_EmojiFactory(JNIEnv* env);
extern int register_android_graphics_Canvas(JNIEnv* env);
extern int register_android_graphics_ColorFilter(JNIEnv* env);
extern int register_android_graphics_DrawFilter(JNIEnv* env);
@@ -1026,6 +1027,7 @@
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
+ REG_JNI(register_android_emoji_EmojiFactory),
REG_JNI(register_android_security_Md5MessageDigest),
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_KeyCharacterMap),
diff --git a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp
index 7f87d80..bf23650 100755
--- a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp
+++ b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp
@@ -49,8 +49,6 @@
#ifdef HAVE_BLUETOOTH
#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/sco.h>
#endif
diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp
new file mode 100644
index 0000000..59f63a8
--- /dev/null
+++ b/core/jni/android_emoji_EmojiFactory.cpp
@@ -0,0 +1,292 @@
+#include "SkTypes.h"
+#include "SkImageDecoder.h"
+
+#define LOG_TAG "DoCoMoEmojiFactory_jni"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "EmojiFactory.h"
+#include <nativehelper/JNIHelp.h>
+
+#include <dlfcn.h>
+// #include <pthread.h>
+
+namespace android {
+
+// Note: This class is originally developed so that libandroid_runtime does
+// not have to depend on libemoji which is optional library. However, we
+// cannot use this class, since current (2009-02-16) bionic libc does not allow
+// dlopen()-ing inside dlopen(), while not only this class but also libemoji
+// uses dlopen().
+class EmojiFactoryCaller {
+ public:
+ EmojiFactoryCaller();
+ virtual ~EmojiFactoryCaller();
+ EmojiFactory *TryCallGetImplementation(const char* name);
+ EmojiFactory *TryCallGetAvailableImplementation();
+ private:
+ void *m_handle;
+ EmojiFactory *(*m_get_implementation)(const char*);
+ EmojiFactory *(*m_get_available_implementation)();
+};
+
+EmojiFactoryCaller::EmojiFactoryCaller() {
+ m_handle = dlopen("libemoji.so", RTLD_LAZY | RTLD_LOCAL);
+ const char* error_str = dlerror();
+ if (error_str) {
+ LOGI("Failed to load libemoji.so: %s", error_str);
+ return;
+ }
+
+ m_get_implementation =
+ reinterpret_cast<EmojiFactory *(*)(const char*)>(
+ dlsym(m_handle, "GetImplementation"));
+ error_str = dlerror();
+ if (error_str) {
+ LOGE("Failed to get symbol of GetImplementation: %s", error_str);
+ dlclose(m_handle);
+ m_handle = NULL;
+ return;
+ }
+
+ m_get_available_implementation =
+ reinterpret_cast<EmojiFactory *(*)()>(
+ dlsym(m_handle,"GetAvailableImplementation"));
+ error_str = dlerror();
+ if (error_str) {
+ LOGE("Failed to get symbol of GetAvailableImplementation: %s", error_str);
+ dlclose(m_handle);
+ m_handle = NULL;
+ return;
+ }
+}
+
+EmojiFactoryCaller::~EmojiFactoryCaller() {
+ if (m_handle) {
+ dlclose(m_handle);
+ }
+}
+
+EmojiFactory *EmojiFactoryCaller::TryCallGetImplementation(
+ const char* name) {
+ if (NULL == m_handle) {
+ return NULL;
+ }
+ return m_get_implementation(name);
+}
+
+EmojiFactory *EmojiFactoryCaller::TryCallGetAvailableImplementation() {
+ if (NULL == m_handle) {
+ return NULL;
+ }
+ return m_get_available_implementation();
+}
+
+// Note: bionic libc's dlopen() does not allow recursive dlopen(). So currently
+// we cannot use EmojiFactoryCaller here.
+// static EmojiFactoryCaller* gCaller;
+// static pthread_once_t g_once = PTHREAD_ONCE_INIT;
+
+static jclass gString_class;
+
+static jclass gBitmap_class;
+static jmethodID gBitmap_constructorMethodID;
+
+static jclass gEmojiFactory_class;
+static jmethodID gEmojiFactory_constructorMethodID;
+
+// static void InitializeCaller() {
+// gCaller = new EmojiFactoryCaller();
+// }
+
+static jobject create_java_EmojiFactory(
+ JNIEnv* env, EmojiFactory* factory, jstring name) {
+ jobject obj = env->AllocObject(gEmojiFactory_class);
+ if (obj) {
+ env->CallVoidMethod(obj, gEmojiFactory_constructorMethodID,
+ (jint)factory, name);
+ if (env->ExceptionCheck() != 0) {
+ LOGE("*** Uncaught exception returned from Java call!\n");
+ env->ExceptionDescribe();
+ obj = NULL;
+ }
+ }
+ return obj;
+}
+
+static jobject android_emoji_EmojiFactory_newInstance(
+ JNIEnv* env, jobject clazz, jstring name) {
+ // pthread_once(&g_once, InitializeCaller);
+
+ if (NULL == name) {
+ return NULL;
+ }
+
+ const jchar* jchars = env->GetStringChars(name, NULL);
+ jsize len = env->GetStringLength(name);
+ String8 str(String16(jchars, len));
+
+ // EmojiFactory *factory = gCaller->TryCallGetImplementation(str.string());
+ EmojiFactory *factory = EmojiFactory::GetImplementation(str.string());
+
+ env->ReleaseStringChars(name, jchars);
+
+ return create_java_EmojiFactory(env, factory, name);
+}
+
+static jobject android_emoji_EmojiFactory_newAvailableInstance(
+ JNIEnv* env, jobject clazz) {
+ // pthread_once(&g_once, InitializeCaller);
+
+ // EmojiFactory *factory = gCaller->TryCallGetAvailableImplementation();
+ EmojiFactory *factory = EmojiFactory::GetAvailableImplementation();
+ if (NULL == factory) {
+ return NULL;
+ }
+ String16 name_16(String8(factory->Name()));
+ jstring jname = env->NewString(name_16.string(), name_16.size());
+ if (NULL == jname) {
+ return NULL;
+ }
+
+ return create_java_EmojiFactory(env, factory, jname);
+}
+
+static jobject android_emoji_EmojiFactory_getBitmapFromAndroidPua(
+ JNIEnv* env, jobject clazz, jint nativeEmojiFactory, jint pua) {
+ EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+
+ int size;
+ const char *bytes = factory->GetImageBinaryFromAndroidPua(pua, &size);
+ if (bytes == NULL) {
+ return NULL;
+ }
+
+ SkBitmap *bitmap = new SkBitmap;
+ if (!SkImageDecoder::DecodeMemory(bytes, size, bitmap)) {
+ LOGE("SkImageDecoder::DecodeMemory() failed.");
+ return NULL;
+ }
+
+ jobject obj = env->AllocObject(gBitmap_class);
+ if (obj) {
+ env->CallVoidMethod(obj, gBitmap_constructorMethodID,
+ reinterpret_cast<jint>(bitmap), false, NULL);
+ if (env->ExceptionCheck() != 0) {
+ LOGE("*** Uncaught exception returned from Java call!\n");
+ env->ExceptionDescribe();
+ return NULL;
+ }
+ }
+
+ return obj;
+}
+
+static void android_emoji_EmojiFactory_destructor(
+ JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+ delete factory;
+}
+
+static jint android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificSjis(
+ JNIEnv* env, jobject obj, jint nativeEmojiFactory, jchar sjis) {
+ EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+ return factory->GetAndroidPuaFromVendorSpecificSjis(sjis);
+}
+
+static jint android_emoji_EmojiFactory_getVendorSpecificSjisFromAndroidPua(
+ JNIEnv* env, jobject obj, jint nativeEmojiFactory, jint pua) {
+ EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+ return factory->GetVendorSpecificSjisFromAndroidPua(pua);
+}
+
+static jint android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificPua(
+ JNIEnv* env, jobject obj, jint nativeEmojiFactory, jint vsu) {
+ EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+ return factory->GetAndroidPuaFromVendorSpecificPua(vsu);
+}
+
+static jint android_emoji_EmojiFactory_getVendorSpecificPuaFromAndroidPua(
+ JNIEnv* env, jobject obj, jint nativeEmojiFactory, jint pua) {
+ EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+ return factory->GetVendorSpecificPuaFromAndroidPua(pua);
+}
+
+static jint android_emoji_EmojiFactory_getMaximumVendorSpecificPua(
+ JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+ return factory->GetMaximumVendorSpecificPua();
+}
+
+static jint android_emoji_EmojiFactory_getMinimumVendorSpecificPua(
+ JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+ return factory->GetMinimumVendorSpecificPua();
+}
+
+static jint android_emoji_EmojiFactory_getMaximumAndroidPua(
+ JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+ return factory->GetMaximumAndroidPua();
+}
+
+static jint android_emoji_EmojiFactory_getMinimumAndroidPua(
+ JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
+ return factory->GetMinimumAndroidPua();
+}
+
+static JNINativeMethod gMethods[] = {
+ { "newInstance", "(Ljava/lang/String;)Landroid/emoji/EmojiFactory;",
+ (void*)android_emoji_EmojiFactory_newInstance},
+ { "newAvailableInstance", "()Landroid/emoji/EmojiFactory;",
+ (void*)android_emoji_EmojiFactory_newAvailableInstance},
+ { "nativeDestructor", "(I)V",
+ (void*)android_emoji_EmojiFactory_destructor},
+ { "nativeGetBitmapFromAndroidPua", "(II)Landroid/graphics/Bitmap;",
+ (void*)android_emoji_EmojiFactory_getBitmapFromAndroidPua},
+ { "nativeGetAndroidPuaFromVendorSpecificSjis", "(IC)I",
+ (void*)android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificSjis},
+ { "nativeGetVendorSpecificSjisFromAndroidPua", "(II)I",
+ (void*)android_emoji_EmojiFactory_getVendorSpecificSjisFromAndroidPua},
+ { "nativeGetAndroidPuaFromVendorSpecificPua", "(II)I",
+ (void*)android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificPua},
+ { "nativeGetVendorSpecificPuaFromAndroidPua", "(II)I",
+ (void*)android_emoji_EmojiFactory_getVendorSpecificPuaFromAndroidPua},
+ { "nativeGetMaximumVendorSpecificPua", "(I)I",
+ (void*)android_emoji_EmojiFactory_getMaximumVendorSpecificPua},
+ { "nativeGetMinimumVendorSpecificPua", "(I)I",
+ (void*)android_emoji_EmojiFactory_getMinimumVendorSpecificPua},
+ { "nativeGetMaximumAndroidPua", "(I)I",
+ (void*)android_emoji_EmojiFactory_getMaximumAndroidPua},
+ { "nativeGetMinimumAndroidPua", "(I)I",
+ (void*)android_emoji_EmojiFactory_getMinimumAndroidPua}
+};
+
+static jclass make_globalref(JNIEnv* env, const char classname[])
+{
+ jclass c = env->FindClass(classname);
+ SkASSERT(c);
+ return (jclass)env->NewGlobalRef(c);
+}
+
+static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
+ const char fieldname[], const char type[])
+{
+ jfieldID id = env->GetFieldID(clazz, fieldname, type);
+ SkASSERT(id);
+ return id;
+}
+
+int register_android_emoji_EmojiFactory(JNIEnv* env) {
+ gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
+ gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
+ "(IZ[B)V");
+ gEmojiFactory_class = make_globalref(env, "android/emoji/EmojiFactory");
+ gEmojiFactory_constructorMethodID = env->GetMethodID(
+ gEmojiFactory_class, "<init>", "(ILjava/lang/String;)V");
+ return jniRegisterNativeMethods(env, "android/emoji/EmojiFactory",
+ gMethods, NELEM(gMethods));
+}
+
+} // namespace android
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index c98207a..fcab813 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -397,6 +397,16 @@
return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
}
+static jboolean android_net_wifi_setBluetoothCoexistenceScanModeCommand(JNIEnv* env, jobject clazz, jboolean setCoexScanMode)
+{
+ char cmdstr[256];
+
+ int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER BTCOEXSCAN-%s", setCoexScanMode ? "START" : "STOP");
+ int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
+
+ return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+}
+
static jboolean android_net_wifi_saveConfigCommand(JNIEnv* env, jobject clazz)
{
// Make sure we never write out a value for AP_SCAN other than 1
@@ -503,6 +513,8 @@
{ "getNumAllowedChannelsCommand", "()I", (void*) android_net_wifi_getNumAllowedChannelsCommand },
{ "setBluetoothCoexistenceModeCommand", "(I)Z",
(void*) android_net_wifi_setBluetoothCoexistenceModeCommand },
+ { "setBluetoothCoexistenceScanModeCommand", "(Z)Z",
+ (void*) android_net_wifi_setBluetoothCoexistenceScanModeCommand },
{ "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand },
{ "getLinkSpeedCommand", "()I", (void*) android_net_wifi_getLinkSpeedCommand },
{ "getMacAddressCommand", "()Ljava/lang/String;", (void*) android_net_wifi_getMacAddressCommand },
diff --git a/core/res/Android.mk b/core/res/Android.mk
index 5fca5d0..cb5524a 100644
--- a/core/res/Android.mk
+++ b/core/res/Android.mk
@@ -24,7 +24,7 @@
# since these resources will be used by many apps.
LOCAL_AAPT_FLAGS := -x
-LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_TAGS := user
# Install this alongside the libraries.
LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)
diff --git a/core/res/res/anim/zoom_ring_enter.xml b/core/res/res/anim/zoom_ring_enter.xml
deleted file mode 100644
index 13d89b2..0000000
--- a/core/res/res/anim/zoom_ring_enter.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/res/anim/fade_in.xml
-**
-** Copyright 2007, 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
- <scale android:fromXScale="0.75" android:toXScale="1.0"
- android:fromYScale="0.75" android:toYScale="1.0"
- android:pivotX="50%" android:pivotY="50%"
- android:duration="75" />
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="75" />
-</set>
diff --git a/core/res/res/anim/zoom_ring_exit.xml b/core/res/res/anim/zoom_ring_exit.xml
deleted file mode 100644
index 177a4c3..0000000
--- a/core/res/res/anim/zoom_ring_exit.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/res/anim/fade_out.xml
-**
-** Copyright 2007, 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.
-*/
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/accelerate_interpolator">
- <scale android:fromXScale="1.0" android:toXScale="0.75"
- android:fromYScale="1.0" android:toYScale="0.75"
- android:pivotX="50%" android:pivotY="50%"
- android:duration="75" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="75"/>
-</set>
diff --git a/core/res/res/drawable-land/statusbar_background.png b/core/res/res/drawable-land/statusbar_background.png
index 8ecc24c..bafa5c5 100644
--- a/core/res/res/drawable-land/statusbar_background.png
+++ b/core/res/res/drawable-land/statusbar_background.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle.xml b/core/res/res/drawable/btn_circle.xml
new file mode 100644
index 0000000..9208010
--- /dev/null
+++ b/core/res/res/drawable/btn_circle.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_window_focused="false" android:state_enabled="true"
+ android:drawable="@drawable/btn_circle_normal" />
+ <item android:state_window_focused="false" android:state_enabled="false"
+ android:drawable="@drawable/btn_circle_disable" />
+ <item android:state_pressed="true"
+ android:drawable="@drawable/btn_circle_pressed" />
+ <item android:state_focused="true" android:state_enabled="true"
+ android:drawable="@drawable/btn_circle_selected" />
+ <item android:state_enabled="true"
+ android:drawable="@drawable/btn_circle_normal" />
+ <item android:state_focused="true"
+ android:drawable="@drawable/btn_circle_disable_focused" />
+ <item
+ android:drawable="@drawable/btn_circle_disable" />
+</selector>
diff --git a/core/res/res/drawable/btn_circle_disable.png b/core/res/res/drawable/btn_circle_disable.png
new file mode 100644
index 0000000..33b74a6
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_disable.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_disable_focused.png b/core/res/res/drawable/btn_circle_disable_focused.png
new file mode 100644
index 0000000..005ad8d
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_longpress.png b/core/res/res/drawable/btn_circle_longpress.png
new file mode 100644
index 0000000..f27d411
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_longpress.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_normal.png b/core/res/res/drawable/btn_circle_normal.png
new file mode 100644
index 0000000..fc5af1c
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_normal.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_pressed.png b/core/res/res/drawable/btn_circle_pressed.png
new file mode 100644
index 0000000..8f40afd
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_selected.png b/core/res/res/drawable/btn_circle_selected.png
new file mode 100644
index 0000000..c74fac2
--- /dev/null
+++ b/core/res/res/drawable/btn_circle_selected.png
Binary files differ
diff --git a/core/res/res/drawable/extract_edit_text.xml b/core/res/res/drawable/extract_edit_text.xml
index 9c6c4ba..c7f66f6 100644
--- a/core/res/res/drawable/extract_edit_text.xml
+++ b/core/res/res/drawable/extract_edit_text.xml
@@ -15,7 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true" android:drawable="@drawable/keyboard_textfield_pressed" />
<item android:state_focused="true" android:drawable="@drawable/keyboard_textfield_selected" />
<item android:drawable="@drawable/textfield_disabled" />
</selector>
diff --git a/core/res/res/drawable/zoom_ring_thumb_minus_arrow_rotatable.xml b/core/res/res/drawable/ic_btn_round_more.xml
similarity index 71%
rename from core/res/res/drawable/zoom_ring_thumb_minus_arrow_rotatable.xml
rename to core/res/res/drawable/ic_btn_round_more.xml
index f2d495b..82b7593 100644
--- a/core/res/res/drawable/zoom_ring_thumb_minus_arrow_rotatable.xml
+++ b/core/res/res/drawable/ic_btn_round_more.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fromDegrees="0"
- android:toDegrees="360"
- android:drawable="@drawable/zoom_ring_thumb_minus_arrow" />
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:drawable="@drawable/ic_btn_round_more_disabled" />
+ <item
+ android:drawable="@drawable/ic_btn_round_more_normal" />
+</selector>
diff --git a/core/res/res/drawable/ic_btn_round_more_disabled.png b/core/res/res/drawable/ic_btn_round_more_disabled.png
new file mode 100644
index 0000000..1ab98c9
--- /dev/null
+++ b/core/res/res/drawable/ic_btn_round_more_disabled.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_round_more_normal.png b/core/res/res/drawable/ic_btn_round_more_normal.png
new file mode 100644
index 0000000..ebdc55c
--- /dev/null
+++ b/core/res/res/drawable/ic_btn_round_more_normal.png
Binary files differ
diff --git a/core/res/res/drawable/keyboard_textfield_pressed.9.png b/core/res/res/drawable/keyboard_textfield_pressed.9.png
deleted file mode 100644
index f4e3f10..0000000
--- a/core/res/res/drawable/keyboard_textfield_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_phone_call.png b/core/res/res/drawable/stat_sys_phone_call.png
index ad53693..c44d062 100644
--- a/core/res/res/drawable/stat_sys_phone_call.png
+++ b/core/res/res/drawable/stat_sys_phone_call.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable/stat_sys_phone_call_bluetooth.png
new file mode 100644
index 0000000..7abfd19
--- /dev/null
+++ b/core/res/res/drawable/stat_sys_phone_call_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable/statusbar_background.png b/core/res/res/drawable/statusbar_background.png
index 945ad92..25a2344 100644
--- a/core/res/res/drawable/statusbar_background.png
+++ b/core/res/res/drawable/statusbar_background.png
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_arrows.png b/core/res/res/drawable/zoom_ring_arrows.png
deleted file mode 100644
index e443de7..0000000
--- a/core/res/res/drawable/zoom_ring_arrows.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_overview_tab.9.png b/core/res/res/drawable/zoom_ring_overview_tab.9.png
deleted file mode 100644
index d2658d8..0000000
--- a/core/res/res/drawable/zoom_ring_overview_tab.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb.png b/core/res/res/drawable/zoom_ring_thumb.png
deleted file mode 100644
index 1002724..0000000
--- a/core/res/res/drawable/zoom_ring_thumb.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb_minus.png b/core/res/res/drawable/zoom_ring_thumb_minus.png
deleted file mode 100644
index faed674..0000000
--- a/core/res/res/drawable/zoom_ring_thumb_minus.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb_minus_arrow.png b/core/res/res/drawable/zoom_ring_thumb_minus_arrow.png
deleted file mode 100644
index abe1b8a..0000000
--- a/core/res/res/drawable/zoom_ring_thumb_minus_arrow.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb_plus.png b/core/res/res/drawable/zoom_ring_thumb_plus.png
deleted file mode 100644
index ba7aff2..0000000
--- a/core/res/res/drawable/zoom_ring_thumb_plus.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb_plus_arrow.png b/core/res/res/drawable/zoom_ring_thumb_plus_arrow.png
deleted file mode 100644
index d01ccb4..0000000
--- a/core/res/res/drawable/zoom_ring_thumb_plus_arrow.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_thumb_plus_arrow_rotatable.xml b/core/res/res/drawable/zoom_ring_thumb_plus_arrow_rotatable.xml
deleted file mode 100644
index b77eaa4..0000000
--- a/core/res/res/drawable/zoom_ring_thumb_plus_arrow_rotatable.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fromDegrees="0"
- android:toDegrees="360"
- android:drawable="@drawable/zoom_ring_thumb_plus_arrow" />
diff --git a/core/res/res/drawable/zoom_ring_track.png b/core/res/res/drawable/zoom_ring_track.png
deleted file mode 100644
index 1d5a44c..0000000
--- a/core/res/res/drawable/zoom_ring_track.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_track_absolute.png b/core/res/res/drawable/zoom_ring_track_absolute.png
deleted file mode 100644
index 2b87699..0000000
--- a/core/res/res/drawable/zoom_ring_track_absolute.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/zoom_ring_trail.xml b/core/res/res/drawable/zoom_ring_trail.xml
deleted file mode 100644
index 08931ac..0000000
--- a/core/res/res/drawable/zoom_ring_trail.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fromDegrees="0"
- android:toDegrees="360">
-
- <shape
- android:shape="ring"
- android:innerRadius="60dip"
- android:thickness="14dip"
- android:useLevel="true">
-
- <gradient
- android:type="sweep"
- android:useLevel="true"
- android:startColor="#1000ceff"
- android:endColor="#ffadd252" />
-
- </shape>
-
- </rotate>
diff --git a/core/res/res/layout/preferences.xml b/core/res/res/layout/preferences.xml
index e6876ff..f0c2535 100644
--- a/core/res/res/layout/preferences.xml
+++ b/core/res/res/layout/preferences.xml
@@ -19,7 +19,8 @@
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginRight="7dip"
+ android:layout_marginRight="4dip"
android:layout_gravity="center_vertical"
- android:src="@drawable/ic_settings_indicator_next_page" />
+ android:background="@drawable/btn_circle"
+ android:src="@drawable/ic_btn_round_more" />
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 18a13554..81fd87d 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -65,13 +65,6 @@
<item>@drawable/spinner_dropdown_background</item>
<item>@drawable/title_bar</item>
<item>@drawable/title_bar_shadow</item>
- <item>@drawable/zoom_ring_arrows</item>
- <item>@drawable/zoom_ring_overview_tab</item>
- <item>@drawable/zoom_ring_thumb</item>
- <item>@drawable/zoom_ring_thumb_minus_arrow_rotatable</item>
- <item>@drawable/zoom_ring_thumb_plus_arrow_rotatable</item>
- <item>@drawable/zoom_ring_track</item>
- <item>@drawable/zoom_ring_track_absolute</item>
<!-- Visual lock screen -->
<item>@drawable/indicator_code_lock_drag_direction_green_up</item>
<item>@drawable/indicator_code_lock_drag_direction_red_up</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 30b26fa..5fa5f8d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -371,8 +371,6 @@
<attr name="spinnerItemStyle" format="reference" />
<!-- Default MapView style. -->
<attr name="mapViewStyle" format="reference" />
- <!-- Default ZoomRing style. -->
- <attr name="zoomRingStyle" format="reference" />
<!-- =================== -->
<!-- Preference styles -->
@@ -585,29 +583,34 @@
<attr name="imeOptions">
<!-- There are no special semantics associated with this editor. -->
<flag name="normal" value="0x00000000" />
- <!-- There is no special action associated with this editor.
+ <!-- There is no specific action associated with this editor, let the
+ editor come up with its own if it can.
+ Corresponds to
+ {@link android.view.inputmethod.EditorInfo#IME_ACTION_UNSPECIFIED}. -->
+ <flag name="actionUnspecified" value="0x00000000" />
+ <!-- This editor has no action associated with it.
Corresponds to
{@link android.view.inputmethod.EditorInfo#IME_ACTION_NONE}. -->
- <flag name="actionNone" value="0x00000000" />
+ <flag name="actionNone" value="0x00000001" />
<!-- The action key performs a "go"
operation to take the user to the target of the text they typed.
Typically used, for example, when entering a URL.
{@link android.view.inputmethod.EditorInfo#IME_ACTION_GO}. -->
- <flag name="actionGo" value="0x00000001" />
+ <flag name="actionGo" value="0x00000002" />
<!-- The action key performs a "search"
operation, taking the user to the results of searching for the text
the have typed (in whatever context is appropriate).
{@link android.view.inputmethod.EditorInfo#IME_ACTION_SEARCH}. -->
- <flag name="actionSearch" value="0x00000002" />
+ <flag name="actionSearch" value="0x00000003" />
<!-- The action key performs a "send"
operation, delivering the text to its target. This is typically used
when composing a message.
{@link android.view.inputmethod.EditorInfo#IME_ACTION_SEND}. -->
- <flag name="actionSend" value="0x00000003" />
+ <flag name="actionSend" value="0x00000004" />
<!-- The action key performs a "next"
operation, taking the user to the next field that will accept text.
{@link android.view.inputmethod.EditorInfo#IME_ACTION_NEXT}. -->
- <flag name="actionNext" value="0x00000004" />
+ <flag name="actionNext" value="0x00000005" />
<!-- Used in conjunction with a custom action,
this indicates that the action should not
be available in-line as the same as a "enter" key. Typically this is
@@ -1935,34 +1938,6 @@
</attr>
<attr name="inputType" />
</declare-styleable>
- <declare-styleable name="ZoomRing">
- <!-- Defines the drawable used as the thumb. -->
- <attr name="thumbDrawable" format="reference" />
- <!-- Defines the distance of the thumb from the center of the ring. -->
- <attr name="thumbDistance" format="dimension" />
- <!-- Defines the distance from the center of the ring to the beginning of the track. -->
- <attr name="trackInnerRadius" format="dimension" />
- <!-- Defines the distance from the center of the ring to the end of the track. -->
- <attr name="trackOuterRadius" format="dimension" />
- <!-- Defines the drawable used as a hint to show which direction is zoom in. This should be
- the same size as the zoom ring's asset. It will be rotated programmatically. -->
- <attr name="zoomInArrowDrawable" format="reference" />
- <!-- Defines the drawable used as a hint to show which direction is zoom out. This should be
- the same size as the zoom ring's asset. It will be rotated programmatically. -->
- <attr name="zoomOutArrowDrawable" format="reference" />
- <!-- Defines the drawable that is laid on top of the zoom in arrow.
- For example, this could be a +. -->
- <attr name="zoomInArrowHintDrawable" format="reference" />
- <!-- Defines the drawable that is laid on top of the zoom out arrow.
- For example, this could be a -. -->
- <attr name="zoomOutArrowHintDrawable" format="reference" />
- <!-- Defines the distance of the zoom arrow hint from the center of the ring. -->
- <attr name="zoomArrowHintDistance" format="dimension" />
- <!-- Defines the offset of the zoom arrow hints from the thumb. Valid ranges are [0, 359] -->
- <attr name="zoomArrowHintOffsetAngle" format="integer" />
- <!-- Defines the drawable used to hint that the zoom ring can pan its owner. -->
- <attr name="panningArrowsDrawable" format="reference" />
- </declare-styleable>
<declare-styleable name="PopupWindow">
<attr name="popupBackground" format="reference|color" />
</declare-styleable>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 9f4d82e..54eba62 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -144,11 +144,6 @@
<item name="windowExitAnimation">@anim/search_bar_exit</item>
</style>
- <!-- Standard animations for the zoom ring. -->
- <style name="Animation.ZoomRing">
- <item name="windowEnterAnimation">@anim/zoom_ring_enter</item>
- <item name="windowExitAnimation">@anim/zoom_ring_exit</item>
- </style>
<!-- Status Bar Styles -->
<style name="TextAppearance.StatusBarTitle">
@@ -461,21 +456,6 @@
<item name="android:shadowRadius">2.75</item>
</style>
- <style name="Widget.ZoomRing">
- <item name="android:background">@android:drawable/zoom_ring_track</item>
- <item name="android:thumbDrawable">@android:drawable/zoom_ring_thumb</item>
- <item name="android:thumbDistance">63dip</item>
- <item name="android:trackInnerRadius">43dip</item>
- <item name="android:trackOuterRadius">91dip</item>
- <item name="android:zoomInArrowDrawable">@android:drawable/zoom_ring_thumb_plus_arrow_rotatable</item>
- <item name="android:zoomOutArrowDrawable">@android:drawable/zoom_ring_thumb_minus_arrow_rotatable</item>
- <item name="android:zoomInArrowHintDrawable">@android:drawable/zoom_ring_thumb_plus</item>
- <item name="android:zoomOutArrowHintDrawable">@android:drawable/zoom_ring_thumb_minus</item>
- <item name="android:zoomArrowHintDistance">69dip</item>
- <item name="android:zoomArrowHintOffsetAngle">33</item>
- <item name="android:panningArrowsDrawable">@android:drawable/zoom_ring_arrows</item>
- </style>
-
<!-- Text Appearances -->
<eat-comment />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index bde6b2a..01c46de 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -162,7 +162,6 @@
<item name="spinnerItemStyle">@android:style/Widget.TextView.SpinnerItem</item>
<item name="dropDownHintAppearance">@android:style/TextAppearance.Widget.DropDownHint</item>
<item name="keyboardViewStyle">@android:style/Widget.KeyboardView</item>
- <item name="zoomRingStyle">@android:style/Widget.ZoomRing</item>
<!-- Preference styles -->
<item name="preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item>
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 4b5464f..a32d8ea 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -21,7 +21,7 @@
LOCAL_MODULE := platform.xml
-LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_TAGS := user
LOCAL_MODULE_CLASS := ETC
diff --git a/data/sounds/Android.mk b/data/sounds/Android.mk
deleted file mode 100644
index 9e3697f..0000000
--- a/data/sounds/Android.mk
+++ /dev/null
@@ -1,240 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# don't understand what's wrong here... bs
-#
-#copy_from := $(wildcard $(LOCAL_PATH)/*.mp3)
-#copy_to := $(addprefix $(TARGET_OUT)/sounds/,$(patsubst $(LOCAL_PATH)/%,%,$(copy_from)))
-#
-#$(copy_to) : PRIVATE_MODULE := sounds
-#$(copy_to) : $(TARGET_OUT)/sounds/% : $(LOCAL_PATH)/% | $(ACP)
-# $(transform-prebuilt-to-target)
-#
-#ALL_PREBUILT += $(copy_to)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/F1_MissedCall.ogg
-$(TARGET_OUT)/media/audio/notifications/F1_MissedCall.ogg : $(LOCAL_PATH)/F1_MissedCall.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/F1_New_MMS.ogg
-$(TARGET_OUT)/media/audio/notifications/F1_New_MMS.ogg : $(LOCAL_PATH)/F1_New_MMS.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/F1_New_SMS.ogg
-$(TARGET_OUT)/media/audio/notifications/F1_New_SMS.ogg : $(LOCAL_PATH)/F1_New_SMS.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Buzzer.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Buzzer.ogg : $(LOCAL_PATH)/Alarm_Buzzer.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Beep_01.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Beep_01.ogg : $(LOCAL_PATH)/Alarm_Beep_01.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Beep_02.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Beep_02.ogg : $(LOCAL_PATH)/Alarm_Beep_02.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Classic.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Classic.ogg : $(LOCAL_PATH)/Alarm_Classic.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Beep_03.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Beep_03.ogg : $(LOCAL_PATH)/Alarm_Beep_03.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/alarms/Alarm_Rooster_02.ogg
-$(TARGET_OUT)/media/audio/alarms/Alarm_Rooster_02.ogg : $(LOCAL_PATH)/Alarm_Rooster_02.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Ring_Classic_02.ogg
-$(TARGET_OUT)/media/audio/ringtones/Ring_Classic_02.ogg : $(LOCAL_PATH)/Ring_Classic_02.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Ring_Digital_02.ogg
-$(TARGET_OUT)/media/audio/ringtones/Ring_Digital_02.ogg : $(LOCAL_PATH)/Ring_Digital_02.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Ring_Synth_04.ogg
-$(TARGET_OUT)/media/audio/ringtones/Ring_Synth_04.ogg : $(LOCAL_PATH)/Ring_Synth_04.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Ring_Synth_02.ogg
-$(TARGET_OUT)/media/audio/ringtones/Ring_Synth_02.ogg : $(LOCAL_PATH)/Ring_Synth_02.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-#
-# --- New Wave Labs ringtones
-#
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/BeatPlucker.ogg
-$(TARGET_OUT)/media/audio/ringtones/BeatPlucker.ogg : $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/BentleyDubs.ogg
-$(TARGET_OUT)/media/audio/ringtones/BentleyDubs.ogg : $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/BirdLoop.ogg
-$(TARGET_OUT)/media/audio/ringtones/BirdLoop.ogg : $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/CaribbeanIce.ogg
-$(TARGET_OUT)/media/audio/ringtones/CaribbeanIce.ogg : $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/CurveBall.ogg
-$(TARGET_OUT)/media/audio/ringtones/CurveBall.ogg : $(LOCAL_PATH)/newwavelabs/CurveBall.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/EtherShake.ogg
-$(TARGET_OUT)/media/audio/ringtones/EtherShake.ogg : $(LOCAL_PATH)/newwavelabs/EtherShake.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/FriendlyGhost.ogg
-$(TARGET_OUT)/media/audio/ringtones/FriendlyGhost.ogg : $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/GameOverGuitar.ogg
-$(TARGET_OUT)/media/audio/ringtones/GameOverGuitar.ogg : $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Growl.ogg
-$(TARGET_OUT)/media/audio/ringtones/Growl.ogg : $(LOCAL_PATH)/newwavelabs/Growl.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/InsertCoin.ogg
-$(TARGET_OUT)/media/audio/ringtones/InsertCoin.ogg : $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/LoopyLounge.ogg
-$(TARGET_OUT)/media/audio/ringtones/LoopyLounge.ogg : $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/LoveFlute.ogg
-$(TARGET_OUT)/media/audio/ringtones/LoveFlute.ogg : $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/MidEvilJaunt.ogg
-$(TARGET_OUT)/media/audio/ringtones/MidEvilJaunt.ogg : $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/MildlyAlarming.ogg
-$(TARGET_OUT)/media/audio/ringtones/MildlyAlarming.ogg : $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/NewPlayer.ogg
-$(TARGET_OUT)/media/audio/ringtones/NewPlayer.ogg : $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Noises1.ogg
-$(TARGET_OUT)/media/audio/ringtones/Noises1.ogg : $(LOCAL_PATH)/newwavelabs/Noises1.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Noises2.ogg
-$(TARGET_OUT)/media/audio/ringtones/Noises2.ogg : $(LOCAL_PATH)/newwavelabs/Noises2.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Noises3.ogg
-$(TARGET_OUT)/media/audio/ringtones/Noises3.ogg : $(LOCAL_PATH)/newwavelabs/Noises3.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/OrganDub.ogg
-$(TARGET_OUT)/media/audio/ringtones/OrganDub.ogg : $(LOCAL_PATH)/newwavelabs/OrganDub.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/RomancingTheTone.ogg
-$(TARGET_OUT)/media/audio/ringtones/RomancingTheTone.ogg : $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/SitarVsSitar.ogg
-$(TARGET_OUT)/media/audio/ringtones/SitarVsSitar.ogg : $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/SpringyJalopy.ogg
-$(TARGET_OUT)/media/audio/ringtones/SpringyJalopy.ogg : $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/Terminated.ogg
-$(TARGET_OUT)/media/audio/ringtones/Terminated.ogg : $(LOCAL_PATH)/newwavelabs/Terminated.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/TwirlAway.ogg
-$(TARGET_OUT)/media/audio/ringtones/TwirlAway.ogg : $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/VeryAlarmed.ogg
-$(TARGET_OUT)/media/audio/ringtones/VeryAlarmed.ogg : $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ringtones/World.ogg
-$(TARGET_OUT)/media/audio/ringtones/World.ogg : $(LOCAL_PATH)/newwavelabs/World.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-#
-# --- New Wave Labs notifications
-#
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/CaffeineSnake.ogg
-$(TARGET_OUT)/media/audio/notifications/CaffeineSnake.ogg : $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/DearDeer.ogg
-$(TARGET_OUT)/media/audio/notifications/DearDeer.ogg : $(LOCAL_PATH)/newwavelabs/DearDeer.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/DontPanic.ogg
-$(TARGET_OUT)/media/audio/notifications/DontPanic.ogg : $(LOCAL_PATH)/newwavelabs/DontPanic.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/Highwire.ogg
-$(TARGET_OUT)/media/audio/notifications/Highwire.ogg : $(LOCAL_PATH)/newwavelabs/Highwire.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/KzurbSonar.ogg
-$(TARGET_OUT)/media/audio/notifications/KzurbSonar.ogg : $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/OnTheHunt.ogg
-$(TARGET_OUT)/media/audio/notifications/OnTheHunt.ogg : $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/Voila.ogg
-$(TARGET_OUT)/media/audio/notifications/Voila.ogg : $(LOCAL_PATH)/newwavelabs/Voila.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/Beat_Box_Android.ogg
-$(TARGET_OUT)/media/audio/notifications/Beat_Box_Android.ogg : $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/Heaven.ogg
-$(TARGET_OUT)/media/audio/notifications/Heaven.ogg : $(LOCAL_PATH)/notifications/Heaven.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/TaDa.ogg
-$(TARGET_OUT)/media/audio/notifications/TaDa.ogg : $(LOCAL_PATH)/notifications/TaDa.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/notifications/Tinkerbell.ogg
-$(TARGET_OUT)/media/audio/notifications/Tinkerbell.ogg : $(LOCAL_PATH)/notifications/Tinkerbell.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-# UI effects
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ui/Effect_Tick.ogg
-$(TARGET_OUT)/media/audio/ui/Effect_Tick.ogg : $(LOCAL_PATH)/effects/Effect_Tick.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ui/KeypressStandard.ogg
-$(TARGET_OUT)/media/audio/ui/KeypressStandard.ogg : $(LOCAL_PATH)/effects/KeypressStandard.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ui/KeypressSpacebar.ogg
-$(TARGET_OUT)/media/audio/ui/KeypressSpacebar.ogg : $(LOCAL_PATH)/effects/KeypressSpacebar.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ui/KeypressDelete.ogg
-$(TARGET_OUT)/media/audio/ui/KeypressDelete.ogg : $(LOCAL_PATH)/effects/KeypressDelete.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
-ALL_PREBUILT += $(TARGET_OUT)/media/audio/ui/KeypressReturn.ogg
-$(TARGET_OUT)/media/audio/ui/KeypressReturn.ogg : $(LOCAL_PATH)/effects/KeypressReturn.ogg | $(ACP)
- $(transform-prebuilt-to-target)
-
diff --git a/data/sounds/OriginalAudio.mk b/data/sounds/OriginalAudio.mk
new file mode 100644
index 0000000..8722983
--- /dev/null
+++ b/data/sounds/OriginalAudio.mk
@@ -0,0 +1,68 @@
+#
+# Original audio package that shipped on G1
+#
+# This file is included from core.mk so that all devices will have these sounds
+#
+# TODO: Clean up for future releases
+#
+
+LOCAL_PATH:= frameworks/base/data/sounds
+
+PRODUCT_COPY_FILES += \
+ $(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
+ $(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
+ $(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
+ $(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
+ $(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
+ $(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
+ $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
+ $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
+ $(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
+ $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
+ $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
+ $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
+ $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
+ $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
+ $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
+ $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
+ $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
+ $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
+ $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
+ $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
+ $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
+ $(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
+ $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
+ $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
+ $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
+ $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
+ $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
+ $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
+ $(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
+ $(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
+ $(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
+ $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
+ $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
+ $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
+ $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
+ $(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
+ $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
+ $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
+ $(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
+ $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \
+ $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
+ $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
+ $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
+ $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
+ $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
+ $(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
+ $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
+ $(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
+ $(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
+ $(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
+ $(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
+ $(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
+ $(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
+ $(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
+ $(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
+ $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
+ $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg
diff --git a/data/sounds/effects/KeypressDelete.ogg b/data/sounds/effects/KeypressDelete.ogg
index b35ea65..738f6ae 100644
--- a/data/sounds/effects/KeypressDelete.ogg
+++ b/data/sounds/effects/KeypressDelete.ogg
Binary files differ
diff --git a/data/sounds/effects/KeypressDelete.wav b/data/sounds/effects/KeypressDelete.wav
index 5903f7f..0a2f3cc5 100644
--- a/data/sounds/effects/KeypressDelete.wav
+++ b/data/sounds/effects/KeypressDelete.wav
Binary files differ
diff --git a/data/sounds/effects/KeypressReturn.ogg b/data/sounds/effects/KeypressReturn.ogg
index 1b0fd56..6c807b0 100644
--- a/data/sounds/effects/KeypressReturn.ogg
+++ b/data/sounds/effects/KeypressReturn.ogg
Binary files differ
diff --git a/data/sounds/effects/KeypressReturn.wav b/data/sounds/effects/KeypressReturn.wav
index 9558fcf..59ff13a 100644
--- a/data/sounds/effects/KeypressReturn.wav
+++ b/data/sounds/effects/KeypressReturn.wav
Binary files differ
diff --git a/data/sounds/effects/KeypressSpacebar.ogg b/data/sounds/effects/KeypressSpacebar.ogg
index 5299337..b59c8ce 100644
--- a/data/sounds/effects/KeypressSpacebar.ogg
+++ b/data/sounds/effects/KeypressSpacebar.ogg
Binary files differ
diff --git a/data/sounds/effects/KeypressSpacebar.wav b/data/sounds/effects/KeypressSpacebar.wav
index 1226a21..1f819c9 100644
--- a/data/sounds/effects/KeypressSpacebar.wav
+++ b/data/sounds/effects/KeypressSpacebar.wav
Binary files differ
diff --git a/data/sounds/effects/KeypressStandard.ogg b/data/sounds/effects/KeypressStandard.ogg
index 8a152d1..a465cf8 100644
--- a/data/sounds/effects/KeypressStandard.ogg
+++ b/data/sounds/effects/KeypressStandard.ogg
Binary files differ
diff --git a/data/sounds/effects/KeypressStandard.wav b/data/sounds/effects/KeypressStandard.wav
index 0c9aa2a..56a7911 100644
--- a/data/sounds/effects/KeypressStandard.wav
+++ b/data/sounds/effects/KeypressStandard.wav
Binary files differ
diff --git a/docs/html/guide/topics/graphics/2d-graphics.jd b/docs/html/guide/topics/graphics/2d-graphics.jd
index a72962e..befb018 100644
--- a/docs/html/guide/topics/graphics/2d-graphics.jd
+++ b/docs/html/guide/topics/graphics/2d-graphics.jd
@@ -447,7 +447,7 @@
<p>This animation runs for just three frames. By setting the <code>android:oneshot</code> attribute of the
list to <var>true</var>, it will cycle just once then stop and hold on the last frame. If it is set <var>false</var> then
-the animation will loop. With this XML saved as <code>rocket_thrust.xml</p> in the <code>res/anim/</code> directory
+the animation will loop. With this XML saved as <code>rocket_thrust.xml</code> in the <code>res/anim/</code> directory
of the project, it can be added as the background image to a View and then called to play. Here's an example Activity,
in which the animation is added to an {@link android.widget.ImageView} and then animated when the screen is touched:</p>
<pre>
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp
index eb00f8c..2974e32 100644
--- a/libs/audioflinger/A2dpAudioInterface.cpp
+++ b/libs/audioflinger/A2dpAudioInterface.cpp
@@ -48,7 +48,6 @@
int format, int channelCount, uint32_t sampleRate, status_t *status)
{
LOGD("A2dpAudioInterface::openOutputStream %d, %d, %d\n", format, channelCount, sampleRate);
- Mutex::Autolock lock(mLock);
status_t err = 0;
// only one output stream allowed
@@ -134,7 +133,8 @@
mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL)
{
// use any address by default
- strncpy(mA2dpAddress, "00:00:00:00:00:00", sizeof(mA2dpAddress));
+ strcpy(mA2dpAddress, "00:00:00:00:00:00");
+ init();
}
status_t A2dpAudioInterface::A2dpAudioStreamOut::set(
@@ -163,18 +163,12 @@
ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
{
- status_t status = NO_INIT;
- size_t remaining = bytes;
+ Mutex::Autolock lock(mLock);
- if (!mData) {
- status = a2dp_init(44100, 2, &mData);
- if (status < 0) {
- LOGE("a2dp_init failed err: %d\n", status);
- mData = NULL;
- goto Error;
- }
- a2dp_set_sink(mData, mA2dpAddress);
- }
+ size_t remaining = bytes;
+ status_t status = init();
+ if (status < 0)
+ goto Error;
while (remaining > 0) {
status = a2dp_write(mData, buffer, remaining);
@@ -197,10 +191,27 @@
return status;
}
+status_t A2dpAudioInterface::A2dpAudioStreamOut::init()
+{
+ if (!mData) {
+ status_t status = a2dp_init(44100, 2, &mData);
+ if (status < 0) {
+ LOGE("a2dp_init failed err: %d\n", status);
+ mData = NULL;
+ return status;
+ }
+ a2dp_set_sink(mData, mA2dpAddress);
+ }
+
+ return 0;
+}
+
status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
{
int result = 0;
+ Mutex::Autolock lock(mLock);
+
if (!mStandby) {
result = a2dp_stop(mData);
if (result == 0)
@@ -212,15 +223,15 @@
status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address)
{
- if (strlen(address) < sizeof(mA2dpAddress))
+ Mutex::Autolock lock(mLock);
+
+ if (strlen(address) != strlen("00:00:00:00:00:00"))
return -EINVAL;
- if (strcmp(address, mA2dpAddress)) {
- strcpy(mA2dpAddress, address);
- if (mData)
- a2dp_set_sink(mData, mA2dpAddress);
- }
-
+ strcpy(mA2dpAddress, address);
+ if (mData)
+ a2dp_set_sink(mData, mA2dpAddress);
+
return NO_ERROR;
}
diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h
index a56e8a0..99614dc 100644
--- a/libs/audioflinger/A2dpAudioInterface.h
+++ b/libs/audioflinger/A2dpAudioInterface.h
@@ -82,11 +82,12 @@
virtual status_t setVolume(float volume) { return INVALID_OPERATION; }
virtual ssize_t write(const void* buffer, size_t bytes);
status_t standby();
- status_t close();
virtual status_t dump(int fd, const Vector<String16>& args);
private:
friend class A2dpAudioInterface;
+ status_t init();
+ status_t close();
status_t setAddress(const char* address);
private:
@@ -96,9 +97,9 @@
int mRetryCount;
char mA2dpAddress[20];
void* mData;
+ Mutex mLock;
};
- Mutex mLock;
A2dpAudioStreamOut* mOutput;
};
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 92c40e9..440778d 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -71,6 +71,9 @@
static const int kStartSleepTime = 30000;
static const int kStopSleepTime = 30000;
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 20000;
+
// Maximum number of pending buffers allocated by OutputTrack::write()
static const uint8_t kMaxOutputTrackBuffers = 5;
@@ -115,8 +118,7 @@
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
- mAudioHardware(0), mA2dpAudioInterface(0),
- mA2dpEnabled(false), mA2dpEnabledReq(false),
+ mAudioHardware(0), mA2dpAudioInterface(0), mA2dpEnabled(false), mNotifyA2dpChange(false),
mForcedSpeakerCount(0), mForcedRoute(0), mRouteRestoreTime(0), mMusicMuteSaved(false)
{
mHardwareStatus = AUDIO_HW_IDLE;
@@ -190,13 +192,44 @@
#ifdef WITH_A2DP
-void AudioFlinger::setA2dpEnabled(bool enable)
+// setA2dpEnabled_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::setA2dpEnabled_l(bool enable)
{
+ SortedVector < sp<MixerThread::Track> > tracks;
+ SortedVector < wp<MixerThread::Track> > activeTracks;
+
LOGV_IF(enable, "set output to A2DP\n");
LOGV_IF(!enable, "set output to hardware audio\n");
- mA2dpEnabledReq = enable;
- mA2dpMixerThread->wakeUp();
+ // Transfer tracks playing on MUSIC stream from one mixer to the other
+ if (enable) {
+ mHardwareMixerThread->getTracks_l(tracks, activeTracks);
+ mA2dpMixerThread->putTracks_l(tracks, activeTracks);
+ } else {
+ mA2dpMixerThread->getTracks_l(tracks, activeTracks);
+ mHardwareMixerThread->putTracks_l(tracks, activeTracks);
+ }
+ mA2dpEnabled = enable;
+ mNotifyA2dpChange = true;
+ mWaitWorkCV.broadcast();
+}
+
+// checkA2dpEnabledChange_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::checkA2dpEnabledChange_l()
+{
+ if (mNotifyA2dpChange) {
+ // Notify AudioSystem of the A2DP activation/deactivation
+ size_t size = mNotificationClients.size();
+ for (size_t i = 0; i < size; i++) {
+ sp<IBinder> binder = mNotificationClients.itemAt(i).promote();
+ if (binder != NULL) {
+ LOGV("Notifying output change to client %p", binder.get());
+ sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
+ client->a2dpEnabledChanged(mA2dpEnabled);
+ }
+ }
+ mNotifyA2dpChange = false;
+ }
}
#endif // WITH_A2DP
@@ -236,8 +269,12 @@
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
-
- snprintf(buffer, SIZE, "Hardware status: %d\n", mHardwareStatus);
+ int hardwareStatus = mHardwareStatus;
+
+ if (hardwareStatus == AUDIO_HW_IDLE && mHardwareMixerThread->mStandby) {
+ hardwareStatus = AUDIO_HW_STANDBY;
+ }
+ snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus);
result.append(buffer);
write(fd, result.string(), result.size());
return NO_ERROR;
@@ -262,7 +299,14 @@
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
dumpPermissionDenial(fd, args);
} else {
- AutoMutex lock(&mLock);
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mLock.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleep);
+ }
dumpClients(fd, args);
dumpInternals(fd, args);
@@ -277,6 +321,7 @@
if (mAudioHardware) {
mAudioHardware->dumpState(fd, args);
}
+ if (locked) mLock.unlock();
}
return NO_ERROR;
}
@@ -320,18 +365,19 @@
}
#ifdef WITH_A2DP
if (isA2dpEnabled() && AudioSystem::routedToA2dpOutput(streamType)) {
- track = mA2dpMixerThread->createTrack(client, streamType, sampleRate, format,
+ track = mA2dpMixerThread->createTrack_l(client, streamType, sampleRate, format,
channelCount, frameCount, sharedBuffer, &lStatus);
} else
#endif
{
- track = mHardwareMixerThread->createTrack(client, streamType, sampleRate, format,
+ track = mHardwareMixerThread->createTrack_l(client, streamType, sampleRate, format,
channelCount, frameCount, sharedBuffer, &lStatus);
}
- if (track != NULL) {
- trackHandle = new TrackHandle(track);
- lStatus = NO_ERROR;
- }
+ }
+ if (lStatus == NO_ERROR) {
+ trackHandle = new TrackHandle(track);
+ } else {
+ track.clear();
}
Exit:
@@ -409,7 +455,7 @@
#ifdef WITH_A2DP
mA2dpMixerThread->setMasterVolume(value);
#endif
-
+
return NO_ERROR;
}
@@ -436,7 +482,7 @@
if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) {
enableA2dp = true;
}
- setA2dpEnabled(enableA2dp);
+ setA2dpEnabled_l(enableA2dp);
LOGV("setOutput done\n");
}
#endif
@@ -704,46 +750,6 @@
}
}
-void AudioFlinger::handleOutputSwitch()
-{
- if (mA2dpEnabled != mA2dpEnabledReq)
- {
- Mutex::Autolock _l(mLock);
-
- if (mA2dpEnabled != mA2dpEnabledReq)
- {
- mA2dpEnabled = mA2dpEnabledReq;
- SortedVector < sp<MixerThread::Track> > tracks;
- SortedVector < wp<MixerThread::Track> > activeTracks;
-
- // We hold mA2dpMixerThread mLock already
- Mutex::Autolock _l(mHardwareMixerThread->mLock);
-
- // Transfer tracks playing on MUSIC stream from one mixer to the other
- if (mA2dpEnabled) {
- mHardwareMixerThread->getTracks(tracks, activeTracks);
- mA2dpMixerThread->putTracks(tracks, activeTracks);
- } else {
- mA2dpMixerThread->getTracks(tracks, activeTracks);
- mHardwareMixerThread->putTracks(tracks, activeTracks);
- }
-
- // Notify AudioSystem of the A2DP activation/deactivation
- size_t size = mNotificationClients.size();
- for (size_t i = 0; i < size; i++) {
- sp<IBinder> binder = mNotificationClients.itemAt(i).promote();
- if (binder != NULL) {
- LOGV("Notifying output change to client %p", binder.get());
- sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
- client->a2dpEnabledChanged(mA2dpEnabled);
- }
- }
-
- mHardwareMixerThread->wakeUp();
- }
- }
-}
-
void AudioFlinger::removeClient(pid_t pid)
{
LOGV("removeClient() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid());
@@ -751,17 +757,9 @@
mClients.removeItem(pid);
}
-void AudioFlinger::wakeUp()
-{
- mHardwareMixerThread->wakeUp();
-#ifdef WITH_A2DP
- mA2dpMixerThread->wakeUp();
-#endif // WITH_A2DP
-}
-
bool AudioFlinger::isA2dpEnabled() const
{
- return mA2dpEnabledReq;
+ return mA2dpEnabled;
}
void AudioFlinger::handleForcedSpeakerRoute(int command)
@@ -803,7 +801,7 @@
LOGV("mForcedSpeakerCount decremented to %d", mForcedSpeakerCount);
} else {
LOGE("mForcedSpeakerCount is already zero");
- }
+ }
}
break;
case CHECK_ROUTE_RESTORE_TIME:
@@ -946,20 +944,20 @@
do {
enabledTracks = 0;
- { // scope for the mLock
+ { // scope for the AudioFlinger::mLock
- Mutex::Autolock _l(mLock);
+ Mutex::Autolock _l(mAudioFlinger->mLock);
#ifdef WITH_A2DP
- if (mOutputType == AudioSystem::AUDIO_OUTPUT_A2DP) {
- mAudioFlinger->handleOutputSwitch();
- }
if (mOutputTrack != NULL && !mAudioFlinger->isA2dpEnabled()) {
if (outputTrackActive) {
+ mAudioFlinger->mLock.unlock();
mOutputTrack->stop();
+ mAudioFlinger->mLock.lock();
outputTrackActive = false;
}
}
+ mAudioFlinger->checkA2dpEnabledChange_l();
#endif
const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
@@ -968,7 +966,6 @@
if UNLIKELY(!activeTracks.size() && systemTime() > standbyTime) {
// wait until we have something to do...
LOGV("Audio hardware entering standby, output %d\n", mOutputType);
-// mAudioFlinger->mHardwareStatus = AUDIO_HW_STANDBY;
if (!mStandby) {
mOutput->standby();
mStandby = true;
@@ -976,17 +973,18 @@
#ifdef WITH_A2DP
if (outputTrackActive) {
+ mAudioFlinger->mLock.unlock();
mOutputTrack->stop();
+ mAudioFlinger->mLock.lock();
outputTrackActive = false;
}
#endif
if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) {
mAudioFlinger->handleForcedSpeakerRoute(FORCE_ROUTE_RESTORE);
}
-// mHardwareStatus = AUDIO_HW_IDLE;
// we're about to wait, flush the binder command buffer
IPCThreadState::self()->flushCommands();
- mWaitWorkCV.wait(mLock);
+ mAudioFlinger->mWaitWorkCV.wait(mAudioFlinger->mLock);
LOGV("Audio hardware exiting standby, output %d\n", mOutputType);
if (mMasterMute == false) {
@@ -1104,13 +1102,13 @@
if (UNLIKELY(count)) {
for (size_t i=0 ; i<count ; i++) {
const sp<Track>& track = tracksToRemove[i];
- removeActiveTrack(track);
+ removeActiveTrack_l(track);
if (track->isTerminated()) {
mTracks.remove(track);
- deleteTrackName(track->mName);
+ deleteTrackName_l(track->mName);
}
}
- }
+ }
}
if (LIKELY(enabledTracks)) {
@@ -1197,8 +1195,8 @@
run(buffer, ANDROID_PRIORITY_URGENT_AUDIO);
}
-
-sp<AudioFlinger::MixerThread::Track> AudioFlinger::MixerThread::createTrack(
+// MixerThread::createTrack_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::MixerThread::Track> AudioFlinger::MixerThread::createTrack_l(
const sp<AudioFlinger::Client>& client,
int streamType,
uint32_t sampleRate,
@@ -1218,26 +1216,22 @@
goto Exit;
}
- {
- Mutex::Autolock _l(mLock);
- if (mSampleRate == 0) {
- LOGE("Audio driver not initialized.");
- lStatus = NO_INIT;
- goto Exit;
- }
-
- track = new Track(this, client, streamType, sampleRate, format,
- channelCount, frameCount, sharedBuffer);
- if (track->getCblk() == NULL) {
- track.clear();
- lStatus = NO_MEMORY;
- goto Exit;
- }
- mTracks.add(track);
- lStatus = NO_ERROR;
+ if (mSampleRate == 0) {
+ LOGE("Audio driver not initialized.");
+ lStatus = NO_INIT;
+ goto Exit;
}
+ track = new Track(this, client, streamType, sampleRate, format,
+ channelCount, frameCount, sharedBuffer);
+ if (track->getCblk() == NULL) {
+ lStatus = NO_MEMORY;
+ goto Exit;
+ }
+ mTracks.add(track);
+ lStatus = NO_ERROR;
+
Exit:
if(status) {
*status = lStatus;
@@ -1245,12 +1239,13 @@
return track;
}
-void AudioFlinger::MixerThread::getTracks(
+// getTracks_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::getTracks_l(
SortedVector < sp<Track> >& tracks,
SortedVector < wp<Track> >& activeTracks)
{
size_t size = mTracks.size();
- LOGV ("MixerThread::getTracks() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType, mTracks.size(), mActiveTracks.size());
+ LOGV ("MixerThread::getTracks_l() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType, mTracks.size(), mActiveTracks.size());
for (size_t i = 0; i < size; i++) {
sp<Track> t = mTracks[i];
if (AudioSystem::routedToA2dpOutput(t->mStreamType)) {
@@ -1267,28 +1262,29 @@
size = activeTracks.size();
for (size_t i = 0; i < size; i++) {
- removeActiveTrack(activeTracks[i]);
+ removeActiveTrack_l(activeTracks[i]);
}
size = tracks.size();
for (size_t i = 0; i < size; i++) {
sp<Track> t = tracks[i];
mTracks.remove(t);
- deleteTrackName(t->name());
+ deleteTrackName_l(t->name());
}
}
-void AudioFlinger::MixerThread::putTracks(
+// putTracks_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::putTracks_l(
SortedVector < sp<Track> >& tracks,
SortedVector < wp<Track> >& activeTracks)
{
- LOGV ("MixerThread::putTracks() for output %d, tracks.size %d, activeTracks.size %d", mOutputType, tracks.size(), activeTracks.size());
+ LOGV ("MixerThread::putTracks_l() for output %d, tracks.size %d, activeTracks.size %d", mOutputType, tracks.size(), activeTracks.size());
size_t size = tracks.size();
for (size_t i = 0; i < size ; i++) {
sp<Track> t = tracks[i];
- int name = getTrackName();
+ int name = getTrackName_l();
if (name < 0) return;
@@ -1298,7 +1294,7 @@
int j = activeTracks.indexOf(t);
if (j >= 0) {
- addActiveTrack(t);
+ addActiveTrack_l(t);
}
}
}
@@ -1390,10 +1386,10 @@
return false;
}
-status_t AudioFlinger::MixerThread::addTrack(const sp<Track>& track)
+// addTrack_l() must be called with AudioFlinger::mLock held
+status_t AudioFlinger::MixerThread::addTrack_l(const sp<Track>& track)
{
status_t status = ALREADY_EXISTS;
- Mutex::Autolock _l(mLock);
// here the track could be either new, or restarted
// in both cases "unstop" the track
@@ -1412,55 +1408,41 @@
// effectively get the latency it requested.
track->mFillingUpStatus = Track::FS_FILLING;
track->mResetDone = false;
- addActiveTrack(track);
+ addActiveTrack_l(track);
status = NO_ERROR;
}
LOGV("mWaitWorkCV.broadcast");
- mWaitWorkCV.broadcast();
+ mAudioFlinger->mWaitWorkCV.broadcast();
return status;
}
-void AudioFlinger::MixerThread::removeTrack(wp<Track> track, int name)
+// removeTrack_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::removeTrack_l(wp<Track> track, int name)
{
- Mutex::Autolock _l(mLock);
sp<Track> t = track.promote();
if (t!=NULL && (t->mState <= TrackBase::STOPPED)) {
- remove_track_l(track, name);
- }
-}
-
-void AudioFlinger::MixerThread::remove_track_l(wp<Track> track, int name)
-{
- sp<Track> t = track.promote();
- if (t!=NULL) {
t->reset();
+ deleteTrackName_l(name);
+ removeActiveTrack_l(track);
+ mAudioFlinger->mWaitWorkCV.broadcast();
}
- deleteTrackName(name);
- removeActiveTrack(track);
- mWaitWorkCV.broadcast();
}
-void AudioFlinger::MixerThread::destroyTrack(const sp<Track>& track)
+// destroyTrack_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::destroyTrack_l(const sp<Track>& track)
{
- // NOTE: We're acquiring a strong reference on the track before
- // acquiring the lock, this is to make sure removing it from
- // mTracks won't cause the destructor to be called while the lock is
- // held (note that technically, 'track' could be a reference to an item
- // in mTracks, which is why we need to do this).
- sp<Track> keep(track);
- Mutex::Autolock _l(mLock);
track->mState = TrackBase::TERMINATED;
if (mActiveTracks.indexOf(track) < 0) {
LOGV("remove track (%d) and delete from mixer", track->name());
mTracks.remove(track);
- deleteTrackName(keep->name());
+ deleteTrackName_l(track->name());
}
}
-
-void AudioFlinger::MixerThread::addActiveTrack(const wp<Track>& t)
+// addActiveTrack_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::addActiveTrack_l(const wp<Track>& t)
{
mActiveTracks.add(t);
@@ -1476,7 +1458,8 @@
}
}
-void AudioFlinger::MixerThread::removeActiveTrack(const wp<Track>& t)
+// removeActiveTrack_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::removeActiveTrack_l(const wp<Track>& t)
{
mActiveTracks.remove(t);
@@ -1492,12 +1475,14 @@
}
}
-int AudioFlinger::MixerThread::getTrackName()
+// getTrackName_l() must be called with AudioFlinger::mLock held
+int AudioFlinger::MixerThread::getTrackName_l()
{
return mAudioMixer->getTrackName();
}
-void AudioFlinger::MixerThread::deleteTrackName(int name)
+// deleteTrackName_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::MixerThread::deleteTrackName_l(int name)
{
mAudioMixer->deleteTrackName(name);
}
@@ -1509,6 +1494,7 @@
// ----------------------------------------------------------------------------
+// TrackBase constructor must be called with AudioFlinger::mLock held
AudioFlinger::MixerThread::TrackBase::TrackBase(
const sp<MixerThread>& mixerThread,
const sp<Client>& client,
@@ -1529,7 +1515,7 @@
mFormat(format),
mFlags(flags & ~SYSTEM_FLAGS_MASK)
{
- mName = mixerThread->getTrackName();
+ mName = mixerThread->getTrackName_l();
LOGV("TrackBase contructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid());
if (mName < 0) {
LOGE("no more track names availlable");
@@ -1661,6 +1647,7 @@
// ----------------------------------------------------------------------------
+// Track constructor must be called with AudioFlinger::mLock held
AudioFlinger::MixerThread::Track::Track(
const sp<MixerThread>& mixerThread,
const sp<Client>& client,
@@ -1681,13 +1668,26 @@
AudioFlinger::MixerThread::Track::~Track()
{
wp<Track> weak(this); // never create a strong ref from the dtor
+ Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
mState = TERMINATED;
- mMixerThread->removeTrack(weak, mName);
+ mMixerThread->removeTrack_l(weak, mName);
}
void AudioFlinger::MixerThread::Track::destroy()
{
- mMixerThread->destroyTrack(this);
+ // NOTE: destroyTrack_l() can remove a strong reference to this Track
+ // by removing it from mTracks vector, so there is a risk that this Tracks's
+ // desctructor is called. As the destructor needs to lock AudioFlinger::mLock,
+ // we must acquire a strong reference on this Track before locking AudioFlinger::mLock
+ // here so that the destructor is called only when exiting this function.
+ // On the other hand, as long as Track::destroy() is only called by
+ // TrackHandle destructor, the TrackHandle still holds a strong ref on
+ // this Track with its member mTrack.
+ sp<Track> keep(this);
+ { // scope for AudioFlinger::mLock
+ Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
+ mMixerThread->destroyTrack_l(this);
+ }
}
void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size)
@@ -1765,14 +1765,15 @@
status_t AudioFlinger::MixerThread::Track::start()
{
LOGV("start(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType);
- mMixerThread->addTrack(this);
+ Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
+ mMixerThread->addTrack_l(this);
return NO_ERROR;
}
void AudioFlinger::MixerThread::Track::stop()
{
LOGV("stop(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType);
- Mutex::Autolock _l(mMixerThread->mLock);
+ Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
if (mState > STOPPED) {
mState = STOPPED;
// If the track is not active (PAUSED and buffers full), flush buffers
@@ -1786,7 +1787,7 @@
void AudioFlinger::MixerThread::Track::pause()
{
LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
- Mutex::Autolock _l(mMixerThread->mLock);
+ Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
if (mState == ACTIVE || mState == RESUMING) {
mState = PAUSING;
LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName);
@@ -1796,7 +1797,7 @@
void AudioFlinger::MixerThread::Track::flush()
{
LOGV("flush(%d)", mName);
- Mutex::Autolock _l(mMixerThread->mLock);
+ Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
if (mState != STOPPED && mState != PAUSED && mState != PAUSING) {
return;
}
@@ -1840,6 +1841,7 @@
// ----------------------------------------------------------------------------
+// RecordTrack constructor must be called with AudioFlinger::mLock held
AudioFlinger::MixerThread::RecordTrack::RecordTrack(
const sp<MixerThread>& mixerThread,
const sp<Client>& client,
@@ -1857,7 +1859,8 @@
AudioFlinger::MixerThread::RecordTrack::~RecordTrack()
{
- mMixerThread->deleteTrackName(mName);
+ Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
+ mMixerThread->deleteTrackName_l(mName);
}
status_t AudioFlinger::MixerThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer)
@@ -2183,7 +2186,6 @@
uint32_t flags,
status_t *status)
{
- sp<AudioRecordThread> thread;
sp<MixerThread::RecordTrack> recordTrack;
sp<RecordHandle> recordHandle;
sp<Client> client;
@@ -2227,7 +2229,7 @@
}
// add client to list
- {
+ { // scope for mLock
Mutex::Autolock _l(mLock);
wclient = mClients.valueFor(pid);
if (wclient != NULL) {
@@ -2236,15 +2238,15 @@
client = new Client(this, pid);
mClients.add(pid, client);
}
+
+ // frameCount must be a multiple of input buffer size
+ inFrameCount = inputBufferSize/channelCount/sizeof(short);
+ frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount;
+
+ // create new record track. The record track uses one track in mHardwareMixerThread by convention.
+ recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate,
+ format, channelCount, frameCount, flags);
}
-
- // frameCount must be a multiple of input buffer size
- inFrameCount = inputBufferSize/channelCount/sizeof(short);
- frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount;
-
- // create new record track and pass to record thread
- recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate,
- format, channelCount, frameCount, flags);
if (recordTrack->getCblk() == NULL) {
recordTrack.clear();
lStatus = NO_MEMORY;
@@ -2369,7 +2371,8 @@
} else if (mRecordTrack != 0) {
buffer.frameCount = inFrameCount;
- if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+ if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR &&
+ (int)buffer.frameCount == inFrameCount)) {
LOGV("AudioRecordThread read: %d frames", buffer.frameCount);
ssize_t bytesRead = input->read(buffer.raw, inBufferSize);
if (bytesRead < 0) {
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index 77f064b..c505336 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -112,7 +112,7 @@
virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
- virtual void wakeUp();
+ virtual void wakeUp() { mWaitWorkCV.broadcast(); }
// IBinder::DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
@@ -161,7 +161,8 @@
void doSetOutput(int outputType);
#ifdef WITH_A2DP
- void setA2dpEnabled(bool enable);
+ void setA2dpEnabled_l(bool enable);
+ void checkA2dpEnabledChange_l();
#endif
static bool streamForcedToSpeaker(int streamType);
@@ -459,7 +460,7 @@
bool isMusicActive() const;
- sp<Track> createTrack(
+ sp<Track> createTrack_l(
const sp<AudioFlinger::Client>& client,
int streamType,
uint32_t sampleRate,
@@ -468,12 +469,10 @@
int frameCount,
const sp<IMemory>& sharedBuffer,
status_t *status);
-
- void wakeUp() { mWaitWorkCV.broadcast(); }
- void getTracks(SortedVector < sp<Track> >& tracks,
+ void getTracks_l(SortedVector < sp<Track> >& tracks,
SortedVector < wp<Track> >& activeTracks);
- void putTracks(SortedVector < sp<Track> >& tracks,
+ void putTracks_l(SortedVector < sp<Track> >& tracks,
SortedVector < wp<Track> >& activeTracks);
void setOuputTrack(OutputTrack *track) { mOutputTrack = track; }
@@ -498,22 +497,19 @@
MixerThread(const Client&);
MixerThread& operator = (const MixerThread&);
- status_t addTrack(const sp<Track>& track);
- void removeTrack(wp<Track> track, int name);
- void remove_track_l(wp<Track> track, int name);
- void destroyTrack(const sp<Track>& track);
- int getTrackName();
- void deleteTrackName(int name);
- void addActiveTrack(const wp<Track>& t);
- void removeActiveTrack(const wp<Track>& t);
+ status_t addTrack_l(const sp<Track>& track);
+ void removeTrack_l(wp<Track> track, int name);
+ void destroyTrack_l(const sp<Track>& track);
+ int getTrackName_l();
+ void deleteTrackName_l(int name);
+ void addActiveTrack_l(const wp<Track>& t);
+ void removeActiveTrack_l(const wp<Track>& t);
size_t getOutputFrameCount();
status_t dumpInternals(int fd, const Vector<String16>& args);
status_t dumpTracks(int fd, const Vector<String16>& args);
sp<AudioFlinger> mAudioFlinger;
- mutable Mutex mLock;
- mutable Condition mWaitWorkCV;
SortedVector< wp<Track> > mActiveTracks;
SortedVector< sp<Track> > mTracks;
stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES];
@@ -607,11 +603,11 @@
status_t startRecord(MixerThread::RecordTrack* recordTrack);
void stopRecord(MixerThread::RecordTrack* recordTrack);
-
- void handleOutputSwitch();
- mutable Mutex mHardwareLock;
- mutable Mutex mLock;
+ mutable Mutex mHardwareLock;
+ mutable Mutex mLock;
+ mutable Condition mWaitWorkCV;
+
DefaultKeyedVector< pid_t, wp<Client> > mClients;
sp<MixerThread> mA2dpMixerThread;
@@ -620,7 +616,7 @@
AudioHardwareInterface* mA2dpAudioInterface;
sp<AudioRecordThread> mAudioRecordThread;
bool mA2dpEnabled;
- bool mA2dpEnabledReq;
+ bool mNotifyA2dpChange;
mutable int mHardwareStatus;
SortedVector< wp<IBinder> > mNotificationClients;
int mForcedSpeakerCount;
diff --git a/location/data/Android.mk b/location/data/Android.mk
index befd792..794e6c7 100644
--- a/location/data/Android.mk
+++ b/location/data/Android.mk
@@ -13,7 +13,7 @@
LOCAL_MODULE := nmea
-LOCAL_MODULE_TAGS := development
+LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(local_target_dir)/gps
@@ -27,7 +27,7 @@
LOCAL_MODULE := location
-LOCAL_MODULE_TAGS := development
+LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(local_target_dir)/gps
@@ -41,7 +41,7 @@
LOCAL_MODULE := properties
-LOCAL_MODULE_TAGS := development
+LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(local_target_dir)/gps
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 5416629..8560593 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -182,8 +182,9 @@
mLock.lock();
if (mState == TONE_STARTING) {
LOGV("Wait for start callback");
- if (mWaitCbkCond.waitRelative(mLock, seconds(1)) != NO_ERROR) {
- LOGE("--- Immediate start timed out");
+ status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
+ if (lStatus != NO_ERROR) {
+ LOGE("--- Immediate start timed out, status %d", lStatus);
mState = TONE_IDLE;
lResult = false;
}
@@ -195,13 +196,14 @@
LOGV("Delayed start\n");
mState = TONE_RESTARTING;
- if (mWaitCbkCond.waitRelative(mLock, seconds(1)) == NO_ERROR) {
+ status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
+ if (lStatus == NO_ERROR) {
if (mState != TONE_IDLE) {
lResult = true;
}
LOGV("cond received");
} else {
- LOGE("--- Delayed start timed out");
+ LOGE("--- Delayed start timed out, status %d", lStatus);
mState = TONE_IDLE;
}
}
@@ -368,6 +370,8 @@
break;
default:
LOGV("Extra Cbk");
+ // Force loop exit
+ lNumSmp = 0;
goto audioCallback_EndLoop;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 5e9c488..07b43bb 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -494,4 +494,18 @@
"/sdcard/media_api/video_stress/h263/mpeg4_QVGA.3gp",
"/sdcard/media_api/video_stress/h263/mpeg4_SQVGA.mp4"
};
+
+ //Streaming test files
+ public static final String STREAM_H264_480_360_1411k =
+ "http://sridharg.googlejunta.com/yslau/stress_media/h264_regular.mp4";
+ public static final String STREAM_WMV =
+ "http://sridharg.googlejunta.com/yslau/stress_media/bugs.wmv";
+ public static final String STREAM_H263_176x144_325k =
+ "http://sridharg.googlejunta.com/yslau/stress_media/h263_regular.3gp";
+ public static final String STREAM_H264_352x288_1536k =
+ "http://sridharg.googlejunta.com/yslau/stress_media/h264_highBitRate.mp4";
+ public static final String STREAM_MP3=
+ "http://sridharg.googlejunta.com/yslau/stress_media/mp3_regular.mp3";
+ public static final String STREAM_MPEG4_QVGA_128k =
+ "http://sridharg.googlejunta.com/yslau/stress_media/mpeg4_qvga_24fps.3gp";
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
index 0e88719..caba47c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
@@ -28,6 +28,7 @@
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
+import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
@@ -39,6 +40,16 @@
*/
public class CodecTest {
private static String TAG = "MediaPlayerApiTest";
+ private static MediaPlayer mMediaPlayer;
+ private MediaPlayer.OnPreparedListener mOnPreparedListener;
+
+ private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000; //10 seconds max.
+ private static boolean mInitialized = false;
+ private static Looper mLooper = null;
+ private static final Object lock = new Object();
+ private static final Object prepareDone = new Object();
+ private static boolean onPrepareSuccess = false;
+
public static String printCpuInfo(){
String cm = "dumpsys cpuinfo";
@@ -573,5 +584,89 @@
return true;
}
+
+ /*
+ * Initializes the message looper so that the mediaPlayer object can
+ * receive the callback messages.
+ */
+ private static void initializeMessageLooper() {
+ Log.v(TAG, "start looper");
+ new Thread() {
+ @Override
+ public void run() {
+ // Set up a looper to be used by camera.
+ Looper.prepare();
+ Log.v(TAG, "start loopRun");
+ // Save the looper so that we can terminate this thread
+ // after we are done with it.
+ mLooper = Looper.myLooper();
+ mMediaPlayer = new MediaPlayer();
+ synchronized (lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop(); // Blocks forever until Looper.quit() is called.
+ Log.v(TAG, "initializeMessageLooper: quit.");
+ }
+ }.start();
+ }
+
+ /*
+ * Terminates the message looper thread.
+ */
+ private static void terminateMessageLooper() {
+ mLooper.quit();
+ mMediaPlayer.release();
+ }
+
+ static MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
+ public void onPrepared(MediaPlayer mp) {
+ synchronized (prepareDone) {
+ Log.v(TAG, "notify the prepare callback");
+ prepareDone.notify();
+ onPrepareSuccess = true;
+ }
+ }
+ };
+
+ public static boolean prepareAsyncCallback(String filePath) throws Exception {
+ int videoWidth = 0;
+ int videoHeight = 0;
+ boolean checkVideoDimension = false;
+
+ initializeMessageLooper();
+ synchronized (lock) {
+ try {
+ lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
+ } catch(Exception e) {
+ Log.v(TAG, "looper was interrupted.");
+ return false;
+ }
+ }
+ try{
+ mMediaPlayer.setOnPreparedListener(mPreparedListener);
+ mMediaPlayer.setDataSource(filePath);
+ mMediaPlayer.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
+ mMediaPlayer.prepareAsync();
+ synchronized (prepareDone) {
+ try {
+ prepareDone.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
+ Log.v(TAG, "setPreview done");
+ } catch (Exception e) {
+ Log.v(TAG, "wait was interrupted.");
+ }
+ }
+ videoWidth = mMediaPlayer.getVideoWidth();
+ videoHeight = mMediaPlayer.getVideoHeight();
+
+ terminateMessageLooper();
+ }catch (Exception e){
+ Log.v(TAG,e.getMessage());
+ }
+ return onPrepareSuccess;
+ }
+
+
+
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
index dd94164..ee6a727 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
@@ -52,7 +52,8 @@
return true;
}
-
+
+
//Audio
//Wait for PV bugs for MP3 duration
@MediumTest
@@ -410,8 +411,6 @@
assertTrue("Play mp3 from resources", mp3Resources);
}
- //Bug# 1422662
- @Suppress
@MediumTest
public void testPrepareAsyncReset() throws Exception {
boolean isReset = CodecTest.prepareAsyncReset(MediaNames.STREAM_LARGE_MP3);
@@ -429,5 +428,19 @@
boolean isLooping = CodecTest.isLoopingAfterReset(MediaNames.AMR);
assertTrue("isLooping after reset", isLooping);
}
+
+ @LargeTest
+ public void testLocalMp3PrepareAsyncCallback() throws Exception {
+ boolean onPrepareSuccess =
+ CodecTest.prepareAsyncCallback(MediaNames.VIDEO_H263_AMR);
+ assertTrue("LocalMp3prepareAsyncCallback", onPrepareSuccess);
+ }
+
+ @LargeTest
+ public void testStreamPrepareAsyncCallback() throws Exception {
+ boolean onPrepareSuccess =
+ CodecTest.prepareAsyncCallback(MediaNames.STREAM_H264_480_360_1411k);
+ assertTrue("StreamH264PrepareAsyncCallback", onPrepareSuccess);
+ }
}
diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk
index 330a673..724e988 100644
--- a/packages/SettingsProvider/Android.mk
+++ b/packages/SettingsProvider/Android.mk
@@ -1,7 +1,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_TAGS := user
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/SettingsProvider/etc/Android.mk b/packages/SettingsProvider/etc/Android.mk
index e3f958c..d73175f 100644
--- a/packages/SettingsProvider/etc/Android.mk
+++ b/packages/SettingsProvider/etc/Android.mk
@@ -21,7 +21,7 @@
LOCAL_MODULE := bookmarks.xml
-LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_TAGS := user
# This will install the file in /system/etc
#
@@ -36,7 +36,7 @@
LOCAL_MODULE := favorites.xml
-LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_TAGS := user
# This will install the file in /system/etc
#
diff --git a/services/Android.mk b/services/java/Android.mk
similarity index 100%
rename from services/Android.mk
rename to services/java/Android.mk
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index a254081..9948322 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -196,6 +196,11 @@
ClientState mCurClient;
/**
+ * The last window token that gained focus.
+ */
+ IBinder mCurFocusedWindow;
+
+ /**
* The input context last provided by the current client.
*/
IInputContext mCurInputContext;
@@ -557,7 +562,7 @@
}
}
- void unbindCurrentInputLocked() {
+ void unbindCurrentClientLocked() {
if (mCurClient != null) {
if (DEBUG) Log.v(TAG, "unbindCurrentInputLocked: client = "
+ mCurClient.client.asBinder());
@@ -658,7 +663,7 @@
if (mCurClient != cs) {
// If the client is changing, we need to switch over to the new
// one.
- unbindCurrentInputLocked();
+ unbindCurrentClientLocked();
if (DEBUG) Log.v(TAG, "switching to client: client = "
+ cs.client.asBinder());
@@ -721,21 +726,7 @@
throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
}
- if (mHaveConnection) {
- mContext.unbindService(this);
- mHaveConnection = false;
- }
-
- if (mCurToken != null) {
- try {
- if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken);
- mIWindowManager.removeWindowToken(mCurToken);
- } catch (RemoteException e) {
- }
- mCurToken = null;
- }
-
- clearCurMethod();
+ unbindCurrentMethodLocked(false);
mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
mCurIntent.setComponent(info.getComponent());
@@ -814,7 +805,30 @@
}
}
- void clearCurMethod() {
+ void unbindCurrentMethodLocked(boolean reportToClient) {
+ if (mHaveConnection) {
+ mContext.unbindService(this);
+ mHaveConnection = false;
+ }
+
+ if (mCurToken != null) {
+ try {
+ if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken);
+ mIWindowManager.removeWindowToken(mCurToken);
+ } catch (RemoteException e) {
+ }
+ mCurToken = null;
+ }
+
+ clearCurMethodLocked();
+
+ if (reportToClient && mCurClient != null) {
+ executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
+ MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
+ }
+ }
+
+ void clearCurMethodLocked() {
if (mCurMethod != null) {
for (ClientState cs : mClients.values()) {
cs.sessionRequested = false;
@@ -831,7 +845,7 @@
+ " mCurIntent=" + mCurIntent);
if (mCurMethod != null && mCurIntent != null
&& name.equals(mCurIntent.getComponent())) {
- clearCurMethod();
+ clearCurMethodLocked();
// We consider this to be a new bind attempt, since the system
// should now try to restart the service for us.
mLastBindTime = SystemClock.uptimeMillis();
@@ -871,14 +885,22 @@
}
void updateFromSettingsLocked() {
+ // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
+ // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
+ // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
+ // enabled.
String id = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD);
- if (id != null) {
+ if (id != null && id.length() > 0) {
try {
setInputMethodLocked(id);
} catch (IllegalArgumentException e) {
Log.w(TAG, "Unknown input method from prefs: " + id, e);
+ unbindCurrentMethodLocked(true);
}
+ } else {
+ // There is no longer an input method set, so stop any current one.
+ unbindCurrentMethodLocked(true);
}
}
@@ -903,7 +925,7 @@
intent.putExtra("input_method_id", id);
mContext.sendBroadcast(intent);
}
- unbindCurrentInputLocked();
+ unbindCurrentClientLocked();
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1023,7 +1045,7 @@
return res;
}
- public void windowGainedFocus(IInputMethodClient client,
+ public void windowGainedFocus(IInputMethodClient client, IBinder windowToken,
boolean viewHasFocus, boolean isTextEditor, int softInputMode,
boolean first, int windowFlags) {
long ident = Binder.clearCallingIdentity();
@@ -1043,13 +1065,19 @@
// focus in the window manager, to allow this call to
// be made before input is started in it.
if (!mIWindowManager.inputMethodClientHasFocus(client)) {
- Log.w(TAG, "Ignoring focus gain of: " + client);
+ Log.w(TAG, "Client not active, ignoring focus gain of: " + client);
return;
}
} catch (RemoteException e) {
}
}
+ if (mCurFocusedWindow == windowToken) {
+ Log.w(TAG, "Window already focused, ignoring focus gain of: " + client);
+ return;
+ }
+ mCurFocusedWindow = windowToken;
+
switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
if (!isTextEditor || (softInputMode &
@@ -1558,7 +1586,8 @@
p.println(" mInputMethodData=" + mInputMethodData);
p.println(" mCurrentMethod=" + mCurMethodId);
client = mCurClient;
- p.println(" mCurSeq=" + mCurSeq + " mCurClient=" + client);
+ p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq);
+ p.println(" mCurFocusedWindow=" + mCurFocusedWindow);
p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
+ " mBoundToMethod=" + mBoundToMethod);
p.println(" mCurToken=" + mCurToken);
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index eece581..e298f49 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -22,9 +22,9 @@
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
-import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothA2dp;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -1491,6 +1491,12 @@
return;
}
mPluggedType = pluggedType;
+ } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
+ boolean isBluetoothPlaying =
+ intent.getIntExtra(
+ BluetoothA2dp.SINK_STATE,
+ BluetoothA2dp.STATE_DISCONNECTED) == BluetoothA2dp.STATE_PLAYING;
+ mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
} else {
return;
}
@@ -1603,13 +1609,11 @@
private void registerForBroadcasts() {
IntentFilter intentFilter = new IntentFilter();
- if (isAirplaneSensitive()) {
- intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- }
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
intentFilter.addAction(ACTION_DEVICE_IDLE);
+ intentFilter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
mContext.registerReceiver(mReceiver, intentFilter);
}
diff --git a/core/jni/server/Android.mk b/services/jni/Android.mk
similarity index 100%
rename from core/jni/server/Android.mk
rename to services/jni/Android.mk
diff --git a/core/jni/server/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp
similarity index 100%
rename from core/jni/server/com_android_server_AlarmManagerService.cpp
rename to services/jni/com_android_server_AlarmManagerService.cpp
diff --git a/core/jni/server/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp
similarity index 100%
rename from core/jni/server/com_android_server_BatteryService.cpp
rename to services/jni/com_android_server_BatteryService.cpp
diff --git a/core/jni/server/com_android_server_HardwareService.cpp b/services/jni/com_android_server_HardwareService.cpp
similarity index 100%
rename from core/jni/server/com_android_server_HardwareService.cpp
rename to services/jni/com_android_server_HardwareService.cpp
diff --git a/core/jni/server/com_android_server_KeyInputQueue.cpp b/services/jni/com_android_server_KeyInputQueue.cpp
similarity index 100%
rename from core/jni/server/com_android_server_KeyInputQueue.cpp
rename to services/jni/com_android_server_KeyInputQueue.cpp
diff --git a/core/jni/server/com_android_server_SensorService.cpp b/services/jni/com_android_server_SensorService.cpp
similarity index 100%
rename from core/jni/server/com_android_server_SensorService.cpp
rename to services/jni/com_android_server_SensorService.cpp
diff --git a/core/jni/server/com_android_server_SystemServer.cpp b/services/jni/com_android_server_SystemServer.cpp
similarity index 100%
rename from core/jni/server/com_android_server_SystemServer.cpp
rename to services/jni/com_android_server_SystemServer.cpp
diff --git a/core/jni/server/onload.cpp b/services/jni/onload.cpp
similarity index 100%
rename from core/jni/server/onload.cpp
rename to services/jni/onload.cpp
diff --git a/tests/FrameworkTest/tests/src/com/android/frameworktest/layout/table/VerticalGravityTest.java b/tests/FrameworkTest/tests/src/com/android/frameworktest/layout/table/VerticalGravityTest.java
index de3e68b..d731243 100644
--- a/tests/FrameworkTest/tests/src/com/android/frameworktest/layout/table/VerticalGravityTest.java
+++ b/tests/FrameworkTest/tests/src/com/android/frameworktest/layout/table/VerticalGravityTest.java
@@ -21,6 +21,7 @@
import android.test.ActivityInstrumentationTestCase;
import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
import android.test.ViewAsserts;
import android.view.View;
@@ -73,6 +74,7 @@
ViewAsserts.assertVerticalCenterAligned(mReference2, mCenter);
}
+ @Suppress
@MediumTest
public void testBottomGravity() throws Exception {
ViewAsserts.assertBottomAligned(mReference3, mBottom);
diff --git a/tests/StatusBar/Android.mk b/tests/StatusBar/Android.mk
index 44f5099..18fcad0 100644
--- a/tests/StatusBar/Android.mk
+++ b/tests/StatusBar/Android.mk
@@ -1,7 +1,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := test
+LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 2a47a71..3851ac0 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -117,6 +117,16 @@
* @return Whether the mode was successfully set.
*/
public native static boolean setBluetoothCoexistenceModeCommand(int mode);
+
+ /**
+ * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
+ * some of the low-level scan parameters used by the driver are changed to
+ * reduce interference with A2DP streaming.
+ *
+ * @param isSet whether to enable or disable this mode
+ * @return {@code true} if the command succeeded, {@code false} otherwise.
+ */
+ public native static boolean setBluetoothCoexistenceScanModeCommand(boolean setCoexScanMode);
public native static boolean saveConfigCommand();
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index f0009be..452a8fa 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -38,6 +38,7 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothA2dp;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.Context;
@@ -244,6 +245,10 @@
private int mRunState;
private boolean mIsScanOnly;
+
+ private BluetoothA2dp mBluetoothA2dp;
+
+ private boolean mBluetoothScanMode;
private String mInterfaceName;
private static String LS = System.getProperty("line.separator");
@@ -577,6 +582,30 @@
}
}
+ /**
+ * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
+ * some of the low-level scan parameters used by the driver are changed to
+ * reduce interference with A2DP streaming.
+ *
+ * @param isBluetoothPlaying whether to enable or disable this mode
+ */
+ public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
+ WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
+ }
+
+ private void checkIsBluetoothPlaying() {
+ boolean isBluetoothPlaying = false;
+ List<String> connected = mBluetoothA2dp.listConnectedSinks();
+
+ for (String address : connected) {
+ if (mBluetoothA2dp.getSinkState(address) == BluetoothA2dp.STATE_PLAYING) {
+ isBluetoothPlaying = true;
+ break;
+ }
+ }
+ setBluetoothScanMode(isBluetoothPlaying);
+ }
+
@Override
public void releaseWakeLock() {
if (mReleaseWakeLockCallback != null) {
@@ -682,9 +711,13 @@
* are going to end up being thrown away. Obviously, if we
* ever want to support multicast, this will have to change.
*/
+ if (mBluetoothA2dp == null) {
+ mBluetoothA2dp = new BluetoothA2dp(mContext);
+ }
synchronized (this) {
WifiNative.startPacketFiltering();
}
+ checkIsBluetoothPlaying();
break;
case EVENT_SUPPLICANT_DISCONNECT: