diff options
Diffstat (limited to 'core/java')
| -rwxr-xr-x | core/java/android/animation/ValueAnimator.java | 2 | ||||
| -rw-r--r-- | core/java/android/app/Activity.java | 5 | ||||
| -rw-r--r-- | core/java/android/app/ActivityOptions.java | 12 | ||||
| -rw-r--r-- | core/java/android/app/ActivityThread.java | 8 | ||||
| -rw-r--r-- | core/java/android/net/EthernetDataTracker.java | 19 | ||||
| -rw-r--r-- | core/java/android/os/Trace.java | 1 | ||||
| -rw-r--r-- | core/java/android/server/BluetoothAdapterStateMachine.java | 58 | ||||
| -rwxr-xr-x | core/java/android/server/BluetoothService.java | 12 | ||||
| -rw-r--r-- | core/java/android/view/Choreographer.java | 33 | ||||
| -rw-r--r-- | core/java/android/view/ViewTreeObserver.java | 206 | ||||
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 104 | ||||
| -rw-r--r-- | core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java | 103 |
12 files changed, 449 insertions, 114 deletions
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 326f27c..f3a442a 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -910,7 +910,7 @@ public class ValueAnimator extends Animator { animationHandler.mPendingAnimations.add(this); if (mStartDelay == 0) { // This sets the initial value of the animation, prior to actually starting it running - setCurrentPlayTime(getCurrentPlayTime()); + setCurrentPlayTime(0); mPlayingState = STOPPED; mRunning = true; notifyStartListeners(); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 29d96fe..781eea5 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2540,11 +2540,10 @@ public class Activity extends ContextThemeWrapper if (item.getItemId() == android.R.id.home && mActionBar != null && (mActionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) { if (mParent == null) { - onNavigateUp(); + return onNavigateUp(); } else { - mParent.onNavigateUpFromChild(this); + return mParent.onNavigateUpFromChild(this); } - return true; } return false; diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index b730581..3d0b7d8 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -147,12 +147,17 @@ public class ActivityOptions { * activity is scaled from a small originating area of the screen to * its final full representation. * + * <p>If the Intent this is being used with has not set its + * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds}, + * those bounds will be filled in for you based on the initial + * bounds passed in here. + * * @param source The View that the new activity is animating from. This * defines the coordinate space for <var>startX</var> and <var>startY</var>. * @param startX The x starting location of the new activity, relative to <var>source</var>. * @param startY The y starting location of the activity, relative to <var>source</var>. * @param startWidth The initial width of the new activity. - * @param startWidth The initial height of the new activity. + * @param startHeight The initial height of the new activity. * @return Returns a new ActivityOptions object that you can use to * supply these options as the options Bundle when starting an activity. */ @@ -175,6 +180,11 @@ public class ActivityOptions { * is scaled from a given position to the new activity window that is * being started. * + * <p>If the Intent this is being used with has not set its + * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds}, + * those bounds will be filled in for you based on the initial + * thumbnail location and size provided here. + * * @param source The View that this thumbnail is animating from. This * defines the coordinate space for <var>startX</var> and <var>startY</var>. * @param thumbnail The bitmap that will be shown as the initial thumbnail diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b55ee26..314f5c2 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -136,7 +136,7 @@ public final class ActivityThread { /** @hide */ public static final boolean DEBUG_BROADCAST = false; private static final boolean DEBUG_RESULTS = false; - private static final boolean DEBUG_BACKUP = true; + private static final boolean DEBUG_BACKUP = false; private static final boolean DEBUG_CONFIGURATION = false; private static final boolean DEBUG_SERVICE = false; private static final boolean DEBUG_MEMORY_TRIM = false; @@ -1172,10 +1172,10 @@ public final class ActivityThread { case DUMP_PROVIDER: return "DUMP_PROVIDER"; } } - return "(unknown)"; + return Integer.toString(code); } public void handleMessage(Message msg) { - if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what); + if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); @@ -1378,7 +1378,7 @@ public final class ActivityThread { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; } - if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what); + if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); } private void maybeSnapshot() { diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java index fb09ba5..28bd289 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -59,6 +59,8 @@ public class EthernetDataTracker implements NetworkStateTracker { private static String sIfaceMatch = ""; private static String mIface = ""; + private INetworkManagementService mNMService; + private static class InterfaceObserver extends INetworkManagementEventObserver.Stub { private EthernetDataTracker mTracker; @@ -117,6 +119,13 @@ public class EthernetDataTracker implements NetworkStateTracker { mIface = iface; } + // we don't get link status indications unless the iface is up - bring it up + try { + mNMService.setInterfaceUp(iface); + } catch (Exception e) { + Log.e(TAG, "Error upping interface " + iface + ": " + e); + } + mNetworkInfo.setIsAvailable(true); Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); msg.sendToTarget(); @@ -199,7 +208,7 @@ public class EthernetDataTracker implements NetworkStateTracker { // register for notifications from NetworkManagement Service IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + mNMService = INetworkManagementService.Stub.asInterface(b); mInterfaceObserver = new InterfaceObserver(this); @@ -208,12 +217,12 @@ public class EthernetDataTracker implements NetworkStateTracker { sIfaceMatch = context.getResources().getString( com.android.internal.R.string.config_ethernet_iface_regex); try { - final String[] ifaces = service.listInterfaces(); + final String[] ifaces = mNMService.listInterfaces(); for (String iface : ifaces) { if (iface.matches(sIfaceMatch)) { mIface = iface; - service.setInterfaceUp(iface); - InterfaceConfiguration config = service.getInterfaceConfig(iface); + mNMService.setInterfaceUp(iface); + InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); mLinkUp = config.isActive(); if (config != null && mHwAddr == null) { mHwAddr = config.getHardwareAddress(); @@ -230,7 +239,7 @@ public class EthernetDataTracker implements NetworkStateTracker { } try { - service.registerObserver(mInterfaceObserver); + mNMService.registerObserver(mInterfaceObserver); } catch (RemoteException e) { Log.e(TAG, "Could not register InterfaceObserver " + e); } diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 05acd63..ac9ee26 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -37,6 +37,7 @@ public final class Trace { public static final long TRACE_TAG_WINDOW_MANAGER = 1L << 5; public static final long TRACE_TAG_ACTIVITY_MANAGER = 1L << 6; public static final long TRACE_TAG_SYNC_MANAGER = 1L << 7; + public static final long TRACE_TAG_AUDIO = 1L << 8; private static final long sEnabledTags = nativeGetEnabledTags(); diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java index f543de9..2a994b2 100644 --- a/core/java/android/server/BluetoothAdapterStateMachine.java +++ b/core/java/android/server/BluetoothAdapterStateMachine.java @@ -39,7 +39,7 @@ import java.io.PrintWriter; * (BluetootOn)<----------------------<- * | ^ -------------------->- | * | | | | - * TURN_OFF | | SCAN_MODE_CHANGED m1 | | USER_TURN_ON + * USER_TURN_OFF | | SCAN_MODE_CHANGED m1 | | USER_TURN_ON * AIRPLANE_MODE_ON | | | | * V | | | * (Switching) (PerProcessState) @@ -121,8 +121,10 @@ final class BluetoothAdapterStateMachine extends StateMachine { private static final int DEVICES_DISCONNECT_TIMEOUT = 103; // Prepare Bluetooth timeout happens private static final int PREPARE_BLUETOOTH_TIMEOUT = 104; - // Bluetooth Powerdown timeout happens - private static final int POWER_DOWN_TIMEOUT = 105; + // Bluetooth turn off wait timeout happens + private static final int TURN_OFF_TIMEOUT = 105; + // Bluetooth device power off wait timeout happens + private static final int POWER_DOWN_TIMEOUT = 106; private Context mContext; private BluetoothService mBluetoothService; @@ -137,13 +139,17 @@ final class BluetoothAdapterStateMachine extends StateMachine { // this is the BluetoothAdapter state that reported externally private int mPublicState; + // When turning off, broadcast STATE_OFF in the last HotOff state + // This is because we do HotOff -> PowerOff -> HotOff for USER_TURN_OFF + private boolean mDelayBroadcastStateOff; // timeout value waiting for all the devices to be disconnected private static final int DEVICES_DISCONNECT_TIMEOUT_TIME = 3000; private static final int PREPARE_BLUETOOTH_TIMEOUT_TIME = 10000; - private static final int POWER_DOWN_TIMEOUT_TIME = 5000; + private static final int TURN_OFF_TIMEOUT_TIME = 5000; + private static final int POWER_DOWN_TIMEOUT_TIME = 20; BluetoothAdapterStateMachine(Context context, BluetoothService bluetoothService, BluetoothAdapter bluetoothAdapter) { @@ -168,6 +174,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { setInitialState(mPowerOff); mPublicState = BluetoothAdapter.STATE_OFF; + mDelayBroadcastStateOff = false; } /** @@ -315,6 +322,10 @@ final class BluetoothAdapterStateMachine extends StateMachine { case SERVICE_RECORD_LOADED: removeMessages(PREPARE_BLUETOOTH_TIMEOUT); transitionTo(mHotOff); + if (mDelayBroadcastStateOff) { + broadcastState(BluetoothAdapter.STATE_OFF); + mDelayBroadcastStateOff = false; + } break; case PREPARE_BLUETOOTH_TIMEOUT: Log.e(TAG, "Bluetooth adapter SDP failed to load"); @@ -373,8 +384,17 @@ final class BluetoothAdapterStateMachine extends StateMachine { case AIRPLANE_MODE_ON: case TURN_COLD: shutoffBluetooth(); + // we cannot go to power off state yet, we need wait for the Bluetooth + // device power off. Unfortunately the stack does not give a event back + // so we wait a little bit here + sendMessageDelayed(POWER_DOWN_TIMEOUT, + POWER_DOWN_TIMEOUT_TIME); + break; + case POWER_DOWN_TIMEOUT: transitionTo(mPowerOff); - broadcastState(BluetoothAdapter.STATE_OFF); + if (!mDelayBroadcastStateOff) { + broadcastState(BluetoothAdapter.STATE_OFF); + } break; case AIRPLANE_MODE_OFF: if (getBluetoothPersistedSetting()) { @@ -402,6 +422,9 @@ final class BluetoothAdapterStateMachine extends StateMachine { recoverStateMachine(TURN_HOT, null); } break; + case TURN_HOT: + deferMessage(message); + break; default: return NOT_HANDLED; } @@ -436,15 +459,17 @@ final class BluetoothAdapterStateMachine extends StateMachine { } break; case POWER_STATE_CHANGED: - removeMessages(POWER_DOWN_TIMEOUT); + removeMessages(TURN_OFF_TIMEOUT); if (!((Boolean) message.obj)) { if (mPublicState == BluetoothAdapter.STATE_TURNING_OFF) { transitionTo(mHotOff); - finishSwitchingOff(); + mBluetoothService.finishDisable(); + mBluetoothService.cleanupAfterFinishDisable(); deferMessage(obtainMessage(TURN_COLD)); if (mContext.getResources().getBoolean (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { deferMessage(obtainMessage(TURN_HOT)); + mDelayBroadcastStateOff = true; } } } else { @@ -461,7 +486,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { case ALL_DEVICES_DISCONNECTED: removeMessages(DEVICES_DISCONNECT_TIMEOUT); mBluetoothService.switchConnectable(false); - sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); + sendMessageDelayed(TURN_OFF_TIMEOUT, TURN_OFF_TIMEOUT_TIME); break; case DEVICES_DISCONNECT_TIMEOUT: sendMessage(ALL_DEVICES_DISCONNECTED); @@ -473,7 +498,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { deferMessage(obtainMessage(TURN_HOT)); } break; - case POWER_DOWN_TIMEOUT: + case TURN_OFF_TIMEOUT: transitionTo(mHotOff); finishSwitchingOff(); // reset the hardware for error recovery @@ -536,7 +561,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { DEVICES_DISCONNECT_TIMEOUT_TIME); } else { mBluetoothService.switchConnectable(false); - sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); + sendMessageDelayed(TURN_OFF_TIMEOUT, TURN_OFF_TIMEOUT_TIME); } // we turn all the way to PowerOff with AIRPLANE_MODE_ON @@ -610,13 +635,12 @@ final class BluetoothAdapterStateMachine extends StateMachine { } break; case POWER_STATE_CHANGED: - removeMessages(POWER_DOWN_TIMEOUT); + removeMessages(TURN_OFF_TIMEOUT); if (!((Boolean) message.obj)) { transitionTo(mHotOff); - deferMessage(obtainMessage(TURN_COLD)); - if (mContext.getResources().getBoolean + if (!mContext.getResources().getBoolean (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { - deferMessage(obtainMessage(TURN_HOT)); + deferMessage(obtainMessage(TURN_COLD)); } } else { if (!isTurningOn) { @@ -629,7 +653,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { } } break; - case POWER_DOWN_TIMEOUT: + case TURN_OFF_TIMEOUT: transitionTo(mHotOff); Log.e(TAG, "Power-down timed out, resetting..."); deferMessage(obtainMessage(TURN_COLD)); @@ -676,12 +700,12 @@ final class BluetoothAdapterStateMachine extends StateMachine { perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj); if (mBluetoothService.isApplicationStateChangeTrackerEmpty()) { mBluetoothService.switchConnectable(false); - sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); + sendMessageDelayed(TURN_OFF_TIMEOUT, TURN_OFF_TIMEOUT_TIME); } break; case AIRPLANE_MODE_ON: mBluetoothService.switchConnectable(false); - sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); + sendMessageDelayed(TURN_OFF_TIMEOUT, TURN_OFF_TIMEOUT_TIME); allProcessesCallback(false); // we turn all the way to PowerOff with AIRPLANE_MODE_ON deferMessage(obtainMessage(AIRPLANE_MODE_ON)); diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index a420734..3cf207f 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -526,6 +526,12 @@ public class BluetoothService extends IBluetooth.Stub { return false; } switchConnectable(false); + + // Bluetooth stack needs a small delay here before adding + // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs + try { + Thread.sleep(20); + } catch (InterruptedException e) {} updateSdpRecords(); return true; } @@ -593,6 +599,12 @@ public class BluetoothService extends IBluetooth.Stub { // Add SDP records for profiles maintained by Android userspace addReservedSdpRecords(uuids); + // Bluetooth stack need some a small delay here before adding more + // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs + try { + Thread.sleep(20); + } catch (InterruptedException e) {} + if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) { // Enable profiles maintained by Bluez userspace. setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE, diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index b319cd5..825f351 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -92,6 +92,7 @@ public final class Choreographer { private boolean mFrameScheduled; private boolean mCallbacksRunning; private long mLastFrameTimeNanos; + private long mFrameIntervalNanos; /** * Callback type: Input callback. Runs first. @@ -116,6 +117,8 @@ public final class Choreographer { mHandler = new FrameHandler(looper); mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null; mLastFrameTimeNanos = Long.MIN_VALUE; + mFrameIntervalNanos = (long)(1000000000 / + new Display(Display.DEFAULT_DISPLAY, null).getRefreshRate()); mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { @@ -343,17 +346,37 @@ public final class Choreographer { } void doFrame(long timestampNanos, int frame) { + final long startNanos; synchronized (mLock) { if (!mFrameScheduled) { return; // no work to do } - mFrameScheduled = false; - mLastFrameTimeNanos = timestampNanos; - } - final long startNanos; - if (DEBUG) { startNanos = System.nanoTime(); + final long jitterNanos = startNanos - timestampNanos; + if (jitterNanos >= mFrameIntervalNanos) { + final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; + if (DEBUG) { + Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms " + + "which is more than the frame interval of " + + (mFrameIntervalNanos * 0.000001f) + " ms! " + + "Setting frame time to " + (lastFrameOffset * 0.000001f) + + " ms in the past."); + } + timestampNanos = startNanos - lastFrameOffset; + } + + if (timestampNanos < mLastFrameTimeNanos) { + if (DEBUG) { + Log.d(TAG, "Frame time appears to be going backwards. May be due to a " + + "previously skipped frame. Waiting for next vsync"); + } + scheduleVsyncLocked(); + return; + } + + mFrameScheduled = false; + mLastFrameTimeNanos = timestampNanos; } doCallbacks(Choreographer.CALLBACK_INPUT); diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index 1c5d436..6a8a60a 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -20,7 +20,6 @@ import android.graphics.Rect; import android.graphics.Region; import java.util.ArrayList; -import java.util.concurrent.CopyOnWriteArrayList; /** * A view tree observer is used to register listeners that can be notified of global @@ -32,12 +31,12 @@ import java.util.concurrent.CopyOnWriteArrayList; * for more information. */ public final class ViewTreeObserver { - private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; - private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners; - private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners; - private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; - private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners; - private ArrayList<OnPreDrawListener> mOnPreDrawListeners; + private CopyOnWriteArray<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; + private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners; + private CopyOnWriteArray<OnTouchModeChangeListener> mOnTouchModeChangeListeners; + private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; + private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners; + private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners; private ArrayList<OnDrawListener> mOnDrawListeners; private boolean mAlive = true; @@ -147,7 +146,7 @@ public final class ViewTreeObserver { * windows behind it should be placed. */ public final Rect contentInsets = new Rect(); - + /** * Offsets from the frame of the window at which windows behind it * are visible. @@ -166,13 +165,13 @@ public final class ViewTreeObserver { * can be touched. */ public static final int TOUCHABLE_INSETS_FRAME = 0; - + /** * Option for {@link #setTouchableInsets(int)}: the area inside of * the content insets can be touched. */ public static final int TOUCHABLE_INSETS_CONTENT = 1; - + /** * Option for {@link #setTouchableInsets(int)}: the area inside of * the visible insets can be touched. @@ -195,7 +194,7 @@ public final class ViewTreeObserver { } int mTouchableInsets; - + void reset() { contentInsets.setEmpty(); visibleInsets.setEmpty(); @@ -231,7 +230,7 @@ public final class ViewTreeObserver { mTouchableInsets = other.mTouchableInsets; } } - + /** * Interface definition for a callback to be invoked when layout has * completed and the client can compute its interior insets. @@ -328,7 +327,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnGlobalFocusListeners == null) { - mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>(); + mOnGlobalFocusListeners = new CopyOnWriteArray<OnGlobalFocusChangeListener>(); } mOnGlobalFocusListeners.add(listener); @@ -363,7 +362,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnGlobalLayoutListeners == null) { - mOnGlobalLayoutListeners = new CopyOnWriteArrayList<OnGlobalLayoutListener>(); + mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>(); } mOnGlobalLayoutListeners.add(listener); @@ -413,7 +412,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnPreDrawListeners == null) { - mOnPreDrawListeners = new ArrayList<OnPreDrawListener>(); + mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>(); } mOnPreDrawListeners.add(listener); @@ -485,7 +484,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnScrollChangedListeners == null) { - mOnScrollChangedListeners = new CopyOnWriteArrayList<OnScrollChangedListener>(); + mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>(); } mOnScrollChangedListeners.add(listener); @@ -519,7 +518,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnTouchModeChangeListeners == null) { - mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>(); + mOnTouchModeChangeListeners = new CopyOnWriteArray<OnTouchModeChangeListener>(); } mOnTouchModeChangeListeners.add(listener); @@ -558,7 +557,7 @@ public final class ViewTreeObserver { if (mOnComputeInternalInsetsListeners == null) { mOnComputeInternalInsetsListeners = - new CopyOnWriteArrayList<OnComputeInternalInsetsListener>(); + new CopyOnWriteArray<OnComputeInternalInsetsListener>(); } mOnComputeInternalInsetsListeners.add(listener); @@ -622,10 +621,16 @@ public final class ViewTreeObserver { // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. - final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners; + final CopyOnWriteArray<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners; if (listeners != null && listeners.size() > 0) { - for (OnGlobalFocusChangeListener listener : listeners) { - listener.onGlobalFocusChanged(oldFocus, newFocus); + CopyOnWriteArray.Access<OnGlobalFocusChangeListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).onGlobalFocusChanged(oldFocus, newFocus); + } + } finally { + listeners.end(); } } } @@ -640,10 +645,16 @@ public final class ViewTreeObserver { // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. - final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; + final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; if (listeners != null && listeners.size() > 0) { - for (OnGlobalLayoutListener listener : listeners) { - listener.onGlobalLayout(); + CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).onGlobalLayout(); + } + } finally { + listeners.end(); } } } @@ -658,17 +669,17 @@ public final class ViewTreeObserver { */ @SuppressWarnings("unchecked") public final boolean dispatchOnPreDraw() { - // NOTE: we *must* clone the listener list to perform the dispatching. - // The clone is a safe guard against listeners that - // could mutate the list by calling the various add/remove methods. This prevents - // the array from being modified while we process it. boolean cancelDraw = false; - if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) { - final ArrayList<OnPreDrawListener> listeners = - (ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone(); - int numListeners = listeners.size(); - for (int i = 0; i < numListeners; ++i) { - cancelDraw |= !(listeners.get(i).onPreDraw()); + final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners; + if (listeners != null && listeners.size() > 0) { + CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + cancelDraw |= !(access.get(i).onPreDraw()); + } + } finally { + listeners.end(); } } return cancelDraw; @@ -693,11 +704,17 @@ public final class ViewTreeObserver { * @param inTouchMode True if the touch mode is now enabled, false otherwise. */ final void dispatchOnTouchModeChanged(boolean inTouchMode) { - final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners = + final CopyOnWriteArray<OnTouchModeChangeListener> listeners = mOnTouchModeChangeListeners; if (listeners != null && listeners.size() > 0) { - for (OnTouchModeChangeListener listener : listeners) { - listener.onTouchModeChanged(inTouchMode); + CopyOnWriteArray.Access<OnTouchModeChangeListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).onTouchModeChanged(inTouchMode); + } + } finally { + listeners.end(); } } } @@ -710,10 +727,16 @@ public final class ViewTreeObserver { // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. - final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners; + final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners; if (listeners != null && listeners.size() > 0) { - for (OnScrollChangedListener listener : listeners) { - listener.onScrollChanged(); + CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).onScrollChanged(); + } + } finally { + listeners.end(); } } } @@ -722,11 +745,11 @@ public final class ViewTreeObserver { * Returns whether there are listeners for computing internal insets. */ final boolean hasComputeInternalInsetsListeners() { - final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = + final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners; return (listeners != null && listeners.size() > 0); } - + /** * Calls all listeners to compute the current insets. */ @@ -735,12 +758,105 @@ public final class ViewTreeObserver { // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. - final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = + final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners; if (listeners != null && listeners.size() > 0) { - for (OnComputeInternalInsetsListener listener : listeners) { - listener.onComputeInternalInsets(inoutInfo); + CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).onComputeInternalInsets(inoutInfo); + } + } finally { + listeners.end(); + } + } + } + + /** + * Copy on write array. This array is not thread safe, and only one loop can + * iterate over this array at any given time. This class avoids allocations + * until a concurrent modification happens. + * + * Usage: + * + * CopyOnWriteArray.Access<MyData> access = array.start(); + * try { + * for (int i = 0; i < access.size(); i++) { + * MyData d = access.get(i); + * } + * } finally { + * access.end(); + * } + */ + static class CopyOnWriteArray<T> { + private ArrayList<T> mData = new ArrayList<T>(); + private ArrayList<T> mDataCopy; + + private final Access<T> mAccess = new Access<T>(); + + private boolean mStart; + + static class Access<T> { + private ArrayList<T> mData; + private int mSize; + + T get(int index) { + return mData.get(index); } + + int size() { + return mSize; + } + } + + CopyOnWriteArray() { + } + + private ArrayList<T> getArray() { + if (mStart) { + if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData); + return mDataCopy; + } + return mData; + } + + Access<T> start() { + if (mStart) throw new IllegalStateException("Iteration already started"); + mStart = true; + mDataCopy = null; + mAccess.mData = mData; + mAccess.mSize = mData.size(); + return mAccess; + } + + void end() { + if (!mStart) throw new IllegalStateException("Iteration not started"); + mStart = false; + if (mDataCopy != null) { + mData = mDataCopy; + } + mDataCopy = null; + } + + int size() { + return getArray().size(); + } + + void add(T item) { + getArray().add(item); + } + + void addAll(CopyOnWriteArray<T> array) { + getArray().addAll(array.mData); + } + + void remove(T item) { + getArray().remove(item); + } + + void clear() { + getArray().clear(); } } } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 449870e..5fe0f5f 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3985,7 +3985,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } class PositionScroller implements Runnable { - private static final int SCROLL_DURATION = 400; + private static final int SCROLL_DURATION = 200; private static final int MOVE_DOWN_POS = 1; private static final int MOVE_UP_POS = 2; @@ -4006,21 +4006,35 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength(); } - void start(int position) { + void start(final int position) { stop(); + final int childCount = getChildCount(); + if (childCount == 0) { + // Can't scroll without children. + if (mDataChanged) { + // But we might have something in a minute. + post(new Runnable() { + @Override public void run() { + start(position); + } + }); + } + return; + } + final int firstPos = mFirstPosition; - final int lastPos = firstPos + getChildCount() - 1; + final int lastPos = firstPos + childCount - 1; int viewTravelCount; - if (position <= firstPos) { + if (position < firstPos) { viewTravelCount = firstPos - position + 1; mMode = MOVE_UP_POS; - } else if (position >= lastPos) { + } else if (position > lastPos) { viewTravelCount = position - lastPos + 1; mMode = MOVE_DOWN_POS; } else { - // Already on screen, nothing to do + scrollToVisible(position, INVALID_POSITION, SCROLL_DURATION); return; } @@ -4036,7 +4050,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te postOnAnimation(this); } - void start(int position, int boundPosition) { + void start(final int position, final int boundPosition) { stop(); if (boundPosition == INVALID_POSITION) { @@ -4044,11 +4058,25 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return; } + final int childCount = getChildCount(); + if (childCount == 0) { + // Can't scroll without children. + if (mDataChanged) { + // But we might have something in a minute. + post(new Runnable() { + @Override public void run() { + start(position, boundPosition); + } + }); + } + return; + } + final int firstPos = mFirstPosition; - final int lastPos = firstPos + getChildCount() - 1; + final int lastPos = firstPos + childCount - 1; int viewTravelCount; - if (position <= firstPos) { + if (position < firstPos) { final int boundPosFromLast = lastPos - boundPosition; if (boundPosFromLast < 1) { // Moving would shift our bound position off the screen. Abort. @@ -4064,7 +4092,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te viewTravelCount = posTravel; mMode = MOVE_UP_POS; } - } else if (position >= lastPos) { + } else if (position > lastPos) { final int boundPosFromFirst = boundPosition - firstPos; if (boundPosFromFirst < 1) { // Moving would shift our bound position off the screen. Abort. @@ -4081,7 +4109,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mMode = MOVE_DOWN_POS; } } else { - // Already on screen, nothing to do + scrollToVisible(position, boundPosition, SCROLL_DURATION); return; } @@ -4137,6 +4165,60 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te postOnAnimation(this); } + /** + * Scroll such that targetPos is in the visible padded region without scrolling + * boundPos out of view. Assumes targetPos is onscreen. + */ + void scrollToVisible(int targetPos, int boundPos, int duration) { + final int firstPos = mFirstPosition; + final int childCount = getChildCount(); + final int lastPos = firstPos + childCount - 1; + final int paddedTop = mListPadding.top; + final int paddedBottom = getHeight() - mListPadding.bottom; + + if (targetPos < firstPos || targetPos > lastPos) { + Log.w(TAG, "scrollToVisible called with targetPos " + targetPos + + " not visible [" + firstPos + ", " + lastPos + "]"); + } + if (boundPos < firstPos || boundPos > lastPos) { + // boundPos doesn't matter, it's already offscreen. + boundPos = INVALID_POSITION; + } + + final View targetChild = getChildAt(targetPos - firstPos); + final int targetTop = targetChild.getTop(); + final int targetBottom = targetChild.getBottom(); + int scrollBy = 0; + + if (targetBottom > paddedBottom) { + scrollBy = targetBottom - paddedBottom; + } + if (targetTop < paddedTop) { + scrollBy = targetTop - paddedTop; + } + + if (scrollBy == 0) { + return; + } + + if (boundPos >= 0) { + final View boundChild = getChildAt(boundPos - firstPos); + final int boundTop = boundChild.getTop(); + final int boundBottom = boundChild.getBottom(); + final int absScroll = Math.abs(scrollBy); + + if (scrollBy < 0 && boundBottom + absScroll > paddedBottom) { + // Don't scroll the bound view off the bottom of the screen. + scrollBy = Math.max(0, boundBottom - paddedBottom); + } else if (scrollBy > 0 && boundTop - absScroll < paddedTop) { + // Don't scroll the bound view off the top of the screen. + scrollBy = Math.min(0, boundTop - paddedTop); + } + } + + smoothScrollBy(scrollBy, duration); + } + void stop() { removeCallbacks(this); } diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java index 624dea8..a74ecd3 100644 --- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java +++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java @@ -32,6 +32,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityEvent; @@ -99,8 +100,11 @@ public class MultiWaveView extends View { private float mTapRadius; private float mWaveCenterX; private float mWaveCenterY; - private float mVerticalOffset; + private int mMaxTargetHeight; + private int mMaxTargetWidth; private float mHorizontalOffset; + private float mVerticalOffset; + private float mOuterRadius = 0.0f; private float mHitRadius = 0.0f; private float mSnapMargin = 0.0f; @@ -142,6 +146,9 @@ public class MultiWaveView extends View { private int mTargetDescriptionsResourceId; private int mDirectionDescriptionsResourceId; private boolean mAlwaysTrackFinger; + private int mHorizontalInset; + private int mVerticalInset; + private int mGravity = Gravity.TOP; public MultiWaveView(Context context) { this(context, null); @@ -153,10 +160,9 @@ public class MultiWaveView extends View { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiWaveView); mOuterRadius = a.getDimension(R.styleable.MultiWaveView_outerRadius, mOuterRadius); - mHorizontalOffset = a.getDimension(R.styleable.MultiWaveView_horizontalOffset, - mHorizontalOffset); - mVerticalOffset = a.getDimension(R.styleable.MultiWaveView_verticalOffset, - mVerticalOffset); +// mHorizontalOffset = a.getDimension(R.styleable.MultiWaveView_horizontalOffset, +// mHorizontalOffset); +// mVerticalOffset = a.getDimension(R.styleable.MultiWaveView_verticalOffset, mVerticalOffset); mHitRadius = a.getDimension(R.styleable.MultiWaveView_hitRadius, mHitRadius); mSnapMargin = a.getDimension(R.styleable.MultiWaveView_snapMargin, mSnapMargin); mVibrationDuration = a.getInt(R.styleable.MultiWaveView_vibrationDuration, @@ -169,6 +175,7 @@ public class MultiWaveView extends View { mOuterRing = new TargetDrawable(res, a.peekValue(R.styleable.MultiWaveView_waveDrawable).resourceId); mAlwaysTrackFinger = a.getBoolean(R.styleable.MultiWaveView_alwaysTrackFinger, false); + mGravity = a.getInt(R.styleable.MultiWaveView_gravity, Gravity.TOP); // Read chevron animation drawables final int chevrons[] = { R.styleable.MultiWaveView_leftChevronDrawable, @@ -231,16 +238,16 @@ public class MultiWaveView extends View { @Override protected int getSuggestedMinimumWidth() { - // View should be large enough to contain the background + target drawable on either edge - return mOuterRing.getWidth() - + (mTargetDrawables.size() > 0 ? (mTargetDrawables.get(0).getWidth()/2) : 0); + // View should be large enough to contain the background + handle and + // target drawable on either edge. + return mOuterRing.getWidth() + mMaxTargetWidth; } @Override protected int getSuggestedMinimumHeight() { - // View should be large enough to contain the unlock ring + target drawable on either edge - return mOuterRing.getHeight() - + (mTargetDrawables.size() > 0 ? (mTargetDrawables.get(0).getHeight()/2) : 0); + // View should be large enough to contain the unlock ring + target and + // target drawable on either edge + return mOuterRing.getHeight() + mMaxTargetHeight; } private int resolveMeasured(int measureSpec, int desired) @@ -265,9 +272,10 @@ public class MultiWaveView extends View { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int minimumWidth = getSuggestedMinimumWidth(); final int minimumHeight = getSuggestedMinimumHeight(); - int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth); - int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight); - setMeasuredDimension(viewWidth, viewHeight); + int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth); + int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight); + setupGravity((computedWidth - minimumWidth), (computedHeight - minimumHeight)); + setMeasuredDimension(computedWidth, computedHeight); } private void switchToState(int state, float x, float y) { @@ -521,14 +529,25 @@ public class MultiWaveView extends View { TypedArray array = res.obtainTypedArray(resourceId); int count = array.length(); ArrayList<TargetDrawable> targetDrawables = new ArrayList<TargetDrawable>(count); + int maxWidth = mHandleDrawable.getWidth(); + int maxHeight = mHandleDrawable.getHeight(); for (int i = 0; i < count; i++) { TypedValue value = array.peekValue(i); - targetDrawables.add(new TargetDrawable(res, value != null ? value.resourceId : 0)); + TargetDrawable target= new TargetDrawable(res, value != null ? value.resourceId : 0); + targetDrawables.add(target); + maxWidth = Math.max(maxWidth, target.getWidth()); + maxHeight = Math.max(maxHeight, target.getHeight()); } - array.recycle(); mTargetResourceId = resourceId; mTargetDrawables = targetDrawables; - updateTargetPositions(); + if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) { + mMaxTargetWidth = maxWidth; + mMaxTargetHeight = maxHeight; + requestLayout(); // required to resize layout and call updateTargetPositions() + } else { + updateTargetPositions(); + } + array.recycle(); } /** @@ -638,23 +657,27 @@ public class MultiWaveView extends View { boolean handled = false; switch (action) { case MotionEvent.ACTION_DOWN: + if (DEBUG) Log.v(TAG, "*** DOWN ***"); handleDown(event); handled = true; break; case MotionEvent.ACTION_MOVE: + if (DEBUG) Log.v(TAG, "*** MOVE ***"); handleMove(event); handled = true; break; case MotionEvent.ACTION_UP: + if (DEBUG) Log.v(TAG, "*** UP ***"); handleMove(event); handleUp(event); handled = true; break; case MotionEvent.ACTION_CANCEL: - handleMove(event); + if (DEBUG) Log.v(TAG, "*** CANCEL ***"); + // handleMove(event); handleCancel(event); handled = true; break; @@ -795,6 +818,11 @@ public class MultiWaveView extends View { } mGrabbedState = newState; if (mOnTriggerListener != null) { + if (newState == OnTriggerListener.NO_HANDLE) { + mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE); + } else { + mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE); + } mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState); } } @@ -832,13 +860,45 @@ public class MultiWaveView extends View { moveHandleTo(centerX, centerY, false); } + private void setupGravity(int dx, int dy) { + final int layoutDirection = getResolvedLayoutDirection(); + final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); + + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + case Gravity.LEFT: + mHorizontalInset = 0; + break; + case Gravity.RIGHT: + mHorizontalInset = dx; + break; + case Gravity.CENTER_HORIZONTAL: + default: + mHorizontalInset = dx / 2; + break; + } + switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { + case Gravity.TOP: + mVerticalInset = 0; + break; + case Gravity.BOTTOM: + mVerticalInset = dy; + break; + case Gravity.CENTER_VERTICAL: + default: + mVerticalInset = dy / 2; + break; + } + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); final int width = right - left; final int height = bottom - top; - float newWaveCenterX = mHorizontalOffset + Math.max(width, mOuterRing.getWidth() ) / 2; - float newWaveCenterY = mVerticalOffset + Math.max(height, mOuterRing.getHeight()) / 2; + float newWaveCenterX = mHorizontalOffset + mHorizontalInset + + Math.max(width, mMaxTargetWidth + mOuterRing.getWidth()) / 2; + float newWaveCenterY = mVerticalOffset + mVerticalInset + + Math.max(height, + mMaxTargetHeight + mOuterRing.getHeight()) / 2; if (newWaveCenterX != mWaveCenterX || newWaveCenterY != mWaveCenterY) { if (mWaveCenterX == 0 && mWaveCenterY == 0) { performInitialLayout(newWaveCenterX, newWaveCenterY); @@ -848,9 +908,8 @@ public class MultiWaveView extends View { mOuterRing.setX(mWaveCenterX); mOuterRing.setY(Math.max(mWaveCenterY, mWaveCenterY)); - - updateTargetPositions(); } + updateTargetPositions(); if (DEBUG) dump(); } |
