diff options
Diffstat (limited to 'core/java')
63 files changed, 2309 insertions, 894 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 211be52..a463a62 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -17,8 +17,10 @@ package android.accessibilityservice; import android.app.Service; +import android.content.Context; import android.content.Intent; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; @@ -218,10 +220,17 @@ public abstract class AccessibilityService extends Service { private static final String LOG_TAG = "AccessibilityService"; - private AccessibilityServiceInfo mInfo; + interface Callbacks { + public void onAccessibilityEvent(AccessibilityEvent event); + public void onInterrupt(); + public void onServiceConnected(); + public void onSetConnectionId(int connectionId); + } private int mConnectionId; + private AccessibilityServiceInfo mInfo; + /** * Callback for {@link android.view.accessibility.AccessibilityEvent}s. * @@ -282,27 +291,49 @@ public abstract class AccessibilityService extends Service { */ @Override public final IBinder onBind(Intent intent) { - return new IEventListenerWrapper(this); + return new IEventListenerWrapper(this, getMainLooper(), new Callbacks() { + @Override + public void onServiceConnected() { + AccessibilityService.this.onServiceConnected(); + } + + @Override + public void onInterrupt() { + AccessibilityService.this.onInterrupt(); + } + + @Override + public void onAccessibilityEvent(AccessibilityEvent event) { + AccessibilityService.this.onAccessibilityEvent(event); + } + + @Override + public void onSetConnectionId( int connectionId) { + mConnectionId = connectionId; + } + }); } /** * Implements the internal {@link IEventListener} interface to convert * incoming calls to it back to calls on an {@link AccessibilityService}. */ - class IEventListenerWrapper extends IEventListener.Stub + static class IEventListenerWrapper extends IEventListener.Stub implements HandlerCaller.Callback { + static final int NO_ID = -1; + private static final int DO_SET_SET_CONNECTION = 10; private static final int DO_ON_INTERRUPT = 20; private static final int DO_ON_ACCESSIBILITY_EVENT = 30; private final HandlerCaller mCaller; - private final AccessibilityService mTarget; + private final Callbacks mCallback; - public IEventListenerWrapper(AccessibilityService context) { - mTarget = context; - mCaller = new HandlerCaller(context, this); + public IEventListenerWrapper(Context context, Looper looper, Callbacks callback) { + mCallback = callback; + mCaller = new HandlerCaller(context, looper, this); } public void setConnection(IAccessibilityServiceConnection connection, int connectionId) { @@ -326,12 +357,13 @@ public abstract class AccessibilityService extends Service { case DO_ON_ACCESSIBILITY_EVENT : AccessibilityEvent event = (AccessibilityEvent) message.obj; if (event != null) { - mTarget.onAccessibilityEvent(event); + AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); + mCallback.onAccessibilityEvent(event); event.recycle(); } return; case DO_ON_INTERRUPT : - mTarget.onInterrupt(); + mCallback.onInterrupt(); return; case DO_SET_SET_CONNECTION : final int connectionId = message.arg1; @@ -340,12 +372,11 @@ public abstract class AccessibilityService extends Service { if (connection != null) { AccessibilityInteractionClient.getInstance().addConnection(connectionId, connection); - mConnectionId = connectionId; - mTarget.onServiceConnected(); + mCallback.onSetConnectionId(connectionId); + mCallback.onServiceConnected(); } else { AccessibilityInteractionClient.getInstance().removeConnection(connectionId); - mConnectionId = AccessibilityInteractionClient.NO_ID; - // TODO: Do we need a onServiceDisconnected callback? + mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID); } return; default : diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index e53b313..c9468eb 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -32,8 +32,13 @@ interface IAccessibilityServiceConnection { /** * Finds an {@link AccessibilityNodeInfo} by accessibility id. * - * @param accessibilityWindowId A unique window id. - * @param accessibilityNodeId A unique view id or virtual descendant id. + * @param accessibilityWindowId A unique window id. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * to start from the root. * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. @@ -46,57 +51,58 @@ interface IAccessibilityServiceConnection { /** * Finds {@link AccessibilityNodeInfo}s by View text. The match is case * insensitive containment. The search is performed in the window whose - * id is specified and starts from the View whose accessibility id is + * id is specified and starts from the node whose accessibility id is * specified. * - * @param text The searched text. - * @param accessibilityWindowId A unique window id. + * @param accessibilityWindowId A unique window id. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from - * where to start the search. Use {@link android.view.View#NO_ID} to start from the root. - * @param interactionId The id of the interaction for matching with the callback result. - * @param callback Callback which to receive the result. - * @param threadId The id of the calling thread. - * @return The current window scale, where zero means a failure. - */ - float findAccessibilityNodeInfosByText(String text, int accessibilityWindowId, - long accessibilityNodeId, int interractionId, - IAccessibilityInteractionConnectionCallback callback, long threadId); - - /** - * Finds {@link AccessibilityNodeInfo}s by View text. The match is case - * insensitive containment. The search is performed in the currently - * active window and start from the root View in the window. - * + * where to start the search. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * to start from the root. * @param text The searched text. - * @param accessibilityId The id of the view from which to start searching. - * Use {@link android.view.View#NO_ID} to start from the root. * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. * @return The current window scale, where zero means a failure. */ - float findAccessibilityNodeInfosByTextInActiveWindow(String text, - int interactionId, IAccessibilityInteractionConnectionCallback callback, + float findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, + String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); /** - * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed - * in the currently active window and starts from the root View in the window. + * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in + * the window whose id is specified and starts from the node whose accessibility + * id is specified. * + * @param accessibilityWindowId A unique window id. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * to start from the root. * @param id The id of the node. * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. * @param threadId The id of the calling thread. * @return The current window scale, where zero means a failure. */ - float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId, int interactionId, - IAccessibilityInteractionConnectionCallback callback, long threadId); + float findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId, + int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, + long threadId); /** * Performs an accessibility action on an {@link AccessibilityNodeInfo}. * - * @param accessibilityWindowId The id of the window. - * @param accessibilityNodeId A unique view id or virtual descendant id. + * @param accessibilityWindowId A unique window id. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * to start from the root. * @param action The action to perform. * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. diff --git a/core/java/android/accessibilityservice/UiTestAutomationBridge.java b/core/java/android/accessibilityservice/UiTestAutomationBridge.java new file mode 100644 index 0000000..9d48efc --- /dev/null +++ b/core/java/android/accessibilityservice/UiTestAutomationBridge.java @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.accessibilityservice; + +import android.accessibilityservice.AccessibilityService.Callbacks; +import android.accessibilityservice.AccessibilityService.IEventListenerWrapper; +import android.content.Context; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.util.Log; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityInteractionClient; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.IAccessibilityManager; + +import com.android.internal.util.Predicate; + +import java.util.List; +import java.util.concurrent.TimeoutException; + +/** + * This class represents a bridge that can be used for UI test + * automation. It is responsible for connecting to the system, + * keeping track of the last accessibility event, and exposing + * window content querying APIs. This class is designed to be + * used from both an Android application and a Java program + * run from the shell. + * + * @hide + */ +public class UiTestAutomationBridge { + + private static final String LOG_TAG = UiTestAutomationBridge.class.getSimpleName(); + + public static final int ACTIVE_WINDOW_ID = -1; + + public static final long ROOT_NODE_ID = -1; + + private static final int TIMEOUT_REGISTER_SERVICE = 5000; + + private final Object mLock = new Object(); + + private volatile int mConnectionId = AccessibilityInteractionClient.NO_ID; + + private IEventListenerWrapper mListener; + + private AccessibilityEvent mLastEvent; + + private volatile boolean mWaitingForEventDelivery; + + private volatile boolean mUnprocessedEventAvailable; + + /** + * Gets the last received {@link AccessibilityEvent}. + * + * @return The event. + */ + public AccessibilityEvent getLastAccessibilityEvent() { + return mLastEvent; + } + + /** + * Callback for receiving an {@link AccessibilityEvent}. + * + * <strong>Note:</strong> This method is <strong>NOT</strong> + * executed on the application main thread. The client are + * responsible for proper synchronization. + * + * @param event The received event. + */ + public void onAccessibilityEvent(AccessibilityEvent event) { + /* hook - do nothing */ + } + + /** + * Callback for requests to stop feedback. + * + * <strong>Note:</strong> This method is <strong>NOT</strong> + * executed on the application main thread. The client are + * responsible for proper synchronization. + */ + public void onInterrupt() { + /* hook - do nothing */ + } + + /** + * Connects this service. + * + * @throws IllegalStateException If already connected. + */ + public void connect() { + if (isConnected()) { + throw new IllegalStateException("Already connected."); + } + + // Serialize binder calls to a handler on a dedicated thread + // different from the main since we expose APIs that block + // the main thread waiting for a result the deliver of which + // on the main thread will prevent that thread from waking up. + // The serialization is needed also to ensure that events are + // examined in delivery order. Otherwise, a fair locking + // is needed for making sure the binder calls are interleaved + // with check for the expected event and also to make sure the + // binder threads are allowed to proceed in the received order. + HandlerThread handlerThread = new HandlerThread("UiTestAutomationBridge"); + handlerThread.start(); + Looper looper = handlerThread.getLooper(); + + mListener = new IEventListenerWrapper(null, looper, new Callbacks() { + @Override + public void onServiceConnected() { + /* do nothing */ + } + + @Override + public void onInterrupt() { + UiTestAutomationBridge.this.onInterrupt(); + } + + @Override + public void onAccessibilityEvent(AccessibilityEvent event) { + synchronized (mLock) { + while (true) { + if (!mWaitingForEventDelivery) { + break; + } + if (!mUnprocessedEventAvailable) { + mUnprocessedEventAvailable = true; + mLastEvent = AccessibilityEvent.obtain(event); + mLock.notifyAll(); + break; + } + try { + mLock.wait(); + } catch (InterruptedException ie) { + /* ignore */ + } + } + } + UiTestAutomationBridge.this.onAccessibilityEvent(event); + } + + @Override + public void onSetConnectionId(int connectionId) { + synchronized (mLock) { + mConnectionId = connectionId; + mLock.notifyAll(); + } + } + }); + + final IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( + ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); + + final AccessibilityServiceInfo info = new AccessibilityServiceInfo(); + info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; + info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; + + try { + manager.registerUiTestAutomationService(mListener, info); + } catch (RemoteException re) { + throw new IllegalStateException("Cound not register UiAutomationService.", re); + } + + synchronized (mLock) { + final long startTimeMillis = SystemClock.uptimeMillis(); + while (true) { + if (isConnected()) { + return; + } + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + final long remainingTimeMillis = TIMEOUT_REGISTER_SERVICE - elapsedTimeMillis; + if (remainingTimeMillis <= 0) { + throw new IllegalStateException("Cound not register UiAutomationService."); + } + try { + mLock.wait(remainingTimeMillis); + } catch (InterruptedException ie) { + /* ignore */ + } + } + } + } + + /** + * Disconnects this service. + * + * @throws IllegalStateException If already disconnected. + */ + public void disconnect() { + if (!isConnected()) { + throw new IllegalStateException("Already disconnected."); + } + + IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( + ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); + + try { + manager.unregisterUiTestAutomationService(mListener); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while unregistering UiTestAutomationService", re); + } + } + + /** + * Gets whether this service is connected. + * + * @return True if connected. + */ + public boolean isConnected() { + return (mConnectionId != AccessibilityInteractionClient.NO_ID); + } + + /** + * Executes a command and waits for a specific accessibility event type up + * to a given timeout. + * + * @param command The command to execute before starting to wait for the event. + * @param predicate Predicate for recognizing the awaited event. + * @param timeoutMillis The max wait time in milliseconds. + */ + public AccessibilityEvent executeCommandAndWaitForAccessibilityEvent(Runnable command, + Predicate<AccessibilityEvent> predicate, long timeoutMillis) + throws TimeoutException, Exception { + synchronized (mLock) { + // Prepare to wait for an event. + mWaitingForEventDelivery = true; + mUnprocessedEventAvailable = false; + if (mLastEvent != null) { + mLastEvent.recycle(); + mLastEvent = null; + } + // Execute the command. + command.run(); + // Wait for the event. + final long startTimeMillis = SystemClock.uptimeMillis(); + while (true) { + // If the expected event is received, that's it. + if ((mUnprocessedEventAvailable && predicate.apply(mLastEvent))) { + mWaitingForEventDelivery = false; + mUnprocessedEventAvailable = false; + mLock.notifyAll(); + return mLastEvent; + } + // Ask for another event. + mWaitingForEventDelivery = true; + mUnprocessedEventAvailable = false; + mLock.notifyAll(); + // Check if timed out and if not wait. + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis; + if (remainingTimeMillis <= 0) { + mWaitingForEventDelivery = false; + mUnprocessedEventAvailable = false; + mLock.notifyAll(); + throw new TimeoutException("Expacted event not received within: " + + timeoutMillis + " ms."); + } + try { + mLock.wait(remainingTimeMillis); + } catch (InterruptedException ie) { + /* ignore */ + } + } + } + } + + /** + * Finds an {@link AccessibilityNodeInfo} by accessibility id in the active + * window. The search is performed from the root node. + * + * @param accessibilityNodeId A unique view id or virtual descendant id for + * which to search. + * @return The current window scale, where zero means a failure. + */ + public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityIdInActiveWindow( + long accessibilityNodeId) { + return findAccessibilityNodeInfoByAccessibilityId(ACTIVE_WINDOW_ID, accessibilityNodeId); + } + + /** + * Finds an {@link AccessibilityNodeInfo} by accessibility id. + * + * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique view id or virtual descendant id for + * which to search. + * @return The current window scale, where zero means a failure. + */ + public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId( + int accessibilityWindowId, long accessibilityNodeId) { + // Cache the id to avoid locking + final int connectionId = mConnectionId; + ensureValidConnection(connectionId); + return AccessibilityInteractionClient.getInstance() + .findAccessibilityNodeInfoByAccessibilityId(mConnectionId, + accessibilityWindowId, accessibilityNodeId); + } + + /** + * Finds an {@link AccessibilityNodeInfo} by View id in the active + * window. The search is performed from the root node. + * + * @return The current window scale, where zero means a failure. + */ + public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId) { + return findAccessibilityNodeInfoByViewId(ACTIVE_WINDOW_ID, ROOT_NODE_ID, viewId); + } + + /** + * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in + * the window whose id is specified and starts from the node whose accessibility + * id is specified. + * + * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use {@link #ROOT_NODE_ID} to start from the root. + * @return The current window scale, where zero means a failure. + */ + public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int accessibilityWindowId, + long accessibilityNodeId, int viewId) { + // Cache the id to avoid locking + final int connectionId = mConnectionId; + ensureValidConnection(connectionId); + return AccessibilityInteractionClient.getInstance() + .findAccessibilityNodeInfoByViewId(connectionId, accessibilityWindowId, + accessibilityNodeId, viewId); + } + + /** + * Finds {@link AccessibilityNodeInfo}s by View text in the active + * window. The search is performed from the root node. + * + * @param text The searched text. + * @return The current window scale, where zero means a failure. + */ + public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByTextInActiveWindow(String text) { + return findAccessibilityNodeInfosByText(ACTIVE_WINDOW_ID, ROOT_NODE_ID, text); + } + + /** + * Finds {@link AccessibilityNodeInfo}s by View text. The match is case + * insensitive containment. The search is performed in the window whose + * id is specified and starts from the node whose accessibility id is + * specified. + * + * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use {@link #ROOT_NODE_ID} to start from the root. + * @param text The searched text. + * @return The current window scale, where zero means a failure. + */ + public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int accessibilityWindowId, + long accessibilityNodeId, String text) { + // Cache the id to avoid locking + final int connectionId = mConnectionId; + ensureValidConnection(connectionId); + return AccessibilityInteractionClient.getInstance() + .findAccessibilityNodeInfosByText(connectionId, accessibilityWindowId, + accessibilityNodeId, text); + } + + /** + * Performs an accessibility action on an {@link AccessibilityNodeInfo} + * in the active window. + * + * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id). + * @param action The action to perform. + * @return Whether the action was performed. + */ + public boolean performAccessibilityActionInActiveWindow(long accessibilityNodeId, int action) { + return performAccessibilityAction(ACTIVE_WINDOW_ID, accessibilityNodeId, action); + } + + /** + * Performs an accessibility action on an {@link AccessibilityNodeInfo}. + * + * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id). + * @param action The action to perform. + * @return Whether the action was performed. + */ + public boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId, + int action) { + // Cache the id to avoid locking + final int connectionId = mConnectionId; + ensureValidConnection(connectionId); + return AccessibilityInteractionClient.getInstance().performAccessibilityAction(connectionId, + accessibilityWindowId, accessibilityNodeId, action); + } + + private void ensureValidConnection(int connectionId) { + if (connectionId == AccessibilityInteractionClient.NO_ID) { + throw new IllegalStateException("UiAutomationService not connected." + + " Did you call #register()?"); + } + } +} diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index 894f428..634e4d8 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -960,17 +960,17 @@ public class LayoutTransition { if (anim instanceof ObjectAnimator) { ((ObjectAnimator) anim).setCurrentPlayTime(0); } - if (mListeners != null) { - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator anim) { - currentAppearingAnimations.remove(child); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator anim) { + currentAppearingAnimations.remove(child); + if (mListeners != null) { for (TransitionListener listener : mListeners) { listener.endTransition(LayoutTransition.this, parent, child, APPEARING); } } - }); - } + } + }); currentAppearingAnimations.put(child, anim); anim.start(); } @@ -998,17 +998,19 @@ public class LayoutTransition { anim.setStartDelay(mDisappearingDelay); anim.setDuration(mDisappearingDuration); anim.setTarget(child); - if (mListeners != null) { - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator anim) { - currentDisappearingAnimations.remove(child); + final float preAnimAlpha = child.getAlpha(); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator anim) { + currentDisappearingAnimations.remove(child); + child.setAlpha(preAnimAlpha); + if (mListeners != null) { for (TransitionListener listener : mListeners) { listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING); } } - }); - } + } + }); if (anim instanceof ObjectAnimator) { ((ObjectAnimator) anim).setCurrentPlayTime(0); } @@ -1024,18 +1026,25 @@ public class LayoutTransition { * * @param parent The ViewGroup to which the View is being added. * @param child The View being added to the ViewGroup. + * @param changesLayout Whether the removal will cause changes in the layout of other views + * in the container. INVISIBLE views becoming VISIBLE will not cause changes and thus will not + * affect CHANGE_APPEARING or CHANGE_DISAPPEARING animations. */ - public void addChild(ViewGroup parent, View child) { + private void addChild(ViewGroup parent, View child, boolean changesLayout) { // Want disappearing animations to finish up before proceeding cancel(DISAPPEARING); - // Also, cancel changing animations so that we start fresh ones from current locations - cancel(CHANGE_APPEARING); + if (changesLayout) { + // Also, cancel changing animations so that we start fresh ones from current locations + cancel(CHANGE_APPEARING); + } if (mListeners != null) { for (TransitionListener listener : mListeners) { listener.startTransition(this, parent, child, APPEARING); } } - runChangeTransition(parent, child, APPEARING); + if (changesLayout) { + runChangeTransition(parent, child, APPEARING); + } runAppearingTransition(parent, child); } @@ -1048,8 +1057,31 @@ public class LayoutTransition { * @param parent The ViewGroup to which the View is being added. * @param child The View being added to the ViewGroup. */ + public void addChild(ViewGroup parent, View child) { + addChild(parent, child, true); + } + + /** + * @deprecated Use {@link #showChild(android.view.ViewGroup, android.view.View, int)}. + */ + @Deprecated public void showChild(ViewGroup parent, View child) { - addChild(parent, child); + addChild(parent, child, true); + } + + /** + * This method is called by ViewGroup when a child view is about to be made visible in the + * container. This callback starts the process of a transition; we grab the starting + * values, listen for changes to all of the children of the container, and start appropriate + * animations. + * + * @param parent The ViewGroup in which the View is being made visible. + * @param child The View being made visible. + * @param oldVisibility The previous visibility value of the child View, either + * {@link View#GONE} or {@link View#INVISIBLE}. + */ + public void showChild(ViewGroup parent, View child, int oldVisibility) { + addChild(parent, child, oldVisibility == View.GONE); } /** @@ -1060,18 +1092,25 @@ public class LayoutTransition { * * @param parent The ViewGroup from which the View is being removed. * @param child The View being removed from the ViewGroup. + * @param changesLayout Whether the removal will cause changes in the layout of other views + * in the container. Views becoming INVISIBLE will not cause changes and thus will not + * affect CHANGE_APPEARING or CHANGE_DISAPPEARING animations. */ - public void removeChild(ViewGroup parent, View child) { + private void removeChild(ViewGroup parent, View child, boolean changesLayout) { // Want appearing animations to finish up before proceeding cancel(APPEARING); - // Also, cancel changing animations so that we start fresh ones from current locations - cancel(CHANGE_DISAPPEARING); + if (changesLayout) { + // Also, cancel changing animations so that we start fresh ones from current locations + cancel(CHANGE_DISAPPEARING); + } if (mListeners != null) { for (TransitionListener listener : mListeners) { listener.startTransition(this, parent, child, DISAPPEARING); } } - runChangeTransition(parent, child, DISAPPEARING); + if (changesLayout) { + runChangeTransition(parent, child, DISAPPEARING); + } runDisappearingTransition(parent, child); } @@ -1084,8 +1123,31 @@ public class LayoutTransition { * @param parent The ViewGroup from which the View is being removed. * @param child The View being removed from the ViewGroup. */ + public void removeChild(ViewGroup parent, View child) { + removeChild(parent, child, true); + } + + /** + * @deprecated Use {@link #hideChild(android.view.ViewGroup, android.view.View, int)}. + */ + @Deprecated public void hideChild(ViewGroup parent, View child) { - removeChild(parent, child); + removeChild(parent, child, true); + } + + /** + * This method is called by ViewGroup when a child view is about to be hidden in + * container. This callback starts the process of a transition; we grab the starting + * values, listen for changes to all of the children of the container, and start appropriate + * animations. + * + * @param parent The parent ViewGroup of the View being hidden. + * @param child The View being hidden. + * @param newVisibility The new visibility value of the child View, either + * {@link View#GONE} or {@link View#INVISIBLE}. + */ + public void hideChild(ViewGroup parent, View child, int newVisibility) { + removeChild(parent, child, newVisibility == View.GONE); } /** diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index c7a129e..cc1efb9 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -19,6 +19,7 @@ package android.animation; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.SystemProperties; import android.util.AndroidRuntimeException; import android.view.Choreographer; import android.view.animation.AccelerateDecelerateInterpolator; @@ -52,6 +53,7 @@ public class ValueAnimator extends Animator { /** * Internal constants */ + private static float sDurationScale = 1.0f; /** * Messages sent to timing handler: START is sent when an animation first begins. @@ -158,10 +160,12 @@ public class ValueAnimator extends Animator { // // How long the animation should last in ms - private long mDuration = 300; + private long mDuration = (long)(300 * sDurationScale); + private long mUnscaledDuration = 300; // The amount of time in ms to delay starting the animation after start() is called private long mStartDelay = 0; + private long mUnscaledStartDelay = 0; // The number of times the animation will repeat. The default is 0, which means the animation // will play only once @@ -217,6 +221,14 @@ public class ValueAnimator extends Animator { */ public static final int INFINITE = -1; + + /** + * @hide + */ + public static void setDurationScale(float durationScale) { + sDurationScale = durationScale; + } + /** * Creates a new ValueAnimator object. This default constructor is primarily for * use internally; the factory methods which take parameters are more generally @@ -453,7 +465,8 @@ public class ValueAnimator extends Animator { throw new IllegalArgumentException("Animators cannot have negative duration: " + duration); } - mDuration = duration; + mUnscaledDuration = duration; + mDuration = (long)(duration * sDurationScale); return this; } @@ -463,7 +476,7 @@ public class ValueAnimator extends Animator { * @return The length of the animation, in milliseconds. */ public long getDuration() { - return mDuration; + return mUnscaledDuration; } /** @@ -658,7 +671,7 @@ public class ValueAnimator extends Animator { * @return the number of milliseconds to delay running the animation */ public long getStartDelay() { - return mStartDelay; + return mUnscaledStartDelay; } /** @@ -668,7 +681,8 @@ public class ValueAnimator extends Animator { * @param startDelay The amount of the delay, in milliseconds */ public void setStartDelay(long startDelay) { - this.mStartDelay = startDelay; + this.mStartDelay = (long)(startDelay * sDurationScale); + mUnscaledStartDelay = startDelay; } /** diff --git a/core/java/android/annotation/SuppressLint.java b/core/java/android/annotation/SuppressLint.java new file mode 100644 index 0000000..2d3456b --- /dev/null +++ b/core/java/android/annotation/SuppressLint.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Indicates that Lint should ignore the specified warnings for the annotated element. */ +@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) +@Retention(RetentionPolicy.CLASS) +public @interface SuppressLint { + /** + * The set of warnings (identified by the lint issue id) that should be + * ignored by lint. It is not an error to specify an unrecognized name. + */ + String[] value(); +} diff --git a/core/java/android/annotation/TargetApi.java b/core/java/android/annotation/TargetApi.java new file mode 100644 index 0000000..ea17890 --- /dev/null +++ b/core/java/android/annotation/TargetApi.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Indicates that Lint should treat this type as targeting a given API level, no matter what the + project target is. */ +@Target({TYPE, METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface TargetApi { + /** + * This sets the target api level for the type.. + */ + int value(); +} diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 9807b89..3c5f53a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4413,7 +4413,7 @@ public final class ActivityThread { }); } - public static final ActivityThread systemMain() { + public static ActivityThread systemMain() { HardwareRenderer.disable(true); ActivityThread thread = new ActivityThread(); thread.attach(true); @@ -4454,6 +4454,8 @@ public final class ActivityThread { ActivityThread thread = new ActivityThread(); thread.attach(false); + AsyncTask.init(); + if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index d83d2e6..ff71ee7 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -17,6 +17,7 @@ package android.app; import android.content.Loader; +import android.content.Loader.OnLoadCanceledListener; import android.os.Bundle; import android.util.DebugUtils; import android.util.Log; @@ -219,7 +220,8 @@ class LoaderManagerImpl extends LoaderManager { boolean mCreatingLoader; - final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> { + final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>, + Loader.OnLoadCanceledListener<Object> { final int mId; final Bundle mArgs; LoaderManager.LoaderCallbacks<Object> mCallbacks; @@ -271,6 +273,7 @@ class LoaderManagerImpl extends LoaderManager { } if (!mListenerRegistered) { mLoader.registerListener(mId, this); + mLoader.registerOnLoadCanceledListener(this); mListenerRegistered = true; } mLoader.startLoading(); @@ -329,11 +332,21 @@ class LoaderManagerImpl extends LoaderManager { // Let the loader know we're done with it mListenerRegistered = false; mLoader.unregisterListener(this); + mLoader.unregisterOnLoadCanceledListener(this); mLoader.stopLoading(); } } } - + + void cancel() { + if (DEBUG) Log.v(TAG, " Canceling: " + this); + if (mStarted && mLoader != null && mListenerRegistered) { + if (!mLoader.cancelLoad()) { + onLoadCanceled(mLoader); + } + } + } + void destroy() { if (DEBUG) Log.v(TAG, " Destroying: " + this); mDestroyed = true; @@ -361,6 +374,7 @@ class LoaderManagerImpl extends LoaderManager { if (mListenerRegistered) { mListenerRegistered = false; mLoader.unregisterListener(this); + mLoader.unregisterOnLoadCanceledListener(this); } mLoader.reset(); } @@ -368,8 +382,38 @@ class LoaderManagerImpl extends LoaderManager { mPendingLoader.destroy(); } } - - @Override public void onLoadComplete(Loader<Object> loader, Object data) { + + @Override + public void onLoadCanceled(Loader<Object> loader) { + if (DEBUG) Log.v(TAG, "onLoadCanceled: " + this); + + if (mDestroyed) { + if (DEBUG) Log.v(TAG, " Ignoring load canceled -- destroyed"); + return; + } + + if (mLoaders.get(mId) != this) { + // This cancellation message is not coming from the current active loader. + // We don't care about it. + if (DEBUG) Log.v(TAG, " Ignoring load canceled -- not active"); + return; + } + + LoaderInfo pending = mPendingLoader; + if (pending != null) { + // There is a new request pending and we were just + // waiting for the old one to cancel or complete before starting + // it. So now it is time, switch over to the new loader. + if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending); + mPendingLoader = null; + mLoaders.put(mId, null); + destroy(); + installLoader(pending); + } + } + + @Override + public void onLoadComplete(Loader<Object> loader, Object data) { if (DEBUG) Log.v(TAG, "onLoadComplete: " + this); if (mDestroyed) { @@ -632,7 +676,9 @@ class LoaderManagerImpl extends LoaderManager { } else { // Now we have three active loaders... we'll queue // up this request to be processed once one of the other loaders - // finishes. + // finishes or is canceled. + if (DEBUG) Log.v(TAG, " Current loader is running; attempting to cancel"); + info.cancel(); if (info.mPendingLoader != null) { if (DEBUG) Log.v(TAG, " Removing pending loader: " + info.mPendingLoader); info.mPendingLoader.destroy(); diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java index 944ca6b..da51952 100644 --- a/core/java/android/content/AsyncTaskLoader.java +++ b/core/java/android/content/AsyncTaskLoader.java @@ -53,19 +53,33 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { static final boolean DEBUG = false; final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable { + private final CountDownLatch mDone = new CountDownLatch(1); - D result; + // Set to true to indicate that the task has been posted to a handler for + // execution at a later time. Used to throttle updates. boolean waiting; - private CountDownLatch done = new CountDownLatch(1); - /* Runs on a worker thread */ @Override protected D doInBackground(Void... params) { if (DEBUG) Slog.v(TAG, this + " >>> doInBackground"); - result = AsyncTaskLoader.this.onLoadInBackground(); - if (DEBUG) Slog.v(TAG, this + " <<< doInBackground"); - return result; + try { + D data = AsyncTaskLoader.this.onLoadInBackground(); + if (DEBUG) Slog.v(TAG, this + " <<< doInBackground"); + return data; + } catch (OperationCanceledException ex) { + if (!isCancelled()) { + // onLoadInBackground threw a canceled exception spuriously. + // This is problematic because it means that the LoaderManager did not + // cancel the Loader itself and still expects to receive a result. + // Additionally, the Loader's own state will not have been updated to + // reflect the fact that the task was being canceled. + // So we treat this case as an unhandled exception. + throw ex; + } + if (DEBUG) Slog.v(TAG, this + " <<< doInBackground (was canceled)"); + return null; + } } /* Runs on the UI thread */ @@ -75,25 +89,37 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { try { AsyncTaskLoader.this.dispatchOnLoadComplete(this, data); } finally { - done.countDown(); + mDone.countDown(); } } + /* Runs on the UI thread */ @Override - protected void onCancelled() { + protected void onCancelled(D data) { if (DEBUG) Slog.v(TAG, this + " onCancelled"); try { - AsyncTaskLoader.this.dispatchOnCancelled(this, result); + AsyncTaskLoader.this.dispatchOnCancelled(this, data); } finally { - done.countDown(); + mDone.countDown(); } } + /* Runs on the UI thread, when the waiting task is posted to a handler. + * This method is only executed when task execution was deferred (waiting was true). */ @Override public void run() { waiting = false; AsyncTaskLoader.this.executePendingTask(); } + + /* Used for testing purposes to wait for the task to complete. */ + public void waitForLoader() { + try { + mDone.await(); + } catch (InterruptedException e) { + // Ignore + } + } } volatile LoadTask mTask; @@ -109,7 +135,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { /** * Set amount to throttle updates by. This is the minimum time from - * when the last {@link #onLoadInBackground()} call has completed until + * when the last {@link #loadInBackground()} call has completed until * a new load is scheduled. * * @param delayMS Amount of delay, in milliseconds. @@ -130,24 +156,9 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { executePendingTask(); } - /** - * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)} - * for more info. Must be called on the main thread of the process. - * - * <p>Cancelling is not an immediate operation, since the load is performed - * in a background thread. If there is currently a load in progress, this - * method requests that the load be cancelled, and notes this is the case; - * once the background thread has completed its work its remaining state - * will be cleared. If another load request comes in during this time, - * it will be held until the cancelled load is complete. - * - * @return Returns <tt>false</tt> if the task could not be cancelled, - * typically because it has already completed normally, or - * because {@link #startLoading()} hasn't been called; returns - * <tt>true</tt> otherwise. - */ - public boolean cancelLoad() { - if (DEBUG) Slog.v(TAG, "cancelLoad: mTask=" + mTask); + @Override + protected boolean onCancelLoad() { + if (DEBUG) Slog.v(TAG, "onCancelLoad: mTask=" + mTask); if (mTask != null) { if (mCancellingTask != null) { // There was a pending task already waiting for a previous @@ -173,7 +184,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { if (DEBUG) Slog.v(TAG, "cancelLoad: cancelled=" + cancelled); if (cancelled) { mCancellingTask = mTask; - onCancelLoadInBackground(); + cancelLoadInBackground(); } mTask = null; return cancelled; @@ -184,7 +195,10 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { /** * Called if the task was canceled before it was completed. Gives the class a chance - * to properly dispose of the result. + * to clean up post-cancellation and to properly dispose of the result. + * + * @param data The value that was returned by {@link #loadInBackground}, or null + * if the task threw {@link OperationCanceledException}. */ public void onCanceled(D data) { } @@ -218,6 +232,8 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { if (DEBUG) Slog.v(TAG, "Cancelled task is now canceled!"); mLastLoadCompleteTime = SystemClock.uptimeMillis(); mCancellingTask = null; + if (DEBUG) Slog.v(TAG, "Delivering cancellation"); + deliverCancellation(); executePendingTask(); } } @@ -240,38 +256,72 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { } /** + * Called on a worker thread to perform the actual load and to return + * the result of the load operation. + * + * Implementations should not deliver the result directly, but should return them + * from this method, which will eventually end up calling {@link #deliverResult} on + * the UI thread. If implementations need to process the results on the UI thread + * they may override {@link #deliverResult} and do so there. + * + * To support cancellation, this method should periodically check the value of + * {@link #isLoadInBackgroundCanceled} and terminate when it returns true. + * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load + * directly instead of polling {@link #isLoadInBackgroundCanceled}. + * + * When the load is canceled, this method may either return normally or throw + * {@link OperationCanceledException}. In either case, the {@link Loader} will + * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the + * result object, if any. + * + * @return The result of the load operation. + * + * @throws OperationCanceledException if the load is canceled during execution. + * + * @see #isLoadInBackgroundCanceled + * @see #cancelLoadInBackground + * @see #onCanceled */ public abstract D loadInBackground(); /** - * Called on a worker thread to perform the actual load. Implementations should not deliver the - * result directly, but should return them from this method, which will eventually end up - * calling {@link #deliverResult} on the UI thread. If implementations need to process - * the results on the UI thread they may override {@link #deliverResult} and do so - * there. + * Calls {@link #loadInBackground()}. + * + * This method is reserved for use by the loader framework. + * Subclasses should override {@link #loadInBackground} instead of this method. + * + * @return The result of the load operation. * - * @return Implementations must return the result of their load operation. + * @throws OperationCanceledException if the load is canceled during execution. + * + * @see #loadInBackground */ protected D onLoadInBackground() { return loadInBackground(); } /** - * Override this method to try to abort the computation currently taking - * place on a background thread. + * Called on the main thread to abort a load in progress. + * + * Override this method to abort the current invocation of {@link #loadInBackground} + * that is running in the background on a worker thread. * - * Note that when this method is called, it is possible that {@link #loadInBackground} - * has not started yet or has already completed. + * This method should do nothing if {@link #loadInBackground} has not started + * running or if it has already finished. + * + * @see #loadInBackground */ - protected void onCancelLoadInBackground() { + public void cancelLoadInBackground() { } /** - * Returns true if the current execution of {@link #loadInBackground()} is being canceled. + * Returns true if the current invocation of {@link #loadInBackground} is being canceled. + * + * @return True if the current invocation of {@link #loadInBackground} is being canceled. * - * @return True if the current execution of {@link #loadInBackground()} is being canceled. + * @see #loadInBackground */ - protected boolean isLoadInBackgroundCanceled() { + public boolean isLoadInBackgroundCanceled() { return mCancellingTask != null; } @@ -288,11 +338,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { public void waitForLoader() { LoadTask task = mTask; if (task != null) { - try { - task.done.await(); - } catch (InterruptedException e) { - // Ignore - } + task.waitForLoader(); } } diff --git a/core/java/android/content/CancelationSignal.java b/core/java/android/content/CancellationSignal.java index 58cf59d..2dbbe54 100644 --- a/core/java/android/content/CancelationSignal.java +++ b/core/java/android/content/CancellationSignal.java @@ -21,15 +21,15 @@ import android.os.RemoteException; /** * Provides the ability to cancel an operation in progress. */ -public final class CancelationSignal { +public final class CancellationSignal { private boolean mIsCanceled; private OnCancelListener mOnCancelListener; - private ICancelationSignal mRemote; + private ICancellationSignal mRemote; /** - * Creates a cancelation signal, initially not canceled. + * Creates a cancellation signal, initially not canceled. */ - public CancelationSignal() { + public CancellationSignal() { } /** @@ -55,7 +55,7 @@ public final class CancelationSignal { } /** - * Cancels the operation and signals the cancelation listener. + * Cancels the operation and signals the cancellation listener. * If the operation has not yet started, then it will be canceled as soon as it does. */ public void cancel() { @@ -76,17 +76,23 @@ public final class CancelationSignal { } /** - * Sets the cancelation listener to be called when canceled. - * If {@link CancelationSignal#cancel} has already been called, then the provided + * Sets the cancellation listener to be called when canceled. + * + * This method is intended to be used by the recipient of a cancellation signal + * such as a database or a content provider to handle cancellation requests + * while performing a long-running operation. This method is not intended to be + * used by applications themselves. + * + * If {@link CancellationSignal#cancel} has already been called, then the provided * listener is invoked immediately. * - * The listener is called while holding the cancelation signal's lock which is + * The listener is called while holding the cancellation signal's lock which is * also held while registering or unregistering the listener. Because of the lock, * it is not possible for the listener to run after it has been unregistered. - * This design choice makes it easier for clients of {@link CancelationSignal} to + * This design choice makes it easier for clients of {@link CancellationSignal} to * prevent race conditions related to listener registration and unregistration. * - * @param listener The cancelation listener, or null to remove the current listener. + * @param listener The cancellation listener, or null to remove the current listener. */ public void setOnCancelListener(OnCancelListener listener) { synchronized (this) { @@ -104,7 +110,7 @@ public final class CancelationSignal { * * @hide */ - public void setRemote(ICancelationSignal remote) { + public void setRemote(ICancellationSignal remote) { synchronized (this) { mRemote = remote; if (mIsCanceled && remote != null) { @@ -118,47 +124,47 @@ public final class CancelationSignal { /** * Creates a transport that can be returned back to the caller of - * a Binder function and subsequently used to dispatch a cancelation signal. + * a Binder function and subsequently used to dispatch a cancellation signal. * - * @return The new cancelation signal transport. + * @return The new cancellation signal transport. * * @hide */ - public static ICancelationSignal createTransport() { + public static ICancellationSignal createTransport() { return new Transport(); } /** - * Given a locally created transport, returns its associated cancelation signal. + * Given a locally created transport, returns its associated cancellation signal. * * @param transport The locally created transport, or null if none. - * @return The associated cancelation signal, or null if none. + * @return The associated cancellation signal, or null if none. * * @hide */ - public static CancelationSignal fromTransport(ICancelationSignal transport) { + public static CancellationSignal fromTransport(ICancellationSignal transport) { if (transport instanceof Transport) { - return ((Transport)transport).mCancelationSignal; + return ((Transport)transport).mCancellationSignal; } return null; } /** - * Listens for cancelation. + * Listens for cancellation. */ public interface OnCancelListener { /** - * Called when {@link CancelationSignal#cancel} is invoked. + * Called when {@link CancellationSignal#cancel} is invoked. */ void onCancel(); } - private static final class Transport extends ICancelationSignal.Stub { - final CancelationSignal mCancelationSignal = new CancelationSignal(); + private static final class Transport extends ICancellationSignal.Stub { + final CancellationSignal mCancellationSignal = new CancellationSignal(); @Override public void cancel() throws RemoteException { - mCancelationSignal.cancel(); + mCancellationSignal.cancel(); } } } diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index adbeb6a..12e3ccf 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -178,10 +178,10 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, - ICancelationSignal cancelationSignal) { + ICancellationSignal cancellationSignal) { enforceReadPermission(uri); return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder, - CancelationSignal.fromTransport(cancelationSignal)); + CancellationSignal.fromTransport(cancellationSignal)); } @Override @@ -263,8 +263,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } @Override - public ICancelationSignal createCancelationSignal() throws RemoteException { - return CancelationSignal.createTransport(); + public ICancellationSignal createCancellationSignal() throws RemoteException { + return CancellationSignal.createTransport(); } private void enforceReadPermission(Uri uri) { @@ -557,7 +557,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { String selection, String[] selectionArgs, String sortOrder); /** - * Implement this to handle query requests from clients with support for cancelation. + * Implement this to handle query requests from clients with support for cancellation. * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes * and Threads</a>. @@ -597,9 +597,9 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return c;</pre> * <p> * If you implement this method then you must also implement the version of - * {@link #query(Uri, String[], String, String[], String)} that does not take a cancelation - * provider to ensure correct operation on older versions of the Android Framework in - * which the cancelation signal overload was not available. + * {@link #query(Uri, String[], String, String[], String)} that does not take a cancellation + * signal to ensure correct operation on older versions of the Android Framework in + * which the cancellation signal overload was not available. * * @param uri The URI to query. This will be the full URI sent by the client; * if the client is requesting a specific record, the URI will end in a record number @@ -614,14 +614,14 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * The values will be bound as Strings. * @param sortOrder How the rows in the cursor should be sorted. * If null then the provider is free to define the sort order. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. * @return a Cursor or null. */ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { return query(uri, projection, selection, selectionArgs, sortOrder); } diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index 9a1fa65..3ac5e07 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -52,15 +52,15 @@ public class ContentProviderClient { /** See {@link ContentProvider#query ContentProvider.query} */ public Cursor query(Uri url, String[] projection, String selection, - String[] selectionArgs, String sortOrder, CancelationSignal cancelationSignal) + String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) throws RemoteException { - ICancelationSignal remoteCancelationSignal = null; - if (cancelationSignal != null) { - remoteCancelationSignal = mContentProvider.createCancelationSignal(); - cancelationSignal.setRemote(remoteCancelationSignal); + ICancellationSignal remoteCancellationSignal = null; + if (cancellationSignal != null) { + remoteCancellationSignal = mContentProvider.createCancellationSignal(); + cancellationSignal.setRemote(remoteCancellationSignal); } return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder, - remoteCancelationSignal); + remoteCancellationSignal); } /** See {@link ContentProvider#getType ContentProvider.getType} */ diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index e0e277a..eb83dbc 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -105,11 +105,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr String sortOrder = data.readString(); IContentObserver observer = IContentObserver.Stub.asInterface( data.readStrongBinder()); - ICancelationSignal cancelationSignal = ICancelationSignal.Stub.asInterface( + ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface( data.readStrongBinder()); Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder, - cancelationSignal); + cancellationSignal); if (cursor != null) { CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor( cursor, observer, getProviderName()); @@ -300,9 +300,9 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { data.enforceInterface(IContentProvider.descriptor); - ICancelationSignal cancelationSignal = createCancelationSignal(); + ICancellationSignal cancellationSignal = createCancellationSignal(); reply.writeNoException(); - reply.writeStrongBinder(cancelationSignal.asBinder()); + reply.writeStrongBinder(cancellationSignal.asBinder()); return true; } } @@ -334,7 +334,7 @@ final class ContentProviderProxy implements IContentProvider } public Cursor query(Uri url, String[] projection, String selection, - String[] selectionArgs, String sortOrder, ICancelationSignal cancelationSignal) + String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal) throws RemoteException { BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); Parcel data = Parcel.obtain(); @@ -363,7 +363,7 @@ final class ContentProviderProxy implements IContentProvider } data.writeString(sortOrder); data.writeStrongBinder(adaptor.getObserver().asBinder()); - data.writeStrongBinder(cancelationSignal != null ? cancelationSignal.asBinder() : null); + data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null); mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); @@ -632,7 +632,7 @@ final class ContentProviderProxy implements IContentProvider } } - public ICancelationSignal createCancelationSignal() throws RemoteException { + public ICancellationSignal createCancellationSignal() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { @@ -642,9 +642,9 @@ final class ContentProviderProxy implements IContentProvider data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); - ICancelationSignal cancelationSignal = ICancelationSignal.Stub.asInterface( + ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface( reply.readStrongBinder()); - return cancelationSignal; + return cancellationSignal; } finally { data.recycle(); reply.recycle(); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index e79475a..96a65da 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -335,7 +335,7 @@ public abstract class ContentResolver { * @param sortOrder How to order the rows, formatted as an SQL ORDER BY * clause (excluding the ORDER BY itself). Passing null will use the * default sort order, which may be unordered. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. * @return A Cursor object, which is positioned before the first entry, or null @@ -343,7 +343,7 @@ public abstract class ContentResolver { */ public final Cursor query(final Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { IContentProvider provider = acquireProvider(uri); if (provider == null) { return null; @@ -351,14 +351,14 @@ public abstract class ContentResolver { try { long startTime = SystemClock.uptimeMillis(); - ICancelationSignal remoteCancelationSignal = null; - if (cancelationSignal != null) { - cancelationSignal.throwIfCanceled(); - remoteCancelationSignal = provider.createCancelationSignal(); - cancelationSignal.setRemote(remoteCancelationSignal); + ICancellationSignal remoteCancellationSignal = null; + if (cancellationSignal != null) { + cancellationSignal.throwIfCanceled(); + remoteCancellationSignal = provider.createCancellationSignal(); + cancellationSignal.setRemote(remoteCancellationSignal); } Cursor qCursor = provider.query(uri, projection, - selection, selectionArgs, sortOrder, remoteCancelationSignal); + selection, selectionArgs, sortOrder, remoteCancellationSignal); if (qCursor == null) { releaseProvider(provider); return null; diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java index 6e4aca8..aed3728 100644 --- a/core/java/android/content/CursorLoader.java +++ b/core/java/android/content/CursorLoader.java @@ -48,7 +48,7 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> { String mSortOrder; Cursor mCursor; - CancelationSignal mCancelationSignal; + CancellationSignal mCancellationSignal; /* Runs on a worker thread */ @Override @@ -57,11 +57,11 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> { if (isLoadInBackgroundCanceled()) { throw new OperationCanceledException(); } - mCancelationSignal = new CancelationSignal(); + mCancellationSignal = new CancellationSignal(); } try { Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection, - mSelectionArgs, mSortOrder, mCancelationSignal); + mSelectionArgs, mSortOrder, mCancellationSignal); if (cursor != null) { // Ensure the cursor window is filled cursor.getCount(); @@ -70,18 +70,18 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> { return cursor; } finally { synchronized (this) { - mCancelationSignal = null; + mCancellationSignal = null; } } } @Override - protected void onCancelLoadInBackground() { - super.onCancelLoadInBackground(); + public void cancelLoadInBackground() { + super.cancelLoadInBackground(); synchronized (this) { - if (mCancelationSignal != null) { - mCancelationSignal.cancel(); + if (mCancellationSignal != null) { + mCancellationSignal.cancel(); } } } diff --git a/core/java/android/content/ICancelationSignal.aidl b/core/java/android/content/ICancellationSignal.aidl index 3f5a24d..cf1c5d3 100644 --- a/core/java/android/content/ICancelationSignal.aidl +++ b/core/java/android/content/ICancellationSignal.aidl @@ -19,6 +19,6 @@ package android.content; /** * @hide */ -interface ICancelationSignal { +interface ICancellationSignal { oneway void cancel(); } diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index f52157f..16478b7 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -34,7 +34,7 @@ import java.util.ArrayList; */ public interface IContentProvider extends IInterface { public Cursor query(Uri url, String[] projection, String selection, - String[] selectionArgs, String sortOrder, ICancelationSignal cancelationSignal) + String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal) throws RemoteException; public String getType(Uri url) throws RemoteException; public Uri insert(Uri url, ContentValues initialValues) @@ -51,7 +51,7 @@ public interface IContentProvider extends IInterface { public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException; public Bundle call(String method, String arg, Bundle extras) throws RemoteException; - public ICancelationSignal createCancelationSignal() throws RemoteException; + public ICancellationSignal createCancellationSignal() throws RemoteException; // Data interchange. public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index fbc1b2b..ab62c44 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2957,6 +2957,13 @@ public class Intent implements Parcelable, Cloneable { */ public static final int FLAG_RECEIVER_REPLACE_PENDING = 0x20000000; /** + * If set, when sending a broadcast the recipient is allowed to run at + * foreground priority, with a shorter timeout interval. During normal + * broadcasts the receivers are not automatically hoisted out of the + * background priority class. + */ + public static final int FLAG_RECEIVER_FOREGROUND = 0x10000000; + /** * If set, when sending a broadcast <i>before boot has completed</i> only * registered receivers will be called -- no BroadcastReceiver components * will be launched. Sticky intent state will be recorded properly even @@ -2969,14 +2976,14 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ - public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x10000000; + public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x08000000; /** * Set when this broadcast is for a boot upgrade, a special mode that * allows the broadcast to be sent before the system is ready and launches * the app process with no providers running in it. * @hide */ - public static final int FLAG_RECEIVER_BOOT_UPGRADE = 0x08000000; + public static final int FLAG_RECEIVER_BOOT_UPGRADE = 0x04000000; /** * @hide Flags that can't be changed with PendingIntent. diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java index ac05682..3052414 100644 --- a/core/java/android/content/Loader.java +++ b/core/java/android/content/Loader.java @@ -52,6 +52,7 @@ import java.io.PrintWriter; public class Loader<D> { int mId; OnLoadCompleteListener<D> mListener; + OnLoadCanceledListener<D> mOnLoadCanceledListener; Context mContext; boolean mStarted = false; boolean mAbandoned = false; @@ -100,6 +101,23 @@ public class Loader<D> { } /** + * Interface that is implemented to discover when a Loader has been canceled + * before it finished loading its data. You do not normally need to implement + * this yourself; it is used in the implementation of {@link android.app.LoaderManager} + * to find out when a Loader it is managing has been canceled so that it + * can schedule the next Loader. This interface should only be used if a + * Loader is not being used in conjunction with LoaderManager. + */ + public interface OnLoadCanceledListener<D> { + /** + * Called on the thread that created the Loader when the load is canceled. + * + * @param loader the loader that canceled the load + */ + public void onLoadCanceled(Loader<D> loader); + } + + /** * Stores away the application context associated with context. * Since Loaders can be used across multiple activities it's dangerous to * store the context directly; always use {@link #getContext()} to retrieve @@ -127,6 +145,18 @@ public class Loader<D> { } /** + * Informs the registered {@link OnLoadCanceledListener} that the load has been canceled. + * Should only be called by subclasses. + * + * Must be called from the process's main thread. + */ + public void deliverCancellation() { + if (mOnLoadCanceledListener != null) { + mOnLoadCanceledListener.onLoadCanceled(this); + } + } + + /** * @return an application context retrieved from the Context passed to the constructor. */ public Context getContext() { @@ -171,6 +201,40 @@ public class Loader<D> { } /** + * Registers a listener that will receive callbacks when a load is canceled. + * The callback will be called on the process's main thread so it's safe to + * pass the results to widgets. + * + * Must be called from the process's main thread. + * + * @param listener The listener to register. + */ + public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) { + if (mOnLoadCanceledListener != null) { + throw new IllegalStateException("There is already a listener registered"); + } + mOnLoadCanceledListener = listener; + } + + /** + * Unregisters a listener that was previously added with + * {@link #registerOnLoadCanceledListener}. + * + * Must be called from the process's main thread. + * + * @param listener The listener to unregister. + */ + public void unregisterOnLoadCanceledListener(OnLoadCanceledListener<D> listener) { + if (mOnLoadCanceledListener == null) { + throw new IllegalStateException("No listener register"); + } + if (mOnLoadCanceledListener != listener) { + throw new IllegalArgumentException("Attempting to unregister the wrong listener"); + } + mOnLoadCanceledListener = null; + } + + /** * Return whether this load has been started. That is, its {@link #startLoading()} * has been called and no calls to {@link #stopLoading()} or * {@link #reset()} have yet been made. @@ -234,6 +298,43 @@ public class Loader<D> { } /** + * Attempt to cancel the current load task. + * Must be called on the main thread of the process. + * + * <p>Cancellation is not an immediate operation, since the load is performed + * in a background thread. If there is currently a load in progress, this + * method requests that the load be canceled, and notes this is the case; + * once the background thread has completed its work its remaining state + * will be cleared. If another load request comes in during this time, + * it will be held until the canceled load is complete. + * + * @return Returns <tt>false</tt> if the task could not be canceled, + * typically because it has already completed normally, or + * because {@link #startLoading()} hasn't been called; returns + * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task + * is still running and the {@link OnLoadCanceledListener} will be called + * when the task completes. + */ + public boolean cancelLoad() { + return onCancelLoad(); + } + + /** + * Subclasses must implement this to take care of requests to {@link #cancelLoad()}. + * This will always be called from the process's main thread. + * + * @return Returns <tt>false</tt> if the task could not be canceled, + * typically because it has already completed normally, or + * because {@link #startLoading()} hasn't been called; returns + * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task + * is still running and the {@link OnLoadCanceledListener} will be called + * when the task completes. + */ + protected boolean onCancelLoad() { + return false; + } + + /** * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously * loaded data set and load a new one. This simply calls through to the * implementation's {@link #onForceLoad()}. You generally should only call this diff --git a/core/java/android/content/OperationCanceledException.java b/core/java/android/content/OperationCanceledException.java index 24afcfa..d783a07 100644 --- a/core/java/android/content/OperationCanceledException.java +++ b/core/java/android/content/OperationCanceledException.java @@ -19,7 +19,7 @@ package android.content; /** * An exception type that is thrown when an operation in progress is canceled. * - * @see CancelationSignal + * @see CancellationSignal */ public class OperationCanceledException extends RuntimeException { public OperationCanceledException() { diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 6015668..e04b2f7 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -347,6 +347,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration sb.append(" (no locale)"); } switch (textLayoutDirection) { + case LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE: /* ltr not interesting */ break; case LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE: sb.append(" rtl"); break; default: sb.append(" layoutdir="); sb.append(textLayoutDirection); break; } diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index b69d9bf..0022118 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -21,6 +21,7 @@ import org.apache.commons.codec.binary.Hex; import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; +import android.content.OperationCanceledException; import android.database.sqlite.SQLiteAbortException; import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; @@ -107,6 +108,9 @@ public class DatabaseUtils { code = 9; } else if (e instanceof OperationApplicationException) { code = 10; + } else if (e instanceof OperationCanceledException) { + code = 11; + logException = false; } else { reply.writeException(e); Log.e(TAG, "Writing exception to parcel", e); @@ -178,6 +182,8 @@ public class DatabaseUtils { throw new SQLiteDiskIOException(msg); case 9: throw new SQLiteException(msg); + case 11: + throw new OperationCanceledException(msg); default: reply.readException(code, msg); } diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 710bd53..b5cef81 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -19,7 +19,7 @@ package android.database.sqlite; import dalvik.system.BlockGuard; import dalvik.system.CloseGuard; -import android.content.CancelationSignal; +import android.content.CancellationSignal; import android.content.OperationCanceledException; import android.database.Cursor; import android.database.CursorWindow; @@ -84,7 +84,7 @@ import java.util.regex.Pattern; * * @hide */ -public final class SQLiteConnection implements CancelationSignal.OnCancelListener { +public final class SQLiteConnection implements CancellationSignal.OnCancelListener { private static final String TAG = "SQLiteConnection"; private static final boolean DEBUG = false; @@ -110,11 +110,11 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene private boolean mOnlyAllowReadOnlyOperations; - // The number of times attachCancelationSignal has been called. + // The number of times attachCancellationSignal has been called. // Because SQLite statement execution can be re-entrant, we keep track of how many - // times we have attempted to attach a cancelation signal to the connection so that + // times we have attempted to attach a cancellation signal to the connection so that // we can ensure that we detach the signal at the right time. - private int mCancelationSignalAttachCount; + private int mCancellationSignalAttachCount; private static native int nativeOpen(String path, int openFlags, String label, boolean enableTrace, boolean enableProfile); @@ -355,14 +355,14 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * * @param sql The SQL statement to execute. * @param bindArgs The arguments to bind, or null if none. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * * @throws SQLiteException if an error occurs, such as a syntax error * or invalid number of bind arguments. * @throws OperationCanceledException if the operation was canceled. */ public void execute(String sql, Object[] bindArgs, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } @@ -374,11 +374,11 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); applyBlockGuardPolicy(statement); - attachCancelationSignal(cancelationSignal); + attachCancellationSignal(cancellationSignal); try { nativeExecute(mConnectionPtr, statement.mStatementPtr); } finally { - detachCancelationSignal(cancelationSignal); + detachCancellationSignal(cancellationSignal); } } finally { releasePreparedStatement(statement); @@ -396,7 +396,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * * @param sql The SQL statement to execute. * @param bindArgs The arguments to bind, or null if none. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The value of the first column in the first row of the result set * as a <code>long</code>, or zero if none. * @@ -405,7 +405,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * @throws OperationCanceledException if the operation was canceled. */ public long executeForLong(String sql, Object[] bindArgs, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } @@ -417,11 +417,11 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); applyBlockGuardPolicy(statement); - attachCancelationSignal(cancelationSignal); + attachCancellationSignal(cancellationSignal); try { return nativeExecuteForLong(mConnectionPtr, statement.mStatementPtr); } finally { - detachCancelationSignal(cancelationSignal); + detachCancellationSignal(cancellationSignal); } } finally { releasePreparedStatement(statement); @@ -439,7 +439,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * * @param sql The SQL statement to execute. * @param bindArgs The arguments to bind, or null if none. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The value of the first column in the first row of the result set * as a <code>String</code>, or null if none. * @@ -448,7 +448,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * @throws OperationCanceledException if the operation was canceled. */ public String executeForString(String sql, Object[] bindArgs, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } @@ -460,11 +460,11 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); applyBlockGuardPolicy(statement); - attachCancelationSignal(cancelationSignal); + attachCancellationSignal(cancellationSignal); try { return nativeExecuteForString(mConnectionPtr, statement.mStatementPtr); } finally { - detachCancelationSignal(cancelationSignal); + detachCancellationSignal(cancellationSignal); } } finally { releasePreparedStatement(statement); @@ -483,7 +483,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * * @param sql The SQL statement to execute. * @param bindArgs The arguments to bind, or null if none. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The file descriptor for a shared memory region that contains * the value of the first column in the first row of the result set as a BLOB, * or null if none. @@ -493,7 +493,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * @throws OperationCanceledException if the operation was canceled. */ public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } @@ -506,13 +506,13 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); applyBlockGuardPolicy(statement); - attachCancelationSignal(cancelationSignal); + attachCancellationSignal(cancellationSignal); try { int fd = nativeExecuteForBlobFileDescriptor( mConnectionPtr, statement.mStatementPtr); return fd >= 0 ? ParcelFileDescriptor.adoptFd(fd) : null; } finally { - detachCancelationSignal(cancelationSignal); + detachCancellationSignal(cancellationSignal); } } finally { releasePreparedStatement(statement); @@ -531,7 +531,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * * @param sql The SQL statement to execute. * @param bindArgs The arguments to bind, or null if none. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The number of rows that were changed. * * @throws SQLiteException if an error occurs, such as a syntax error @@ -539,7 +539,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * @throws OperationCanceledException if the operation was canceled. */ public int executeForChangedRowCount(String sql, Object[] bindArgs, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } @@ -552,12 +552,12 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); applyBlockGuardPolicy(statement); - attachCancelationSignal(cancelationSignal); + attachCancellationSignal(cancellationSignal); try { return nativeExecuteForChangedRowCount( mConnectionPtr, statement.mStatementPtr); } finally { - detachCancelationSignal(cancelationSignal); + detachCancellationSignal(cancellationSignal); } } finally { releasePreparedStatement(statement); @@ -576,7 +576,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * * @param sql The SQL statement to execute. * @param bindArgs The arguments to bind, or null if none. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The row id of the last row that was inserted, or 0 if none. * * @throws SQLiteException if an error occurs, such as a syntax error @@ -584,7 +584,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * @throws OperationCanceledException if the operation was canceled. */ public long executeForLastInsertedRowId(String sql, Object[] bindArgs, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } @@ -597,12 +597,12 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); applyBlockGuardPolicy(statement); - attachCancelationSignal(cancelationSignal); + attachCancellationSignal(cancellationSignal); try { return nativeExecuteForLastInsertedRowId( mConnectionPtr, statement.mStatementPtr); } finally { - detachCancelationSignal(cancelationSignal); + detachCancellationSignal(cancellationSignal); } } finally { releasePreparedStatement(statement); @@ -629,7 +629,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene * so that it does. Must be greater than or equal to <code>startPos</code>. * @param countAllRows True to count all rows that the query would return * regagless of whether they fit in the window. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The number of rows that were counted during query execution. Might * not be all rows in the result set unless <code>countAllRows</code> is true. * @@ -639,7 +639,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene */ public int executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } @@ -658,7 +658,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); applyBlockGuardPolicy(statement); - attachCancelationSignal(cancelationSignal); + attachCancellationSignal(cancellationSignal); try { final long result = nativeExecuteForCursorWindow( mConnectionPtr, statement.mStatementPtr, window.mWindowPtr, @@ -669,7 +669,7 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene window.setStartPosition(actualPos); return countedRows; } finally { - detachCancelationSignal(cancelationSignal); + detachCancellationSignal(cancellationSignal); } } finally { releasePreparedStatement(statement); @@ -751,40 +751,40 @@ public final class SQLiteConnection implements CancelationSignal.OnCancelListene recyclePreparedStatement(statement); } - private void attachCancelationSignal(CancelationSignal cancelationSignal) { - if (cancelationSignal != null) { - cancelationSignal.throwIfCanceled(); + private void attachCancellationSignal(CancellationSignal cancellationSignal) { + if (cancellationSignal != null) { + cancellationSignal.throwIfCanceled(); - mCancelationSignalAttachCount += 1; - if (mCancelationSignalAttachCount == 1) { - // Reset cancelation flag before executing the statement. + mCancellationSignalAttachCount += 1; + if (mCancellationSignalAttachCount == 1) { + // Reset cancellation flag before executing the statement. nativeResetCancel(mConnectionPtr, true /*cancelable*/); // After this point, onCancel() may be called concurrently. - cancelationSignal.setOnCancelListener(this); + cancellationSignal.setOnCancelListener(this); } } } - private void detachCancelationSignal(CancelationSignal cancelationSignal) { - if (cancelationSignal != null) { - assert mCancelationSignalAttachCount > 0; + private void detachCancellationSignal(CancellationSignal cancellationSignal) { + if (cancellationSignal != null) { + assert mCancellationSignalAttachCount > 0; - mCancelationSignalAttachCount -= 1; - if (mCancelationSignalAttachCount == 0) { + mCancellationSignalAttachCount -= 1; + if (mCancellationSignalAttachCount == 0) { // After this point, onCancel() cannot be called concurrently. - cancelationSignal.setOnCancelListener(null); + cancellationSignal.setOnCancelListener(null); - // Reset cancelation flag after executing the statement. + // Reset cancellation flag after executing the statement. nativeResetCancel(mConnectionPtr, false /*cancelable*/); } } } - // CancelationSignal.OnCancelationListener callback. + // CancellationSignal.OnCancelListener callback. // This method may be called on a different thread than the executing statement. - // However, it will only be called between calls to attachCancelationSignal and - // detachCancelationSignal, while a statement is executing. We can safely assume + // However, it will only be called between calls to attachCancellationSignal and + // detachCancellationSignal, while a statement is executing. We can safely assume // that the SQLite connection is still alive. @Override public void onCancel() { diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index d335738..236948e 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -18,7 +18,7 @@ package android.database.sqlite; import dalvik.system.CloseGuard; -import android.content.CancelationSignal; +import android.content.CancellationSignal; import android.content.OperationCanceledException; import android.database.sqlite.SQLiteDebug.DbStats; import android.os.SystemClock; @@ -284,7 +284,7 @@ public final class SQLiteConnectionPool implements Closeable { * @param sql If not null, try to find a connection that already has * the specified SQL statement in its prepared statement cache. * @param connectionFlags The connection request flags. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The connection that was acquired, never null. * * @throws IllegalStateException if the pool has been closed. @@ -292,8 +292,8 @@ public final class SQLiteConnectionPool implements Closeable { * @throws OperationCanceledException if the operation was canceled. */ public SQLiteConnection acquireConnection(String sql, int connectionFlags, - CancelationSignal cancelationSignal) { - return waitForConnection(sql, connectionFlags, cancelationSignal); + CancellationSignal cancellationSignal) { + return waitForConnection(sql, connectionFlags, cancellationSignal); } /** @@ -503,7 +503,7 @@ public final class SQLiteConnectionPool implements Closeable { // Might throw. private SQLiteConnection waitForConnection(String sql, int connectionFlags, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { final boolean wantPrimaryConnection = (connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0; @@ -512,8 +512,8 @@ public final class SQLiteConnectionPool implements Closeable { throwIfClosedLocked(); // Abort if canceled. - if (cancelationSignal != null) { - cancelationSignal.throwIfCanceled(); + if (cancellationSignal != null) { + cancellationSignal.throwIfCanceled(); } // Try to acquire a connection. @@ -550,9 +550,9 @@ public final class SQLiteConnectionPool implements Closeable { mConnectionWaiterQueue = waiter; } - if (cancelationSignal != null) { + if (cancellationSignal != null) { final int nonce = waiter.mNonce; - cancelationSignal.setOnCancelListener(new CancelationSignal.OnCancelListener() { + cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() { @Override public void onCancel() { synchronized (mLock) { @@ -588,8 +588,8 @@ public final class SQLiteConnectionPool implements Closeable { final SQLiteConnection connection = waiter.mAssignedConnection; final RuntimeException ex = waiter.mException; if (connection != null || ex != null) { - if (cancelationSignal != null) { - cancelationSignal.setOnCancelListener(null); + if (cancellationSignal != null) { + cancellationSignal.setOnCancelListener(null); } recycleConnectionWaiterLocked(waiter); if (connection != null) { diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 7db7bfb..505f83e 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.content.CancelationSignal; +import android.content.CancellationSignal; import android.content.ContentValues; import android.content.OperationCanceledException; import android.content.res.Resources; @@ -967,7 +967,7 @@ public class SQLiteDatabase extends SQLiteClosable { * default sort order, which may be unordered. * @param limit Limits the number of rows returned by the query, * formatted as LIMIT clause. Passing null denotes no LIMIT clause. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. * @return A {@link Cursor} object, which is positioned before the first entry. Note that @@ -976,9 +976,9 @@ public class SQLiteDatabase extends SQLiteClosable { */ public Cursor query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, - String having, String orderBy, String limit, CancelationSignal cancelationSignal) { + String having, String orderBy, String limit, CancellationSignal cancellationSignal) { return queryWithFactory(null, distinct, table, columns, selection, selectionArgs, - groupBy, having, orderBy, limit, cancelationSignal); + groupBy, having, orderBy, limit, cancellationSignal); } /** @@ -1049,7 +1049,7 @@ public class SQLiteDatabase extends SQLiteClosable { * default sort order, which may be unordered. * @param limit Limits the number of rows returned by the query, * formatted as LIMIT clause. Passing null denotes no LIMIT clause. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. * @return A {@link Cursor} object, which is positioned before the first entry. Note that @@ -1059,13 +1059,13 @@ public class SQLiteDatabase extends SQLiteClosable { public Cursor queryWithFactory(CursorFactory cursorFactory, boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, - String having, String orderBy, String limit, CancelationSignal cancelationSignal) { + String having, String orderBy, String limit, CancellationSignal cancellationSignal) { throwIfNotOpen(); // fail fast String sql = SQLiteQueryBuilder.buildQueryString( distinct, table, columns, selection, groupBy, having, orderBy, limit); return rawQueryWithFactory(cursorFactory, sql, selectionArgs, - findEditTable(table), cancelationSignal); + findEditTable(table), cancellationSignal); } /** @@ -1163,15 +1163,15 @@ public class SQLiteDatabase extends SQLiteClosable { * @param selectionArgs You may include ?s in where clause in the query, * which will be replaced by the values from selectionArgs. The * values will be bound as Strings. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. * @return A {@link Cursor} object, which is positioned before the first entry. Note that * {@link Cursor}s are not synchronized, see the documentation for more details. */ public Cursor rawQuery(String sql, String[] selectionArgs, - CancelationSignal cancelationSignal) { - return rawQueryWithFactory(null, sql, selectionArgs, null, cancelationSignal); + CancellationSignal cancellationSignal) { + return rawQueryWithFactory(null, sql, selectionArgs, null, cancellationSignal); } /** @@ -1201,7 +1201,7 @@ public class SQLiteDatabase extends SQLiteClosable { * which will be replaced by the values from selectionArgs. The * values will be bound as Strings. * @param editTable the name of the first table, which is editable - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. * @return A {@link Cursor} object, which is positioned before the first entry. Note that @@ -1209,11 +1209,11 @@ public class SQLiteDatabase extends SQLiteClosable { */ public Cursor rawQueryWithFactory( CursorFactory cursorFactory, String sql, String[] selectionArgs, - String editTable, CancelationSignal cancelationSignal) { + String editTable, CancellationSignal cancellationSignal) { throwIfNotOpen(); // fail fast SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable, - cancelationSignal); + cancellationSignal); return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory, selectionArgs); } diff --git a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java index c490dc6..3375e74 100644 --- a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java +++ b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.content.CancelationSignal; +import android.content.CancellationSignal; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase.CursorFactory; @@ -29,19 +29,19 @@ public class SQLiteDirectCursorDriver implements SQLiteCursorDriver { private final SQLiteDatabase mDatabase; private final String mEditTable; private final String mSql; - private final CancelationSignal mCancelationSignal; + private final CancellationSignal mCancellationSignal; private SQLiteQuery mQuery; public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { mDatabase = db; mEditTable = editTable; mSql = sql; - mCancelationSignal = cancelationSignal; + mCancellationSignal = cancellationSignal; } public Cursor query(CursorFactory factory, String[] selectionArgs) { - final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancelationSignal); + final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal); final Cursor cursor; try { query.bindAllArgsAsStrings(selectionArgs); diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java index f3da2a6..9f0edfb 100644 --- a/core/java/android/database/sqlite/SQLiteProgram.java +++ b/core/java/android/database/sqlite/SQLiteProgram.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.content.CancelationSignal; +import android.content.CancellationSignal; import android.database.DatabaseUtils; import java.util.Arrays; @@ -38,7 +38,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { private final Object[] mBindArgs; SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs, - CancelationSignal cancelationSignalForPrepare) { + CancellationSignal cancellationSignalForPrepare) { mDatabase = db; mSql = sql.trim(); @@ -57,7 +57,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { SQLiteStatementInfo info = new SQLiteStatementInfo(); db.getThreadSession().prepare(mSql, db.getThreadDefaultConnectionFlags(assumeReadOnly), - cancelationSignalForPrepare, info); + cancellationSignalForPrepare, info); mReadOnly = info.readOnly; mColumnNames = info.columnNames; mNumParameters = info.numParameters; diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java index df2e260..30e77b5 100644 --- a/core/java/android/database/sqlite/SQLiteQuery.java +++ b/core/java/android/database/sqlite/SQLiteQuery.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.content.CancelationSignal; +import android.content.CancellationSignal; import android.content.OperationCanceledException; import android.database.CursorWindow; import android.util.Log; @@ -31,12 +31,12 @@ import android.util.Log; public final class SQLiteQuery extends SQLiteProgram { private static final String TAG = "SQLiteQuery"; - private final CancelationSignal mCancelationSignal; + private final CancellationSignal mCancellationSignal; - SQLiteQuery(SQLiteDatabase db, String query, CancelationSignal cancelationSignal) { - super(db, query, null, cancelationSignal); + SQLiteQuery(SQLiteDatabase db, String query, CancellationSignal cancellationSignal) { + super(db, query, null, cancellationSignal); - mCancelationSignal = cancelationSignal; + mCancellationSignal = cancellationSignal; } /** @@ -61,7 +61,7 @@ public final class SQLiteQuery extends SQLiteProgram { try { int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(), window, startPos, requiredPos, countAllRows, getConnectionFlags(), - mCancelationSignal); + mCancellationSignal); return numRows; } catch (SQLiteDatabaseCorruptException ex) { onCorruption(); diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index 89469cb..6f84b5e 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.content.CancelationSignal; +import android.content.CancellationSignal; import android.content.OperationCanceledException; import android.database.Cursor; import android.database.DatabaseUtils; @@ -292,7 +292,7 @@ public class SQLiteQueryBuilder String selection, String[] selectionArgs, String groupBy, String having, String sortOrder) { return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder, - null /* limit */, null /* cancelationSignal */); + null /* limit */, null /* cancellationSignal */); } /** @@ -362,7 +362,7 @@ public class SQLiteQueryBuilder * will use the default sort order, which may be unordered. * @param limit Limits the number of rows returned by the query, * formatted as LIMIT clause. Passing null denotes no LIMIT clause. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. * @return a cursor over the result set @@ -371,7 +371,7 @@ public class SQLiteQueryBuilder */ public Cursor query(SQLiteDatabase db, String[] projectionIn, String selection, String[] selectionArgs, String groupBy, - String having, String sortOrder, String limit, CancelationSignal cancelationSignal) { + String having, String sortOrder, String limit, CancellationSignal cancellationSignal) { if (mTables == null) { return null; } @@ -387,7 +387,7 @@ public class SQLiteQueryBuilder String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy, having, sortOrder, limit); validateQuerySql(db, sqlForValidation, - cancelationSignal); // will throw if query is invalid + cancellationSignal); // will throw if query is invalid } String sql = buildQuery( @@ -400,7 +400,7 @@ public class SQLiteQueryBuilder return db.rawQueryWithFactory( mFactory, sql, selectionArgs, SQLiteDatabase.findEditTable(mTables), - cancelationSignal); // will throw if query is invalid + cancellationSignal); // will throw if query is invalid } /** @@ -408,9 +408,9 @@ public class SQLiteQueryBuilder * If the SQL statement is not valid, this method will throw a {@link SQLiteException}. */ private void validateQuerySql(SQLiteDatabase db, String sql, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { db.getThreadSession().prepare(sql, - db.getThreadDefaultConnectionFlags(true /*readOnly*/), cancelationSignal, null); + db.getThreadDefaultConnectionFlags(true /*readOnly*/), cancellationSignal, null); } /** diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java index b5a3e31..43efb03 100644 --- a/core/java/android/database/sqlite/SQLiteSession.java +++ b/core/java/android/database/sqlite/SQLiteSession.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.content.CancelationSignal; +import android.content.CancellationSignal; import android.content.OperationCanceledException; import android.database.CursorWindow; import android.database.DatabaseUtils; @@ -280,7 +280,7 @@ public final class SQLiteSession { * @param transactionListener The transaction listener, or null if none. * @param connectionFlags The connection flags to use if a connection must be * acquired by this operation. Refer to {@link SQLiteConnectionPool}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * * @throws IllegalStateException if {@link #setTransactionSuccessful} has already been * called for the current transaction. @@ -293,21 +293,21 @@ public final class SQLiteSession { */ public void beginTransaction(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { throwIfTransactionMarkedSuccessful(); beginTransactionUnchecked(transactionMode, transactionListener, connectionFlags, - cancelationSignal); + cancellationSignal); } private void beginTransactionUnchecked(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, - CancelationSignal cancelationSignal) { - if (cancelationSignal != null) { - cancelationSignal.throwIfCanceled(); + CancellationSignal cancellationSignal) { + if (cancellationSignal != null) { + cancellationSignal.throwIfCanceled(); } if (mTransactionStack == null) { - acquireConnection(null, connectionFlags, cancelationSignal); // might throw + acquireConnection(null, connectionFlags, cancellationSignal); // might throw } try { // Set up the transaction such that we can back out safely @@ -317,14 +317,14 @@ public final class SQLiteSession { switch (transactionMode) { case TRANSACTION_MODE_IMMEDIATE: mConnection.execute("BEGIN IMMEDIATE;", null, - cancelationSignal); // might throw + cancellationSignal); // might throw break; case TRANSACTION_MODE_EXCLUSIVE: mConnection.execute("BEGIN EXCLUSIVE;", null, - cancelationSignal); // might throw + cancellationSignal); // might throw break; default: - mConnection.execute("BEGIN;", null, cancelationSignal); // might throw + mConnection.execute("BEGIN;", null, cancellationSignal); // might throw break; } } @@ -335,7 +335,7 @@ public final class SQLiteSession { transactionListener.onBegin(); // might throw } catch (RuntimeException ex) { if (mTransactionStack == null) { - mConnection.execute("ROLLBACK;", null, cancelationSignal); // might throw + mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw } throw ex; } @@ -384,7 +384,7 @@ public final class SQLiteSession { * This method must be called exactly once for each call to {@link #beginTransaction}. * </p> * - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * * @throws IllegalStateException if there is no current transaction. * @throws SQLiteException if an error occurs. @@ -394,16 +394,16 @@ public final class SQLiteSession { * @see #setTransactionSuccessful * @see #yieldTransaction */ - public void endTransaction(CancelationSignal cancelationSignal) { + public void endTransaction(CancellationSignal cancellationSignal) { throwIfNoTransaction(); assert mConnection != null; - endTransactionUnchecked(cancelationSignal); + endTransactionUnchecked(cancellationSignal); } - private void endTransactionUnchecked(CancelationSignal cancelationSignal) { - if (cancelationSignal != null) { - cancelationSignal.throwIfCanceled(); + private void endTransactionUnchecked(CancellationSignal cancellationSignal) { + if (cancellationSignal != null) { + cancellationSignal.throwIfCanceled(); } final Transaction top = mTransactionStack; @@ -434,9 +434,9 @@ public final class SQLiteSession { } else { try { if (successful) { - mConnection.execute("COMMIT;", null, cancelationSignal); // might throw + mConnection.execute("COMMIT;", null, cancellationSignal); // might throw } else { - mConnection.execute("ROLLBACK;", null, cancelationSignal); // might throw + mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw } } finally { releaseConnection(); // might throw @@ -487,7 +487,7 @@ public final class SQLiteSession { * @param throwIfUnsafe If true, then instead of returning false when no * transaction is in progress, a nested transaction is in progress, or when * the transaction has already been marked successful, throws {@link IllegalStateException}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return True if the transaction was actually yielded. * * @throws IllegalStateException if <code>throwIfNested</code> is true and @@ -500,7 +500,7 @@ public final class SQLiteSession { * @see #endTransaction */ public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (throwIfUnsafe) { throwIfNoTransaction(); throwIfTransactionMarkedSuccessful(); @@ -518,13 +518,13 @@ public final class SQLiteSession { } return yieldTransactionUnchecked(sleepAfterYieldDelayMillis, - cancelationSignal); // might throw + cancellationSignal); // might throw } private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis, - CancelationSignal cancelationSignal) { - if (cancelationSignal != null) { - cancelationSignal.throwIfCanceled(); + CancellationSignal cancellationSignal) { + if (cancellationSignal != null) { + cancellationSignal.throwIfCanceled(); } if (!mConnectionPool.shouldYieldConnection(mConnection, mConnectionFlags)) { @@ -534,7 +534,7 @@ public final class SQLiteSession { final int transactionMode = mTransactionStack.mMode; final SQLiteTransactionListener listener = mTransactionStack.mListener; final int connectionFlags = mConnectionFlags; - endTransactionUnchecked(cancelationSignal); // might throw + endTransactionUnchecked(cancellationSignal); // might throw if (sleepAfterYieldDelayMillis > 0) { try { @@ -545,7 +545,7 @@ public final class SQLiteSession { } beginTransactionUnchecked(transactionMode, listener, connectionFlags, - cancelationSignal); // might throw + cancellationSignal); // might throw return true; } @@ -566,24 +566,24 @@ public final class SQLiteSession { * @param sql The SQL statement to prepare. * @param connectionFlags The connection flags to use if a connection must be * acquired by this operation. Refer to {@link SQLiteConnectionPool}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @param outStatementInfo The {@link SQLiteStatementInfo} object to populate * with information about the statement, or null if none. * * @throws SQLiteException if an error occurs, such as a syntax error. * @throws OperationCanceledException if the operation was canceled. */ - public void prepare(String sql, int connectionFlags, CancelationSignal cancelationSignal, + public void prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal, SQLiteStatementInfo outStatementInfo) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } - if (cancelationSignal != null) { - cancelationSignal.throwIfCanceled(); + if (cancellationSignal != null) { + cancellationSignal.throwIfCanceled(); } - acquireConnection(sql, connectionFlags, cancelationSignal); // might throw + acquireConnection(sql, connectionFlags, cancellationSignal); // might throw try { mConnection.prepare(sql, outStatementInfo); // might throw } finally { @@ -598,25 +598,25 @@ public final class SQLiteSession { * @param bindArgs The arguments to bind, or null if none. * @param connectionFlags The connection flags to use if a connection must be * acquired by this operation. Refer to {@link SQLiteConnectionPool}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * * @throws SQLiteException if an error occurs, such as a syntax error * or invalid number of bind arguments. * @throws OperationCanceledException if the operation was canceled. */ public void execute(String sql, Object[] bindArgs, int connectionFlags, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } - if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) { + if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { return; } - acquireConnection(sql, connectionFlags, cancelationSignal); // might throw + acquireConnection(sql, connectionFlags, cancellationSignal); // might throw try { - mConnection.execute(sql, bindArgs, cancelationSignal); // might throw + mConnection.execute(sql, bindArgs, cancellationSignal); // might throw } finally { releaseConnection(); // might throw } @@ -629,7 +629,7 @@ public final class SQLiteSession { * @param bindArgs The arguments to bind, or null if none. * @param connectionFlags The connection flags to use if a connection must be * acquired by this operation. Refer to {@link SQLiteConnectionPool}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The value of the first column in the first row of the result set * as a <code>long</code>, or zero if none. * @@ -638,18 +638,18 @@ public final class SQLiteSession { * @throws OperationCanceledException if the operation was canceled. */ public long executeForLong(String sql, Object[] bindArgs, int connectionFlags, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } - if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) { + if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { return 0; } - acquireConnection(sql, connectionFlags, cancelationSignal); // might throw + acquireConnection(sql, connectionFlags, cancellationSignal); // might throw try { - return mConnection.executeForLong(sql, bindArgs, cancelationSignal); // might throw + return mConnection.executeForLong(sql, bindArgs, cancellationSignal); // might throw } finally { releaseConnection(); // might throw } @@ -662,7 +662,7 @@ public final class SQLiteSession { * @param bindArgs The arguments to bind, or null if none. * @param connectionFlags The connection flags to use if a connection must be * acquired by this operation. Refer to {@link SQLiteConnectionPool}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The value of the first column in the first row of the result set * as a <code>String</code>, or null if none. * @@ -671,18 +671,18 @@ public final class SQLiteSession { * @throws OperationCanceledException if the operation was canceled. */ public String executeForString(String sql, Object[] bindArgs, int connectionFlags, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } - if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) { + if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { return null; } - acquireConnection(sql, connectionFlags, cancelationSignal); // might throw + acquireConnection(sql, connectionFlags, cancellationSignal); // might throw try { - return mConnection.executeForString(sql, bindArgs, cancelationSignal); // might throw + return mConnection.executeForString(sql, bindArgs, cancellationSignal); // might throw } finally { releaseConnection(); // might throw } @@ -696,7 +696,7 @@ public final class SQLiteSession { * @param bindArgs The arguments to bind, or null if none. * @param connectionFlags The connection flags to use if a connection must be * acquired by this operation. Refer to {@link SQLiteConnectionPool}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The file descriptor for a shared memory region that contains * the value of the first column in the first row of the result set as a BLOB, * or null if none. @@ -706,19 +706,19 @@ public final class SQLiteSession { * @throws OperationCanceledException if the operation was canceled. */ public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs, - int connectionFlags, CancelationSignal cancelationSignal) { + int connectionFlags, CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } - if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) { + if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { return null; } - acquireConnection(sql, connectionFlags, cancelationSignal); // might throw + acquireConnection(sql, connectionFlags, cancellationSignal); // might throw try { return mConnection.executeForBlobFileDescriptor(sql, bindArgs, - cancelationSignal); // might throw + cancellationSignal); // might throw } finally { releaseConnection(); // might throw } @@ -732,7 +732,7 @@ public final class SQLiteSession { * @param bindArgs The arguments to bind, or null if none. * @param connectionFlags The connection flags to use if a connection must be * acquired by this operation. Refer to {@link SQLiteConnectionPool}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The number of rows that were changed. * * @throws SQLiteException if an error occurs, such as a syntax error @@ -740,19 +740,19 @@ public final class SQLiteSession { * @throws OperationCanceledException if the operation was canceled. */ public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } - if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) { + if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { return 0; } - acquireConnection(sql, connectionFlags, cancelationSignal); // might throw + acquireConnection(sql, connectionFlags, cancellationSignal); // might throw try { return mConnection.executeForChangedRowCount(sql, bindArgs, - cancelationSignal); // might throw + cancellationSignal); // might throw } finally { releaseConnection(); // might throw } @@ -766,7 +766,7 @@ public final class SQLiteSession { * @param bindArgs The arguments to bind, or null if none. * @param connectionFlags The connection flags to use if a connection must be * acquired by this operation. Refer to {@link SQLiteConnectionPool}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The row id of the last row that was inserted, or 0 if none. * * @throws SQLiteException if an error occurs, such as a syntax error @@ -774,19 +774,19 @@ public final class SQLiteSession { * @throws OperationCanceledException if the operation was canceled. */ public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } - if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) { + if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { return 0; } - acquireConnection(sql, connectionFlags, cancelationSignal); // might throw + acquireConnection(sql, connectionFlags, cancellationSignal); // might throw try { return mConnection.executeForLastInsertedRowId(sql, bindArgs, - cancelationSignal); // might throw + cancellationSignal); // might throw } finally { releaseConnection(); // might throw } @@ -808,7 +808,7 @@ public final class SQLiteSession { * regagless of whether they fit in the window. * @param connectionFlags The connection flags to use if a connection must be * acquired by this operation. Refer to {@link SQLiteConnectionPool}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return The number of rows that were counted during query execution. Might * not be all rows in the result set unless <code>countAllRows</code> is true. * @@ -818,7 +818,7 @@ public final class SQLiteSession { */ public int executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, - int connectionFlags, CancelationSignal cancelationSignal) { + int connectionFlags, CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } @@ -826,16 +826,16 @@ public final class SQLiteSession { throw new IllegalArgumentException("window must not be null."); } - if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) { + if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) { window.clear(); return 0; } - acquireConnection(sql, connectionFlags, cancelationSignal); // might throw + acquireConnection(sql, connectionFlags, cancellationSignal); // might throw try { return mConnection.executeForCursorWindow(sql, bindArgs, window, startPos, requiredPos, countAllRows, - cancelationSignal); // might throw + cancellationSignal); // might throw } finally { releaseConnection(); // might throw } @@ -854,7 +854,7 @@ public final class SQLiteSession { * @param bindArgs The arguments to bind, or null if none. * @param connectionFlags The connection flags to use if a connection must be * acquired by this operation. Refer to {@link SQLiteConnectionPool}. - * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * @return True if the statement was of a special form that was handled here, * false otherwise. * @@ -863,36 +863,36 @@ public final class SQLiteSession { * @throws OperationCanceledException if the operation was canceled. */ private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags, - CancelationSignal cancelationSignal) { - if (cancelationSignal != null) { - cancelationSignal.throwIfCanceled(); + CancellationSignal cancellationSignal) { + if (cancellationSignal != null) { + cancellationSignal.throwIfCanceled(); } final int type = DatabaseUtils.getSqlStatementType(sql); switch (type) { case DatabaseUtils.STATEMENT_BEGIN: beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags, - cancelationSignal); + cancellationSignal); return true; case DatabaseUtils.STATEMENT_COMMIT: setTransactionSuccessful(); - endTransaction(cancelationSignal); + endTransaction(cancellationSignal); return true; case DatabaseUtils.STATEMENT_ABORT: - endTransaction(cancelationSignal); + endTransaction(cancellationSignal); return true; } return false; } private void acquireConnection(String sql, int connectionFlags, - CancelationSignal cancelationSignal) { + CancellationSignal cancellationSignal) { if (mConnection == null) { assert mConnectionUseCount == 0; mConnection = mConnectionPool.acquireConnection(sql, connectionFlags, - cancelationSignal); // might throw + cancellationSignal); // might throw mConnectionFlags = connectionFlags; } mConnectionUseCount += 1; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index a569317..2eef8f4 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -142,8 +142,19 @@ public class ConnectivityManager { * If an application uses the network in the background, it should listen * for this broadcast and stop using the background data if the value is * {@code false}. + * <p> + * + * @deprecated As of {@link VERSION_CODES#ICE_CREAM_SANDWICH}, availability + * of background data depends on several combined factors, and + * this broadcast is no longer sent. Instead, when background + * data is unavailable, {@link #getActiveNetworkInfo()} will now + * appear disconnected. During first boot after a platform + * upgrade, this broadcast will be sent once if + * {@link #getBackgroundDataSetting()} was {@code false} before + * the upgrade. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @Deprecated public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 633c38e..442535a 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -43,7 +43,7 @@ interface INetworkPolicyManager { NetworkPolicy[] getNetworkPolicies(); /** Snooze limit on policy matching given template. */ - void snoozePolicy(in NetworkTemplate template); + void snoozeLimit(in NetworkTemplate template); /** Control if background data is restricted system-wide. */ void setRestrictBackground(boolean restrictBackground); diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index d9ea700..04cf1a3 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -38,18 +38,25 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { public int cycleDay; public long warningBytes; public long limitBytes; - public long lastSnooze; + public long lastWarningSnooze; + public long lastLimitSnooze; public boolean metered; private static final long DEFAULT_MTU = 1500; - public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes, long limitBytes, - long lastSnooze, boolean metered) { + public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes, + long limitBytes, boolean metered) { + this(template, cycleDay, warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, metered); + } + + public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes, + long limitBytes, long lastWarningSnooze, long lastLimitSnooze, boolean metered) { this.template = checkNotNull(template, "missing NetworkTemplate"); this.cycleDay = cycleDay; this.warningBytes = warningBytes; this.limitBytes = limitBytes; - this.lastSnooze = lastSnooze; + this.lastWarningSnooze = lastWarningSnooze; + this.lastLimitSnooze = lastLimitSnooze; this.metered = metered; } @@ -58,7 +65,8 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { cycleDay = in.readInt(); warningBytes = in.readLong(); limitBytes = in.readLong(); - lastSnooze = in.readLong(); + lastWarningSnooze = in.readLong(); + lastLimitSnooze = in.readLong(); metered = in.readInt() != 0; } @@ -68,7 +76,8 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { dest.writeInt(cycleDay); dest.writeLong(warningBytes); dest.writeLong(limitBytes); - dest.writeLong(lastSnooze); + dest.writeLong(lastWarningSnooze); + dest.writeLong(lastLimitSnooze); dest.writeInt(metered ? 1 : 0); } @@ -78,6 +87,13 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { } /** + * Test if given measurement is over {@link #warningBytes}. + */ + public boolean isOverWarning(long totalBytes) { + return warningBytes != WARNING_DISABLED && totalBytes >= warningBytes; + } + + /** * Test if given measurement is near enough to {@link #limitBytes} to be * considered over-limit. */ @@ -88,6 +104,14 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { return limitBytes != LIMIT_DISABLED && totalBytes >= limitBytes; } + /** + * Clear any existing snooze values, setting to {@link #SNOOZE_NEVER}. + */ + public void clearSnooze() { + lastWarningSnooze = SNOOZE_NEVER; + lastLimitSnooze = SNOOZE_NEVER; + } + /** {@inheritDoc} */ public int compareTo(NetworkPolicy another) { if (another == null || another.limitBytes == LIMIT_DISABLED) { @@ -103,7 +127,8 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { @Override public int hashCode() { - return Objects.hashCode(template, cycleDay, warningBytes, limitBytes, lastSnooze, metered); + return Objects.hashCode(template, cycleDay, warningBytes, limitBytes, lastWarningSnooze, + lastLimitSnooze, metered); } @Override @@ -111,8 +136,10 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { if (obj instanceof NetworkPolicy) { final NetworkPolicy other = (NetworkPolicy) obj; return cycleDay == other.cycleDay && warningBytes == other.warningBytes - && limitBytes == other.limitBytes && lastSnooze == other.lastSnooze - && metered == other.metered && Objects.equal(template, other.template); + && limitBytes == other.limitBytes + && lastWarningSnooze == other.lastWarningSnooze + && lastLimitSnooze == other.lastLimitSnooze && metered == other.metered + && Objects.equal(template, other.template); } return false; } @@ -120,8 +147,9 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { @Override public String toString() { return "NetworkPolicy[" + template + "]: cycleDay=" + cycleDay + ", warningBytes=" - + warningBytes + ", limitBytes=" + limitBytes + ", lastSnooze=" + lastSnooze - + ", metered=" + metered; + + warningBytes + ", limitBytes=" + limitBytes + ", lastWarningSnooze=" + + lastWarningSnooze + ", lastLimitSnooze=" + lastLimitSnooze + ", metered=" + + metered; } public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() { diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 5e9abb7..fd6bed7 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -135,6 +135,8 @@ import java.util.concurrent.atomic.AtomicInteger; * <p>There are a few threading rules that must be followed for this class to * work properly:</p> * <ul> + * <li>The AsyncTask class must be loaded on the UI thread. This is done + * automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li> * <li>The task instance must be created on the UI thread.</li> * <li>{@link #execute} must be invoked on the UI thread.</li> * <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute}, diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 4e01672..d11219b 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -62,6 +62,14 @@ public final class MediaStore { public static final String ACTION_MTP_SESSION_END = "android.provider.action.MTP_SESSION_END"; /** + * The method name used by the media scanner and mtp to tell the media provider to + * rescan and reclassify that have become unhidden because of renaming folders or + * removing nomedia files + * @hide + */ + public static final String UNHIDE_CALL = "unhide"; + + /** * Activity Action: Launch a music player. * The activity should be able to play, browse, or manipulate music files stored on the device. * diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f14d27e..375e5e4 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1678,6 +1678,13 @@ public final class Settings { public static final String TRANSITION_ANIMATION_SCALE = "transition_animation_scale"; /** + * Scaling factor for Animator-based animations. This affects both the start delay and + * duration of all such animations. Setting to 0 will cause animations to end immediately. + * The default value is 1. + */ + public static final String ANIMATOR_DURATION_SCALE = "animator_duration_scale"; + + /** * Scaling factor for normal window animations. Setting to 0 will disable window * animations. * @hide @@ -2475,6 +2482,11 @@ public final class Settings { Uri.parse("content://" + AUTHORITY + "/secure"); /** + * Whether user has enabled development settings. + */ + public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled"; + + /** * Whether ADB is enabled. */ public static final String ADB_ENABLED = "adb_enabled"; diff --git a/core/java/android/view/AccessibilityNodeInfoCache.java b/core/java/android/view/AccessibilityNodeInfoCache.java new file mode 100644 index 0000000..244a491 --- /dev/null +++ b/core/java/android/view/AccessibilityNodeInfoCache.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.util.LongSparseArray; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; + +/** + * Simple cache for AccessibilityNodeInfos. The cache is mapping an + * accessibility id to an info. The cache allows storing of + * <code>null</code> values. It also tracks accessibility events + * and invalidates accordingly. + * + * @hide + */ +public class AccessibilityNodeInfoCache { + + private final boolean ENABLED = true; + + /** + * @return A new <strong>not synchronized</strong> AccessibilityNodeInfoCache. + */ + public static AccessibilityNodeInfoCache newAccessibilityNodeInfoCache() { + return new AccessibilityNodeInfoCache(); + } + + /** + * @return A new <strong>synchronized</strong> AccessibilityNodeInfoCache. + */ + public static AccessibilityNodeInfoCache newSynchronizedAccessibilityNodeInfoCache() { + return new AccessibilityNodeInfoCache() { + private final Object mLock = new Object(); + + @Override + public void clear() { + synchronized(mLock) { + super.clear(); + } + } + + @Override + public AccessibilityNodeInfo get(long accessibilityNodeId) { + synchronized(mLock) { + return super.get(accessibilityNodeId); + } + } + + @Override + public void put(long accessibilityNodeId, AccessibilityNodeInfo info) { + synchronized(mLock) { + super.put(accessibilityNodeId, info); + } + } + + @Override + public void remove(long accessibilityNodeId) { + synchronized(mLock) { + super.remove(accessibilityNodeId); + } + } + }; + } + + private final LongSparseArray<AccessibilityNodeInfo> mCacheImpl; + + private AccessibilityNodeInfoCache() { + if (ENABLED) { + mCacheImpl = new LongSparseArray<AccessibilityNodeInfo>(); + } else { + mCacheImpl = null; + } + } + + /** + * The cache keeps track of {@link AccessibilityEvent}s and invalidates + * cached nodes as appropriate. + * + * @param event An event. + */ + public void onAccessibilityEvent(AccessibilityEvent event) { + final int eventType = event.getEventType(); + switch (eventType) { + case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: + case AccessibilityEvent.TYPE_VIEW_SCROLLED: + clear(); + break; + case AccessibilityEvent.TYPE_VIEW_FOCUSED: + case AccessibilityEvent.TYPE_VIEW_SELECTED: + case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: + case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: + final long accessibilityNodeId = event.getSourceNodeId(); + remove(accessibilityNodeId); + break; + } + } + + /** + * Gets a cached {@link AccessibilityNodeInfo} given its accessibility node id. + * + * @param accessibilityNodeId The info accessibility node id. + * @return The cached {@link AccessibilityNodeInfo} or null if such not found. + */ + public AccessibilityNodeInfo get(long accessibilityNodeId) { + if (ENABLED) { + return mCacheImpl.get(accessibilityNodeId); + } else { + return null; + } + } + + /** + * Caches an {@link AccessibilityNodeInfo} given its accessibility node id. + * + * @param accessibilityNodeId The info accessibility node id. + * @param info The {@link AccessibilityNodeInfo} to cache. + */ + public void put(long accessibilityNodeId, AccessibilityNodeInfo info) { + if (ENABLED) { + mCacheImpl.put(accessibilityNodeId, info); + } + } + + /** + * Returns whether the cache contains an accessibility node id key. + * + * @param accessibilityNodeId The key for which to check. + * @return True if the key is in the cache. + */ + public boolean containsKey(long accessibilityNodeId) { + if (ENABLED) { + return (mCacheImpl.indexOfKey(accessibilityNodeId) >= 0); + } else { + return false; + } + } + + /** + * Removes a cached {@link AccessibilityNodeInfo}. + * + * @param accessibilityNodeId The info accessibility node id. + */ + public void remove(long accessibilityNodeId) { + if (ENABLED) { + mCacheImpl.remove(accessibilityNodeId); + } + } + + /** + * Clears the cache. + */ + public void clear() { + if (ENABLED) { + mCacheImpl.clear(); + } + } +} diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java index 34e7d4d..0349a2b 100644 --- a/core/java/android/view/ActionMode.java +++ b/core/java/android/view/ActionMode.java @@ -18,9 +18,15 @@ package android.view; /** - * Represents a contextual mode of the user interface. Action modes can be used for - * modal interactions with content and replace parts of the normal UI until finished. - * Examples of good action modes include selection modes, search, content editing, etc. + * Represents a contextual mode of the user interface. Action modes can be used to provide + * alternative interaction modes and replace parts of the normal UI until finished. + * Examples of good action modes include text selection and contextual actions. + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For information about how to provide contextual actions with {@code ActionMode}, + * read the <a href="{@docRoot}guide/topics/ui/menu.html#context-menu">Menus</a> + * developer guide.</p> + * </div> */ public abstract class ActionMode { private Object mTag; diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 63de128..c86ea77 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -26,7 +26,7 @@ import android.os.SystemProperties; import android.util.Log; /** - * Coodinates animations and drawing for UI on a particular thread. + * Coordinates animations and drawing for UI on a particular thread. * @hide */ public final class Choreographer extends Handler { @@ -94,8 +94,8 @@ public final class Choreographer extends Handler { } /** - * Gets the choreographer for this thread. - * Must be called on the UI thread. + * Gets the choreographer for the calling thread. Must be called from + * a thread that already has a {@link android.os.Looper} associated with it. * * @return The choreographer for this thread. * @throws IllegalStateException if the thread does not have a looper. @@ -163,6 +163,15 @@ public final class Choreographer extends Handler { } /** + * Return true if {@link #scheduleAnimation()} has been called but + * {@link OnAnimateListener#onAnimate() OnAnimateListener.onAnimate()} has + * not yet been called. + */ + public boolean isAnimationScheduled() { + return mAnimationScheduled; + } + + /** * Schedules drawing to occur on the next frame synchronization boundary. * Must be called on the UI thread. */ @@ -180,6 +189,15 @@ public final class Choreographer extends Handler { } } + /** + * Return true if {@link #scheduleDraw()} has been called but + * {@link OnDrawListener#onDraw() OnDrawListener.onDraw()} has + * not yet been called. + */ + public boolean isDrawScheduled() { + return mDrawScheduled; + } + @Override public void handleMessage(Message msg) { switch (msg.what) { diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index c08a402..fa4dd25 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -189,7 +189,7 @@ class GLES20Canvas extends HardwareCanvas { } private static native int nGetMaximumTextureWidth(); - private static native int nGetMaximumTextureHeight(); + private static native int nGetMaximumTextureHeight(); /////////////////////////////////////////////////////////////////////////// // Setup @@ -268,6 +268,24 @@ class GLES20Canvas extends HardwareCanvas { private static native void nFinish(int renderer); + /** + * Returns the size of the stencil buffer required by the underlying + * implementation. + * + * @return The minimum number of bits the stencil buffer must. Always >= 0. + * + * @hide + */ + public static int getStencilSize() { + return nGetStencilSize(); + } + + private static native int nGetStencilSize(); + + /////////////////////////////////////////////////////////////////////////// + // Functor + /////////////////////////////////////////////////////////////////////////// + @Override public boolean callDrawGLFunction(int drawGLFunction) { return nCallDrawGLFunction(mRenderer, drawGLFunction); @@ -275,7 +293,6 @@ class GLES20Canvas extends HardwareCanvas { private static native boolean nCallDrawGLFunction(int renderer, int drawGLFunction); - /////////////////////////////////////////////////////////////////////////// // Memory /////////////////////////////////////////////////////////////////////////// @@ -361,6 +378,12 @@ class GLES20Canvas extends HardwareCanvas { private static native int nGetDisplayListSize(int displayList); + static void setDisplayListName(int displayList, String name) { + nSetDisplayListName(displayList, name); + } + + private static native void nSetDisplayListName(int displayList, String name); + @Override public boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty) { return nDrawDisplayList(mRenderer, diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java index 0cb9449..969c9ab 100644 --- a/core/java/android/view/GLES20DisplayList.java +++ b/core/java/android/view/GLES20DisplayList.java @@ -31,10 +31,17 @@ class GLES20DisplayList extends DisplayList { private GLES20RecordingCanvas mCanvas; private boolean mValid; + // Used for debugging + private final String mName; + // The native display list will be destroyed when this object dies. // DO NOT overwrite this reference once it is set. private DisplayListFinalizer mFinalizer; + GLES20DisplayList(String name) { + mName = name; + } + int getNativeDisplayList() { if (!mValid || mFinalizer == null) { throw new IllegalStateException("The display list is not valid."); @@ -75,6 +82,7 @@ class GLES20DisplayList extends DisplayList { mCanvas.end(mFinalizer.mNativeDisplayList); } else { mFinalizer = new DisplayListFinalizer(mCanvas.end(0)); + GLES20Canvas.setDisplayListName(mFinalizer.mNativeDisplayList, mName); } mCanvas.recycle(); mCanvas = null; diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 1c9cbbf..9e8a228 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -283,9 +283,12 @@ public abstract class HardwareRenderer { * Creates a new display list that can be used to record batches of * drawing operations. * + * @param name The name of the display list, used for debugging purpose. + * May be null + * * @return A new display list. */ - public abstract DisplayList createDisplayList(); + public abstract DisplayList createDisplayList(String name); /** * Creates a new hardware layer. A hardware layer built by calling this @@ -1047,7 +1050,7 @@ public abstract class HardwareRenderer { EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, - EGL_STENCIL_SIZE, 0, + EGL_STENCIL_SIZE, GLES20Canvas.getStencilSize(), EGL_SURFACE_TYPE, EGL_WINDOW_BIT | (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), EGL_NONE @@ -1094,8 +1097,8 @@ public abstract class HardwareRenderer { } @Override - public DisplayList createDisplayList() { - return new GLES20DisplayList(); + public DisplayList createDisplayList(String name) { + return new GLES20DisplayList(name); } @Override diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 8cac57d..7ba17b3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3761,8 +3761,14 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** - * Called when this view wants to give up focus. This will cause - * {@link #onFocusChanged(boolean, int, android.graphics.Rect)} to be called. + * Called when this view wants to give up focus. If focus is cleared + * {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called. + * <p> + * <strong>Note:</strong> When a View clears focus the framework is trying + * to give focus to the first focusable View from the top. Hence, if this + * View is the first from the top that can take focus, then its focus will + * not be cleared nor will the focus change callback be invoked. + * </p> */ public void clearFocus() { if (DBG) { @@ -3770,6 +3776,14 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } if ((mPrivateFlags & FOCUSED) != 0) { + // If this is the first focusable do not clear focus since the we + // try to give it focus every time a view clears its focus. Hence, + // the view that would gain focus already has it. + View firstFocusable = getFirstFocusable(); + if (firstFocusable == this) { + return; + } + mPrivateFlags &= ~FOCUSED; if (mParent != null) { @@ -3778,9 +3792,24 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal onFocusChanged(false, 0, null); refreshDrawableState(); + + // The view cleared focus and invoked the callbacks, so now is the + // time to give focus to the the first focusable to ensure that the + // gain focus is announced after clear focus. + if (firstFocusable != null) { + firstFocusable.requestFocus(FOCUS_FORWARD); + } } } + private View getFirstFocusable() { + ViewRootImpl viewRoot = getViewRootImpl(); + if (viewRoot != null) { + return viewRoot.focusSearch(null, FOCUS_FORWARD); + } + return null; + } + /** * Called to clear the focus of a view that is about to be removed. * Doesn't call clearChildFocus, which prevents this view from taking @@ -6822,7 +6851,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal if ((changed & VISIBILITY_MASK) != 0) { if (mParent instanceof ViewGroup) { - ((ViewGroup) mParent).onChildVisibilityChanged(this, (flags & VISIBILITY_MASK)); + ((ViewGroup) mParent).onChildVisibilityChanged(this, (changed & VISIBILITY_MASK), + (flags & VISIBILITY_MASK)); ((View) mParent).invalidate(true); } else if (mParent != null) { mParent.invalidateChild(this, null); @@ -7282,6 +7312,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @return The degrees of rotation. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getRotation() { return mTransformationInfo != null ? mTransformationInfo.mRotation : 0; } @@ -7323,6 +7354,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @return The degrees of Y rotation. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getRotationY() { return mTransformationInfo != null ? mTransformationInfo.mRotationY : 0; } @@ -7369,6 +7401,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @return The degrees of X rotation. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getRotationX() { return mTransformationInfo != null ? mTransformationInfo.mRotationX : 0; } @@ -7416,6 +7449,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @see #getPivotY() * @return The scaling factor. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getScaleX() { return mTransformationInfo != null ? mTransformationInfo.mScaleX : 1; } @@ -7454,6 +7488,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @see #getPivotY() * @return The scaling factor. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getScaleY() { return mTransformationInfo != null ? mTransformationInfo.mScaleY : 1; } @@ -7492,6 +7527,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @see #getPivotY() * @return The x location of the pivot point. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getPivotX() { return mTransformationInfo != null ? mTransformationInfo.mPivotX : 0; } @@ -7536,6 +7572,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @see #getPivotY() * @return The y location of the pivot point. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getPivotY() { return mTransformationInfo != null ? mTransformationInfo.mPivotY : 0; } @@ -7576,6 +7613,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * <p>By default this is 1.0f. * @return The opacity of the view. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getAlpha() { return mTransformationInfo != null ? mTransformationInfo.mAlpha : 1; } @@ -7589,6 +7627,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * equivalent to calling {@link #setLayerType(int, android.graphics.Paint)} and * setting a hardware layer.</p> * + * <p>Note that setting alpha to a translucent value (0 < alpha < 1) may have + * performance implications. It is generally best to use the alpha property sparingly and + * transiently, as in the case of fading animations.</p> + * * @param alpha The opacity of the view. * * @see #setLayerType(int, android.graphics.Paint) @@ -7886,6 +7928,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @return The visual x position of this view, in pixels. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getX() { return mLeft + (mTransformationInfo != null ? mTransformationInfo.mTranslationX : 0); } @@ -7908,6 +7951,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @return The visual y position of this view, in pixels. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getY() { return mTop + (mTransformationInfo != null ? mTransformationInfo.mTranslationY : 0); } @@ -7931,6 +7975,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @return The horizontal position of this view relative to its left position, in pixels. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getTranslationX() { return mTransformationInfo != null ? mTransformationInfo.mTranslationX : 0; } @@ -7967,6 +8012,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @return The vertical position of this view relative to its top position, * in pixels. */ + @ViewDebug.ExportedProperty(category = "drawing") public float getTranslationY() { return mTransformationInfo != null ? mTransformationInfo.mTranslationY : 0; } @@ -10373,7 +10419,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal // we copy in child display lists into ours in drawChild() mRecreateDisplayList = true; if (mDisplayList == null) { - mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(); + final String name = getClass().getSimpleName(); + mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(name); // If we're creating a new display list, make sure our parent gets invalidated // since they will need to recreate their display list to account for this // new child display list. @@ -12654,6 +12701,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT); } + if (getAccessibilityNodeProvider() != null) { + throw new IllegalStateException("Views with AccessibilityNodeProvider" + + " can't have children."); + } + mPrivateFlags |= FORCE_LAYOUT; mPrivateFlags |= INVALIDATED; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index dda695f..d3af618 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -675,11 +675,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override public void clearFocus() { - super.clearFocus(); - - // clear any child focus if it exists - if (mFocused != null) { + if (DBG) { + System.out.println(this + " clearFocus()"); + } + if (mFocused == null) { + super.clearFocus(); + } else { mFocused.clearFocus(); + mFocused = null; } } @@ -691,12 +694,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (DBG) { System.out.println(this + " unFocus()"); } - - super.unFocus(); - if (mFocused != null) { + if (mFocused == null) { + super.unFocus(); + } else { mFocused.unFocus(); + mFocused = null; } - mFocused = null; } /** @@ -888,18 +891,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * Called when a view's visibility has changed. Notify the parent to take any appropriate + * action. + * + * @param child The view whose visibility has changed + * @param oldVisibility The previous visibility value (GONE, INVISIBLE, or VISIBLE). + * @param newVisibility The new visibility value (GONE, INVISIBLE, or VISIBLE). * @hide - * @param child - * @param visibility */ - protected void onChildVisibilityChanged(View child, int visibility) { + protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) { if (mTransition != null) { - if (visibility == VISIBLE) { - mTransition.showChild(this, child); + if (newVisibility == VISIBLE) { + mTransition.showChild(this, child, oldVisibility); } else { - mTransition.hideChild(this, child); - } - if (visibility != VISIBLE) { + mTransition.hideChild(this, child, newVisibility); // Only track this on disappearing views - appearing views are already visible // and don't need special handling during drawChild() if (mVisibilityChangingChildren == null) { @@ -914,7 +919,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // in all cases, for drags if (mCurrentDrag != null) { - if (visibility == VISIBLE) { + if (newVisibility == VISIBLE) { notifyChildOfDrag(child); } } @@ -3351,6 +3356,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) { + if (getAccessibilityNodeProvider() != null) { + throw new IllegalStateException("Views with AccessibilityNodeProvider" + + " can't have children."); + } + if (mTransition != null) { // Don't prevent other add transitions from completing, but cancel remove // transitions to let them complete the process before we add to the container diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index 89a1ef2..0fdcd0f 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -113,6 +113,10 @@ public class ViewPropertyAnimator { * on that list are added to the list of properties associated with that animator. */ ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>(); + private Runnable mPendingSetupAction; + private Runnable mPendingCleanupAction; + private Runnable mPendingOnStartAction; + private Runnable mPendingOnEndAction; /** * Constants used to associate a property being requested and the mechanism used to set @@ -199,6 +203,10 @@ public class ViewPropertyAnimator { */ private HashMap<Animator, PropertyBundle> mAnimatorMap = new HashMap<Animator, PropertyBundle>(); + private HashMap<Animator, Runnable> mAnimatorSetupMap; + private HashMap<Animator, Runnable> mAnimatorCleanupMap; + private HashMap<Animator, Runnable> mAnimatorOnStartMap; + private HashMap<Animator, Runnable> mAnimatorOnEndMap; /** * This is the information we need to set each property during the animation. @@ -614,6 +622,93 @@ public class ViewPropertyAnimator { } /** + * The View associated with this ViewPropertyAnimator will have its + * {@link View#setLayerType(int, android.graphics.Paint) layer type} set to + * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation. This state + * is not persistent, either on the View or on this ViewPropertyAnimator: the layer type + * of the View will be restored when the animation ends to what it was when this method was + * called, and this setting on ViewPropertyAnimator is only valid for the next animation. + * Note that calling this method and then independently setting the layer type of the View + * (by a direct call to {@link View#setLayerType(int, android.graphics.Paint)}) will result + * in some inconsistency, including having the layer type restored to its pre-withLayer() + * value when the animation ends. + * + * @see View#setLayerType(int, android.graphics.Paint) + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator withLayer() { + mPendingSetupAction= new Runnable() { + @Override + public void run() { + mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + }; + final int currentLayerType = mView.getLayerType(); + mPendingCleanupAction = new Runnable() { + @Override + public void run() { + mView.setLayerType(currentLayerType, null); + } + }; + if (mAnimatorSetupMap == null) { + mAnimatorSetupMap = new HashMap<Animator, Runnable>(); + } + if (mAnimatorCleanupMap == null) { + mAnimatorCleanupMap = new HashMap<Animator, Runnable>(); + } + + return this; + } + + /** + * Specifies an action to take place when the next animation runs. If there is a + * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the + * action will run after that startDelay expires, when the actual animation begins. + * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate + * choreographing ViewPropertyAnimator animations with other animations or actions + * in the application. + * + * @param runnable The action to run when the next animation starts. + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator withStartAction(Runnable runnable) { + mPendingOnStartAction = runnable; + if (runnable != null && mAnimatorOnStartMap == null) { + mAnimatorOnStartMap = new HashMap<Animator, Runnable>(); + } + return this; + } + + /** + * Specifies an action to take place when the next animation ends. The action is only + * run if the animation ends normally; if the ViewPropertyAnimator is canceled during + * that animation, the runnable will not run. + * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate + * choreographing ViewPropertyAnimator animations with other animations or actions + * in the application. + * + * <p>For example, the following code animates a view to x=200 and then back to 0:</p> + * <pre> + * Runnable endAction = new Runnable() { + * public void run() { + * view.animate().x(0); + * } + * }; + * view.animate().x(200).onEnd(endAction); + * </pre> + * + * @param runnable The action to run when the next animation ends. + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator withEndAction(Runnable runnable) { + mPendingOnEndAction = runnable; + if (runnable != null && mAnimatorOnEndMap == null) { + mAnimatorOnEndMap = new HashMap<Animator, Runnable>(); + } + return this; + } + + /** * Starts the underlying Animator for a set of properties. We use a single animator that * simply runs from 0 to 1, and then use that fractional value to set each property * value accordingly. @@ -630,6 +725,22 @@ public class ViewPropertyAnimator { propertyMask |= nameValuesHolder.mNameConstant; } mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList)); + if (mPendingSetupAction != null) { + mAnimatorSetupMap.put(animator, mPendingSetupAction); + mPendingSetupAction = null; + } + if (mPendingCleanupAction != null) { + mAnimatorCleanupMap.put(animator, mPendingCleanupAction); + mPendingCleanupAction = null; + } + if (mPendingOnStartAction != null) { + mAnimatorOnStartMap.put(animator, mPendingOnStartAction); + mPendingOnStartAction = null; + } + if (mPendingOnEndAction != null) { + mAnimatorOnEndMap.put(animator, mPendingOnEndAction); + mPendingOnEndAction = null; + } animator.addUpdateListener(mAnimatorEventListener); animator.addListener(mAnimatorEventListener); if (mStartDelaySet) { @@ -800,6 +911,20 @@ public class ViewPropertyAnimator { implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener { @Override public void onAnimationStart(Animator animation) { + if (mAnimatorSetupMap != null) { + Runnable r = mAnimatorSetupMap.get(animation); + if (r != null) { + r.run(); + } + mAnimatorSetupMap.remove(animation); + } + if (mAnimatorOnStartMap != null) { + Runnable r = mAnimatorOnStartMap.get(animation); + if (r != null) { + r.run(); + } + mAnimatorOnStartMap.remove(animation); + } if (mListener != null) { mListener.onAnimationStart(animation); } @@ -810,6 +935,9 @@ public class ViewPropertyAnimator { if (mListener != null) { mListener.onAnimationCancel(animation); } + if (mAnimatorOnEndMap != null) { + mAnimatorOnEndMap.remove(animation); + } } @Override @@ -824,6 +952,20 @@ public class ViewPropertyAnimator { if (mListener != null) { mListener.onAnimationEnd(animation); } + if (mAnimatorOnEndMap != null) { + Runnable r = mAnimatorOnEndMap.get(animation); + if (r != null) { + r.run(); + } + mAnimatorOnEndMap.remove(animation); + } + if (mAnimatorCleanupMap != null) { + Runnable r = mAnimatorCleanupMap.get(animation); + if (r != null) { + r.run(); + } + mAnimatorCleanupMap.remove(animation); + } mAnimatorMap.remove(animation); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1a4bdf4..cbf4b5a 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -18,11 +18,13 @@ package android.view; import android.Manifest; import android.animation.LayoutTransition; +import android.animation.ValueAnimator; import android.app.ActivityManagerNative; import android.content.ClipDescription; import android.content.ComponentCallbacks; import android.content.ComponentCallbacks2; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -52,6 +54,7 @@ import android.util.AndroidRuntimeException; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; +import android.util.LongSparseArray; import android.util.Pool; import android.util.Poolable; import android.util.PoolableManager; @@ -81,7 +84,6 @@ import com.android.internal.view.RootViewSurfaceTaker; import java.io.IOException; import java.io.OutputStream; -import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -139,6 +141,10 @@ public final class ViewRootImpl extends Handler implements ViewParent, static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList<ComponentCallbacks>(); + private static boolean sUseRenderThread = false; + private static boolean sRenderThreadQueried = false; + private static final Object[] sRenderThreadQueryLock = new Object[0]; + long mLastTrackballTime = 0; final TrackballAxis mTrackballAxisX = new TrackballAxis(); final TrackballAxis mTrackballAxisY = new TrackballAxis(); @@ -168,6 +174,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, View mView; View mFocusedView; View mRealFocusedView; // this is not set to null in touch mode + View mOldFocusedView; int mViewVisibility; boolean mAppVisible = true; int mOrigWindowType = -1; @@ -301,6 +308,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent; + AccessibilityPrefetchStrategy mAccessibilityPrefetchStrategy; + private final int mDensity; /** @@ -315,8 +324,11 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (!mInitialized) { try { InputMethodManager imm = InputMethodManager.getInstance(mainLooper); - sWindowSession = Display.getWindowManager().openSession( + IWindowManager windowManager = Display.getWindowManager(); + sWindowSession = windowManager.openSession( imm.getClient(), imm.getInputContext()); + float animatorScale = windowManager.getAnimationScale(2); + ValueAnimator.setDurationScale(animatorScale); mInitialized = true; } catch (RemoteException e) { } @@ -377,6 +389,31 @@ public final class ViewRootImpl extends Handler implements ViewParent, mChoreographer = Choreographer.getInstance(); } + /** + * @return True if the application requests the use of a separate render thread, + * false otherwise + */ + private static boolean isRenderThreadRequested(Context context) { + synchronized (sRenderThreadQueryLock) { + if (!sRenderThreadQueried) { + final PackageManager packageManager = context.getPackageManager(); + final String packageName = context.getApplicationInfo().packageName; + try { + ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, + PackageManager.GET_META_DATA); + if (applicationInfo.metaData != null) { + sUseRenderThread = applicationInfo.metaData.getBoolean( + "android.graphics.renderThread", false); + } + } catch (PackageManager.NameNotFoundException e) { + } finally { + sRenderThreadQueried = true; + } + } + return sUseRenderThread; + } + } + public static void addFirstDrawHandler(Runnable callback) { synchronized (sFirstDrawHandlers) { if (!sFirstDrawComplete) { @@ -447,7 +484,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, // If the application owns the surface, don't enable hardware acceleration if (mSurfaceHolder == null) { - enableHardwareAcceleration(attrs); + enableHardwareAcceleration(mView.getContext(), attrs); } boolean restore = false; @@ -607,7 +644,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, } } - private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { + private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) { mAttachInfo.mHardwareAccelerated = false; mAttachInfo.mHardwareAccelerationRequested = false; @@ -640,20 +677,27 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled && forceHwAccelerated)) { // Don't enable hardware acceleration when we're not on the main thread - if (!HardwareRenderer.sSystemRendererDisabled - && Looper.getMainLooper() != Looper.myLooper()) { - Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware " + if (!HardwareRenderer.sSystemRendererDisabled && + Looper.getMainLooper() != Looper.myLooper()) { + Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware " + "acceleration outside of the main thread, aborting"); return; } - final boolean translucent = attrs.format != PixelFormat.OPAQUE; + boolean renderThread = isRenderThreadRequested(context); + if (renderThread) { + Log.i(HardwareRenderer.LOG_TAG, "Render threat initiated"); + } + if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.destroy(true); - } + } + + final boolean translucent = attrs.format != PixelFormat.OPAQUE; mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = mAttachInfo.mHardwareRenderer != null; + } else if (fakeHwAccelerated) { // The window had wanted to use hardware acceleration, but this // is not allowed in its process. By setting this flag, it can @@ -2226,32 +2270,33 @@ public final class ViewRootImpl extends Handler implements ViewParent, public void requestChildFocus(View child, View focused) { checkThread(); - if (mFocusedView != focused) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused); - scheduleTraversals(); + + if (DEBUG_INPUT_RESIZE) { + Log.v(TAG, "Request child focus: focus now " + focused); } + + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused); + scheduleTraversals(); + mFocusedView = mRealFocusedView = focused; - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now " - + mFocusedView); } public void clearChildFocus(View child) { checkThread(); - View oldFocus = mFocusedView; + if (DEBUG_INPUT_RESIZE) { + Log.v(TAG, "Clearing child focus"); + } - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus"); - mFocusedView = mRealFocusedView = null; - if (mView != null && !mView.hasFocus()) { - // If a view gets the focus, the listener will be invoked from requestChildFocus() - if (!mView.requestFocus(View.FOCUS_FORWARD)) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); - } - } else if (oldFocus != null) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); + mOldFocusedView = mFocusedView; + + // Invoke the listener only if there is no view to take focus + if (focusSearch(null, View.FOCUS_FORWARD) == null) { + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, null); } - } + mFocusedView = mRealFocusedView = null; + } public void focusableViewAvailable(View v) { checkThread(); @@ -2724,6 +2769,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, mView.unFocus(); mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null); mFocusedView = null; + mOldFocusedView = null; return true; } } @@ -3438,11 +3484,11 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (args.localChanges != 0) { if (mAttachInfo != null) { mAttachInfo.mSystemUiVisibility = - (mAttachInfo.mSystemUiVisibility&~args.localChanges) - | (args.localValue&args.localChanges); + (mAttachInfo.mSystemUiVisibility & ~args.localChanges) | + (args.localValue & args.localChanges); + mAttachInfo.mRecomputeGlobalAttributes = true; } mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges); - mAttachInfo.mRecomputeGlobalAttributes = true; scheduleTraversals(); } mView.dispatchSystemUiVisibilityChanged(args.globalVisibility); @@ -3480,6 +3526,17 @@ public final class ViewRootImpl extends Handler implements ViewParent, return mAccessibilityInteractionController; } + public AccessibilityPrefetchStrategy getAccessibilityPrefetchStrategy() { + if (mView == null) { + throw new IllegalStateException("getAccessibilityPrefetchStrategy" + + " called when there is no mView"); + } + if (mAccessibilityPrefetchStrategy == null) { + mAccessibilityPrefetchStrategy = new AccessibilityPrefetchStrategy(); + } + return mAccessibilityPrefetchStrategy; + } + private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { @@ -3585,7 +3642,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, mView.debug(); } - public void dumpGfxInfo(PrintWriter pw, int[] info) { + public void dumpGfxInfo(int[] info) { if (mView != null) { getGfxInfo(mView, info); } else { @@ -3697,7 +3754,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, * Represents a pending input event that is waiting in a queue. * * Input events are processed in serial order by the timestamp specified by - * {@link InputEvent#getEventTime()}. In general, the input dispatcher delivers + * {@link InputEvent#getEventTimeNano()}. In general, the input dispatcher delivers * one input event to the application at a time and waits for the application * to finish handling it before delivering the next one. * @@ -3706,7 +3763,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, * needing a queue on the application's side. */ private static final class QueuedInputEvent { - public static final int FLAG_DELIVER_POST_IME = 1 << 0; + public static final int FLAG_DELIVER_POST_IME = 1; public QueuedInputEvent mNext; @@ -3980,6 +4037,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (mView == null) { return false; } + getAccessibilityPrefetchStrategy().onAccessibilityEvent(event); mAccessibilityManager.sendAccessibilityEvent(event); return true; } @@ -4539,6 +4597,13 @@ public final class ViewRootImpl extends Handler implements ViewParent, viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId, interactionId, callback, interrogatingPid, interrogatingTid); + } else { + // We cannot make the call and notify the caller so it does not wait. + try { + callback.setFindAccessibilityNodeInfosResult(null, interactionId); + } catch (RemoteException re) { + /* best effort - ignore */ + } } } @@ -4550,28 +4615,49 @@ public final class ViewRootImpl extends Handler implements ViewParent, viewRootImpl.getAccessibilityInteractionController() .performAccessibilityActionClientThread(accessibilityNodeId, action, interactionId, callback, interogatingPid, interrogatingTid); + } else { + // We cannot make the call and notify the caller so it does not + try { + callback.setPerformAccessibilityActionResult(false, interactionId); + } catch (RemoteException re) { + /* best effort - ignore */ + } } } - public void findAccessibilityNodeInfoByViewId(int viewId, + public void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, long interrogatingTid) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() - .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback, - interrogatingPid, interrogatingTid); + .findAccessibilityNodeInfoByViewIdClientThread(accessibilityNodeId, viewId, + interactionId, callback, interrogatingPid, interrogatingTid); + } else { + // We cannot make the call and notify the caller so it does not + try { + callback.setFindAccessibilityNodeInfoResult(null, interactionId); + } catch (RemoteException re) { + /* best effort - ignore */ + } } } - public void findAccessibilityNodeInfosByText(String text, long accessibilityNodeId, + public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, long interrogatingTid) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() - .findAccessibilityNodeInfosByTextClientThread(text, accessibilityNodeId, + .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text, interactionId, callback, interrogatingPid, interrogatingTid); + } else { + // We cannot make the call and notify the caller so it does not + try { + callback.setFindAccessibilityNodeInfosResult(null, interactionId); + } catch (RemoteException re) { + /* best effort - ignore */ + } } } } @@ -4649,6 +4735,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, long interrogatingTid) { Message message = Message.obtain(); message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID; + message.arg1 = interrogatingPid; SomeArgs args = mPool.acquire(); args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); @@ -4671,40 +4758,47 @@ public final class ViewRootImpl extends Handler implements ViewParent, public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) { SomeArgs args = (SomeArgs) message.obj; + final int interrogatingPid = message.arg1; final int accessibilityViewId = args.argi1; final int virtualDescendantId = args.argi2; final int interactionId = args.argi3; final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; mPool.release(args); - AccessibilityNodeInfo info = null; + List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; + infos.clear(); try { View target = findViewByAccessibilityId(accessibilityViewId); if (target != null && target.getVisibility() == View.VISIBLE) { AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); if (provider != null) { - info = provider.createAccessibilityNodeInfo(virtualDescendantId); + infos.add(provider.createAccessibilityNodeInfo(virtualDescendantId)); } else if (virtualDescendantId == View.NO_ID) { - info = target.createAccessibilityNodeInfo(); + getAccessibilityPrefetchStrategy().prefetchAccessibilityNodeInfos( + interrogatingPid, target, infos); } } } finally { try { - callback.setFindAccessibilityNodeInfoResult(info, interactionId); + callback.setFindAccessibilityNodeInfosResult(infos, interactionId); + infos.clear(); } catch (RemoteException re) { /* ignore - the other side will time out */ } } } - public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId, - IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, - long interrogatingTid) { + public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId, + int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, + int interrogatingPid, long interrogatingTid) { Message message = Message.obtain(); message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID; - message.arg1 = viewId; - message.arg2 = interactionId; - message.obj = callback; + message.arg1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); + SomeArgs args = mPool.acquire(); + args.argi1 = viewId; + args.argi2 = interactionId; + args.arg1 = callback; + message.obj = args; // If the interrogation is performed by the same thread as the main UI // thread in this process, set the message as a static reference so // after this call completes the same thread but in the interrogating @@ -4720,17 +4814,26 @@ public final class ViewRootImpl extends Handler implements ViewParent, } public void findAccessibilityNodeInfoByViewIdUiThread(Message message) { - final int viewId = message.arg1; - final int interactionId = message.arg2; + final int accessibilityViewId = message.arg1; + SomeArgs args = (SomeArgs) message.obj; + final int viewId = args.argi1; + final int interactionId = args.argi2; final IAccessibilityInteractionConnectionCallback callback = - (IAccessibilityInteractionConnectionCallback) message.obj; - + (IAccessibilityInteractionConnectionCallback) args.arg1; + mPool.release(args); AccessibilityNodeInfo info = null; try { - View root = ViewRootImpl.this.mView; - View target = root.findViewById(viewId); - if (target != null && target.getVisibility() == View.VISIBLE) { - info = target.createAccessibilityNodeInfo(); + View root = null; + if (accessibilityViewId != View.NO_ID) { + root = findViewByAccessibilityId(accessibilityViewId); + } else { + root = ViewRootImpl.this.mView; + } + if (root != null) { + View target = root.findViewById(viewId); + if (target != null && target.getVisibility() == View.VISIBLE) { + info = target.createAccessibilityNodeInfo(); + } } } finally { try { @@ -4741,8 +4844,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, } } - public void findAccessibilityNodeInfosByTextClientThread(String text, - long accessibilityNodeId, int interactionId, + public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId, + String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, long interrogatingTid) { Message message = Message.obtain(); @@ -4779,7 +4882,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, mPool.release(args); List<AccessibilityNodeInfo> infos = null; try { - View target = null; + View target; if (accessibilityViewId != View.NO_ID) { target = findViewByAccessibilityId(accessibilityViewId); } else { @@ -4934,4 +5037,88 @@ public final class ViewRootImpl extends Handler implements ViewParent, } } } + + /** + * This class encapsulates a prefetching strategy for the accessibility APIs for + * querying window content.It is responsible to prefetch a batch of + * AccessibilityNodeInfos in addition to the one for a requested node. It caches + * the ids of the prefeteched nodes such that they are fetched only once. + */ + class AccessibilityPrefetchStrategy { + private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 100; + + // We need to keep track of what we have sent for each interrogating + // process. Usually there will be only one such process but we + // should support the general case. Note that the accessibility event + // stream will take care of clearing caches of querying processes that + // are not longer alive, so we do not waste memory. + private final LongSparseArray<AccessibilityNodeInfoCache> mAccessibilityNodeInfoCaches = + new LongSparseArray<AccessibilityNodeInfoCache>(); + + private AccessibilityNodeInfoCache getCacheForInterrogatingPid(long interrogatingPid) { + AccessibilityNodeInfoCache cache = mAccessibilityNodeInfoCaches.get(interrogatingPid); + if (cache == null) { + cache = AccessibilityNodeInfoCache.newAccessibilityNodeInfoCache(); + mAccessibilityNodeInfoCaches.put(interrogatingPid, cache); + } + return cache; + } + + public void onAccessibilityEvent(AccessibilityEvent event) { + final int cacheCount = mAccessibilityNodeInfoCaches.size(); + for (int i = 0; i < cacheCount; i++) { + AccessibilityNodeInfoCache cache = mAccessibilityNodeInfoCaches.valueAt(i); + cache.onAccessibilityEvent(event); + } + } + + public void prefetchAccessibilityNodeInfos(long interrogatingPid, View root, + List<AccessibilityNodeInfo> outInfos) { + addAndCacheNotCachedNodeInfo(interrogatingPid, root, outInfos); + addAndCacheNotCachedPredecessorInfos(interrogatingPid, root, outInfos); + addAndCacheNotCachedDescendantInfos(interrogatingPid, root, outInfos); + } + + private void addAndCacheNotCachedNodeInfo(long interrogatingPid, + View view, List<AccessibilityNodeInfo> outInfos) { + final long accessibilityNodeId = AccessibilityNodeInfo.makeNodeId( + view.getAccessibilityViewId(), View.NO_ID); + AccessibilityNodeInfoCache cache = getCacheForInterrogatingPid(interrogatingPid); + if (!cache.containsKey(accessibilityNodeId)) { + // Account for the ids of the fetched infos. The infos will be + // cached in the window querying process. We just need to know + // which infos are cached to avoid fetching a cached one again. + cache.put(accessibilityNodeId, null); + outInfos.add(view.createAccessibilityNodeInfo()); + } + } + + private void addAndCacheNotCachedPredecessorInfos(long interrogatingPid, View view, + List<AccessibilityNodeInfo> outInfos) { + ViewParent predecessor = view.getParent(); + while (predecessor instanceof View + && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { + View predecessorView = (View) predecessor; + addAndCacheNotCachedNodeInfo(interrogatingPid, predecessorView, outInfos); + predecessor = predecessor.getParent(); + } + } + + private void addAndCacheNotCachedDescendantInfos(long interrogatingPid, View view, + List<AccessibilityNodeInfo> outInfos) { + if (outInfos.size() > MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE + || view.getAccessibilityNodeProvider() != null) { + return; + } + addAndCacheNotCachedNodeInfo(interrogatingPid, view, outInfos); + if (view instanceof ViewGroup) { + ViewGroup rootGroup = (ViewGroup) view; + final int childCount = rootGroup.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = rootGroup.getChildAt(i); + addAndCacheNotCachedDescendantInfos(interrogatingPid, child, outInfos); + } + } + } + } } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index d711337..6bdc4e8 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -490,7 +490,7 @@ public class WindowManagerImpl implements WindowManager { for (int i = 0; i < count; i++) { ViewRootImpl root = mRoots[i]; - root.dumpGfxInfo(pw, info); + root.dumpGfxInfo(info); String name = root.getClass().getName() + '@' + Integer.toHexString(hashCode()); diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 95c070c..072fdd8 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -24,7 +24,9 @@ import android.os.SystemClock; import android.util.Log; import android.util.LongSparseArray; import android.util.SparseArray; +import android.view.AccessibilityNodeInfoCache; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -97,6 +99,11 @@ public final class AccessibilityInteractionClient private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache = new SparseArray<IAccessibilityServiceConnection>(); + // The connection cache is shared between all interrogating threads since + // at any given time there is only one window allowing querying. + private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache = + AccessibilityNodeInfoCache.newSynchronizedAccessibilityNodeInfoCache(); + /** * @return The client for the current thread. */ @@ -145,7 +152,9 @@ public final class AccessibilityInteractionClient * Finds an {@link AccessibilityNodeInfo} by accessibility id. * * @param connectionId The id of a connection for interacting with the system. - * @param accessibilityWindowId A unique window id. + * @param accessibilityWindowId A unique window id. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * to query the currently active window. * @param accessibilityNodeId A unique node accessibility id * (accessibility view and virtual descendant id). * @return An {@link AccessibilityNodeInfo} if found, null otherwise. @@ -155,16 +164,22 @@ public final class AccessibilityInteractionClient try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { + AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(accessibilityNodeId); + if (cachedInfo != null) { + return cachedInfo; + } final int interactionId = mInteractionIdCounter.getAndIncrement(); final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId( accessibilityWindowId, accessibilityNodeId, interactionId, this, Thread.currentThread().getId()); // If the scale is zero the call has failed. if (windowScale > 0) { - AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( + List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - finalizeAccessibilityNodeInfo(info, connectionId, windowScale); - return info; + finalizeAccessibilityNodeInfos(infos, connectionId, windowScale); + if (infos != null && !infos.isEmpty()) { + return infos.get(0); + } } } else { if (DEBUG) { @@ -181,22 +196,30 @@ public final class AccessibilityInteractionClient } /** - * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed - * in the currently active window and starts from the root View in the window. + * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in + * the window whose id is specified and starts from the node whose accessibility + * id is specified. * * @param connectionId The id of a connection for interacting with the system. + * @param accessibilityWindowId A unique window id. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique view id from where to start the search. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * to start from the root. * @param viewId The id of the view. * @return An {@link AccessibilityNodeInfo} if found, null otherwise. */ - public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int connectionId, - int viewId) { + public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int connectionId, + int accessibilityWindowId, long accessibilityNodeId, int viewId) { try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final float windowScale = - connection.findAccessibilityNodeInfoByViewIdInActiveWindow(viewId, - interactionId, this, Thread.currentThread().getId()); + connection.findAccessibilityNodeInfoByViewId(accessibilityWindowId, + accessibilityNodeId, viewId, interactionId, this, + Thread.currentThread().getId()); // If the scale is zero the call has failed. if (windowScale > 0) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( @@ -220,64 +243,27 @@ public final class AccessibilityInteractionClient /** * Finds {@link AccessibilityNodeInfo}s by View text. The match is case - * insensitive containment. The search is performed in the currently - * active window and starts from the root View in the window. - * - * @param connectionId The id of a connection for interacting with the system. - * @param text The searched text. - * @return A list of found {@link AccessibilityNodeInfo}s. - */ - public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByTextInActiveWindow( - int connectionId, String text) { - try { - IAccessibilityServiceConnection connection = getConnection(connectionId); - if (connection != null) { - final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = - connection.findAccessibilityNodeInfosByTextInActiveWindow(text, - interactionId, this, Thread.currentThread().getId()); - // If the scale is zero the call has failed. - if (windowScale > 0) { - List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( - interactionId); - finalizeAccessibilityNodeInfos(infos, connectionId, windowScale); - return infos; - } - } else { - if (DEBUG) { - Log.w(LOG_TAG, "No connection for connection id: " + connectionId); - } - } - } catch (RemoteException re) { - if (DEBUG) { - Log.w(LOG_TAG, "Error while calling remote" - + " findAccessibilityNodeInfosByViewTextInActiveWindow", re); - } - } - return null; - } - - /** - * Finds {@link AccessibilityNodeInfo}s by View text. The match is case * insensitive containment. The search is performed in the window whose - * id is specified and starts from the View whose accessibility id is + * id is specified and starts from the node whose accessibility id is * specified. * * @param connectionId The id of a connection for interacting with the system. + * @param accessibilityWindowId A unique window id. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique view id from where to start the search. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} * @param text The searched text. - * @param accessibilityWindowId A unique window id. - * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id) from - * where to start the search. Use {@link android.view.View#NO_ID} to start from the root. * @return A list of found {@link AccessibilityNodeInfo}s. */ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int connectionId, - String text, int accessibilityWindowId, long accessibilityNodeId) { + int accessibilityWindowId, long accessibilityNodeId, String text) { try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfosByText(text, - accessibilityWindowId, accessibilityNodeId, interactionId, this, + final float windowScale = connection.findAccessibilityNodeInfosByText( + accessibilityWindowId, accessibilityNodeId, text, interactionId, this, Thread.currentThread().getId()); // If the scale is zero the call has failed. if (windowScale > 0) { @@ -304,7 +290,9 @@ public final class AccessibilityInteractionClient * Performs an accessibility action on an {@link AccessibilityNodeInfo}. * * @param connectionId The id of a connection for interacting with the system. - * @param accessibilityWindowId The id of the window. + * @param accessibilityWindowId A unique window id. Use + * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * to query the currently active window. * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id). * @param action The action to perform. * @return Whether the action was performed. @@ -319,7 +307,7 @@ public final class AccessibilityInteractionClient accessibilityWindowId, accessibilityNodeId, action, interactionId, this, Thread.currentThread().getId()); if (success) { - return getPerformAccessibilityActionResult(interactionId); + return getPerformAccessibilityActionResultAndClear(interactionId); } } else { if (DEBUG) { @@ -334,6 +322,24 @@ public final class AccessibilityInteractionClient return false; } + public void clearCache() { + if (DEBUG) { + Log.w(LOG_TAG, "clearCache()"); + } + sAccessibilityNodeInfoCache.clear(); + } + + public void removeCachedNode(long accessibilityNodeId) { + if (DEBUG) { + Log.w(LOG_TAG, "removeCachedNode(" + accessibilityNodeId +")"); + } + sAccessibilityNodeInfoCache.remove(accessibilityNodeId); + } + + public void onAccessibilityEvent(AccessibilityEvent event) { + sAccessibilityNodeInfoCache.onAccessibilityEvent(event); + } + /** * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}. * @@ -358,6 +364,9 @@ public final class AccessibilityInteractionClient if (interactionId > mInteractionId) { mFindAccessibilityNodeInfoResult = info; mInteractionId = interactionId; + if (info != null) { + sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info); + } } mInstanceLock.notifyAll(); } @@ -386,8 +395,20 @@ public final class AccessibilityInteractionClient int interactionId) { synchronized (mInstanceLock) { if (interactionId > mInteractionId) { - mFindAccessibilityNodeInfosResult = infos; + // If the call is not an IPC, i.e. it is made from the same process, we need to + // instantiate new result list to avoid passing internal instances to clients. + final boolean isIpcCall = (queryLocalInterface(getInterfaceDescriptor()) == null); + if (!isIpcCall) { + mFindAccessibilityNodeInfosResult = new ArrayList<AccessibilityNodeInfo>(infos); + } else { + mFindAccessibilityNodeInfosResult = infos; + } mInteractionId = interactionId; + final int infoCount = infos.size(); + for (int i = 0; i < infoCount; i ++) { + AccessibilityNodeInfo info = infos.get(i); + sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info); + } } mInstanceLock.notifyAll(); } @@ -399,7 +420,7 @@ public final class AccessibilityInteractionClient * @param interactionId The interaction id to match the result with the request. * @return Whether the action was performed. */ - private boolean getPerformAccessibilityActionResult(int interactionId) { + private boolean getPerformAccessibilityActionResultAndClear(int interactionId) { synchronized (mInstanceLock) { final boolean success = waitForResultTimedLocked(interactionId); final boolean result = success ? mPerformAccessibilityActionResult : false; diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 6939c2c..d7d6792 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -380,8 +380,8 @@ public class AccessibilityNodeInfo implements Parcelable { return Collections.emptyList(); } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfosByText(mConnectionId, text, mWindowId, - mSourceNodeId); + return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId, + text); } /** @@ -903,6 +903,17 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Gets the id of the source node. + * + * @return The id. + * + * @hide + */ + public long getSourceNodeId() { + return mSourceNodeId; + } + + /** * Sets if this instance is sealed. * * @param sealed Whether is sealed. diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index 07aeb9a..b60f50e 100644 --- a/core/java/android/view/accessibility/AccessibilityRecord.java +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -564,6 +564,17 @@ public class AccessibilityRecord { } /** + * Gets the id of the source node. + * + * @return The id. + * + * @hide + */ + public long getSourceNodeId() { + return mSourceNodeId; + } + + /** * Sets the unique id of the IAccessibilityServiceConnection over which * this instance can send requests to the system. * diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index a90c427..ae6869c 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -31,13 +31,13 @@ oneway interface IAccessibilityInteractionConnection { IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, long interrogatingTid); - void findAccessibilityNodeInfoByViewId(int id, int interactionId, + void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int id, int interactionId, IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, long interrogatingTid); - void findAccessibilityNodeInfosByText(String text, long accessibilityNodeId, - int interactionId, IAccessibilityInteractionConnectionCallback callback, - int interrogatingPid, long interrogatingTid); + void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, + long interrogatingTid); void performAccessibilityAction(long accessibilityNodeId, int action, int interactionId, IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index c3794be..320c75d 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -49,5 +49,7 @@ interface IAccessibilityManager { void removeAccessibilityInteractionConnection(IWindow windowToken); - void registerEventListener(IEventListener client); + void registerUiTestAutomationService(IEventListener listener, in AccessibilityServiceInfo info); + + void unregisterUiTestAutomationService(IEventListener listener); } diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java index 30bbb04..ee9b949 100644 --- a/core/java/android/webkit/Network.java +++ b/core/java/android/webkit/Network.java @@ -169,7 +169,9 @@ class Network { if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) return; - NetworkInfo info = (NetworkInfo)intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); + final ConnectivityManager connManager = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo info = connManager.getActiveNetworkInfo(); if (info != null) mRoaming = info.isRoaming(); }; diff --git a/core/java/android/webkit/WebCoreThreadWatchdog.java b/core/java/android/webkit/WebCoreThreadWatchdog.java index d100260..0541d5d 100644 --- a/core/java/android/webkit/WebCoreThreadWatchdog.java +++ b/core/java/android/webkit/WebCoreThreadWatchdog.java @@ -40,9 +40,6 @@ class WebCoreThreadWatchdog implements Runnable { // WebCore thread unresponsive. private static final int TIMED_OUT = 101; - // Message to tell the Watchdog thread to terminate. - private static final int QUIT = 102; - // Wait 10s after hearing back from the WebCore thread before checking it's still alive. private static final int HEARTBEAT_PERIOD = 10 * 1000; @@ -57,7 +54,6 @@ class WebCoreThreadWatchdog implements Runnable { private Handler mWebCoreThreadHandler; private Handler mHandler; private boolean mPaused; - private boolean mPendingQuit; private static WebCoreThreadWatchdog sInstance; @@ -88,12 +84,6 @@ class WebCoreThreadWatchdog implements Runnable { } } - public synchronized static void quit() { - if (sInstance != null) { - sInstance.quitWatchdog(); - } - } - private void setContext(Context context) { mContext = context; } @@ -103,19 +93,6 @@ class WebCoreThreadWatchdog implements Runnable { mWebCoreThreadHandler = webCoreThreadHandler; } - private void quitWatchdog() { - if (mHandler == null) { - // The thread hasn't started yet, so set a flag to stop it starting. - mPendingQuit = true; - return; - } - // Clear any pending messages, and then post a quit to the WatchDog handler. - mHandler.removeMessages(TIMED_OUT); - mHandler.removeMessages(IS_ALIVE); - mWebCoreThreadHandler.removeMessages(EventHub.HEARTBEAT); - mHandler.obtainMessage(QUIT).sendToTarget(); - } - private void pauseWatchdog() { mPaused = true; @@ -146,12 +123,8 @@ class WebCoreThreadWatchdog implements Runnable { mHandler.sendMessageDelayed(mHandler.obtainMessage(TIMED_OUT), TIMEOUT_PERIOD); } - private boolean createHandler() { + private void createHandler() { synchronized (WebCoreThreadWatchdog.class) { - if (mPendingQuit) { - return false; - } - mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -206,15 +179,9 @@ class WebCoreThreadWatchdog implements Runnable { .setIcon(android.R.drawable.ic_dialog_alert) .show(); break; - - case QUIT: - Looper.myLooper().quit(); - break; } } }; - - return true; } } @@ -222,9 +189,7 @@ class WebCoreThreadWatchdog implements Runnable { public void run() { Looper.prepare(); - if (!createHandler()) { - return; - } + createHandler(); // Send the initial control to WebViewCore and start the timeout timer as long as we aren't // paused. diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index cc8eef2..4a42e92 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -789,12 +789,18 @@ public class WebView extends AbsoluteLayout // know to handle Shift and arrows natively first private boolean mAccessibilityScriptInjected; - static final boolean USE_JAVA_TEXT_SELECTION = true; - static final boolean DEBUG_TEXT_HANDLES = false; - private Region mTextSelectionRegion = new Region(); - private Paint mTextSelectionPaint; private Drawable mSelectHandleLeft; private Drawable mSelectHandleRight; + private Rect mSelectCursorBase = new Rect(); + private int mSelectCursorBaseLayerId; + private Rect mSelectCursorExtent = new Rect(); + private int mSelectCursorExtentLayerId; + private Rect mSelectDraggingCursor; + private Point mSelectDraggingOffset = new Point(); + static final int HANDLE_ID_START = 0; + static final int HANDLE_ID_END = 1; + static final int HANDLE_ID_BASE = 2; + static final int HANDLE_ID_EXTENT = 3; static boolean sDisableNavcache = false; // the color used to highlight the touch rectangles @@ -2656,12 +2662,6 @@ public class WebView extends AbsoluteLayout return mZoomManager.getScale(); } - // Called by JNI. Returns the scale to apply to the text selection handles - /* package */ float getTextHandleScale() { - float density = mContext.getResources().getDisplayMetrics().density; - return density / getScale(); - } - /** * Compute the reading level scale of the WebView * @param scale The current scale. @@ -3852,6 +3852,16 @@ public class WebView extends AbsoluteLayout if (x == mScrollingLayerRect.left && y == mScrollingLayerRect.top) { return; } + if (mSelectingText) { + int dx = mScrollingLayerRect.left - x; + int dy = mScrollingLayerRect.top - y; + if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) { + mSelectCursorBase.offset(dx, dy); + } + if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) { + mSelectCursorExtent.offset(dx, dy); + } + } nativeScrollLayer(mCurrentScrollingLayerId, x, y); mScrollingLayerRect.left = x; mScrollingLayerRect.top = y; @@ -4624,12 +4634,7 @@ public class WebView extends AbsoluteLayout * Select the word at the indicated content coordinates. */ boolean selectText(int x, int y) { - if (!setUpSelect(true, x, y)) { - return false; - } - nativeSetExtendSelection(); - mDrawSelectionPointer = false; - mTouchMode = TOUCH_DRAG_MODE; + mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y); return true; } @@ -4830,11 +4835,8 @@ public class WebView extends AbsoluteLayout int extras = DRAW_EXTRAS_NONE; if (mFindIsUp) { extras = DRAW_EXTRAS_FIND; - } else if (mSelectingText && (!USE_JAVA_TEXT_SELECTION || DEBUG_TEXT_HANDLES)) { + } else if (mSelectingText) { extras = DRAW_EXTRAS_SELECTION; - nativeSetSelectionPointer(mNativeClass, - mDrawSelectionPointer, - mZoomManager.getInvScale(), mSelectX, mSelectY - getTitleHeight()); } else if (drawCursorRing) { extras = DRAW_EXTRAS_CURSOR_RING; } @@ -4879,7 +4881,7 @@ public class WebView extends AbsoluteLayout } canvas.restoreToCount(saveCount); - if (mSelectingText && USE_JAVA_TEXT_SELECTION) { + if (mSelectingText) { drawTextSelectionHandles(canvas); } @@ -4901,30 +4903,12 @@ public class WebView extends AbsoluteLayout } private void drawTextSelectionHandles(Canvas canvas) { - if (mTextSelectionPaint == null) { - mTextSelectionPaint = new Paint(); - mTextSelectionPaint.setColor(HIGHLIGHT_COLOR); - } - mTextSelectionRegion.setEmpty(); - nativeGetTextSelectionRegion(mNativeClass, mTextSelectionRegion); - Rect r = new Rect(); - RegionIterator iter = new RegionIterator(mTextSelectionRegion); - Rect clip = canvas.getClipBounds(); - while (iter.next(r)) { - r.set(contentToViewDimension(r.left), - contentToViewDimension(r.top), - contentToViewDimension(r.right), - contentToViewDimension(r.bottom)); - if (r.intersect(clip)) { - canvas.drawRect(r, mTextSelectionPaint); - } - } if (mSelectHandleLeft == null) { mSelectHandleLeft = mContext.getResources().getDrawable( com.android.internal.R.drawable.text_select_handle_left); } int[] handles = new int[4]; - nativeGetSelectionHandles(mNativeClass, handles); + getSelectionHandles(handles); int start_x = contentToViewDimension(handles[0]); int start_y = contentToViewDimension(handles[1]); int end_x = contentToViewDimension(handles[2]); @@ -4942,14 +4926,31 @@ public class WebView extends AbsoluteLayout mSelectHandleRight.setBounds(end_x, end_y, end_x + mSelectHandleRight.getIntrinsicWidth(), end_y + mSelectHandleRight.getIntrinsicHeight()); - if (DEBUG_TEXT_HANDLES) { - mSelectHandleLeft.setAlpha(125); - mSelectHandleRight.setAlpha(125); - } mSelectHandleLeft.draw(canvas); mSelectHandleRight.draw(canvas); } + /** + * Takes an int[4] array as an output param with the values being + * startX, startY, endX, endY + */ + private void getSelectionHandles(int[] handles) { + handles[0] = mSelectCursorBase.right; + handles[1] = mSelectCursorBase.bottom - + (mSelectCursorBase.height() / 4); + handles[2] = mSelectCursorExtent.left; + handles[3] = mSelectCursorExtent.bottom + - (mSelectCursorExtent.height() / 4); + if (!nativeIsBaseFirst(mNativeClass)) { + int swap = handles[0]; + handles[0] = handles[2]; + handles[2] = swap; + swap = handles[1]; + handles[1] = handles[3]; + handles[3] = swap; + } + } + // draw history private boolean mDrawHistory = false; private Picture mHistoryPicture = null; @@ -5009,7 +5010,7 @@ public class WebView extends AbsoluteLayout /* package */ void deleteSelection(int start, int end) { mTextGeneration++; WebViewCore.TextSelectionData data - = new WebViewCore.TextSelectionData(start, end); + = new WebViewCore.TextSelectionData(start, end, 0); mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0, data); } @@ -5462,15 +5463,6 @@ public class WebView extends AbsoluteLayout return pinScrollTo(mContentWidth, mScrollY, true, 0); } } - if (mSelectingText) { - int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT - ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0; - int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ? - -1 : keyCode == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : 0; - int multiplier = event.getRepeatCount() + 1; - moveSelection(xRate * multiplier, yRate * multiplier); - return true; - } if (navHandledKey(keyCode, 1, false, event.getEventTime())) { playSoundEffect(keyCodeToSoundsEffect(keyCode)); return true; @@ -5623,14 +5615,8 @@ public class WebView extends AbsoluteLayout mGotCenterDown = false; if (mSelectingText) { - if (mExtendSelection) { - copySelection(); - selectionDone(); - } else { - mExtendSelection = true; - nativeSetExtendSelection(); - invalidate(); // draw the i-beam instead of the arrow - } + copySelection(); + selectionDone(); return true; // discard press if copy in progress } @@ -5676,21 +5662,7 @@ public class WebView extends AbsoluteLayout return false; } - /* - * Enter selecting text mode, and see if CAB should be shown. - * Returns true if the WebView is now in - * selecting text mode (including if it was already in that mode, and this - * method did nothing). - */ - private boolean setUpSelect(boolean selectWord, int x, int y) { - if (0 == mNativeClass) return false; // client isn't initialized - if (inFullScreenMode()) return false; - if (mSelectingText) return true; - nativeResetSelection(); - if (selectWord && !nativeWordSelection(x, y)) { - selectionDone(); - return false; - } + private boolean startSelectActionMode() { mSelectCallback = new SelectActionModeCallback(); mSelectCallback.setWebView(this); if (startActionMode(mSelectCallback) == null) { @@ -5699,52 +5671,41 @@ public class WebView extends AbsoluteLayout selectionDone(); return false; } - mExtendSelection = false; - mSelectingText = mDrawSelectionPointer = true; - if (DEBUG_TEXT_HANDLES) { - // Debugging text handles requires running in software mode - setLayerType(LAYER_TYPE_SOFTWARE, null); + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + return true; + } + + private void syncSelectionCursors() { + mSelectCursorBaseLayerId = + nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE, mSelectCursorBase); + mSelectCursorExtentLayerId = + nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT, mSelectCursorExtent); + } + + private boolean setupWebkitSelect() { + syncSelectionCursors(); + if (!startSelectActionMode()) { + selectionDone(); + return false; } - // don't let the picture change during text selection - WebViewCore.pauseUpdatePicture(mWebViewCore); - if (nativeHasCursorNode()) { - Rect rect = nativeCursorNodeBounds(); - mSelectX = contentToViewX(rect.left); - mSelectY = contentToViewY(rect.top); - } else if (mLastTouchY > getVisibleTitleHeightImpl()) { - mSelectX = mScrollX + mLastTouchX; - mSelectY = mScrollY + mLastTouchY; + mSelectingText = true; + mTouchMode = TOUCH_DRAG_MODE; + return true; + } + + private void updateWebkitSelection() { + int[] handles = null; + if (mSelectingText) { + handles = new int[4]; + handles[0] = mSelectCursorBase.centerX(); + handles[1] = mSelectCursorBase.centerY(); + handles[2] = mSelectCursorExtent.centerX(); + handles[3] = mSelectCursorExtent.centerY(); } else { - mSelectX = mScrollX + getViewWidth() / 2; - mSelectY = mScrollY + getViewHeightWithTitle() / 2; + nativeSetTextSelection(mNativeClass, 0); } - nativeHideCursor(); - mMinAutoScrollX = 0; - mMaxAutoScrollX = getViewWidth(); - mMinAutoScrollY = 0; - mMaxAutoScrollY = getViewHeightWithTitle(); - mCurrentScrollingLayerId = nativeScrollableLayer(viewToContentX(mSelectX), - viewToContentY(mSelectY), mScrollingLayerRect, - mScrollingLayerBounds); - if (mCurrentScrollingLayerId != 0) { - if (mScrollingLayerRect.left != mScrollingLayerRect.right) { - mMinAutoScrollX = Math.max(mMinAutoScrollX, - contentToViewX(mScrollingLayerBounds.left)); - mMaxAutoScrollX = Math.min(mMaxAutoScrollX, - contentToViewX(mScrollingLayerBounds.right)); - } - if (mScrollingLayerRect.top != mScrollingLayerRect.bottom) { - mMinAutoScrollY = Math.max(mMinAutoScrollY, - contentToViewY(mScrollingLayerBounds.top)); - mMaxAutoScrollY = Math.min(mMaxAutoScrollY, - contentToViewY(mScrollingLayerBounds.bottom)); - } - } - mMinAutoScrollX += SELECT_SCROLL; - mMaxAutoScrollX -= SELECT_SCROLL; - mMinAutoScrollY += SELECT_SCROLL; - mMaxAutoScrollY -= SELECT_SCROLL; - return true; + mWebViewCore.removeMessages(EventHub.SELECT_TEXT); + mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, handles); } /** @@ -5755,7 +5716,6 @@ public class WebView extends AbsoluteLayout @Deprecated public void emulateShiftHeld() { checkThread(); - setUpSelect(false, 0, 0); } /** @@ -5764,17 +5724,7 @@ public class WebView extends AbsoluteLayout * @hide This is an implementation detail. */ public void selectAll() { - if (0 == mNativeClass) return; // client isn't initialized - if (inFullScreenMode()) return; - if (!mSelectingText) { - // retrieve a point somewhere within the text - Point select = nativeSelectableText(); - if (!selectText(select.x, select.y)) return; - } - nativeSelectAll(); - mDrawSelectionPointer = false; - mExtendSelection = true; - invalidate(); + mWebViewCore.sendMessage(EventHub.SELECT_ALL); } /** @@ -5783,17 +5733,11 @@ public class WebView extends AbsoluteLayout void selectionDone() { if (mSelectingText) { mSelectingText = false; - if (DEBUG_TEXT_HANDLES) { - // Debugging text handles required running in software mode, set - // back to default now - setLayerType(LAYER_TYPE_NONE, null); - } // finish is idempotent, so this is fine even if selectionDone was // called by mSelectCallback.onDestroyActionMode mSelectCallback.finish(); mSelectCallback = null; - WebViewCore.resumePriority(); - WebViewCore.resumeUpdatePicture(mWebViewCore); + updateWebkitSelection(); invalidate(); // redraw without selection mAutoScrollX = 0; mAutoScrollY = 0; @@ -5821,7 +5765,7 @@ public class WebView extends AbsoluteLayout .getSystemService(Context.CLIPBOARD_SERVICE); cm.setText(selection); int[] handles = new int[4]; - nativeGetSelectionHandles(mNativeClass, handles); + getSelectionHandles(handles); mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles); } invalidate(); // remove selection region and pointer @@ -5836,7 +5780,7 @@ public class WebView extends AbsoluteLayout public void cutSelection() { copySelection(); int[] handles = new int[4]; - nativeGetSelectionHandles(mNativeClass, handles); + getSelectionHandles(handles); mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles); } @@ -5854,7 +5798,7 @@ public class WebView extends AbsoluteLayout CharSequence pasteText = clipItem.getText(); if (pasteText != null) { int[] handles = new int[4]; - nativeGetSelectionHandles(mNativeClass, handles); + getSelectionHandles(handles); mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles); mWebViewCore.sendMessage(EventHub.INSERT_TEXT, pasteText.toString()); @@ -6406,13 +6350,28 @@ public class WebView extends AbsoluteLayout EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION, (eventTime - mLastTouchUpTime), eventTime); } - if (mSelectingText) { - mDrawSelectionPointer = false; - mSelectionStarted = nativeStartSelection(contentX, contentY); + mSelectionStarted = false; + if (mSelectingText && mSelectHandleLeft != null + && mSelectHandleRight != null) { + int shiftedY = y - getTitleHeight() + mScrollY; + int shiftedX = x + mScrollX; + if (mSelectHandleLeft.getBounds() + .contains(shiftedX, shiftedY)) { + mSelectionStarted = true; + mSelectDraggingCursor = mSelectCursorBase; + } else if (mSelectHandleRight.getBounds() + .contains(shiftedX, shiftedY)) { + mSelectionStarted = true; + mSelectDraggingCursor = mSelectCursorExtent; + } + if (mSelectDraggingCursor != null) { + mSelectDraggingOffset.set( + mSelectDraggingCursor.left - contentX, + mSelectDraggingCursor.top - contentY); + } if (DebugFlags.WEB_VIEW) { Log.v(LOGTAG, "select=" + contentX + "," + contentY); } - invalidate(); } } // Trigger the link @@ -6478,6 +6437,26 @@ public class WebView extends AbsoluteLayout removeTouchHighlight(); } } + if (mSelectingText && mSelectionStarted) { + if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "extend=" + contentX + "," + contentY); + } + ViewParent parent = getParent(); + if (parent != null) { + parent.requestDisallowInterceptTouchEvent(true); + } + if (deltaX != 0 || deltaY != 0) { + mSelectDraggingCursor.offsetTo( + contentX + mSelectDraggingOffset.x, + contentY + mSelectDraggingOffset.y); + updateWebkitSelection(); + mLastTouchX = x; + mLastTouchY = y; + invalidate(); + } + break; + } + // pass the touch events from UI thread to WebCore thread if (shouldForwardTouchEvent() && mConfirmMove && (firstMove || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) { @@ -6520,30 +6499,6 @@ public class WebView extends AbsoluteLayout } else { mVelocityTracker.addMovement(ev); } - if (mSelectingText && mSelectionStarted) { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "extend=" + contentX + "," + contentY); - } - ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } - mAutoScrollX = x <= mMinAutoScrollX ? -SELECT_SCROLL - : x >= mMaxAutoScrollX ? SELECT_SCROLL : 0; - mAutoScrollY = y <= mMinAutoScrollY ? -SELECT_SCROLL - : y >= mMaxAutoScrollY ? SELECT_SCROLL : 0; - if ((mAutoScrollX != 0 || mAutoScrollY != 0) - && !mSentAutoScrollMessage) { - mSentAutoScrollMessage = true; - mPrivateHandler.sendEmptyMessageDelayed( - SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL); - } - if (deltaX != 0 || deltaY != 0) { - nativeExtendSelection(contentX, contentY); - invalidate(); - } - break; - } if (mTouchMode != TOUCH_DRAG_MODE && mTouchMode != TOUCH_DRAG_LAYER_MODE) { @@ -6758,7 +6713,7 @@ public class WebView extends AbsoluteLayout } else { if (mSelectingText) { // tapping on selection or controls does nothing - if (!nativeHitSelection(contentX, contentY)) { + if (!mSelectionStarted) { selectionDone(); } break; @@ -7055,6 +7010,12 @@ public class WebView extends AbsoluteLayout if (mOverScrollGlow != null) { mOverScrollGlow.releaseAll(); } + + if (mSelectingText) { + mSelectionStarted = false; + syncSelectionCursors(); + invalidate(); + } } private void cancelTouch() { @@ -7119,8 +7080,6 @@ public class WebView extends AbsoluteLayout private int mTrackballYMove = 0; private boolean mSelectingText = false; private boolean mSelectionStarted = false; - private boolean mExtendSelection = false; - private boolean mDrawSelectionPointer = false; private static final int TRACKBALL_KEY_TIMEOUT = 1000; private static final int TRACKBALL_TIMEOUT = 200; private static final int TRACKBALL_WAIT = 100; @@ -7189,14 +7148,8 @@ public class WebView extends AbsoluteLayout mTrackballDown = false; mTrackballUpTime = time; if (mSelectingText) { - if (mExtendSelection) { - copySelection(); - selectionDone(); - } else { - mExtendSelection = true; - nativeSetExtendSelection(); - invalidate(); // draw the i-beam instead of the arrow - } + copySelection(); + selectionDone(); return true; // discard press if copy in progress } if (DebugFlags.WEB_VIEW) { @@ -7239,42 +7192,6 @@ public class WebView extends AbsoluteLayout return true; } - void moveSelection(float xRate, float yRate) { - if (mNativeClass == 0) - return; - int width = getViewWidth(); - int height = getViewHeight(); - mSelectX += xRate; - mSelectY += yRate; - int maxX = width + mScrollX; - int maxY = height + mScrollY; - mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET - , mSelectX)); - mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET - , mSelectY)); - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "moveSelection" - + " mSelectX=" + mSelectX - + " mSelectY=" + mSelectY - + " mScrollX=" + mScrollX - + " mScrollY=" + mScrollY - + " xRate=" + xRate - + " yRate=" + yRate - ); - } - nativeMoveSelection(viewToContentX(mSelectX), viewToContentY(mSelectY)); - int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET - : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET - : 0; - int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET - : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET - : 0; - pinScrollBy(scrollX, scrollY, true, 0); - Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1); - requestRectangleOnScreen(select); - invalidate(); - } - private int scaleTrackballX(float xRate, int width) { int xMove = (int) (xRate / TRACKBALL_SCALE * width); int nextXMove = xMove; @@ -7328,21 +7245,6 @@ public class WebView extends AbsoluteLayout float yRate = mTrackballRemainsY * 1000 / elapsed; int viewWidth = getViewWidth(); int viewHeight = getViewHeight(); - if (mSelectingText) { - if (!mDrawSelectionPointer) { - // The last selection was made by touch, disabling drawing the - // selection pointer. Allow the trackball to adjust the - // position of the touch control. - mSelectX = contentToViewX(nativeSelectionX()); - mSelectY = contentToViewY(nativeSelectionY()); - mDrawSelectionPointer = mExtendSelection = true; - nativeSetExtendSelection(); - } - moveSelection(scaleTrackballX(xRate, viewWidth), - scaleTrackballY(yRate, viewHeight)); - mTrackballRemainsX = mTrackballRemainsY = 0; - return; - } float ax = Math.abs(xRate); float ay = Math.abs(yRate); float maxA = Math.max(ax, ay); @@ -9204,6 +9106,18 @@ public class WebView extends AbsoluteLayout mInputConnection.setSelection(data.mStart, data.mEnd); } } + + nativeSetTextSelection(mNativeClass, data.mSelectTextPtr); + if (data.mSelectTextPtr != 0) { + if (!mSelectingText) { + setupWebkitSelect(); + } else if (!mSelectionStarted) { + syncSelectionCursors(); + } + } else { + selectionDone(); + } + invalidate(); } // Class used to use a dropdown for a <select> element @@ -9952,7 +9866,6 @@ public class WebView extends AbsoluteLayout private native boolean nativeMoveCursor(int keyCode, int count, boolean noScroll); private native int nativeMoveGeneration(); - private native void nativeMoveSelection(int x, int y); /** * @return true if the page should get the shift and arrow keys, rather * than select text/navigation. @@ -9962,15 +9875,8 @@ public class WebView extends AbsoluteLayout */ private native boolean nativePageShouldHandleShiftAndArrows(); private native boolean nativePointInNavCache(int x, int y, int slop); - // Like many other of our native methods, you must make sure that - // mNativeClass is not null before calling this method. - private native void nativeResetSelection(); - private native Point nativeSelectableText(); - private native void nativeSelectAll(); private native void nativeSelectBestAt(Rect rect); private native void nativeSelectAt(int x, int y); - private native int nativeSelectionX(); - private native int nativeSelectionY(); private native int nativeFindIndex(); private native void nativeSetExtendSelection(); private native void nativeSetFindIsEmpty(); @@ -10026,12 +9932,14 @@ public class WebView extends AbsoluteLayout private native int nativeGetBackgroundColor(); native boolean nativeSetProperty(String key, String value); native String nativeGetProperty(String key); - private native void nativeGetTextSelectionRegion(int instance, Region region); - private native void nativeGetSelectionHandles(int instance, int[] handles); /** * See {@link ComponentCallbacks2} for the trim levels and descriptions */ private static native void nativeOnTrimMemory(int level); private static native void nativeSetPauseDrawing(int instance, boolean pause); private static native boolean nativeDisableNavcache(); + private static native void nativeSetTextSelection(int instance, int selection); + private static native int nativeGetHandleLayerId(int instance, int handle, + Rect cursorLocation); + private static native boolean nativeIsBaseFirst(int instance); } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index fe51581..395a638 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -835,12 +835,14 @@ public final class WebViewCore { } static class TextSelectionData { - public TextSelectionData(int start, int end) { + public TextSelectionData(int start, int end, int selectTextPtr) { mStart = start; mEnd = end; + mSelectTextPtr = selectTextPtr; } int mStart; int mEnd; + int mSelectTextPtr; } static class TouchUpData { @@ -1118,6 +1120,9 @@ public final class WebViewCore { static final int COPY_TEXT = 210; static final int DELETE_TEXT = 211; static final int INSERT_TEXT = 212; + static final int SELECT_TEXT = 213; + static final int SELECT_WORD_AT = 214; + static final int SELECT_ALL = 215; // Private handler for WebCore messages. private Handler mHandler; @@ -1194,7 +1199,6 @@ public final class WebViewCore { mSettings.onDestroyed(); mNativeClass = 0; mWebView = null; - WebCoreThreadWatchdog.quit(); } break; @@ -1747,6 +1751,25 @@ public final class WebViewCore { case INSERT_TEXT: nativeInsertText(mNativeClass, (String) msg.obj); break; + case SELECT_TEXT: { + int[] args = (int[]) msg.obj; + if (args == null) { + nativeClearTextSelection(mNativeClass); + } else { + nativeSelectText(mNativeClass, args[0], + args[1], args[2], args[3]); + } + break; + } + case SELECT_WORD_AT: { + int x = msg.arg1; + int y = msg.arg2; + nativeSelectWordAt(mNativeClass, x, y); + break; + } + case SELECT_ALL: + nativeSelectAll(mNativeClass); + break; } } }; @@ -2700,11 +2723,11 @@ public final class WebViewCore { // called by JNI private void updateTextSelection(int pointer, int start, int end, - int textGeneration) { + int textGeneration, int selectionPtr) { if (mWebView != null) { Message.obtain(mWebView.mPrivateHandler, WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration, - new TextSelectionData(start, end)).sendToTarget(); + new TextSelectionData(start, end, selectionPtr)).sendToTarget(); } } @@ -2771,7 +2794,7 @@ public final class WebViewCore { if (mWebView != null) { Message.obtain(mWebView.mPrivateHandler, WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer, - textGeneration, new TextSelectionData(selStart, selEnd)) + textGeneration, new TextSelectionData(selStart, selEnd, 0)) .sendToTarget(); } } @@ -3026,4 +3049,9 @@ public final class WebViewCore { */ private native String nativeGetText(int nativeClass, int startX, int startY, int endX, int endY); + private native void nativeSelectText(int nativeClass, + int startX, int startY, int endX, int endY); + private native void nativeClearTextSelection(int nativeClass); + private native void nativeSelectWordAt(int nativeClass, int x, int y); + private native void nativeSelectAll(int nativeClass); } diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 7d58011..984ec79 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -842,9 +842,11 @@ public class GridLayout extends ViewGroup { * @hide */ @Override - protected void onChildVisibilityChanged(View child, int visibility) { - super.onChildVisibilityChanged(child, visibility); - invalidateStructure(); + protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) { + super.onChildVisibilityChanged(child, oldVisibility, newVisibility); + if (oldVisibility == GONE || newVisibility == GONE) { + invalidateStructure(); + } } // Measurement diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index e20d12a..67fd059 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1651,6 +1651,7 @@ public class ListView extends AbsListView { // are focusable if (mItemsCanFocus && hasFocus() && !sel.hasFocus()) { final boolean focusWasTaken = (sel == focusLayoutRestoreDirectChild && + focusLayoutRestoreView != null && focusLayoutRestoreView.requestFocus()) || sel.requestFocus(); if (!focusWasTaken) { // selected item didn't take focus, fine, but still want diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index d395fb2..84e86af 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -56,13 +56,13 @@ import com.android.internal.R; /** * A widget that enables the user to select a number form a predefined range. * The widget presents an input field and up and down buttons for selecting the - * current value. Pressing/long pressing the up and down buttons increments and + * current value. Pressing/long-pressing the up and down buttons increments and * decrements the current value respectively. Touching the input field shows a - * scroll wheel, tapping on which while shown and not moving allows direct edit - * of the current value. Sliding motions up or down hide the buttons and the - * input field, show the scroll wheel, and rotate the latter. Flinging is + * scroll wheel, which when touched allows direct edit + * of the current value. Sliding gestures up or down hide the buttons and the + * input filed, show and rotate the scroll wheel. Flinging is * also supported. The widget enables mapping from positions to strings such - * that instead the position index the corresponding string is displayed. + * that, instead of the position index, the corresponding string is displayed. * <p> * For an example of using this widget, see {@link android.widget.TimePicker}. * </p> diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 40d8a77..164bc64 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -256,10 +256,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private float mShadowRadius, mShadowDx, mShadowDy; - private static final int PREDRAW_NOT_REGISTERED = 0; - private static final int PREDRAW_PENDING = 1; - private static final int PREDRAW_DONE = 2; - private int mPreDrawState = PREDRAW_NOT_REGISTERED; + private boolean mPreDrawRegistered; private TextUtils.TruncateAt mEllipsize = null; @@ -365,8 +362,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private SpellChecker mSpellChecker; - private boolean mSoftInputShownOnFocus = true; - // The alignment to pass to Layout, or null if not resolved. private Layout.Alignment mLayoutAlignment; @@ -2384,29 +2379,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Sets whether the soft input method will be made visible when this - * TextView gets focused. The default is true. - * - * @attr ref android.R.styleable#TextView_softInputShownOnFocus - * @hide - */ - @android.view.RemotableViewMethod - public final void setSoftInputShownOnFocus(boolean show) { - mSoftInputShownOnFocus = show; - } - - /** - * Returns whether the soft input method will be made visible when this - * TextView gets focused. The default is true. - * - * @attr ref android.R.styleable#TextView_softInputShownOnFocus - * @hide - */ - public final boolean getSoftInputShownOnFocus() { - return mSoftInputShownOnFocus; - } - - /** * Returns the list of URLSpans attached to the text * (by {@link Linkify} or otherwise) if any. You can call * {@link URLSpan#getURL} on them to find where they link to @@ -4387,26 +4359,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void registerForPreDraw() { - final ViewTreeObserver observer = getViewTreeObserver(); - - if (mPreDrawState == PREDRAW_NOT_REGISTERED) { - observer.addOnPreDrawListener(this); - mPreDrawState = PREDRAW_PENDING; - } else if (mPreDrawState == PREDRAW_DONE) { - mPreDrawState = PREDRAW_PENDING; + if (!mPreDrawRegistered) { + getViewTreeObserver().addOnPreDrawListener(this); + mPreDrawRegistered = true; } - - // else state is PREDRAW_PENDING, so keep waiting. } /** * {@inheritDoc} */ public boolean onPreDraw() { - if (mPreDrawState != PREDRAW_PENDING) { - return true; - } - if (mLayout == null) { assumeLayout(); } @@ -4457,7 +4419,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener startSelectionActionMode(); } - mPreDrawState = PREDRAW_DONE; + getViewTreeObserver().removeOnPreDrawListener(this); + mPreDrawRegistered = false; + return !changed; } @@ -4492,10 +4456,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - final ViewTreeObserver observer = getViewTreeObserver(); - if (mPreDrawState != PREDRAW_NOT_REGISTERED) { - observer.removeOnPreDrawListener(this); - mPreDrawState = PREDRAW_NOT_REGISTERED; + if (mPreDrawRegistered) { + getViewTreeObserver().removeOnPreDrawListener(this); + mPreDrawRegistered = false; } if (mError != null) { @@ -4768,12 +4731,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected void onDraw(Canvas canvas) { - if (mPreDrawState == PREDRAW_DONE) { - final ViewTreeObserver observer = getViewTreeObserver(); - observer.removeOnPreDrawListener(this); - mPreDrawState = PREDRAW_NOT_REGISTERED; - } - if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return; restartMarqueeIfNeeded(); @@ -5040,7 +4997,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mTextDisplayList == null || !mTextDisplayList.isValid() || !mTextDisplayListIsValid) { if (mTextDisplayList == null) { - mTextDisplayList = getHardwareRenderer().createDisplayList(); + mTextDisplayList = getHardwareRenderer().createDisplayList("Text"); } final HardwareCanvas hardwareCanvas = mTextDisplayList.start(); @@ -5539,7 +5496,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener && mLayout != null && onCheckIsTextEditor()) { InputMethodManager imm = InputMethodManager.peekInstance(); viewClicked(imm); - if (imm != null && mSoftInputShownOnFocus) { + if (imm != null) { imm.showSoftInput(this, 0); } } @@ -8395,7 +8352,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Show the IME, except when selecting in read-only text. final InputMethodManager imm = InputMethodManager.peekInstance(); viewClicked(imm); - if (!mTextIsSelectable && mSoftInputShownOnFocus) { + if (!mTextIsSelectable) { handled |= imm != null && imm.showSoftInput(this, 0); } @@ -10190,7 +10147,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } final boolean selectionStarted = mSelectionActionMode != null || willExtract; - if (selectionStarted && !mTextIsSelectable && mSoftInputShownOnFocus) { + if (selectionStarted && !mTextIsSelectable) { // Show the IME to be able to replace text, except when selecting non editable text. final InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { diff --git a/core/java/com/android/internal/util/FastMath.java b/core/java/com/android/internal/util/FastMath.java index efd0871..88a17e6 100644 --- a/core/java/com/android/internal/util/FastMath.java +++ b/core/java/com/android/internal/util/FastMath.java @@ -26,8 +26,8 @@ public class FastMath { * thought it may return slightly different results. It does not try to * handle (in any meaningful way) NaN or infinities. */ - public static int round(float x) { - long lx = (long)(x * (65536 * 256f)); - return (int)((lx + 0x800000) >> 24); + public static int round(float value) { + long lx = (long) (value * (65536 * 256f)); + return (int) ((lx + 0x800000) >> 24); } } diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java index ebd355a..d51ced1 100644 --- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java +++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java @@ -653,6 +653,7 @@ public class MultiWaveView extends View { case MotionEvent.ACTION_CANCEL: handleMove(event); + handleCancel(event); handled = true; break; } @@ -678,6 +679,12 @@ public class MultiWaveView extends View { if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE"); switchToState(STATE_FINISH, event.getX(), event.getY()); } + + private void handleCancel(MotionEvent event) { + if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL"); + mActiveTarget = -1; // Drop the active target if canceled. + switchToState(STATE_FINISH, event.getX(), event.getY()); + } private void handleMove(MotionEvent event) { if (!mDragging) { |
