diff options
Diffstat (limited to 'core/java/android')
26 files changed, 385 insertions, 496 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 8236943..849a37d 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -890,9 +890,9 @@ public class Activity extends ContextThemeWrapper } /** - * Called after {@link #onCreate} or {@link #onStop} when the current - * activity is now being displayed to the user. It will - * be followed by {@link #onRestart}. + * Called after {@link #onCreate} — or after {@link #onRestart} when + * the activity had been stopped, but is now again being displayed to the + * user. It will be followed by {@link #onResume}. * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be @@ -907,9 +907,9 @@ public class Activity extends ContextThemeWrapper } /** - * Called after {@link #onStart} when the current activity is being + * Called after {@link #onStop} when the current activity is being * re-displayed to the user (the user has navigated back to it). It will - * be followed by {@link #onResume}. + * be followed by {@link #onStart} and then {@link #onResume}. * * <p>For activities that are using raw {@link Cursor} objects (instead of * creating them through @@ -923,6 +923,7 @@ public class Activity extends ContextThemeWrapper * thrown.</em></p> * * @see #onStop + * @see #onStart * @see #onResume */ protected void onRestart() { @@ -1220,7 +1221,7 @@ public class Activity extends ContextThemeWrapper /** * Called when you are no longer visible to the user. You will next - * receive either {@link #onStart}, {@link #onDestroy}, or nothing, + * receive either {@link #onRestart}, {@link #onDestroy}, or nothing, * depending on later user activity. * * <p>Note that this method may never be called, in low memory situations diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 3981f27..e232c2c 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -620,6 +620,9 @@ public class SensorManager extends IRotationWatcher.Stub */ @Deprecated public boolean registerListener(SensorListener listener, int sensors, int rate) { + if (listener == null) { + return false; + } boolean result = false; result = registerLegacyListener(SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER, listener, sensors, rate) || result; @@ -638,6 +641,9 @@ public class SensorManager extends IRotationWatcher.Stub private boolean registerLegacyListener(int legacyType, int type, SensorListener listener, int sensors, int rate) { + if (listener == null) { + return false; + } boolean result = false; // Are we activating this legacy sensor? if ((sensors & legacyType) != 0) { @@ -693,6 +699,9 @@ public class SensorManager extends IRotationWatcher.Stub private void unregisterLegacyListener(int legacyType, int type, SensorListener listener, int sensors) { + if (listener == null) { + return; + } // do we know about this listener? LegacyListener legacyListener = null; synchronized (mLegacyListenersMap) { @@ -800,6 +809,9 @@ public class SensorManager extends IRotationWatcher.Stub */ public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate, Handler handler) { + if (listener == null || sensor == null) { + return false; + } boolean result; int delay = -1; switch (rate) { @@ -856,6 +868,9 @@ public class SensorManager extends IRotationWatcher.Stub } private void unregisterListener(Object listener, Sensor sensor) { + if (listener == null || sensor == null) { + return; + } try { synchronized (sListeners) { final int size = sListeners.size(); @@ -881,6 +896,9 @@ public class SensorManager extends IRotationWatcher.Stub } private void unregisterListener(Object listener) { + if (listener == null) { + return; + } try { synchronized (sListeners) { final int size = sListeners.size(); diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index da67c6d..c37845f 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -141,7 +141,8 @@ class SoftInputWindow extends Dialog { lp.gravity = Gravity.BOTTOM; lp.width = -1; // Let the input method window's orientation follow sensor based rotation - lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; + // Turn this off for now, it is very problematic. + //lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; getWindow().setAttributes(lp); getWindow().setFlags( diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index 3820f28..a255438 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -667,7 +667,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * {@link SharedPreferences}. This should be unique for the package. * * @param key The key for the preference. - * @see #getId() */ public void setKey(String key) { mKey = key; @@ -1460,7 +1459,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * @param container The Bundle in which to save the instance of this Preference. * * @see #restoreHierarchyState - * @see #dispatchSaveInstanceState * @see #onSaveInstanceState */ public void saveHierarchyState(Bundle container) { @@ -1474,7 +1472,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * * @param container The Bundle in which to save the instance of this Preference. * - * @see #dispatchRestoreInstanceState * @see #saveHierarchyState * @see #onSaveInstanceState */ @@ -1503,7 +1500,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * The default implementation returns null. * @see #onRestoreInstanceState * @see #saveHierarchyState - * @see #dispatchSaveInstanceState */ protected Parcelable onSaveInstanceState() { mBaseMethodCalled = true; @@ -1516,7 +1512,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * @param container The Bundle that holds the previously saved state. * * @see #saveHierarchyState - * @see #dispatchRestoreInstanceState * @see #onRestoreInstanceState */ public void restoreHierarchyState(Bundle container) { @@ -1530,7 +1525,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * not want to save state for their children. * * @param container The Bundle that holds the previously saved state. - * @see #dispatchSaveInstanceState * @see #restoreHierarchyState * @see #onRestoreInstanceState */ @@ -1557,7 +1551,6 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis * {@link #onSaveInstanceState}. * @see #onSaveInstanceState * @see #restoreHierarchyState - * @see #dispatchRestoreInstanceState */ protected void onRestoreInstanceState(Parcelable state) { mBaseMethodCalled = true; diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java index 9929b96..5353b53 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -100,7 +100,6 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi * * @return An adapter that provides the {@link Preference} contained in this * {@link PreferenceScreen}. - * @see PreferenceGroupAdapter */ public ListAdapter getRootAdapter() { if (mRootAdapter == null) { diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java index 688e19a..0cdac53 100644 --- a/core/java/android/provider/Checkin.java +++ b/core/java/android/provider/Checkin.java @@ -126,6 +126,11 @@ public final class Checkin { /** Valid tag values. Extend as necessary for your needs. */ public enum Tag { + 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/provider/Gmail.java b/core/java/android/provider/Gmail.java index 253dd4a..5b3c223 100644 --- a/core/java/android/provider/Gmail.java +++ b/core/java/android/provider/Gmail.java @@ -1477,10 +1477,20 @@ public final class Gmail { LABEL_OUTBOX, LABEL_DRAFT, LABEL_ALL, LABEL_SPAM, LABEL_TRASH); + + private static final Set<String> USER_MEANINGFUL_SYSTEM_LABELS_SET = + Sets.newHashSet( + SORTED_USER_MEANINGFUL_SYSTEM_LABELS.toArray( + new String[]{})); + public static List<String> getSortedUserMeaningfulSystemLabels() { return SORTED_USER_MEANINGFUL_SYSTEM_LABELS; } + public static Set<String> getUserMeaningfulSystemLabelsSet() { + return USER_MEANINGFUL_SYSTEM_LABELS_SET; + } + /** * If you are ever tempted to remove outbox or draft from this set make sure you have a * way to stop draft and outbox messages from getting purged before they are sent to the diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 2897b4e..b91bc9d 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -158,6 +158,12 @@ public final class MediaStore public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality"; /** + * Specify the maximum allowed size. + * @hide + */ + public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit"; + + /** * The name of the Intent-extra used to indicate a content resolver Uri to be used to * store the requested image or video. */ diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 86d5a1e..fa53a60 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -47,8 +47,8 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; public class BluetoothDeviceService extends IBluetoothDevice.Stub { @@ -121,7 +121,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { * Bring down bluetooth. Returns true on success. * * @param saveSetting If true, disable BT in settings - * + * */ public synchronized boolean disable(boolean saveSetting) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -172,7 +172,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { * This turns on/off the underlying hardware. * * @param saveSetting If true, enable BT in settings - * + * * @return True on success (so far), guaranteeing the callback with be * notified when complete. */ @@ -275,6 +275,14 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { private final HashMap<String, Integer> mState = new HashMap<String, Integer>(); private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>(); private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>(); + // List of all the vendor_id prefix of Bluetooth addresses for which + // auto pairing is not attempted + private final ArrayList<String> mAutoPairingBlacklisted = + new ArrayList<String>(Arrays.asList( + "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", //ALPS + "00:21:4F", "00:23:06", "00:24:33", "00:A0:79", // ALPS + "00:0E:6D", "00:13:E0", "00:21:E8", "00:60:57"// Murata for Prius 2007 + )); public synchronized void loadBondState() { if (!mIsEnabled) { @@ -322,6 +330,13 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mContext.sendBroadcast(intent, BLUETOOTH_PERM); } + public boolean isAutoPairingBlacklisted(String address) { + for (String blacklistAddress : mAutoPairingBlacklisted) { + if (address.startsWith(blacklistAddress)) return true; + } + return false; + } + public synchronized int getBondState(String address) { Integer state = mState.get(address); if (state == null) { diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 187ec2c..8b09583 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -312,7 +312,8 @@ class BluetoothEventLoop { case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: - if (!mBluetoothService.getBondState().hasAutoPairingFailed(address)) { + if (!mBluetoothService.getBondState().hasAutoPairingFailed(address) && + !mBluetoothService.getBondState().isAutoPairingBlacklisted(address)) { mBluetoothService.getBondState().attempt(address); mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes("0000")); return; diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index d5434b6..d6ea91c 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -117,7 +117,8 @@ public class KeyEvent implements Parcelable { public static final int KEYCODE_PREVIOUSSONG = 88; public static final int KEYCODE_REWIND = 89; public static final int KEYCODE_FORWARD = 90; - private static final int LAST_KEYCODE = KEYCODE_FORWARD; + public static final int KEYCODE_MUTE = 91; + private static final int LAST_KEYCODE = KEYCODE_MUTE; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -502,6 +503,7 @@ public class KeyEvent implements Parcelable { case KEYCODE_ENDCALL: case KEYCODE_VOLUME_UP: case KEYCODE_VOLUME_DOWN: + case KEYCODE_MUTE: case KEYCODE_POWER: case KEYCODE_HEADSETHOOK: case KEYCODE_PLAYPAUSE: diff --git a/core/java/android/view/OrientationEventListener.java b/core/java/android/view/OrientationEventListener.java index cddec11..391ba1e 100755 --- a/core/java/android/view/OrientationEventListener.java +++ b/core/java/android/view/OrientationEventListener.java @@ -69,8 +69,11 @@ public abstract class OrientationEventListener { public OrientationEventListener(Context context, int rate) { mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); mRate = rate; - mSensorEventListener = new SensorEventListenerImpl(); mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + if (mSensor != null) { + // Create listener only if sensors do exist + mSensorEventListener = new SensorEventListenerImpl(); + } } void registerListener(OrientationListener lis) { @@ -82,6 +85,10 @@ public abstract class OrientationEventListener { * {@link #onOrientationChanged} when the device orientation changes. */ public void enable() { + if (mSensor == null) { + Log.w(TAG, "Cannot detect sensors. Not enabled"); + return; + } if (mEnabled == false) { if (localLOGV) Log.d(TAG, "OrientationEventListener enabled"); mSensorManager.registerListener(mSensorEventListener, mSensor, mRate); @@ -93,6 +100,10 @@ public abstract class OrientationEventListener { * Disables the OrientationEventListener. */ public void disable() { + if (mSensor == null) { + Log.w(TAG, "Cannot detect sensors. Invalid disable"); + return; + } if (mEnabled == true) { if (localLOGV) Log.d(TAG, "OrientationEventListener disabled"); mSensorManager.unregisterListener(mSensorEventListener); @@ -138,6 +149,13 @@ public abstract class OrientationEventListener { } } + + /* + * Returns true if sensor is enabled and false otherwise + */ + public boolean canDetectOrientation() { + return mSensor != null; + } /** * Called when the orientation of the device has changed. diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java index 21a72e7..3d0dda3 100644 --- a/core/java/android/view/SurfaceHolder.java +++ b/core/java/android/view/SurfaceHolder.java @@ -240,7 +240,7 @@ public interface SurfaceHolder { * in particular there is no guarantee that the content of the Surface * will remain unchanged when lockCanvas() is called again. * - * @see android.view.SurfaceHolder.lockCanvas + * @see #lockCanvas() * * @param canvas The Canvas previously returned by lockCanvas(). */ diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index db8829f..3cfaf1b 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -1817,6 +1817,11 @@ public final class ViewRoot extends Handler implements ViewParent, if (event != null) { event.recycle(); } + // If we reach this, we delivered a trackball event to mView and + // mView consumed it. Because we will not translate the trackball + // event into a key event, touch mode will not exit, so we exit + // touch mode here. + ensureTouchMode(false); //noinspection ReturnInsideFinallyBlock return; } @@ -2140,7 +2145,7 @@ public final class ViewRoot extends Handler implements ViewParent, } boolean keyHandled = mView.dispatchKeyEvent(event); - if ((!keyHandled && isDown) || (action == KeyEvent.ACTION_MULTIPLE)) { + if (!keyHandled && isDown) { int direction = 0; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: @@ -2541,7 +2546,7 @@ public final class ViewRoot extends Handler implements ViewParent, * for us to consider the user to be doing fast trackball movements, * and thus apply an acceleration. */ - static final long FAST_MOVE_TIME = 100; + static final long FAST_MOVE_TIME = 150; /** * Scaling factor to the time (in milliseconds) between events to how @@ -2549,7 +2554,7 @@ public final class ViewRoot extends Handler implements ViewParent, * is < FAST_MOVE_TIME this multiplies the acceleration; when > * FAST_MOVE_TIME it divides it. */ - static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/50); + static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40); float position; float absPosition; diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 051f823..0f15b17 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -129,14 +129,22 @@ public interface WindowManagerPolicy { Rect contentFrame, Rect visibleFrame); /** - * Retrieve the current frame of the window. Must be called with the - * window manager lock held. + * Retrieve the current frame of the window that has been assigned by + * the window manager. Must be called with the window manager lock held. * * @return Rect The rectangle holding the window frame. */ public Rect getFrameLw(); /** + * Retrieve the current frame of the window that is actually shown. + * Must be called with the window manager lock held. + * + * @return Rect The rectangle holding the shown window frame. + */ + public Rect getShownFrameLw(); + + /** * Retrieve the frame of the display that this window was last * laid out in. Must be called with the * window manager lock held. @@ -273,9 +281,12 @@ public interface WindowManagerPolicy { * false, this is based on the currently requested * frame, which any current animation will be moving * towards. + * @param onlyOpaque If true, this will only pass if the window is + * also opaque. * @return Returns true if the window is both full screen and opaque */ - public boolean fillsScreenLw(int width, int height, boolean shownFrame); + public boolean fillsScreenLw(int width, int height, boolean shownFrame, + boolean onlyOpaque); /** * Returns true if this window has been shown on screen at some time in diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java index 4aa3f7a..5877932 100755 --- a/core/java/android/view/WindowOrientationListener.java +++ b/core/java/android/view/WindowOrientationListener.java @@ -74,8 +74,11 @@ public abstract class WindowOrientationListener { public WindowOrientationListener(Context context, int rate) { mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); mRate = rate; - mSensorEventListener = new SensorEventListenerImpl(); mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + if (mSensor != null) { + // Create listener only if sensors do exist + mSensorEventListener = new SensorEventListenerImpl(); + } } /** @@ -83,6 +86,10 @@ public abstract class WindowOrientationListener { * {@link #onOrientationChanged} when the device orientation changes. */ public void enable() { + if (mSensor == null) { + Log.w(TAG, "Cannot detect sensors. Not enabled"); + return; + } if (mEnabled == false) { if (localLOGV) Log.d(TAG, "WindowOrientationListener enabled"); mSensorManager.registerListener(mSensorEventListener, mSensor, mRate); @@ -94,6 +101,10 @@ public abstract class WindowOrientationListener { * Disables the WindowOrientationListener. */ public void disable() { + if (mSensor == null) { + Log.w(TAG, "Cannot detect sensors. Invalid disable"); + return; + } if (mEnabled == true) { if (localLOGV) Log.d(TAG, "WindowOrientationListener disabled"); mSensorManager.unregisterListener(mSensorEventListener); @@ -145,6 +156,13 @@ public abstract class WindowOrientationListener { } } + /* + * Returns true if sensor is enabled and false otherwise + */ + public boolean canDetectOrientation() { + return mSensor != null; + } + /** * Called when the orientation of the device has changed. * orientation parameter is in degrees, ranging from 0 to 359. diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 5c470cf..4e2b2ab 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -20,6 +20,8 @@ import android.content.Context; import android.os.Build; import android.os.Handler; import android.os.Message; +import android.provider.Checkin; + import java.lang.SecurityException; import android.content.pm.PackageManager; @@ -408,6 +410,10 @@ public class WebSettings { * @see WebSettings.TextSize */ public synchronized void setTextSize(TextSize t) { + if (WebView.mLogEvent && mTextSize != t ) { + Checkin.updateStats(mContext.getContentResolver(), + Checkin.Stats.Tag.BROWSER_TEXT_SIZE_CHANGE, 1, 0.0); + } mTextSize = t; postSync(); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 4d9a8fb..c59a5fc 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -38,11 +38,13 @@ import android.os.Handler; import android.os.Message; import android.os.ServiceManager; import android.os.SystemClock; +import android.provider.Checkin; import android.text.IClipboard; import android.text.Selection; import android.text.Spannable; import android.util.AttributeSet; import android.util.Config; +import android.util.EventLog; import android.util.Log; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; @@ -450,6 +452,13 @@ public class WebView extends AbsoluteLayout // Used to match key downs and key ups private boolean mGotKeyDown; + /* package */ static boolean mLogEvent = true; + private static final int EVENT_LOG_ZOOM_LEVEL_CHANGE = 70101; + private static final int EVENT_LOG_DOUBLE_TAP_DURATION = 70102; + + // for event log + private long mLastTouchUpTime = 0; + /** * URI scheme for telephone number */ @@ -556,7 +565,11 @@ public class WebView extends AbsoluteLayout private ZoomRingController.OnZoomListener mZoomListener = new ZoomRingController.OnZoomListener() { - + + private float mClockwiseBound; + private float mCounterClockwiseBound; + private float mStartScale; + public void onCenter(int x, int y) { // Don't translate when the control is invoked, hence we do nothing // in this callback @@ -564,6 +577,10 @@ public class WebView extends AbsoluteLayout public void onBeginPan() { setZoomOverviewVisible(false); + if (mLogEvent) { + Checkin.updateStats(mContext.getContentResolver(), + Checkin.Stats.Tag.BROWSER_ZOOM_RING_DRAG, 1, 0.0); + } } public boolean onPan(int deltaX, int deltaY) { @@ -576,12 +593,25 @@ public class WebView extends AbsoluteLayout 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) { + 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) { + } else if (mActualScale < 1 && mCounterClockwiseBound > 0) { angle = (float) Math.round(ZOOM_RING_STEPS * (1 - mActualScale) / (1 - mMinZoomScale)) / ZOOM_RING_STEPS; @@ -590,16 +620,26 @@ public class WebView extends AbsoluteLayout // 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); + } } } 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); } @@ -616,21 +656,21 @@ public class WebView extends AbsoluteLayout mZoomCenterY = (float) centerY; float scale = 1.0f; - if (curAngle > (float) Math.PI) - curAngle -= (float) 2 * Math.PI; - if (curAngle > 0) { - if (curAngle >= MAX_ZOOM_RING_ANGLE) { + // 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 < 0) { - if (curAngle <= -MAX_ZOOM_RING_ANGLE) { + } else { + if (curAngle <= mClockwiseBound) { scale = mMaxZoomScale; } else { - scale = 1 + (float) Math.round(-curAngle + scale = 1 + (float) Math.round( + ((float) 2 * Math.PI - curAngle) / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS * (mMaxZoomScale - 1); } @@ -687,12 +727,11 @@ public class WebView extends AbsoluteLayout mScroller = new Scroller(context); mZoomRingController = new ZoomRingController(context, this); mZoomRingController.setResetThumbAutomatically(false); - mZoomRingController.setThumbClockwiseBound( - (float) (2 * Math.PI - MAX_ZOOM_RING_ANGLE)); - mZoomRingController.setThumbCounterclockwiseBound(MAX_ZOOM_RING_ANGLE); mZoomRingController.setCallback(mZoomListener); mZoomRingController.setZoomRingTrack( com.android.internal.R.drawable.zoom_ring_track_absolute); + mZoomRingController.setPannerAcceleration(160); + mZoomRingController.setPannerStartAcceleratingDuration(700); createZoomRingOverviewTab(); } @@ -730,6 +769,10 @@ public class WebView extends AbsoluteLayout 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(); }}); @@ -3468,7 +3511,7 @@ public class WebView extends AbsoluteLayout // update mMinZoomScale if (mMinContentWidth > MAX_FLOAT_CONTENT_WIDTH) { boolean atMin = Math.abs(mActualScale - mMinZoomScale) < 0.01f; - mMinZoomScale = (float) getViewWidth() / mMinContentWidth; + mMinZoomScale = (float) getViewWidth() / mContentWidth; if (atMin) { // if the WebView was at the minimum zoom scale, keep it. e,g., // the WebView was at the minimum zoom scale at the portrait @@ -3535,7 +3578,8 @@ public class WebView extends AbsoluteLayout return false; } - if (mShowZoomRingTutorial && mMinZoomScale < mMaxZoomScale) { + if (mShowZoomRingTutorial && getSettings().supportZoom() + && (mMaxZoomScale - mMinZoomScale) > ZOOM_RING_STEPS * 0.01f) { ZoomRingController.showZoomTutorialOnce(mContext); mShowZoomRingTutorial = false; mPrivateHandler.sendMessageDelayed(mPrivateHandler @@ -3616,10 +3660,18 @@ public class WebView extends AbsoluteLayout mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); mZoomRingController.setVisible(true); mInZoomTapDragMode = true; + if (mLogEvent) { + EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION, + (eventTime - mLastTouchUpTime), eventTime); + } return mZoomRingController.handleDoubleTapEvent(ev); } else { mTouchMode = TOUCH_INIT_MODE; mPreventDrag = mForwardTouchEvents; + if (mLogEvent && eventTime - mLastTouchUpTime < 1000) { + EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION, + (eventTime - mLastTouchUpTime), eventTime); + } } // don't trigger the link if zoom ring is visible if (mTouchMode == TOUCH_INIT_MODE @@ -3783,17 +3835,23 @@ public class WebView extends AbsoluteLayout break; } case MotionEvent.ACTION_UP: { + mLastTouchUpTime = eventTime; switch (mTouchMode) { case TOUCH_INIT_MODE: // tap if (mZoomRingController.isVisible()) { - // don't trigger the link if zoom ring is visible + // 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() - && (mMinZoomScale < mMaxZoomScale)) { + if (getSettings().supportZoom()) { mPrivateHandler.sendMessageDelayed(mPrivateHandler - .obtainMessage(RELEASE_SINGLE_TAP), + .obtainMessage(RELEASE_SINGLE_TAP, + new Boolean(true)), DOUBLE_TAP_TIMEOUT); } else { // do short press now @@ -3841,7 +3899,8 @@ public class WebView extends AbsoluteLayout // as tap instead of short press. mTouchMode = TOUCH_INIT_MODE; mPrivateHandler.sendMessageDelayed(mPrivateHandler - .obtainMessage(RELEASE_SINGLE_TAP), + .obtainMessage(RELEASE_SINGLE_TAP, + new Boolean(true)), DOUBLE_TAP_TIMEOUT); } else { mTouchMode = TOUCH_DONE_MODE; @@ -3963,6 +4022,7 @@ public class WebView extends AbsoluteLayout + " time=" + time + " mLastFocusTime=" + mLastFocusTime); } + if (isInTouchMode()) requestFocusFromTouch(); return false; // let common code in onKeyDown at it } if (ev.getAction() == MotionEvent.ACTION_UP) { @@ -4399,7 +4459,12 @@ public class WebView extends AbsoluteLayout int contentX = viewToContent((int) mLastTouchX + mScrollX); int contentY = viewToContent((int) mLastTouchY + mScrollY); int contentSize = ViewConfiguration.get(getContext()).getScaledTouchSlop(); - nativeMotionUp(contentX, contentY, contentSize, true); + if (nativeMotionUp(contentX, contentY, contentSize, true)) { + if (mLogEvent) { + Checkin.updateStats(mContext.getContentResolver(), + Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0); + } + } if (nativeUpdateFocusNode() && !mFocusNode.mIsTextField && !mFocusNode.mIsTextArea) { playSoundEffect(SoundEffectConstants.CLICK); @@ -4619,7 +4684,9 @@ public class WebView extends AbsoluteLayout } case RELEASE_SINGLE_TAP: { mTouchMode = TOUCH_DONE_MODE; - doShortPress(); + if ((Boolean)msg.obj) { + doShortPress(); + } break; } case SWITCH_TO_ENTER: @@ -4671,7 +4738,7 @@ public class WebView extends AbsoluteLayout mMinContentWidth = msg.arg1; if (mMinContentWidth > MAX_FLOAT_CONTENT_WIDTH) { mMinZoomScale = (float) getViewWidth() - / mMinContentWidth; + / draw.mWidthHeight.x; } // We update the layout (i.e. request a layout from the // view system) if the last view size that we sent to @@ -5236,7 +5303,8 @@ public class WebView extends AbsoluteLayout private native Rect nativeGetNavBounds(); private native void nativeInstrumentReport(); private native void nativeMarkNodeInvalid(int node); - private native void nativeMotionUp(int x, int y, int slop, boolean isClick); + // return true if the page has been scrolled + private native boolean nativeMotionUp(int x, int y, int slop, boolean isClick); // returns false if it handled the key private native boolean nativeMoveFocus(int keyCode, int count, boolean noScroll); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 45113ab..a7261c5 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1197,6 +1197,7 @@ final class WebViewCore { Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); return; } + // negative scale indicate that WebCore should reuse the current scale float scale = (float) viewWidth / w; if (mSettings.getUseWideViewPort() && (w < mViewportWidth || mViewportWidth == -1)) { @@ -1224,7 +1225,7 @@ final class WebViewCore { // keep the same width and screen width so that there is // no reflow when zoom-out width = minContentWidth; - screenWidth = Math.min(screenWidth, viewWidth); + screenWidth = Math.min(screenWidth, Math.abs(viewWidth)); } else { width = Math.max(w, minContentWidth); } @@ -1536,11 +1537,11 @@ final class WebViewCore { // white space in the GMail which uses WebView for message view. if (mWebView != null && mWebView.mHeightCanMeasure) { mWebView.mLastHeightSent = 0; - // Send a negative scale to indicate that WebCore should reuse the - // current scale + // Send a negative screen width to indicate that WebCore should + // reuse the current scale mEventHub.sendMessage(Message.obtain(null, EventHub.VIEW_SIZE_CHANGED, mWebView.mLastWidthSent, - mWebView.mLastHeightSent, new Integer(-1))); + mWebView.mLastHeightSent, -mWebView.mLastWidthSent)); } mBrowserFrame.didFirstLayout(); diff --git a/core/java/android/webkit/gears/GearsPluginSettings.java b/core/java/android/webkit/gears/GearsPluginSettings.java deleted file mode 100644 index d36d3fb..0000000 --- a/core/java/android/webkit/gears/GearsPluginSettings.java +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2008 The Android Open Source Project -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// 3. Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package android.webkit.gears; - -import android.content.ComponentName; -import android.content.Context; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.util.Log; -import android.webkit.Plugin; -import android.webkit.Plugin.PreferencesClickHandler; - -/** - * Simple bridge class intercepting the click in the - * browser plugin list and calling the Gears settings - * dialog. - */ -public class GearsPluginSettings { - - private static final String TAG = "Gears-J-GearsPluginSettings"; - private Context context; - - public GearsPluginSettings(Plugin plugin) { - plugin.setClickHandler(new ClickHandler()); - } - - /** - * We do not need to call the dialog synchronously here (doing so - * actually cause a lot of problems as the main message loop is also - * blocked), which is why we simply call it via a thread. - */ - private class ClickHandler implements PreferencesClickHandler { - public void handleClickEvent(Context aContext) { - context = aContext; - Thread startService = new Thread(new StartService()); - startService.run(); - } - } - - private static native void runSettingsDialog(Context c); - - /** - * StartService is the runnable we use to open the dialog. - * We bind the service to serviceConnection; upon - * onServiceConnected the dialog will be called from the - * native side using the runSettingsDialog method. - */ - private class StartService implements Runnable { - public void run() { - HtmlDialogAndroid.bindToService(context, serviceConnection); - } - } - - /** - * ServiceConnection instance. - * onServiceConnected is called upon connection with the service; - * we can then safely open the dialog. - */ - private ServiceConnection serviceConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - IGearsDialogService gearsDialogService = - IGearsDialogService.Stub.asInterface(service); - HtmlDialogAndroid.setGearsDialogService(gearsDialogService); - runSettingsDialog(context); - context.unbindService(serviceConnection); - HtmlDialogAndroid.setGearsDialogService(null); - } - public void onServiceDisconnected(ComponentName className) { - HtmlDialogAndroid.setGearsDialogService(null); - } - }; -} diff --git a/core/java/android/webkit/gears/HtmlDialogAndroid.java b/core/java/android/webkit/gears/HtmlDialogAndroid.java deleted file mode 100644 index 6209ab9..0000000 --- a/core/java/android/webkit/gears/HtmlDialogAndroid.java +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2008 The Android Open Source Project -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// 3. Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package android.webkit.gears; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; -import android.webkit.CacheManager; - -import java.io.FileInputStream; -import java.io.IOException; - -/** - * Utility class to call a modal HTML dialog on Android - */ -public class HtmlDialogAndroid { - - private static final String TAG = "Gears-J-HtmlDialog"; - private static final String DIALOG_PACKAGE = "com.android.browser"; - private static final String DIALOG_SERVICE = DIALOG_PACKAGE - + ".GearsDialogService"; - private static final String DIALOG_INTERFACE = DIALOG_PACKAGE - + ".IGearsDialogService"; - - private static IGearsDialogService gearsDialogService; - - public static void setGearsDialogService(IGearsDialogService service) { - gearsDialogService = service; - } - - /** - * Bind to the GearsDialogService. - */ - public static boolean bindToService(Context context, - ServiceConnection serviceConnection) { - Intent dialogIntent = new Intent(); - dialogIntent.setClassName(DIALOG_PACKAGE, DIALOG_SERVICE); - dialogIntent.setAction(DIALOG_INTERFACE); - return context.bindService(dialogIntent, serviceConnection, - Context.BIND_AUTO_CREATE); - } - - /** - * Bind to the GearsDialogService synchronously. - * The service is started using our own defaultServiceConnection - * handler, and we wait until the handler notifies us. - */ - public void synchronousBindToService(Context context) { - try { - if (bindToService(context, defaultServiceConnection)) { - if (gearsDialogService == null) { - synchronized(defaultServiceConnection) { - defaultServiceConnection.wait(3000); // timeout after 3s - } - } - } - } catch (InterruptedException e) { - Log.e(TAG, "exception: " + e); - } - } - - /** - * Read the HTML content from the disk - */ - public String readHTML(String filePath) { - FileInputStream inputStream = null; - String content = ""; - try { - inputStream = new FileInputStream(filePath); - StringBuffer out = new StringBuffer(); - byte[] buffer = new byte[4096]; - for (int n; (n = inputStream.read(buffer)) != -1;) { - out.append(new String(buffer, 0, n)); - } - content = out.toString(); - } catch (IOException e) { - Log.e(TAG, "exception: " + e); - } finally { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException e) { - Log.e(TAG, "exception: " + e); - } - } - } - return content; - } - - /** - * Open an HTML dialog synchronously and waits for its completion. - * The dialog is accessed through the GearsDialogService provided by - * the Android Browser. - * We can be called either directly, and then gearsDialogService will - * not be set and we will bind to the service synchronously, and unbind - * after calling the service, or called indirectly via GearsPluginSettings. - * In the latter case, GearsPluginSettings does the binding/unbinding. - */ - public String showDialog(Context context, String htmlFilePath, - String arguments) { - - CacheManager.endCacheTransaction(); - - String ret = null; - boolean synchronousCall = false; - if (gearsDialogService == null) { - synchronousCall = true; - synchronousBindToService(context); - } - - try { - if (gearsDialogService != null) { - String htmlContent = readHTML(htmlFilePath); - if (htmlContent.length() > 0) { - ret = gearsDialogService.showDialog(htmlContent, arguments, - !synchronousCall); - } - } else { - Log.e(TAG, "Could not connect to the GearsDialogService!"); - } - if (synchronousCall) { - context.unbindService(defaultServiceConnection); - gearsDialogService = null; - } - } catch (RemoteException e) { - Log.e(TAG, "remote exception: " + e); - gearsDialogService = null; - } - - CacheManager.startCacheTransaction(); - - return ret; - } - - private ServiceConnection defaultServiceConnection = - new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - synchronized (defaultServiceConnection) { - gearsDialogService = IGearsDialogService.Stub.asInterface(service); - defaultServiceConnection.notify(); - } - } - public void onServiceDisconnected(ComponentName className) { - gearsDialogService = null; - } - }; -} diff --git a/core/java/android/webkit/gears/IGearsDialogService.java b/core/java/android/webkit/gears/IGearsDialogService.java deleted file mode 100644 index 82a3bd9..0000000 --- a/core/java/android/webkit/gears/IGearsDialogService.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is auto-generated. DO NOT MODIFY. - * Original file: android.webkit.gears/IGearsDialogService.aidl - */ -package android.webkit.gears; -import java.lang.String; -import android.os.RemoteException; -import android.os.IBinder; -import android.os.IInterface; -import android.os.Binder; -import android.os.Parcel; -public interface IGearsDialogService extends android.os.IInterface -{ -/** Local-side IPC implementation stub class. */ -public static abstract class Stub extends android.os.Binder implements android.webkit.gears.IGearsDialogService -{ -private static final java.lang.String DESCRIPTOR = "com.android.browser.IGearsDialogService"; -/** Construct the stub at attach it to the interface. */ -public Stub() -{ -this.attachInterface(this, DESCRIPTOR); -} -/** - * Cast an IBinder object into an IGearsDialogService interface, - * generating a proxy if needed. - */ -public static android.webkit.gears.IGearsDialogService asInterface(android.os.IBinder obj) -{ -if ((obj==null)) { -return null; -} -android.webkit.gears.IGearsDialogService in = (android.webkit.gears.IGearsDialogService)obj.queryLocalInterface(DESCRIPTOR); -if ((in!=null)) { -return in; -} -return new android.webkit.gears.IGearsDialogService.Stub.Proxy(obj); -} -public android.os.IBinder asBinder() -{ -return this; -} -public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException -{ -switch (code) -{ -case INTERFACE_TRANSACTION: -{ -reply.writeString(DESCRIPTOR); -return true; -} -case TRANSACTION_showDialog: -{ -data.enforceInterface(DESCRIPTOR); -java.lang.String _arg0; -_arg0 = data.readString(); -java.lang.String _arg1; -_arg1 = data.readString(); -boolean _arg2; -_arg2 = (0!=data.readInt()); -java.lang.String _result = this.showDialog(_arg0, _arg1, _arg2); -reply.writeNoException(); -reply.writeString(_result); -return true; -} -} -return super.onTransact(code, data, reply, flags); -} -private static class Proxy implements android.webkit.gears.IGearsDialogService -{ -private android.os.IBinder mRemote; -Proxy(android.os.IBinder remote) -{ -mRemote = remote; -} -public android.os.IBinder asBinder() -{ -return mRemote; -} -public java.lang.String getInterfaceDescriptor() -{ -return DESCRIPTOR; -} -public java.lang.String showDialog(java.lang.String htmlContent, java.lang.String dialogArguments, boolean inSettings) throws android.os.RemoteException -{ -android.os.Parcel _data = android.os.Parcel.obtain(); -android.os.Parcel _reply = android.os.Parcel.obtain(); -java.lang.String _result; -try { -_data.writeInterfaceToken(DESCRIPTOR); -_data.writeString(htmlContent); -_data.writeString(dialogArguments); -_data.writeInt(((inSettings)?(1):(0))); -mRemote.transact(Stub.TRANSACTION_showDialog, _data, _reply, 0); -_reply.readException(); -_result = _reply.readString(); -} -finally { -_reply.recycle(); -_data.recycle(); -} -return _result; -} -} -static final int TRANSACTION_showDialog = (IBinder.FIRST_CALL_TRANSACTION + 0); -} -public java.lang.String showDialog(java.lang.String htmlContent, java.lang.String dialogArguments, boolean inSettings) throws android.os.RemoteException; -} diff --git a/core/java/android/widget/Adapter.java b/core/java/android/widget/Adapter.java index e952dd5..f2b3e2a 100644 --- a/core/java/android/widget/Adapter.java +++ b/core/java/android/widget/Adapter.java @@ -116,7 +116,7 @@ public interface Adapter { * can be converted to the other in {@link #getView}. Note: Integers must be in the * range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can * also be returned. - * @see IGNORE_ITEM_VIEW_TYPE + * @see #IGNORE_ITEM_VIEW_TYPE */ int getItemViewType(int position); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index bdc54ff..c852be5 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4132,8 +4132,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener removeParcelableSpans(content, start, end); content.replace(start, end, text.text); } - Selection.setSelection((Spannable)getText(), - text.selectionStart, text.selectionEnd); + + // Now set the selection position... make sure it is in range, to + // avoid crashes. If this is a partial update, it is possible that + // the underlying text may have changed, causing us problems here. + // Also we just don't want to trust clients to do the right thing. + Spannable sp = (Spannable)getText(); + final int N = sp.length(); + int start = text.selectionStart; + if (start < 0) start = 0; + else if (start > N) start = N; + int end = text.selectionEnd; + if (end < 0) end = 0; + else if (end > N) end = N; + Selection.setSelection(sp, start, end); } /** diff --git a/core/java/android/widget/ZoomRing.java b/core/java/android/widget/ZoomRing.java index 22881b3..a29e1a0 100644 --- a/core/java/android/widget/ZoomRing.java +++ b/core/java/android/widget/ZoomRing.java @@ -84,6 +84,10 @@ public class ZoomRing extends View { private Drawable mThumbPlusArrowDrawable; /** Shown beneath the thumb if we can still zoom out. */ private Drawable mThumbMinusArrowDrawable; + private static final int THUMB_ARROW_PLUS = 1 << 0; + private static final int THUMB_ARROW_MINUS = 1 << 1; + /** Bitwise-OR of {@link #THUMB_ARROW_MINUS} and {@link #THUMB_ARROW_PLUS} */ + private int mThumbArrowsToDraw; private static final int THUMB_ARROWS_FADE_DURATION = 300; private long mThumbArrowsFadeStartTime; private int mThumbArrowsAlpha = 255; @@ -166,7 +170,7 @@ public class ZoomRing extends View { // TODO: add padding to drawable setBackgroundResource(R.drawable.zoom_ring_track); // TODO get from style - setRingBounds(30, Integer.MAX_VALUE); + setRingBounds(43, Integer.MAX_VALUE); mThumbHalfHeight = mThumbDrawable.getIntrinsicHeight() / 2; mThumbHalfWidth = mThumbDrawable.getIntrinsicWidth() / 2; @@ -276,7 +280,7 @@ public class ZoomRing extends View { thumbCenterX + mThumbHalfWidth, thumbCenterY + mThumbHalfHeight); - if (mThumbArrowsAlpha > 0) { + if (mThumbArrowsToDraw > 0) { setThumbArrowsAngle(angle); } @@ -420,12 +424,15 @@ public class ZoomRing extends View { switch (action) { case MotionEvent.ACTION_DOWN: - mCallback.onUserInteractionStarted(); - if (time - mPreviousUpTime <= DOUBLE_TAP_DISMISS_TIMEOUT) { mCallback.onZoomRingDismissed(true); + onTouchUp(time); + + // Dismissing, so halt here + return true; } + mCallback.onUserInteractionStarted(); mPreviousDownX = x; mPreviousDownY = y; resetState(); @@ -441,23 +448,7 @@ public class ZoomRing extends View { case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: - if (mMode == MODE_MOVE_ZOOM_RING || mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) { - mCallback.onZoomRingSetMovableHintVisible(false); - if (mMode == MODE_MOVE_ZOOM_RING) { - mCallback.onZoomRingMovingStopped(); - } - } else if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG || - mMode == MODE_WAITING_FOR_DRAG_THUMB) { - onThumbDragStopped(); - - if (mMode == MODE_DRAG_THUMB) { - // Animate back to a tick - setThumbAngleAnimated(mPreviousCallbackAngle, 0); - } - } - - mPreviousUpTime = time; - mCallback.onUserInteractionStopped(); + onTouchUp(time); return true; default: @@ -524,10 +515,9 @@ public class ZoomRing extends View { deltaThumbAndTick = getDelta(mThumbAngle, tickAngle, !oldDirectionIsCcw); boundAngle = getBoundIfExceeds(mThumbAngle, deltaThumbAndTick); if (boundAngle != Integer.MIN_VALUE) { - Log - .d( - TAG, - "Tapped somewhere where the shortest distance goes through a bound, but then the opposite direction also went through a bound!"); + // Not allowed to be here, it is between two bounds + mMode = MODE_IGNORE_UNTIL_UP; + return true; } } } @@ -574,6 +564,26 @@ public class ZoomRing extends View { return true; } + + private void onTouchUp(long time) { + if (mMode == MODE_MOVE_ZOOM_RING || mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) { + mCallback.onZoomRingSetMovableHintVisible(false); + if (mMode == MODE_MOVE_ZOOM_RING) { + mCallback.onZoomRingMovingStopped(); + } + } else if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG || + mMode == MODE_WAITING_FOR_DRAG_THUMB) { + onThumbDragStopped(); + + if (mMode == MODE_DRAG_THUMB) { + // Animate back to a tick + setThumbAngleAnimated(mPreviousCallbackAngle, 0); + } + } + + mPreviousUpTime = time; + mCallback.onUserInteractionStopped(); + } private boolean isDeltaInBounds(int startAngle, int deltaAngle) { return getBoundIfExceeds(startAngle, deltaAngle) == Integer.MIN_VALUE; @@ -766,9 +776,11 @@ public class ZoomRing extends View { } } - int deltaAngle = getDelta(mThumbAngle, touchAngle, useDirection, ccw); - mAcculumalatedTrailAngle += Math.toDegrees(deltaAngle / (double) RADIAN_INT_MULTIPLIER); - + 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); @@ -851,15 +863,10 @@ public class ZoomRing extends View { if (DRAW_TRAIL) { mTrail.draw(canvas); } - - // If we aren't near the bounds, draw the corresponding arrows - int callbackAngle = mPreviousCallbackAngle; - if (callbackAngle < mThumbCwBound - RADIAN_INT_ERROR || - callbackAngle > mThumbCwBound + RADIAN_INT_ERROR) { + if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) { mThumbPlusArrowDrawable.draw(canvas); } - if (callbackAngle < mThumbCcwBound - RADIAN_INT_ERROR || - callbackAngle > mThumbCcwBound + RADIAN_INT_ERROR) { + if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) { mThumbMinusArrowDrawable.draw(canvas); } mThumbDrawable.draw(canvas); @@ -875,8 +882,21 @@ public class ZoomRing extends View { public void setThumbArrowsVisible(boolean visible) { if (visible) { mThumbArrowsAlpha = 255; - mThumbPlusArrowDrawable.setAlpha(255); - mThumbMinusArrowDrawable.setAlpha(255); + int callbackAngle = mPreviousCallbackAngle; + if (callbackAngle < mThumbCwBound - RADIAN_INT_ERROR || + callbackAngle > mThumbCwBound + RADIAN_INT_ERROR) { + mThumbPlusArrowDrawable.setAlpha(255); + mThumbArrowsToDraw |= THUMB_ARROW_PLUS; + } else { + mThumbArrowsToDraw &= ~THUMB_ARROW_PLUS; + } + if (callbackAngle < mThumbCcwBound - RADIAN_INT_ERROR || + callbackAngle > mThumbCcwBound + RADIAN_INT_ERROR) { + mThumbMinusArrowDrawable.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) @@ -886,17 +906,24 @@ public class ZoomRing extends View { } private void onThumbArrowsFadeTick() { - if (mThumbArrowsAlpha <= 0) return; + if (mThumbArrowsAlpha <= 0) { + mThumbArrowsToDraw = 0; + return; + } mThumbArrowsAlpha = (int) (255 - (255 * (SystemClock.elapsedRealtime() - mThumbArrowsFadeStartTime) / THUMB_ARROWS_FADE_DURATION)); if (mThumbArrowsAlpha < 0) mThumbArrowsAlpha = 0; - mThumbPlusArrowDrawable.setAlpha(mThumbArrowsAlpha); - mThumbMinusArrowDrawable.setAlpha(mThumbArrowsAlpha); - invalidateDrawable(mThumbPlusArrowDrawable); - invalidateDrawable(mThumbMinusArrowDrawable); - + if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) { + mThumbPlusArrowDrawable.setAlpha(mThumbArrowsAlpha); + invalidateDrawable(mThumbPlusArrowDrawable); + } + if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) { + mThumbMinusArrowDrawable.setAlpha(mThumbArrowsAlpha); + invalidateDrawable(mThumbMinusArrowDrawable); + } + if (!mHandler.hasMessages(MSG_THUMB_ARROWS_FADE_TICK)) { mHandler.sendEmptyMessage(MSG_THUMB_ARROWS_FADE_TICK); } diff --git a/core/java/android/widget/ZoomRingController.java b/core/java/android/widget/ZoomRingController.java index 31074b6..2e97fda 100644 --- a/core/java/android/widget/ZoomRingController.java +++ b/core/java/android/widget/ZoomRingController.java @@ -57,7 +57,7 @@ import android.view.animation.DecelerateInterpolator; public class ZoomRingController implements ZoomRing.OnZoomRingCallback, View.OnTouchListener, View.OnKeyListener { - private static final int ZOOM_RING_RADIUS_INSET = 10; + private static final int ZOOM_RING_RADIUS_INSET = 24; private static final int ZOOM_RING_RECENTERING_DURATION = 500; @@ -79,7 +79,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, private static final int MAX_PAN_GAP = 20; private static final int MAX_INITIATE_PAN_GAP = 10; // TODO view config - private static final int INITIATE_PAN_DELAY = 400; + private static final int INITIATE_PAN_DELAY = 300; private static final String SETTING_NAME_SHOWN_TOAST = "shown_zoom_ring_toast"; @@ -146,7 +146,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, private int mZoomRingHeight; /** Invokes panning of owner view if the zoom ring is touching an edge. */ - private Panner mPanner = new Panner(); + private Panner mPanner; private long mTouchingEdgeStartTime; private boolean mPanningEnabledForThisInteraction; @@ -241,6 +241,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mContext = context; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mPanner = new Panner(); mOwnerView = ownerView; mZoomRing = new ZoomRing(context); @@ -389,6 +390,8 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, }; } + mPanningArrows.setAnimation(null); + mHandler.post(mPostedVisibleInitializer); // Handle configuration changes when visible @@ -409,12 +412,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } 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); } @@ -464,6 +468,9 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public boolean handleDoubleTapEvent(MotionEvent event) { 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(); @@ -493,6 +500,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mViewConfig.getScaledTouchSlop()) { mZoomRing.setTapDragMode(true, x, y); mTouchMode = TOUCH_MODE_FORWARDING_FOR_TAP_DRAG; + setTouchTargetView(mZoomRing); } return true; @@ -587,7 +595,6 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public void onZoomRingMovingStarted() { mScroller.abortAnimation(); - mPanningEnabledForThisInteraction = false; mTouchingEdgeStartTime = 0; if (mCallback != null) { mCallback.onBeginPan(); @@ -658,6 +665,8 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, if (!horizontalPanning) { // Neither are panning, reset any timer to start pan mode mTouchingEdgeStartTime = 0; + mPanningEnabledForThisInteraction = false; + mPanner.stop(); } } } @@ -752,6 +761,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, // 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; } @@ -763,16 +773,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, switch (action) { case MotionEvent.ACTION_DOWN: - targetView = mTouchTargetView = - getViewForTouch((int) event.getRawX(), (int) event.getRawY()); - if (targetView != null) { - targetView.getLocationInWindow(mTouchTargetLocationInWindow); - } + targetView = getViewForTouch((int) event.getRawX(), (int) event.getRawY()); + setTouchTargetView(targetView); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - mTouchTargetView = null; + setTouchTargetView(null); break; } @@ -799,6 +806,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } } + 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. * @@ -950,6 +964,22 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } } + public void setPannerStartVelocity(float startVelocity) { + mPanner.mStartVelocity = startVelocity; + } + + public void setPannerAcceleration(float acceleration) { + mPanner.mAcceleration = acceleration; + } + + public void setPannerMaxVelocity(float maxVelocity) { + mPanner.mMaxVelocity = maxVelocity; + } + + 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; @@ -967,6 +997,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, /** 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) { @@ -1013,11 +1050,12 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, boolean firstRun = mPreviousCallbackTime == 0; long curTime = SystemClock.elapsedRealtime(); - int panAmount = getPanAmount(mStartTime, mPreviousCallbackTime, curTime); + int panAmount = getPanAmount(mPreviousCallbackTime, curTime); mPreviousCallbackTime = curTime; if (firstRun) { mStartTime = curTime; + mVelocity = mStartVelocity; } else { int panX = panAmount * mHorizontalStrength / 100; int panY = panAmount * mVerticalStrength / 100; @@ -1030,12 +1068,22 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mUiHandler.postDelayed(this, RUN_DELAY); } - // TODO make setter for this value so zoom clients can have different pan rates, if they want - private static final int PAN_VELOCITY_PX_S = 30; - private int getPanAmount(long startTime, long previousTime, long currentTime) { - return (int) ((currentTime - previousTime) * PAN_VELOCITY_PX_S / 100); + 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; } + } + + public interface OnZoomListener { void onBeginDrag(); |