diff options
Diffstat (limited to 'core')
80 files changed, 1331 insertions, 4380 deletions
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 @@ public class Intent implements Parcelable { */ @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 @@ public class PackageParser { 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 @@ public final class AssetManager { /** * 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 @@ public class InputMethodService extends AbstractInputMethodService { 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 @@ public final class Checkin { 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 class BluetoothDeviceService extends IBluetoothDevice.Stub { 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 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { 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 @@ class BluetoothEventLoop { 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 @@ class BluetoothEventLoop { 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 @@ class BluetoothEventLoop { } 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 @@ implements MovementMethod 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 @@ public abstract class MetaKeyKeyListener { /** * 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 @@ public class GestureDetector { 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 @@ public class GestureDetector { mPreviousUpEvent = MotionEvent.obtain(ev); mVelocityTracker.recycle(); mVelocityTracker = null; + mIsDoubleTapping = false; mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); break; @@ -529,6 +529,7 @@ public class GestureDetector { 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 class HapticFeedbackConstants { 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } /** + * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback { (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 @@ public class View implements Drawable.Callback, KeyEvent.Callback { 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback { 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback { 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 @@ public class BaseInputConnection implements InputConnection { // 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 class EditorInfo implements InputType, Parcelable { 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 class EditorInfo implements InputType, Parcelable { public static final int IME_FLAG_NO_ENTER_ACTION = 0x40000000; /** - * Generic non-special 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. + * Generic unspecified type for {@link #imeOptions}. */ - 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 class ExtractedText implements Parcelable { 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 @@ public class ExtractedText implements Parcelable { 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 @@ public interface InputConnection { * @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 final class InputMethodManager { 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 final class InputMethodManager { 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 final class InputMethodManager { 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 @@ public final class InputMethodManager { ResultReceiver resultReceiver) { checkFocus(); synchronized (mH) { - if (mServedView != view) { + if (mServedView != view && (mServedView == null + || !mServedView.checkInputConnectionProxy(view))) { return false; } @@ -871,7 +877,8 @@ public final class InputMethodManager { public void restartInput(View view) { checkFocus(); synchronized (mH) { - if (mServedView != view) { + if (mServedView != view && (mServedView == null + || !mServedView.checkInputConnectionProxy(view))) { return; } @@ -1032,7 +1039,7 @@ public final class InputMethodManager { 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 @@ public final class InputMethodManager { 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 @@ public final class InputMethodManager { 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 final class InputMethodManager { 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 final class InputMethodManager { 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.Spannable; 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 @@ import java.util.ArrayList; * @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 @@ import java.util.ArrayList; * 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.Scroller; 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 @@ public class WebView extends AbsoluteLayout */ 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 @@ public class WebView extends AbsoluteLayout // 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout // 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 @@ public class WebView extends AbsoluteLayout } 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout * @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 @@ public class WebView extends AbsoluteLayout p.setOnHierarchyChangeListener(null); } - // Clean up the zoom ring - mZoomRingController.setVisible(false); + // Clean up the zoom controller mZoomButtonsController.setVisible(false); } @@ -3565,9 +3373,7 @@ public class WebView extends AbsoluteLayout @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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout + 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout (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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout } /** - * @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 @@ public class WebView extends AbsoluteLayout }); 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout } 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.ViewConfiguration; 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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } 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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // 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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // 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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * @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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te break; } - if (okToSend && acceptFilter()) { + if (okToSend) { createTextFilter(true); KeyEvent forwardEvent = event; @@ -2906,6 +2913,27 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** + * 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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te 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.RectF; 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 @@ class FastScroller { // 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 @@ public abstract class Filter { 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 @@ public abstract class Filter { * @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 @@ public abstract class Filter { message.sendToTarget(); } - synchronized (this) { + synchronized (mLock) { if (mThreadHandler != null) { Message finishMessage = mThreadHandler.obtainMessage(FINISH_TOKEN); mThreadHandler.sendMessageDelayed(finishMessage, 3000); @@ -229,7 +230,7 @@ public abstract class Filter { } 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 class MediaController extends FrameLayout { 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.GetChars; 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final ExtractedText mTmpExtracted = new ExtractedText(); int mBatchEditNesting; boolean mCursorChanged; + boolean mSelectionModeChanged; boolean mContentChanged; int mChangedStart, mChangedEnd, mChangedDelta; } @@ -287,8 +289,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @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 class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public int getImeOptions() { return mInputContentType != null - ? mInputContentType.imeOptions : EditorInfo.IME_UNDEFINED; + ? mInputContentType.imeOptions : EditorInfo.IME_NULL; } /** @@ -3013,9 +3015,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (imm != null) { imm.hideSoftInputFromWindow(getWindowToken(), 0); } + return; } } @@ -3844,7 +3854,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener && 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener void finishBatchEdit(final InputMethodState ims) { onEndBatchEdit(); - if (ims.mContentChanged) { + if (ims.mContentChanged || ims.mSelectionModeChanged) { updateAfterEdit(); reportExtractedText(); } else if (ims.mCursorChanged) { @@ -5888,6 +5926,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mSelectAllOnFocus) { Selection.setSelection((Spannable) mText, 0, mText.length()); - mTouchFocusSelectedAll = true; } if (selMoved && selStart >= 0 && selEnd >= 0) { @@ -6042,6 +6082,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Selection.setSelection((Spannable) mText, selStart, selEnd); } + mTouchFocusSelected = true; } mFrozenWithFocus = false; @@ -6149,7 +6190,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return super.computeVerticalScrollRange(); } + @Override + protected int computeVerticalScrollExtent() { + return getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom(); + } + public enum BufferType { NORMAL, SPANNABLE, EDITABLE, } @@ -6497,6 +6544,26 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * 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 @@ import java.io.IOException; * 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 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { 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 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { 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 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { 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 class VideoView extends SurfaceView implements MediaPlayerControl { 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.LayoutInflater; 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 @@ import android.view.WindowManager.LayoutParams; * * @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 @@ public class ZoomButtonsController implements View.OnTouchListener { 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 @@ public class ZoomButtonsController implements View.OnTouchListener { */ 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 @@ public class ZoomButtonsController implements View.OnTouchListener { } }; + /** + * 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 @@ public class ZoomButtonsController implements View.OnTouchListener { onPostConfigurationChanged(); break; - case MSG_DISMISS_ZOOM_RING: + case MSG_DISMISS_ZOOM_CONTROLS: setVisible(false); break; @@ -148,7 +162,7 @@ public class ZoomButtonsController implements View.OnTouchListener { 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 @@ public class ZoomButtonsController implements View.OnTouchListener { 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 @@ public class ZoomButtonsController implements View.OnTouchListener { 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 @@ public class ZoomButtonsController implements View.OnTouchListener { 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 @@ public class ZoomButtonsController implements View.OnTouchListener { 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 class ZoomButtonsController implements View.OnTouchListener { } } + 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 @@ public class ZoomButtonsController implements View.OnTouchListener { mOwnerViewBounds.top - targetViewRawY); boolean retValue = targetView.dispatchTouchEvent(containerEvent); containerEvent.recycle(); - return retValue; + return retValue || consumeEvent; } else { - return false; + return consumeEvent; } } @@ -465,10 +518,102 @@ public class ZoomButtonsController implements View.OnTouchListener { refreshPositioningVariables(); } + /* + * 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 ZoomRingController.getZoomType(context) == 2; + 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 class ZoomControls extends LinearLayout { } 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 @@ interface IInputMethodManager { 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 @@ LOCAL_SRC_FILES:= \ 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 @@ LOCAL_C_INCLUDES += \ external/tremor/Tremor \ external/icu4c/i18n \ external/icu4c/common \ + frameworks/opt/emoji LOCAL_SHARED_LIBRARIES := \ libexpat \ @@ -156,12 +158,13 @@ LOCAL_SHARED_LIBRARIES := \ 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 @@ LOCAL_MODULE:= libandroid_runtime 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_EventLog(JNIEnv* env); 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 @@ static const RegJNIRec gRegJNI[] = { 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 @@ static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, 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 @@ static JNINativeMethod gWifiMethods[] = { { "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/jni/server/Android.mk b/core/jni/server/Android.mk deleted file mode 100644 index 2f48edf..0000000 --- a/core/jni/server/Android.mk +++ /dev/null @@ -1,40 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - com_android_server_AlarmManagerService.cpp \ - com_android_server_BatteryService.cpp \ - com_android_server_HardwareService.cpp \ - com_android_server_KeyInputQueue.cpp \ - com_android_server_SensorService.cpp \ - com_android_server_SystemServer.cpp \ - onload.cpp - -LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libhardware \ - libhardware_legacy \ - libnativehelper \ - libsystem_server \ - libutils \ - libui - -ifeq ($(TARGET_SIMULATOR),true) -ifeq ($(TARGET_OS),linux) -ifeq ($(TARGET_ARCH),x86) -LOCAL_LDLIBS += -lpthread -ldl -lrt -endif -endif -endif - -ifeq ($(WITH_MALLOC_LEAK_CHECK),true) - LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK -endif - -LOCAL_MODULE:= libandroid_servers - -include $(BUILD_SHARED_LIBRARY) - diff --git a/core/jni/server/com_android_server_AlarmManagerService.cpp b/core/jni/server/com_android_server_AlarmManagerService.cpp deleted file mode 100644 index 1d66fb1..0000000 --- a/core/jni/server/com_android_server_AlarmManagerService.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* //device/libs/android_runtime/android_server_AlarmManagerService.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "AlarmManagerService" - -#include "JNIHelp.h" -#include "jni.h" -#include "utils/Log.h" -#include "utils/misc.h" - -#include <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <arpa/inet.h> -#include <netinet/in.h> -#include <stdlib.h> -#include <errno.h> -#include <unistd.h> - -#if HAVE_ANDROID_OS -#include <linux/ioctl.h> -#include <linux/android_alarm.h> -#endif - -#define ONE_NANOSECOND 1000000000LL -#define NANOSECONDS_TO_SECONDS(x) (x / ONE_NANOSECOND) -#define SECONDS_TO_NANOSECONDS(x) (x * ONE_NANOSECOND) - -namespace android { - -static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv* env, jobject obj, jint fd, jint minswest) -{ -#if HAVE_ANDROID_OS - struct timezone tz; - - tz.tz_minuteswest = minswest; - tz.tz_dsttime = 0; - - int result = settimeofday(NULL, &tz); - if (result < 0) { - LOGE("Unable to set kernel timezone to %d: %s\n", minswest, strerror(errno)); - return -1; - } else { - LOGD("Kernel timezone updated to %d minutes west of GMT\n", minswest); - } - - return 0; -#else - return -ENOSYS; -#endif -} - -static jint android_server_AlarmManagerService_init(JNIEnv* env, jobject obj) -{ -#if HAVE_ANDROID_OS - return open("/dev/alarm", O_RDWR); -#else - return -1; -#endif -} - -static void android_server_AlarmManagerService_close(JNIEnv* env, jobject obj, jint fd) -{ -#if HAVE_ANDROID_OS - close(fd); -#endif -} - -static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, jint type, jlong nanoseconds) -{ -#if HAVE_ANDROID_OS - struct timespec ts; - ts.tv_sec = NANOSECONDS_TO_SECONDS(nanoseconds); - ts.tv_nsec = nanoseconds - SECONDS_TO_NANOSECONDS(ts.tv_sec); - - int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts); - if (result < 0) - { - LOGE("Unable to set alarm to %lld: %s\n", nanoseconds, strerror(errno)); - } -#endif -} - -static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject obj, jint fd) -{ -#if HAVE_ANDROID_OS - int result = 0; - - do - { - result = ioctl(fd, ANDROID_ALARM_WAIT); - } while (result < 0 && errno == EINTR); - - if (result < 0) - { - LOGE("Unable to wait on alarm: %s\n", strerror(errno)); - return 0; - } - - return result; -#endif -} - -static JNINativeMethod sMethods[] = { - /* name, signature, funcPtr */ - {"init", "()I", (void*)android_server_AlarmManagerService_init}, - {"close", "(I)V", (void*)android_server_AlarmManagerService_close}, - {"set", "(IIJ)V", (void*)android_server_AlarmManagerService_set}, - {"waitForAlarm", "(I)I", (void*)android_server_AlarmManagerService_waitForAlarm}, - {"setKernelTimezone", "(II)I", (void*)android_server_AlarmManagerService_setKernelTimezone}, -}; - -int register_android_server_AlarmManagerService(JNIEnv* env) -{ - jclass clazz = env->FindClass("com/android/server/AlarmManagerService"); - - if (clazz == NULL) - { - LOGE("Can't find com/android/server/AlarmManagerService"); - return -1; - } - - return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService", - sMethods, NELEM(sMethods)); -} - -} /* namespace android */ diff --git a/core/jni/server/com_android_server_BatteryService.cpp b/core/jni/server/com_android_server_BatteryService.cpp deleted file mode 100644 index 6636a97..0000000 --- a/core/jni/server/com_android_server_BatteryService.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (C) 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. - */ - -#define LOG_TAG "BatteryService" - -#include "JNIHelp.h" -#include "jni.h" -#include "utils/Log.h" -#include "utils/misc.h" - -#include <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <arpa/inet.h> -#include <netinet/in.h> -#include <stdlib.h> -#include <errno.h> -#include <unistd.h> - -#if HAVE_ANDROID_OS -#include <linux/ioctl.h> -#endif - -namespace android { - -#define AC_ONLINE_PATH "/sys/class/power_supply/ac/online" -#define USB_ONLINE_PATH "/sys/class/power_supply/usb/online" -#define BATTERY_STATUS_PATH "/sys/class/power_supply/battery/status" -#define BATTERY_HEALTH_PATH "/sys/class/power_supply/battery/health" -#define BATTERY_PRESENT_PATH "/sys/class/power_supply/battery/present" -#define BATTERY_CAPACITY_PATH "/sys/class/power_supply/battery/capacity" -#define BATTERY_VOLTAGE_PATH "/sys/class/power_supply/battery/batt_vol" -#define BATTERY_TEMPERATURE_PATH "/sys/class/power_supply/battery/batt_temp" -#define BATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology" - -struct FieldIds { - // members - jfieldID mAcOnline; - jfieldID mUsbOnline; - jfieldID mBatteryStatus; - jfieldID mBatteryHealth; - jfieldID mBatteryPresent; - jfieldID mBatteryLevel; - jfieldID mBatteryVoltage; - jfieldID mBatteryTemperature; - jfieldID mBatteryTechnology; -}; -static FieldIds gFieldIds; - -struct BatteryManagerConstants { - jint statusUnknown; - jint statusCharging; - jint statusDischarging; - jint statusNotCharging; - jint statusFull; - jint healthUnknown; - jint healthGood; - jint healthOverheat; - jint healthDead; - jint healthOverVoltage; - jint healthUnspecifiedFailure; -}; -static BatteryManagerConstants gConstants; - -static jint getBatteryStatus(const char* status) -{ - switch (status[0]) { - case 'C': return gConstants.statusCharging; // Charging - case 'D': return gConstants.statusDischarging; // Discharging - case 'F': return gConstants.statusFull; // Not charging - case 'N': return gConstants.statusNotCharging; // Full - case 'U': return gConstants.statusUnknown; // Unknown - - default: { - LOGW("Unknown battery status '%s'", status); - return gConstants.statusUnknown; - } - } -} - -static jint getBatteryHealth(const char* status) -{ - switch (status[0]) { - case 'D': return gConstants.healthDead; // Dead - case 'G': return gConstants.healthGood; // Good - case 'O': { - if (strcmp(status, "Overheat") == 0) { - return gConstants.healthOverheat; - } else if (strcmp(status, "Over voltage") == 0) { - return gConstants.healthOverVoltage; - } - LOGW("Unknown battery health[1] '%s'", status); - return gConstants.healthUnknown; - } - - case 'U': { - if (strcmp(status, "Unspecified failure") == 0) { - return gConstants.healthUnspecifiedFailure; - } else if (strcmp(status, "Unknown") == 0) { - return gConstants.healthUnknown; - } - // fall through - } - - default: { - LOGW("Unknown battery health[2] '%s'", status); - return gConstants.healthUnknown; - } - } -} - -static int readFromFile(const char* path, char* buf, size_t size) -{ - int fd = open(path, O_RDONLY, 0); - if (fd == -1) { - LOGE("Could not open '%s'", path); - return -1; - } - - size_t count = read(fd, buf, size); - if (count > 0) { - count = (count < size) ? count : size - 1; - while (count > 0 && buf[count-1] == '\n') count--; - buf[count] = '\0'; - } else { - buf[0] = '\0'; - } - - close(fd); - return count; -} - -static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID) -{ - const int SIZE = 16; - char buf[SIZE]; - - jboolean value = false; - if (readFromFile(path, buf, SIZE) > 0) { - if (buf[0] == '1') { - value = true; - } - } - env->SetBooleanField(obj, fieldID, value); -} - -static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID) -{ - const int SIZE = 128; - char buf[SIZE]; - - jint value = 0; - if (readFromFile(path, buf, SIZE) > 0) { - value = atoi(buf); - } - env->SetIntField(obj, fieldID, value); -} - -static void android_server_BatteryService_update(JNIEnv* env, jobject obj) -{ - setBooleanField(env, obj, AC_ONLINE_PATH, gFieldIds.mAcOnline); - setBooleanField(env, obj, USB_ONLINE_PATH, gFieldIds.mUsbOnline); - setBooleanField(env, obj, BATTERY_PRESENT_PATH, gFieldIds.mBatteryPresent); - - setIntField(env, obj, BATTERY_CAPACITY_PATH, gFieldIds.mBatteryLevel); - setIntField(env, obj, BATTERY_VOLTAGE_PATH, gFieldIds.mBatteryVoltage); - setIntField(env, obj, BATTERY_TEMPERATURE_PATH, gFieldIds.mBatteryTemperature); - - const int SIZE = 128; - char buf[SIZE]; - - if (readFromFile(BATTERY_STATUS_PATH, buf, SIZE) > 0) - env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf)); - - if (readFromFile(BATTERY_HEALTH_PATH, buf, SIZE) > 0) - env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf)); - - if (readFromFile(BATTERY_TECHNOLOGY_PATH, buf, SIZE) > 0) - env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf)); -} - -static JNINativeMethod sMethods[] = { - /* name, signature, funcPtr */ - {"native_update", "()V", (void*)android_server_BatteryService_update}, -}; - -int register_android_server_BatteryService(JNIEnv* env) -{ - jclass clazz = env->FindClass("com/android/server/BatteryService"); - - if (clazz == NULL) { - LOGE("Can't find com/android/server/BatteryService"); - return -1; - } - - gFieldIds.mAcOnline = env->GetFieldID(clazz, "mAcOnline", "Z"); - gFieldIds.mUsbOnline = env->GetFieldID(clazz, "mUsbOnline", "Z"); - gFieldIds.mBatteryStatus = env->GetFieldID(clazz, "mBatteryStatus", "I"); - gFieldIds.mBatteryHealth = env->GetFieldID(clazz, "mBatteryHealth", "I"); - gFieldIds.mBatteryPresent = env->GetFieldID(clazz, "mBatteryPresent", "Z"); - gFieldIds.mBatteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I"); - gFieldIds.mBatteryTechnology = env->GetFieldID(clazz, "mBatteryTechnology", "Ljava/lang/String;"); - gFieldIds.mBatteryVoltage = env->GetFieldID(clazz, "mBatteryVoltage", "I"); - gFieldIds.mBatteryTemperature = env->GetFieldID(clazz, "mBatteryTemperature", "I"); - - LOG_FATAL_IF(gFieldIds.mAcOnline == NULL, "Unable to find BatteryService.AC_ONLINE_PATH"); - LOG_FATAL_IF(gFieldIds.mUsbOnline == NULL, "Unable to find BatteryService.USB_ONLINE_PATH"); - LOG_FATAL_IF(gFieldIds.mBatteryStatus == NULL, "Unable to find BatteryService.BATTERY_STATUS_PATH"); - LOG_FATAL_IF(gFieldIds.mBatteryHealth == NULL, "Unable to find BatteryService.BATTERY_HEALTH_PATH"); - LOG_FATAL_IF(gFieldIds.mBatteryPresent == NULL, "Unable to find BatteryService.BATTERY_PRESENT_PATH"); - LOG_FATAL_IF(gFieldIds.mBatteryLevel == NULL, "Unable to find BatteryService.BATTERY_CAPACITY_PATH"); - LOG_FATAL_IF(gFieldIds.mBatteryVoltage == NULL, "Unable to find BatteryService.BATTERY_VOLTAGE_PATH"); - LOG_FATAL_IF(gFieldIds.mBatteryTemperature == NULL, "Unable to find BatteryService.BATTERY_TEMPERATURE_PATH"); - LOG_FATAL_IF(gFieldIds.mBatteryTechnology == NULL, "Unable to find BatteryService.BATTERY_TECHNOLOGY_PATH"); - - clazz = env->FindClass("android/os/BatteryManager"); - - if (clazz == NULL) { - LOGE("Can't find android/os/BatteryManager"); - return -1; - } - - gConstants.statusUnknown = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_STATUS_UNKNOWN", "I")); - - gConstants.statusCharging = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_STATUS_CHARGING", "I")); - - gConstants.statusDischarging = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_STATUS_DISCHARGING", "I")); - - gConstants.statusNotCharging = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_STATUS_NOT_CHARGING", "I")); - - gConstants.statusFull = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_STATUS_FULL", "I")); - - gConstants.healthUnknown = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNKNOWN", "I")); - - gConstants.healthGood = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_HEALTH_GOOD", "I")); - - gConstants.healthOverheat = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVERHEAT", "I")); - - gConstants.healthDead = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_HEALTH_DEAD", "I")); - - gConstants.healthOverVoltage = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVER_VOLTAGE", "I")); - - gConstants.healthUnspecifiedFailure = env->GetStaticIntField(clazz, - env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNSPECIFIED_FAILURE", "I")); - - return jniRegisterNativeMethods(env, "com/android/server/BatteryService", sMethods, NELEM(sMethods)); -} - -} /* namespace android */ diff --git a/core/jni/server/com_android_server_HardwareService.cpp b/core/jni/server/com_android_server_HardwareService.cpp deleted file mode 100644 index ac36348..0000000 --- a/core/jni/server/com_android_server_HardwareService.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* //device/libs/android_runtime/android_os_Vibrator.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "Vibrator" - -#include "jni.h" -#include "JNIHelp.h" -#include <stdio.h> -#include "android_runtime/AndroidRuntime.h" -#include <utils/misc.h> -#include <utils/Log.h> -#include <hardware_legacy/vibrator.h> - -namespace android -{ - -static void vibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms) -{ - // LOGI("vibratorOn\n"); - vibrator_on(timeout_ms); -} - -static void vibratorOff(JNIEnv *env, jobject clazz) -{ - // LOGI("vibratorOff\n"); - vibrator_off(); -} - -static JNINativeMethod method_table[] = { - { "vibratorOn", "(J)V", (void*)vibratorOn }, - { "vibratorOff", "()V", (void*)vibratorOff } -}; - -int register_android_os_Vibrator(JNIEnv *env) -{ - return jniRegisterNativeMethods(env, "com/android/server/HardwareService", - method_table, NELEM(method_table)); -} - -}; diff --git a/core/jni/server/com_android_server_KeyInputQueue.cpp b/core/jni/server/com_android_server_KeyInputQueue.cpp deleted file mode 100644 index 63830d5..0000000 --- a/core/jni/server/com_android_server_KeyInputQueue.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (C) 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. - */ - -#define LOG_TAG "Input" - -#include "jni.h" -#include "JNIHelp.h" -#include <utils/misc.h> -#include <utils/Log.h> - -#include <ui/EventHub.h> -#include <utils/threads.h> - -#include <stdio.h> - -namespace android { - -// ---------------------------------------------------------------------------- - -static struct input_offsets_t -{ - jfieldID mMinValue; - jfieldID mMaxValue; - jfieldID mFlat; - jfieldID mFuzz; - - jfieldID mDeviceId; - jfieldID mType; - jfieldID mScancode; - jfieldID mKeycode; - jfieldID mFlags; - jfieldID mValue; - jfieldID mWhen; -} gInputOffsets; - -// ---------------------------------------------------------------------------- - -static Mutex gLock; -static sp<EventHub> gHub; - -static jboolean -android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, - jobject event) -{ - gLock.lock(); - sp<EventHub> hub = gHub; - if (hub == NULL) { - hub = new EventHub; - gHub = hub; - } - gLock.unlock(); - - int32_t deviceId; - int32_t type; - int32_t scancode, keycode; - uint32_t flags; - int32_t value; - nsecs_t when; - bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, - &flags, &value, &when); - - env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId); - env->SetIntField(event, gInputOffsets.mType, (jint)type); - env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode); - env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode); - env->SetIntField(event, gInputOffsets.mFlags, (jint)flags); - env->SetIntField(event, gInputOffsets.mValue, value); - env->SetLongField(event, gInputOffsets.mWhen, - (jlong)(nanoseconds_to_milliseconds(when))); - - return res; -} - -static jint -android_server_KeyInputQueue_getDeviceClasses(JNIEnv* env, jobject clazz, - jint deviceId) -{ - jint classes = 0; - gLock.lock(); - if (gHub != NULL) classes = gHub->getDeviceClasses(deviceId); - gLock.unlock(); - return classes; -} - -static jstring -android_server_KeyInputQueue_getDeviceName(JNIEnv* env, jobject clazz, - jint deviceId) -{ - String8 name; - gLock.lock(); - if (gHub != NULL) name = gHub->getDeviceName(deviceId); - gLock.unlock(); - - if (name.size() > 0) { - return env->NewStringUTF(name.string()); - } - return NULL; -} - -static jboolean -android_server_KeyInputQueue_getAbsoluteInfo(JNIEnv* env, jobject clazz, - jint deviceId, jint axis, - jobject info) -{ - int32_t minValue, maxValue, flat, fuzz; - int res = -1; - gLock.lock(); - if (gHub != NULL) { - res = gHub->getAbsoluteInfo(deviceId, axis, - &minValue, &maxValue, &flat, &fuzz); - } - gLock.unlock(); - - if (res < 0) return JNI_FALSE; - - env->SetIntField(info, gInputOffsets.mMinValue, (jint)minValue); - env->SetIntField(info, gInputOffsets.mMaxValue, (jint)maxValue); - env->SetIntField(info, gInputOffsets.mFlat, (jint)flat); - env->SetIntField(info, gInputOffsets.mFuzz, (jint)fuzz); - return JNI_TRUE; -} - -static jint -android_server_KeyInputQueue_getSwitchState(JNIEnv* env, jobject clazz, - jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getSwitchState(sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_getSwitchStateDevice(JNIEnv* env, jobject clazz, - jint deviceId, jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getSwitchState(deviceId, sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_getScancodeState(JNIEnv* env, jobject clazz, - jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getScancodeState(sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_getScancodeStateDevice(JNIEnv* env, jobject clazz, - jint deviceId, jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getScancodeState(deviceId, sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_getKeycodeState(JNIEnv* env, jobject clazz, - jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getKeycodeState(sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_getKeycodeStateDevice(JNIEnv* env, jobject clazz, - jint deviceId, jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getKeycodeState(deviceId, sw); - gLock.unlock(); - - return st; -} - -static jboolean -android_server_KeyInputQueue_hasKeys(JNIEnv* env, jobject clazz, - jintArray keyCodes, jbooleanArray outFlags) -{ - jboolean ret = JNI_FALSE; - - int32_t* codes = env->GetIntArrayElements(keyCodes, NULL); - uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL); - size_t numCodes = env->GetArrayLength(keyCodes); - if (numCodes == env->GetArrayLength(outFlags)) { - gLock.lock(); - if (gHub != NULL) ret = gHub->hasKeys(numCodes, codes, flags); - gLock.unlock(); - } - - env->ReleaseBooleanArrayElements(outFlags, flags, 0); - env->ReleaseIntArrayElements(keyCodes, codes, 0); - return ret; -} - -// ---------------------------------------------------------------------------- - -/* - * JNI registration. - */ -static JNINativeMethod gInputMethods[] = { - /* name, signature, funcPtr */ - { "readEvent", "(Landroid/view/RawInputEvent;)Z", - (void*) android_server_KeyInputQueue_readEvent }, - { "getDeviceClasses", "(I)I", - (void*) android_server_KeyInputQueue_getDeviceClasses }, - { "getDeviceName", "(I)Ljava/lang/String;", - (void*) android_server_KeyInputQueue_getDeviceName }, - { "getAbsoluteInfo", "(IILcom/android/server/InputDevice$AbsoluteInfo;)Z", - (void*) android_server_KeyInputQueue_getAbsoluteInfo }, - { "getSwitchState", "(I)I", - (void*) android_server_KeyInputQueue_getSwitchState }, - { "getSwitchState", "(II)I", - (void*) android_server_KeyInputQueue_getSwitchStateDevice }, - { "getScancodeState", "(I)I", - (void*) android_server_KeyInputQueue_getScancodeState }, - { "getScancodeState", "(II)I", - (void*) android_server_KeyInputQueue_getScancodeStateDevice }, - { "getKeycodeState", "(I)I", - (void*) android_server_KeyInputQueue_getKeycodeState }, - { "getKeycodeState", "(II)I", - (void*) android_server_KeyInputQueue_getKeycodeStateDevice }, - { "hasKeys", "([I[Z)Z", - (void*) android_server_KeyInputQueue_hasKeys }, -}; - -int register_android_server_KeyInputQueue(JNIEnv* env) -{ - jclass input = env->FindClass("com/android/server/KeyInputQueue"); - LOG_FATAL_IF(input == NULL, "Unable to find class com/android/server/KeyInputQueue"); - int res = jniRegisterNativeMethods(env, "com/android/server/KeyInputQueue", - gInputMethods, NELEM(gInputMethods)); - - jclass absoluteInfo = env->FindClass("com/android/server/InputDevice$AbsoluteInfo"); - LOG_FATAL_IF(absoluteInfo == NULL, "Unable to find class com/android/server/InputDevice$AbsoluteInfo"); - - gInputOffsets.mMinValue - = env->GetFieldID(absoluteInfo, "minValue", "I"); - LOG_FATAL_IF(gInputOffsets.mMinValue == NULL, "Unable to find InputDevice.AbsoluteInfo.minValue"); - - gInputOffsets.mMaxValue - = env->GetFieldID(absoluteInfo, "maxValue", "I"); - LOG_FATAL_IF(gInputOffsets.mMaxValue == NULL, "Unable to find InputDevice.AbsoluteInfo.maxValue"); - - gInputOffsets.mFlat - = env->GetFieldID(absoluteInfo, "flat", "I"); - LOG_FATAL_IF(gInputOffsets.mFlat == NULL, "Unable to find InputDevice.AbsoluteInfo.flat"); - - gInputOffsets.mFuzz - = env->GetFieldID(absoluteInfo, "fuzz", "I"); - LOG_FATAL_IF(gInputOffsets.mFuzz == NULL, "Unable to find InputDevice.AbsoluteInfo.fuzz"); - - jclass inputEvent = env->FindClass("android/view/RawInputEvent"); - LOG_FATAL_IF(inputEvent == NULL, "Unable to find class android/view/RawInputEvent"); - - gInputOffsets.mDeviceId - = env->GetFieldID(inputEvent, "deviceId", "I"); - LOG_FATAL_IF(gInputOffsets.mDeviceId == NULL, "Unable to find RawInputEvent.deviceId"); - - gInputOffsets.mType - = env->GetFieldID(inputEvent, "type", "I"); - LOG_FATAL_IF(gInputOffsets.mType == NULL, "Unable to find RawInputEvent.type"); - - gInputOffsets.mScancode - = env->GetFieldID(inputEvent, "scancode", "I"); - LOG_FATAL_IF(gInputOffsets.mScancode == NULL, "Unable to find RawInputEvent.scancode"); - - gInputOffsets.mKeycode - = env->GetFieldID(inputEvent, "keycode", "I"); - LOG_FATAL_IF(gInputOffsets.mKeycode == NULL, "Unable to find RawInputEvent.keycode"); - - gInputOffsets.mFlags - = env->GetFieldID(inputEvent, "flags", "I"); - LOG_FATAL_IF(gInputOffsets.mFlags == NULL, "Unable to find RawInputEvent.flags"); - - gInputOffsets.mValue - = env->GetFieldID(inputEvent, "value", "I"); - LOG_FATAL_IF(gInputOffsets.mValue == NULL, "Unable to find RawInputEvent.value"); - - gInputOffsets.mWhen - = env->GetFieldID(inputEvent, "when", "J"); - LOG_FATAL_IF(gInputOffsets.mWhen == NULL, "Unable to find RawInputEvent.when"); - - return res; -} - -}; // namespace android - diff --git a/core/jni/server/com_android_server_SensorService.cpp b/core/jni/server/com_android_server_SensorService.cpp deleted file mode 100644 index 695a8a3..0000000 --- a/core/jni/server/com_android_server_SensorService.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 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. - */ - -#define LOG_TAG "Sensors" - -#include <hardware/sensors.h> - -#include "jni.h" -#include "JNIHelp.h" - -namespace android { - -static struct file_descriptor_offsets_t -{ - jclass mClass; - jmethodID mConstructor; - jfieldID mDescriptor; -} gFileDescriptorOffsets; - -static struct parcel_file_descriptor_offsets_t -{ - jclass mClass; - jmethodID mConstructor; -} gParcelFileDescriptorOffsets; - -/* - * The method below are not thread-safe and not intended to be - */ - -static sensors_control_device_t* sSensorDevice = 0; - -static jint -android_init(JNIEnv *env, jclass clazz) -{ - sensors_module_t* module; - if (hw_get_module(SENSORS_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) { - if (sensors_control_open(&module->common, &sSensorDevice) == 0) { - const struct sensor_t* list; - int count = module->get_sensors_list(module, &list); - return count; - } - } - return 0; -} - -static jobject -android_open(JNIEnv *env, jclass clazz) -{ - int fd = sSensorDevice->open_data_source(sSensorDevice); - // new FileDescriptor() - jobject filedescriptor = env->NewObject( - gFileDescriptorOffsets.mClass, - gFileDescriptorOffsets.mConstructor); - - if (filedescriptor != NULL) { - env->SetIntField(filedescriptor, gFileDescriptorOffsets.mDescriptor, fd); - // new ParcelFileDescriptor() - return env->NewObject(gParcelFileDescriptorOffsets.mClass, - gParcelFileDescriptorOffsets.mConstructor, - filedescriptor); - } - close(fd); - return NULL; -} - -static jboolean -android_activate(JNIEnv *env, jclass clazz, jint sensor, jboolean activate) -{ - int active = sSensorDevice->activate(sSensorDevice, sensor, activate); - return (active<0) ? false : true; -} - -static jint -android_set_delay(JNIEnv *env, jclass clazz, jint ms) -{ - return sSensorDevice->set_delay(sSensorDevice, ms); -} - -static jint -android_data_wake(JNIEnv *env, jclass clazz) -{ - int res = sSensorDevice->wake(sSensorDevice); - return res; -} - - -static JNINativeMethod gMethods[] = { - {"_sensors_control_init", "()I", (void*) android_init }, - {"_sensors_control_open", "()Landroid/os/ParcelFileDescriptor;", (void*) android_open }, - {"_sensors_control_activate", "(IZ)Z", (void*) android_activate }, - {"_sensors_control_wake", "()I", (void*) android_data_wake }, - {"_sensors_control_set_delay","(I)I", (void*) android_set_delay }, -}; - -int register_android_server_SensorService(JNIEnv *env) -{ - jclass clazz; - - clazz = env->FindClass("java/io/FileDescriptor"); - gFileDescriptorOffsets.mClass = (jclass)env->NewGlobalRef(clazz); - gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V"); - gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); - - clazz = env->FindClass("android/os/ParcelFileDescriptor"); - gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); - - return jniRegisterNativeMethods(env, "com/android/server/SensorService", - gMethods, NELEM(gMethods)); -} - -}; // namespace android diff --git a/core/jni/server/com_android_server_SystemServer.cpp b/core/jni/server/com_android_server_SystemServer.cpp deleted file mode 100644 index ae29405..0000000 --- a/core/jni/server/com_android_server_SystemServer.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 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. - */ -#include <utils/Log.h> -#include <utils/misc.h> - -#include "jni.h" -#include "JNIHelp.h" - -namespace android { - -extern "C" int system_init(); - -static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz) -{ - system_init(); -} - -/* - * JNI registration. - */ -static JNINativeMethod gMethods[] = { - /* name, signature, funcPtr */ - { "init1", "([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 }, -}; - -int register_android_server_SystemServer(JNIEnv* env) -{ - return jniRegisterNativeMethods(env, "com/android/server/SystemServer", - gMethods, NELEM(gMethods)); -} - -}; // namespace android - - diff --git a/core/jni/server/onload.cpp b/core/jni/server/onload.cpp deleted file mode 100644 index 3d68cfb..0000000 --- a/core/jni/server/onload.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "JNIHelp.h" -#include "jni.h" -#include "utils/Log.h" -#include "utils/misc.h" - -namespace android { -int register_android_server_AlarmManagerService(JNIEnv* env); -int register_android_server_BatteryService(JNIEnv* env); -int register_android_server_KeyInputQueue(JNIEnv* env); -int register_android_os_Vibrator(JNIEnv* env); -int register_android_server_SensorService(JNIEnv* env); -int register_android_server_SystemServer(JNIEnv* env); -}; - -using namespace android; - -extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) -{ - JNIEnv* env = NULL; - jint result = -1; - - if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { - LOGE("GetEnv failed!"); - return result; - } - LOG_ASSERT(env, "Could not retrieve the env!"); - - register_android_server_KeyInputQueue(env); - register_android_os_Vibrator(env); - register_android_server_AlarmManagerService(env); - register_android_server_BatteryService(env); - register_android_server_SensorService(env); - register_android_server_SystemServer(env); - - return JNI_VERSION_1_4; -} 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 @@ LOCAL_CERTIFICATE := platform # 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 Binary files differindex 8ecc24c..bafa5c5 100644 --- a/core/res/res/drawable-land/statusbar_background.png +++ b/core/res/res/drawable-land/statusbar_background.png 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 Binary files differnew file mode 100644 index 0000000..33b74a6 --- /dev/null +++ b/core/res/res/drawable/btn_circle_disable.png diff --git a/core/res/res/drawable/btn_circle_disable_focused.png b/core/res/res/drawable/btn_circle_disable_focused.png Binary files differnew file mode 100644 index 0000000..005ad8d --- /dev/null +++ b/core/res/res/drawable/btn_circle_disable_focused.png diff --git a/core/res/res/drawable/btn_circle_longpress.png b/core/res/res/drawable/btn_circle_longpress.png Binary files differnew file mode 100644 index 0000000..f27d411 --- /dev/null +++ b/core/res/res/drawable/btn_circle_longpress.png diff --git a/core/res/res/drawable/btn_circle_normal.png b/core/res/res/drawable/btn_circle_normal.png Binary files differnew file mode 100644 index 0000000..fc5af1c --- /dev/null +++ b/core/res/res/drawable/btn_circle_normal.png diff --git a/core/res/res/drawable/btn_circle_pressed.png b/core/res/res/drawable/btn_circle_pressed.png Binary files differnew file mode 100644 index 0000000..8f40afd --- /dev/null +++ b/core/res/res/drawable/btn_circle_pressed.png diff --git a/core/res/res/drawable/btn_circle_selected.png b/core/res/res/drawable/btn_circle_selected.png Binary files differnew file mode 100644 index 0000000..c74fac2 --- /dev/null +++ b/core/res/res/drawable/btn_circle_selected.png 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_plus_arrow_rotatable.xml b/core/res/res/drawable/ic_btn_round_more.xml index b77eaa4..82b7593 100644 --- a/core/res/res/drawable/zoom_ring_thumb_plus_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_plus_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 Binary files differnew file mode 100644 index 0000000..1ab98c9 --- /dev/null +++ b/core/res/res/drawable/ic_btn_round_more_disabled.png diff --git a/core/res/res/drawable/ic_btn_round_more_normal.png b/core/res/res/drawable/ic_btn_round_more_normal.png Binary files differnew file mode 100644 index 0000000..ebdc55c --- /dev/null +++ b/core/res/res/drawable/ic_btn_round_more_normal.png diff --git a/core/res/res/drawable/keyboard_textfield_pressed.9.png b/core/res/res/drawable/keyboard_textfield_pressed.9.png Binary files differdeleted file mode 100644 index f4e3f10..0000000 --- a/core/res/res/drawable/keyboard_textfield_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable/stat_sys_phone_call.png b/core/res/res/drawable/stat_sys_phone_call.png Binary files differindex ad53693..c44d062 100644 --- a/core/res/res/drawable/stat_sys_phone_call.png +++ b/core/res/res/drawable/stat_sys_phone_call.png diff --git a/core/res/res/drawable/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable/stat_sys_phone_call_bluetooth.png Binary files differnew file mode 100644 index 0000000..7abfd19 --- /dev/null +++ b/core/res/res/drawable/stat_sys_phone_call_bluetooth.png diff --git a/core/res/res/drawable/statusbar_background.png b/core/res/res/drawable/statusbar_background.png Binary files differindex 945ad92..25a2344 100644 --- a/core/res/res/drawable/statusbar_background.png +++ b/core/res/res/drawable/statusbar_background.png diff --git a/core/res/res/drawable/zoom_ring_arrows.png b/core/res/res/drawable/zoom_ring_arrows.png Binary files differdeleted file mode 100644 index e443de7..0000000 --- a/core/res/res/drawable/zoom_ring_arrows.png +++ /dev/null diff --git a/core/res/res/drawable/zoom_ring_overview_tab.9.png b/core/res/res/drawable/zoom_ring_overview_tab.9.png Binary files differdeleted file mode 100644 index d2658d8..0000000 --- a/core/res/res/drawable/zoom_ring_overview_tab.9.png +++ /dev/null diff --git a/core/res/res/drawable/zoom_ring_thumb.png b/core/res/res/drawable/zoom_ring_thumb.png Binary files differdeleted file mode 100644 index 1002724..0000000 --- a/core/res/res/drawable/zoom_ring_thumb.png +++ /dev/null diff --git a/core/res/res/drawable/zoom_ring_thumb_minus.png b/core/res/res/drawable/zoom_ring_thumb_minus.png Binary files differdeleted file mode 100644 index faed674..0000000 --- a/core/res/res/drawable/zoom_ring_thumb_minus.png +++ /dev/null diff --git a/core/res/res/drawable/zoom_ring_thumb_minus_arrow.png b/core/res/res/drawable/zoom_ring_thumb_minus_arrow.png Binary files differdeleted file mode 100644 index abe1b8a..0000000 --- a/core/res/res/drawable/zoom_ring_thumb_minus_arrow.png +++ /dev/null diff --git a/core/res/res/drawable/zoom_ring_thumb_minus_arrow_rotatable.xml b/core/res/res/drawable/zoom_ring_thumb_minus_arrow_rotatable.xml deleted file mode 100644 index f2d495b..0000000 --- a/core/res/res/drawable/zoom_ring_thumb_minus_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_minus_arrow" /> diff --git a/core/res/res/drawable/zoom_ring_thumb_plus.png b/core/res/res/drawable/zoom_ring_thumb_plus.png Binary files differdeleted file mode 100644 index ba7aff2..0000000 --- a/core/res/res/drawable/zoom_ring_thumb_plus.png +++ /dev/null diff --git a/core/res/res/drawable/zoom_ring_thumb_plus_arrow.png b/core/res/res/drawable/zoom_ring_thumb_plus_arrow.png Binary files differdeleted file mode 100644 index d01ccb4..0000000 --- a/core/res/res/drawable/zoom_ring_thumb_plus_arrow.png +++ /dev/null diff --git a/core/res/res/drawable/zoom_ring_track.png b/core/res/res/drawable/zoom_ring_track.png Binary files differdeleted file mode 100644 index 1d5a44c..0000000 --- a/core/res/res/drawable/zoom_ring_track.png +++ /dev/null diff --git a/core/res/res/drawable/zoom_ring_track_absolute.png b/core/res/res/drawable/zoom_ring_track_absolute.png Binary files differdeleted file mode 100644 index 2b87699..0000000 --- a/core/res/res/drawable/zoom_ring_track_absolute.png +++ /dev/null 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 18a1355..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> |