summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/animation/LayoutTransition.java314
-rw-r--r--core/java/android/app/Activity.java4
-rw-r--r--core/java/android/app/ActivityManagerNative.java38
-rw-r--r--core/java/android/app/IActivityManager.java5
-rw-r--r--core/java/android/app/IProcessObserver.aidl25
-rw-r--r--core/java/android/app/IThumbnailRetriever.aidl1
-rw-r--r--core/java/android/app/UiModeManager.java3
-rw-r--r--core/java/android/content/Context.java3
-rw-r--r--core/java/android/content/IOnPrimaryClipChangedListener.aidl3
-rw-r--r--core/java/android/content/res/Configuration.java2
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl3
-rw-r--r--core/java/android/net/NetworkPolicyManager.java5
-rw-r--r--core/java/android/net/NetworkStats.java54
-rw-r--r--core/java/android/net/TrafficStats.java72
-rw-r--r--core/java/android/nfc/INdefPushCallback.aidl28
-rw-r--r--core/java/android/nfc/INfcAdapter.aidl2
-rw-r--r--core/java/android/nfc/NfcAdapter.java71
-rw-r--r--core/java/android/os/INetworkManagementService.aidl6
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java56
-rw-r--r--core/java/android/os/Process.java14
-rw-r--r--core/java/android/os/StrictMode.java7
-rw-r--r--core/java/android/os/storage/StorageVolume.java5
-rw-r--r--core/java/android/provider/Calendar.java26
-rwxr-xr-x[-rw-r--r--]core/java/android/server/BluetoothService.java2
-rw-r--r--core/java/android/speech/tts/PlaybackSynthesisRequest.java89
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java8
-rw-r--r--core/java/android/text/method/ArrowKeyMovementMethod.java6
-rw-r--r--core/java/android/view/HardwareRenderer.java5
-rw-r--r--core/java/android/view/MotionEvent.java4
-rw-r--r--core/java/android/view/View.java20
-rw-r--r--core/java/android/view/ViewAncestor.java220
-rw-r--r--core/java/android/view/ViewDebug.java6
-rw-r--r--core/java/android/view/ViewGroup.java14
-rw-r--r--core/java/android/view/ViewPropertyAnimator.java96
-rw-r--r--core/java/android/webkit/JWebCoreJavaBridge.java7
-rw-r--r--core/java/android/webkit/L10nUtils.java3
-rw-r--r--core/java/android/webkit/WebSettings.java21
-rw-r--r--core/java/android/webkit/WebView.java19
-rw-r--r--core/java/android/webkit/WebViewCore.java48
-rw-r--r--core/java/android/webkit/ZoomManager.java14
-rw-r--r--core/java/android/widget/LinearLayout.java143
-rw-r--r--core/java/android/widget/PopupWindow.java3
-rw-r--r--core/java/android/widget/StackView.java7
-rw-r--r--core/java/android/widget/TextView.java139
44 files changed, 1126 insertions, 495 deletions
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index adfda8e..d25de97 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -18,6 +18,7 @@ package android.animation;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
@@ -70,8 +71,9 @@ import java.util.List;
* moving as a result of the layout event) as well as the values that are changing (such as the
* position and size of that object). The actual values that are pushed to each animation
* depends on what properties are specified for the animation. For example, the default
- * CHANGE_APPEARING animation animates <code>left</code>, <code>top</code>, <code>right</code>,
- * and <code>bottom</code>. Values for these properties are updated with the pre- and post-layout
+ * CHANGE_APPEARING animation animates the <code>left</code>, <code>top</code>, <code>right</code>,
+ * <code>bottom</code>, <code>scrollX</code>, and <code>scrollY</code> properties.
+ * Values for these properties are updated with the pre- and post-layout
* values when the transition begins. Custom animations will be similarly populated with
* the target and values being animated, assuming they use ObjectAnimator objects with
* property names that are known on the target object.</p>
@@ -210,6 +212,14 @@ public class LayoutTransition {
*/
private ArrayList<TransitionListener> mListeners;
+ /**
+ * Controls whether changing animations automatically animate the parent hierarchy as well.
+ * This behavior prevents artifacts when wrap_content layouts snap to the end state as the
+ * transition begins, causing visual glitches and clipping.
+ * Default value is true.
+ */
+ private boolean mAnimateParentHierarchy = true;
+
/**
* Constructs a LayoutTransition object. By default, the object will listen to layout
@@ -223,14 +233,17 @@ public class LayoutTransition {
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1);
PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1);
+ PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1);
+ PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1);
defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder(this,
- pvhLeft, pvhTop, pvhRight, pvhBottom);
+ pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY);
defaultChangeIn.setDuration(DEFAULT_DURATION);
defaultChangeIn.setStartDelay(mChangingAppearingDelay);
defaultChangeIn.setInterpolator(mChangingAppearingInterpolator);
defaultChangeOut = defaultChangeIn.clone();
defaultChangeOut.setStartDelay(mChangingDisappearingDelay);
defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator);
+
defaultFadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f);
defaultFadeIn.setDuration(DEFAULT_DURATION);
defaultFadeIn.setStartDelay(mAppearingDelay);
@@ -572,122 +585,24 @@ public class LayoutTransition {
// only animate the views not being added or removed
if (child != newView) {
-
-
- // Make a copy of the appropriate animation
- final Animator anim = baseAnimator.clone();
-
- // Set the target object for the animation
- anim.setTarget(child);
-
- // A ObjectAnimator (or AnimatorSet of them) can extract start values from
- // its target object
- anim.setupStartValues();
-
- // If there's an animation running on this view already, cancel it
- Animator currentAnimation = pendingAnimations.get(child);
- if (currentAnimation != null) {
- currentAnimation.cancel();
- pendingAnimations.remove(child);
+ setupChangeAnimation(parent, changeReason, baseAnimator, duration, child);
+ }
+ }
+ if (mAnimateParentHierarchy) {
+ ViewGroup tempParent = parent;
+ while (tempParent != null) {
+ ViewParent parentParent = tempParent.getParent();
+ if (parentParent instanceof ViewGroup) {
+ setupChangeAnimation((ViewGroup)parentParent, changeReason, baseAnimator,
+ duration, tempParent);
+ tempParent = (ViewGroup) parentParent;
+ } else {
+ tempParent = null;
}
- // Cache the animation in case we need to cancel it later
- pendingAnimations.put(child, anim);
-
- // For the animations which don't get started, we have to have a means of
- // removing them from the cache, lest we leak them and their target objects.
- // We run an animator for the default duration+100 (an arbitrary time, but one
- // which should far surpass the delay between setting them up here and
- // handling layout events which start them.
- ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f).
- setDuration(duration+100);
- pendingAnimRemover.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- pendingAnimations.remove(child);
- }
- });
- pendingAnimRemover.start();
-
- // Add a listener to track layout changes on this view. If we don't get a callback,
- // then there's nothing to animate.
- final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() {
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
-
- // Tell the animation to extract end values from the changed object
- anim.setupEndValues();
-
- long startDelay;
- if (changeReason == APPEARING) {
- startDelay = mChangingAppearingDelay + staggerDelay;
- staggerDelay += mChangingAppearingStagger;
- } else {
- startDelay = mChangingDisappearingDelay + staggerDelay;
- staggerDelay += mChangingDisappearingStagger;
- }
- anim.setStartDelay(startDelay);
- anim.setDuration(duration);
-
- Animator prevAnimation = currentChangingAnimations.get(child);
- if (prevAnimation != null) {
- prevAnimation.cancel();
- }
- Animator pendingAnimation = pendingAnimations.get(child);
- if (pendingAnimation != null) {
- pendingAnimations.remove(child);
- }
- // Cache the animation in case we need to cancel it later
- currentChangingAnimations.put(child, anim);
-
- if (anim instanceof ObjectAnimator) {
- ((ObjectAnimator) anim).setCurrentPlayTime(0);
- }
- anim.start();
-
- // this only removes listeners whose views changed - must clear the
- // other listeners later
- child.removeOnLayoutChangeListener(this);
- layoutChangeListenerMap.remove(child);
- }
- };
- // Remove the animation from the cache when it ends
- anim.addListener(new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationStart(Animator animator) {
- if (mListeners != null) {
- for (TransitionListener listener : mListeners) {
- listener.startTransition(LayoutTransition.this, parent, child,
- changeReason == APPEARING ?
- CHANGE_APPEARING : CHANGE_DISAPPEARING);
- }
- }
- }
- @Override
- public void onAnimationCancel(Animator animator) {
- child.removeOnLayoutChangeListener(listener);
- layoutChangeListenerMap.remove(child);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- currentChangingAnimations.remove(child);
- if (mListeners != null) {
- for (TransitionListener listener : mListeners) {
- listener.endTransition(LayoutTransition.this, parent, child,
- changeReason == APPEARING ?
- CHANGE_APPEARING : CHANGE_DISAPPEARING);
- }
- }
- }
- });
-
- child.addOnLayoutChangeListener(listener);
- // cache the listener for later removal
- layoutChangeListenerMap.put(child, listener);
}
}
+
// This is the cleanup step. When we get this rendering event, we know that all of
// the appropriate animations have been set up and run. Now we can clear out the
// layout listeners.
@@ -706,6 +621,175 @@ public class LayoutTransition {
}
/**
+ * This flag controls whether CHANGE_APPEARING or CHANGE_DISAPPEARING animations will
+ * cause the same changing animation to be run on the parent hierarchy as well. This allows
+ * containers of transitioning views to also transition, which may be necessary in situations
+ * where the containers bounds change between the before/after states and may clip their
+ * children during the transition animations. For example, layouts with wrap_content will
+ * adjust their bounds according to the dimensions of their children.
+ *
+ * @param animateParentHierarchy A boolean value indicating whether the parents of
+ * transitioning views should also be animated during the transition. Default value is true.
+ */
+ public void setAnimateParentHierarchy(boolean animateParentHierarchy) {
+ mAnimateParentHierarchy = animateParentHierarchy;
+ }
+
+ /**
+ * Utility function called by runChangingTransition for both the children and the parent
+ * hierarchy.
+ */
+ private void setupChangeAnimation(final ViewGroup parent, final int changeReason,
+ Animator baseAnimator, final long duration, final View child) {
+ // Make a copy of the appropriate animation
+ final Animator anim = baseAnimator.clone();
+
+ // Set the target object for the animation
+ anim.setTarget(child);
+
+ // A ObjectAnimator (or AnimatorSet of them) can extract start values from
+ // its target object
+ anim.setupStartValues();
+
+ // If there's an animation running on this view already, cancel it
+ Animator currentAnimation = pendingAnimations.get(child);
+ if (currentAnimation != null) {
+ currentAnimation.cancel();
+ pendingAnimations.remove(child);
+ }
+ // Cache the animation in case we need to cancel it later
+ pendingAnimations.put(child, anim);
+
+ // For the animations which don't get started, we have to have a means of
+ // removing them from the cache, lest we leak them and their target objects.
+ // We run an animator for the default duration+100 (an arbitrary time, but one
+ // which should far surpass the delay between setting them up here and
+ // handling layout events which start them.
+ ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f).
+ setDuration(duration + 100);
+ pendingAnimRemover.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ pendingAnimations.remove(child);
+ }
+ });
+ pendingAnimRemover.start();
+
+ // Add a listener to track layout changes on this view. If we don't get a callback,
+ // then there's nothing to animate.
+ final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() {
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+
+ // Tell the animation to extract end values from the changed object
+ anim.setupEndValues();
+ if (anim instanceof ValueAnimator) {
+ boolean valuesDiffer = false;
+ ValueAnimator valueAnim = (ValueAnimator)anim;
+ PropertyValuesHolder[] oldValues = valueAnim.getValues();
+ for (int i = 0; i < oldValues.length; ++i) {
+ PropertyValuesHolder pvh = oldValues[i];
+ KeyframeSet keyframeSet = pvh.mKeyframeSet;
+ if (keyframeSet.mFirstKeyframe == null ||
+ keyframeSet.mLastKeyframe == null ||
+ !keyframeSet.mFirstKeyframe.getValue().equals(
+ keyframeSet.mLastKeyframe.getValue())) {
+ valuesDiffer = true;
+ }
+ }
+ if (!valuesDiffer) {
+ return;
+ }
+ }
+
+ long startDelay;
+ if (changeReason == APPEARING) {
+ startDelay = mChangingAppearingDelay + staggerDelay;
+ staggerDelay += mChangingAppearingStagger;
+ } else {
+ startDelay = mChangingDisappearingDelay + staggerDelay;
+ staggerDelay += mChangingDisappearingStagger;
+ }
+ anim.setStartDelay(startDelay);
+ anim.setDuration(duration);
+
+ Animator prevAnimation = currentChangingAnimations.get(child);
+ if (prevAnimation != null) {
+ prevAnimation.cancel();
+ }
+ Animator pendingAnimation = pendingAnimations.get(child);
+ if (pendingAnimation != null) {
+ pendingAnimations.remove(child);
+ }
+ // Cache the animation in case we need to cancel it later
+ currentChangingAnimations.put(child, anim);
+
+ parent.requestTransitionStart(LayoutTransition.this);
+
+ // this only removes listeners whose views changed - must clear the
+ // other listeners later
+ child.removeOnLayoutChangeListener(this);
+ layoutChangeListenerMap.remove(child);
+ }
+ };
+ // Remove the animation from the cache when it ends
+ anim.addListener(new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationStart(Animator animator) {
+ if (mListeners != null) {
+ for (TransitionListener listener : mListeners) {
+ listener.startTransition(LayoutTransition.this, parent, child,
+ changeReason == APPEARING ?
+ CHANGE_APPEARING : CHANGE_DISAPPEARING);
+ }
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ child.removeOnLayoutChangeListener(listener);
+ layoutChangeListenerMap.remove(child);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ currentChangingAnimations.remove(child);
+ if (mListeners != null) {
+ for (TransitionListener listener : mListeners) {
+ listener.endTransition(LayoutTransition.this, parent, child,
+ changeReason == APPEARING ?
+ CHANGE_APPEARING : CHANGE_DISAPPEARING);
+ }
+ }
+ }
+ });
+
+ child.addOnLayoutChangeListener(listener);
+ // cache the listener for later removal
+ layoutChangeListenerMap.put(child, listener);
+ }
+
+ /**
+ * Starts the animations set up for a CHANGING transition. We separate the setup of these
+ * animations from actually starting them, to avoid side-effects that starting the animations
+ * may have on the properties of the affected objects. After setup, we tell the affected parent
+ * that this transition should be started. The parent informs its ViewAncestor, which then
+ * starts the transition after the current layout/measurement phase, just prior to drawing
+ * the view hierarchy.
+ *
+ * @hide
+ */
+ public void startChangingAnimations() {
+ for (Animator anim : currentChangingAnimations.values()) {
+ if (anim instanceof ObjectAnimator) {
+ ((ObjectAnimator) anim).setCurrentPlayTime(0);
+ }
+ anim.start();
+ }
+ }
+
+ /**
* Returns true if animations are running which animate layout-related properties. This
* essentially means that either CHANGE_APPEARING or CHANGE_DISAPPEARING animations
* are running, since these animations operate on layout-related properties.
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 87369ab..3877bd0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1398,6 +1398,10 @@ public class Activity extends ContextThemeWrapper
public void onConfigurationChanged(Configuration newConfig) {
mCalled = true;
+ if (mActionBar != null) {
+ mActionBar.onConfigurationChanged(newConfig);
+ }
+
mFragments.dispatchConfigurationChanged(newConfig);
if (mWindow != null) {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f2c9796..2a0d798 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1467,6 +1467,22 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case REGISTER_PROCESS_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IProcessObserver observer = IProcessObserver.Stub.asInterface(
+ data.readStrongBinder());
+ registerProcessObserver(observer);
+ return true;
+ }
+
+ case UNREGISTER_PROCESS_OBSERVER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IProcessObserver observer = IProcessObserver.Stub.asInterface(
+ data.readStrongBinder());
+ unregisterProcessObserver(observer);
+ return true;
+ }
+
}
return super.onTransact(code, data, reply, flags);
@@ -3300,5 +3316,27 @@ class ActivityManagerProxy implements IActivityManager
return result;
}
+ public void registerProcessObserver(IProcessObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(REGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ mRemote.transact(UNREGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 54c3422..1f53c0e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -355,6 +355,9 @@ public interface IActivityManager extends IInterface {
public boolean removeTask(int taskId, int flags) throws RemoteException;
+ public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
+ public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -577,4 +580,6 @@ public interface IActivityManager extends IInterface {
int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+127;
int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+128;
int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129;
+ int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+130;
+ int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131;
}
diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl
new file mode 100644
index 0000000..2094294
--- /dev/null
+++ b/core/java/android/app/IProcessObserver.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2011 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.app;
+
+/** {@hide} */
+oneway interface IProcessObserver {
+
+ void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities);
+ void onProcessDied(int pid, int uid);
+
+}
diff --git a/core/java/android/app/IThumbnailRetriever.aidl b/core/java/android/app/IThumbnailRetriever.aidl
index 2a6737d..410cc20 100644
--- a/core/java/android/app/IThumbnailRetriever.aidl
+++ b/core/java/android/app/IThumbnailRetriever.aidl
@@ -18,6 +18,7 @@ import android.graphics.Bitmap;
/**
* System private API for retrieving thumbnails
+ * {@hide}
*/
interface IThumbnailRetriever {
Bitmap getThumbnail(int index);
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 95451d6..71f6445 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -167,7 +167,8 @@ public class UiModeManager {
* Return the current running mode type. May be one of
* {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL},
* {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or
- * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR},
+ * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, or
+ * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TV}.
*/
public int getCurrentModeType() {
if (mService != null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4c7d87f..a660bd7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1544,6 +1544,9 @@ public abstract class Context {
*/
public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
+ /** {@hide} */
+ public static final String NETWORK_POLICY_SERVICE = "netpolicy";
+
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.net.wifi.WifiManager} for handling management of
diff --git a/core/java/android/content/IOnPrimaryClipChangedListener.aidl b/core/java/android/content/IOnPrimaryClipChangedListener.aidl
index fb42a45..46d7f7c 100644
--- a/core/java/android/content/IOnPrimaryClipChangedListener.aidl
+++ b/core/java/android/content/IOnPrimaryClipChangedListener.aidl
@@ -16,6 +16,9 @@
package android.content;
+/**
+ * {@hide}
+ */
oneway interface IOnPrimaryClipChangedListener {
void dispatchPrimaryClipChanged();
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 12ec258..51a7115 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -226,6 +226,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
public static final int UI_MODE_TYPE_NORMAL = 0x01;
public static final int UI_MODE_TYPE_DESK = 0x02;
public static final int UI_MODE_TYPE_CAR = 0x03;
+ public static final int UI_MODE_TYPE_TELEVISION = 0x04;
public static final int UI_MODE_NIGHT_MASK = 0x30;
public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
@@ -367,6 +368,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break;
case UI_MODE_TYPE_DESK: sb.append(" desk"); break;
case UI_MODE_TYPE_CAR: sb.append(" car"); break;
+ case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break;
default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break;
}
switch ((uiMode&UI_MODE_NIGHT_MASK)) {
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index fa6eae5..d9351ee 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -23,9 +23,6 @@ package android.net;
*/
interface INetworkPolicyManager {
- void onForegroundActivitiesChanged(int uid, int pid, boolean foregroundActivities);
- void onProcessDied(int uid, int pid);
-
void setUidPolicy(int uid, int policy);
int getUidPolicy(int uid);
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 2312bd9..1913aa7 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -16,6 +16,7 @@
package android.net;
+import android.content.Context;
import android.os.RemoteException;
/**
@@ -43,6 +44,10 @@ public class NetworkPolicyManager {
mService = service;
}
+ public static NetworkPolicyManager getSystemService(Context context) {
+ return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE);
+ }
+
/**
* Set policy flags for specific UID.
*
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 4430e00..0f207bc 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -36,6 +36,9 @@ public class NetworkStats implements Parcelable {
/** {@link #uid} value when entry is summarized over all UIDs. */
public static final int UID_ALL = 0;
+ // NOTE: data should only be accounted for once in this structure; if data
+ // is broken out, the summarized version should not be included.
+
/**
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
* generated.
@@ -81,12 +84,13 @@ public class NetworkStats implements Parcelable {
mTx = new long[size];
}
- public void addEntry(String iface, int uid, long rx, long tx) {
+ public Builder addEntry(String iface, int uid, long rx, long tx) {
mIface[mIndex] = iface;
mUid[mIndex] = uid;
mRx[mIndex] = rx;
mTx[mIndex] = tx;
mIndex++;
+ return this;
}
public NetworkStats build() {
@@ -97,11 +101,17 @@ public class NetworkStats implements Parcelable {
}
}
+ public int length() {
+ // length is identical for all fields
+ return iface.length;
+ }
+
/**
* Find first stats index that matches the requested parameters.
*/
public int findIndex(String iface, int uid) {
- for (int i = 0; i < this.iface.length; i++) {
+ final int length = length();
+ for (int i = 0; i < length; i++) {
if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
return i;
}
@@ -109,13 +119,38 @@ public class NetworkStats implements Parcelable {
return -1;
}
- private static boolean equal(Object a, Object b) {
- return a == b || (a != null && a.equals(b));
+ /**
+ * Subtract the given {@link NetworkStats}, effectively leaving the delta
+ * between two snapshots in time. Assumes that statistics rows collect over
+ * time, and that none of them have disappeared.
+ */
+ public NetworkStats subtract(NetworkStats value) {
+ // result will have our rows, but no meaningful timestamp
+ final int length = length();
+ final NetworkStats.Builder result = new NetworkStats.Builder(-1, length);
+
+ for (int i = 0; i < length; i++) {
+ final String iface = this.iface[i];
+ final int uid = this.uid[i];
+
+ // find remote row that matches, and subtract
+ final int j = value.findIndex(iface, uid);
+ if (j == -1) {
+ // newly appearing row, return entire value
+ result.addEntry(iface, uid, this.rx[i], this.tx[i]);
+ } else {
+ // existing row, subtract remote value
+ final long rx = this.rx[i] - value.rx[j];
+ final long tx = this.tx[i] - value.tx[j];
+ result.addEntry(iface, uid, rx, tx);
+ }
+ }
+
+ return result.build();
}
- /** {@inheritDoc} */
- public int describeContents() {
- return 0;
+ private static boolean equal(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
}
public void dump(String prefix, PrintWriter pw) {
@@ -138,6 +173,11 @@ public class NetworkStats implements Parcelable {
}
/** {@inheritDoc} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(elapsedRealtime);
dest.writeStringArray(iface);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 7ee7a81..c0ff734 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -16,6 +16,12 @@
package android.net;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
import dalvik.system.BlockGuard;
import java.net.Socket;
@@ -36,6 +42,17 @@ public class TrafficStats {
public final static int UNSUPPORTED = -1;
/**
+ * Snapshot of {@link NetworkStats} when the currently active profiling
+ * session started, or {@code null} if no session active.
+ *
+ * @see #startDataProfiling(Context)
+ * @see #stopDataProfiling(Context)
+ */
+ private static NetworkStats sActiveProfilingStart;
+
+ private static Object sProfilingLock = new Object();
+
+ /**
* Set active tag to use when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
* <p>
@@ -93,6 +110,44 @@ public class TrafficStats {
}
/**
+ * Start profiling data usage for current UID. Only one profiling session
+ * can be active at a time.
+ *
+ * @hide
+ */
+ public static void startDataProfiling(Context context) {
+ synchronized (sProfilingLock) {
+ if (sActiveProfilingStart != null) {
+ throw new IllegalStateException("already profiling data");
+ }
+
+ // take snapshot in time; we calculate delta later
+ sActiveProfilingStart = getNetworkStatsForUid(context);
+ }
+ }
+
+ /**
+ * Stop profiling data usage for current UID.
+ *
+ * @return Detailed {@link NetworkStats} of data that occurred since last
+ * {@link #startDataProfiling(Context)} call.
+ * @hide
+ */
+ public static NetworkStats stopDataProfiling(Context context) {
+ synchronized (sProfilingLock) {
+ if (sActiveProfilingStart == null) {
+ throw new IllegalStateException("not profiling data");
+ }
+
+ // subtract starting values and return delta
+ final NetworkStats profilingStop = getNetworkStatsForUid(context);
+ final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart);
+ sActiveProfilingStart = null;
+ return profilingDelta;
+ }
+ }
+
+ /**
* Get the total number of packets transmitted through the mobile interface.
*
* @return number of packets. If the statistics are not supported by this device,
@@ -350,4 +405,21 @@ public class TrafficStats {
* {@link #UNSUPPORTED} will be returned.
*/
public static native long getUidUdpRxPackets(int uid);
+
+ /**
+ * Return detailed {@link NetworkStats} for the current UID. Requires no
+ * special permission.
+ */
+ private static NetworkStats getNetworkStatsForUid(Context context) {
+ final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ final INetworkManagementService service = INetworkManagementService.Stub.asInterface(
+ binder);
+
+ final int uid = android.os.Process.myUid();
+ try {
+ return service.getNetworkStatsUidDetail(uid);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl
new file mode 100644
index 0000000..80ba2ed
--- /dev/null
+++ b/core/java/android/nfc/INdefPushCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 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.nfc;
+
+import android.nfc.NdefMessage;
+
+/**
+ * @hide
+ */
+interface INdefPushCallback
+{
+ NdefMessage onConnect();
+ void onMessagePushed();
+}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 870127c..d11fea0 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -25,6 +25,7 @@ import android.nfc.TechListParcel;
import android.nfc.ILlcpSocket;
import android.nfc.ILlcpServiceSocket;
import android.nfc.ILlcpConnectionlessSocket;
+import android.nfc.INdefPushCallback;
import android.nfc.INfcTag;
import android.nfc.IP2pTarget;
import android.nfc.IP2pInitiator;
@@ -51,6 +52,7 @@ interface INfcAdapter
in IntentFilter[] filters, in TechListParcel techLists);
void disableForegroundDispatch(in ComponentName activity);
void enableForegroundNdefPush(in ComponentName activity, in NdefMessage msg);
+ void enableForegroundNdefPushWithCallback(in ComponentName activity, in INdefPushCallback callback);
void disableForegroundNdefPush(in ComponentName activity);
// Non-public methods
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 4689804..738e75f 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -124,7 +124,7 @@ public final class NfcAdapter {
* Intent to start an activity when a tag is discovered.
*
* <p>This intent will not be started when a tag is discovered if any activities respond to
- * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag.
+ * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
@@ -235,6 +235,37 @@ public final class NfcAdapter {
*/
private static final int DISCOVERY_MODE_CARD_EMULATION = 2;
+ /**
+ * Callback passed into {@link #enableForegroundNdefPush(Activity,NdefPushCallback)}. This
+ */
+ public interface NdefPushCallback {
+ /**
+ * Called when a P2P connection is created.
+ */
+ NdefMessage createMessage();
+ /**
+ * Called when the message is pushed.
+ */
+ void onMessagePushed();
+ }
+
+ private static class NdefPushCallbackWrapper extends INdefPushCallback.Stub {
+ private NdefPushCallback mCallback;
+
+ public NdefPushCallbackWrapper(NdefPushCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public NdefMessage onConnect() {
+ return mCallback.createMessage();
+ }
+
+ @Override
+ public void onMessagePushed() {
+ mCallback.onMessagePushed();
+ }
+ }
// Guarded by NfcAdapter.class
private static boolean sIsInitialized = false;
@@ -575,6 +606,44 @@ public final class NfcAdapter {
}
/**
+ * Enable NDEF message push over P2P while this Activity is in the foreground.
+ *
+ * <p>For this to function properly the other NFC device being scanned must
+ * support the "com.android.npp" NDEF push protocol. Support for this
+ * protocol is currently optional for Android NFC devices.
+ *
+ * <p>This method must be called from the main thread.
+ *
+ * <p class="note"><em>NOTE:</em> While foreground NDEF push is active standard tag dispatch is disabled.
+ * Only the foreground activity may receive tag discovered dispatches via
+ * {@link #enableForegroundDispatch}.
+ *
+ * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param activity the foreground Activity
+ * @param callback is called on when the P2P connection is established
+ * @throws IllegalStateException if the Activity is not currently in the foreground
+ * @throws OperationNotSupportedException if this Android device does not support NDEF push
+ */
+ public void enableForegroundNdefPush(Activity activity, NdefPushCallback callback) {
+ if (activity == null || callback == null) {
+ throw new NullPointerException();
+ }
+ if (!activity.isResumed()) {
+ throw new IllegalStateException("Foregorund NDEF push can only be enabled " +
+ "when your activity is resumed");
+ }
+ try {
+ ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
+ mForegroundNdefPushListener);
+ sService.enableForegroundNdefPushWithCallback(activity.getComponentName(),
+ new NdefPushCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ }
+ }
+
+ /**
* Disable NDEF message push over P2P.
*
* <p>After calling {@link #enableForegroundNdefPush}, an activity
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index ecc111b..f17a6f2 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -214,6 +214,12 @@ interface INetworkManagementService
NetworkStats getNetworkStatsDetail();
/**
+ * Return detailed network statistics for the requested UID,
+ * including interface and tag details.
+ */
+ NetworkStats getNetworkStatsUidDetail(int uid);
+
+ /**
* Configures bandwidth throttling on an interface.
*/
void setInterfaceThrottle(String iface, int rxKbps, int txKbps);
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 727fcca..3ea3f56 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -129,7 +129,46 @@ public class ParcelFileDescriptor implements Parcelable {
}
/**
- * Create a new ParcelFileDescriptor from the specified Socket.
+ * Create a new ParcelFileDescriptor from a raw native fd. The new
+ * ParcelFileDescriptor holds a dup of the original fd passed in here,
+ * so you must still close that fd as well as the new ParcelFileDescriptor.
+ *
+ * @param fd The native fd that the ParcelFileDescriptor should dup.
+ *
+ * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
+ * for a dup of the given fd.
+ */
+ public static ParcelFileDescriptor fromFd(int fd) throws IOException {
+ FileDescriptor fdesc = getFileDescriptorFromFd(fd);
+ return new ParcelFileDescriptor(fdesc);
+ }
+
+ // Extracts the file descriptor from the specified socket and returns it untouched
+ private static native FileDescriptor getFileDescriptorFromFd(int fd) throws IOException;
+
+ /**
+ * Take ownership of a raw native fd in to a new ParcelFileDescriptor.
+ * The returned ParcelFileDescriptor now owns the given fd, and will be
+ * responsible for closing it. You must not close the fd yourself.
+ *
+ * @param fd The native fd that the ParcelFileDescriptor should adopt.
+ *
+ * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
+ * for the given fd.
+ */
+ public static ParcelFileDescriptor adoptFd(int fd) {
+ FileDescriptor fdesc = getFileDescriptorFromFdNoDup(fd);
+ return new ParcelFileDescriptor(fdesc);
+ }
+
+ // Extracts the file descriptor from the specified socket and returns it untouched
+ private static native FileDescriptor getFileDescriptorFromFdNoDup(int fd);
+
+ /**
+ * Create a new ParcelFileDescriptor from the specified Socket. The new
+ * ParcelFileDescriptor holds a dup of the original FileDescriptor in
+ * the Socket, so you must still close the Socket as well as the new
+ * ParcelFileDescriptor.
*
* @param socket The Socket whose FileDescriptor is used to create
* a new ParcelFileDescriptor.
@@ -163,17 +202,14 @@ public class ParcelFileDescriptor implements Parcelable {
*/
public static ParcelFileDescriptor[] createPipe() throws IOException {
FileDescriptor[] fds = new FileDescriptor[2];
- int res = createPipeNative(fds);
- if (res == 0) {
- ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2];
- pfds[0] = new ParcelFileDescriptor(fds[0]);
- pfds[1] = new ParcelFileDescriptor(fds[1]);
- return pfds;
- }
- throw new IOException("Unable to create pipe: errno=" + -res);
+ createPipeNative(fds);
+ ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2];
+ pfds[0] = new ParcelFileDescriptor(fds[0]);
+ pfds[1] = new ParcelFileDescriptor(fds[1]);
+ return pfds;
}
- private static native int createPipeNative(FileDescriptor[] outFds);
+ private static native void createPipeNative(FileDescriptor[] outFds) throws IOException;
/**
* @hide Please use createPipe() or ContentProvider.openPipeHelper().
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index f85df6c..d475f36 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -634,6 +634,20 @@ public class Process {
}
/**
+ * Returns the parent process id for a currently running process.
+ * @param pid the process id
+ * @return the parent process id of the process, or -1 if the process is not running.
+ * @hide
+ */
+ public static final int getParentPid(int pid) {
+ String[] procStatusLabels = { "PPid:" };
+ long[] procStatusValues = new long[1];
+ procStatusValues[0] = -1;
+ Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
+ return (int) procStatusValues[0];
+ }
+
+ /**
* Set the priority of a thread, based on Linux priorities.
*
* @param tid The identifier of the thread/process to change.
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 1375a29..01c640a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1481,6 +1481,13 @@ public final class StrictMode {
onVmPolicyViolation(message, originStack);
}
+ /**
+ * @hide
+ */
+ public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) {
+ onVmPolicyViolation(null, originStack);
+ }
+
// Map from VM violation fingerprint to uptime millis.
private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>();
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index d68e6fb..bc6e993 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -36,6 +36,11 @@ public class StorageVolume implements Parcelable {
private final int mMtpReserveSpace;
private int mStorageId;
+ // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
+ // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED,
+ // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
+ public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
+
public StorageVolume(String path, String description,
boolean removable, boolean emulated, int mtpReserveSpace) {
mPath = path;
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 4141879..20614dc 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -616,18 +616,6 @@ public final class Calendar {
public static final String DTEND = "dtend";
/**
- * The time the event starts with allDay events in a local tz
- * <P>Type: INTEGER (long; millis since epoch)</P>
- */
- public static final String DTSTART2 = "dtstart2";
-
- /**
- * The time the event ends with allDay events in a local tz
- * <P>Type: INTEGER (long; millis since epoch)</P>
- */
- public static final String DTEND2 = "dtend2";
-
- /**
* The duration of the event
* <P>Type: TEXT (duration in RFC2445 format)</P>
*/
@@ -734,8 +722,16 @@ public final class Calendar {
public static final String EXDATE = "exdate";
/**
+ * The _id of the original recurring event for which this event is an
+ * exception.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ORIGINAL_ID = "original_id";
+
+ /**
* The _sync_id of the original recurring event for which this event is
- * an exception.
+ * an exception. The provider should keep the original_id in sync when
+ * this is updated.
* <P>Type: TEXT</P>
*/
public static final String ORIGINAL_SYNC_ID = "original_sync_id";
@@ -899,6 +895,7 @@ public final class Calendar {
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DTEND);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DURATION);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_TIMEZONE);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_END_TIMEZONE);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ALL_DAY);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, AVAILABILITY);
@@ -910,6 +907,7 @@ public final class Calendar {
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXRULE);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXDATE);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_SYNC_ID);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_ID);
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv,
ORIGINAL_INSTANCE_TIME);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ORIGINAL_ALL_DAY);
@@ -925,7 +923,7 @@ public final class Calendar {
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
- DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
+ DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC1);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
Events.SYNC_DATA1);
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 2c79385..60bee9a 100644..100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1104,7 +1104,7 @@ public class BluetoothService extends IBluetooth.Stub {
}
/*package*/ synchronized boolean setBondState(String address, int state, int reason) {
- mBondState.setBondState(address.toUpperCase(), state);
+ mBondState.setBondState(address.toUpperCase(), state, reason);
return true;
}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
index 6f4c15b..d698b54 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -18,6 +18,7 @@ package android.speech.tts;
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.os.Bundle;
+import android.os.Handler;
import android.util.Log;
/**
@@ -49,16 +50,20 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
private final float mPan;
private final Object mStateLock = new Object();
- private AudioTrack mAudioTrack = null;
+ private final Handler mAudioTrackHandler;
+ private volatile AudioTrack mAudioTrack = null;
private boolean mStopped = false;
private boolean mDone = false;
+ private volatile boolean mWriteErrorOccured;
PlaybackSynthesisRequest(String text, Bundle params,
- int streamType, float volume, float pan) {
+ int streamType, float volume, float pan, Handler audioTrackHandler) {
super(text, params);
mStreamType = streamType;
mVolume = volume;
mPan = pan;
+ mAudioTrackHandler = audioTrackHandler;
+ mWriteErrorOccured = false;
}
@Override
@@ -70,14 +75,28 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
}
}
+ // Always guarded by mStateLock.
private void cleanUp() {
if (DBG) Log.d(TAG, "cleanUp()");
- if (mAudioTrack != null) {
- mAudioTrack.flush();
- mAudioTrack.stop();
- mAudioTrack.release();
- mAudioTrack = null;
+ if (mAudioTrack == null) {
+ return;
}
+
+ final AudioTrack audioTrack = mAudioTrack;
+ mAudioTrack = null;
+
+ // Clean up on the audiotrack handler thread.
+ //
+ // NOTE: It isn't very clear whether AudioTrack is thread safe.
+ // If it is we can clean up on the current (synthesis) thread.
+ mAudioTrackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ audioTrack.flush();
+ audioTrack.stop();
+ audioTrack.release();
+ }
+ });
}
@Override
@@ -146,10 +165,15 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
+ offset + "," + length + ")");
}
- if (length > getMaxBufferSize()) {
- throw new IllegalArgumentException("buffer is too large (" + length + " bytes)");
+ if (length > getMaxBufferSize() || length <= 0) {
+ throw new IllegalArgumentException("buffer is too large or of zero length (" +
+ + length + " bytes)");
}
synchronized (mStateLock) {
+ if (mWriteErrorOccured) {
+ if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+ return TextToSpeech.ERROR;
+ }
if (mStopped) {
if (DBG) Log.d(TAG, "Request has been aborted.");
return TextToSpeech.ERROR;
@@ -158,22 +182,33 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
Log.e(TAG, "audioAvailable(): Not started");
return TextToSpeech.ERROR;
}
- int playState = mAudioTrack.getPlayState();
- if (playState == AudioTrack.PLAYSTATE_STOPPED) {
- if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
- mAudioTrack.play();
- }
- // TODO: loop until all data is written?
- if (DBG) Log.d(TAG, "AudioTrack.write()");
- int count = mAudioTrack.write(buffer, offset, length);
- if (DBG) Log.d(TAG, "AudioTrack.write() returned " + count);
- if (count < 0) {
- Log.e(TAG, "Writing to AudioTrack failed: " + count);
- cleanUp();
- return TextToSpeech.ERROR;
- } else {
- return TextToSpeech.SUCCESS;
- }
+ final AudioTrack audioTrack = mAudioTrack;
+ // Sigh, another copy.
+ final byte[] bufferCopy = new byte[length];
+ System.arraycopy(buffer, offset, bufferCopy, 0, length);
+
+ mAudioTrackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ int playState = audioTrack.getPlayState();
+ if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+ if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
+ audioTrack.play();
+ }
+ // TODO: loop until all data is written?
+ if (DBG) Log.d(TAG, "AudioTrack.write()");
+ int count = audioTrack.write(bufferCopy, 0, bufferCopy.length);
+ // The semantics of this change very slightly. Earlier, we would
+ // report an error immediately, Now we will return an error on
+ // the next API call, usually done( ) or another audioAvailable( )
+ // call.
+ if (count < 0) {
+ mWriteErrorOccured = true;
+ }
+ }
+ });
+
+ return TextToSpeech.SUCCESS;
}
}
@@ -181,6 +216,10 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
public int done() {
if (DBG) Log.d(TAG, "done()");
synchronized (mStateLock) {
+ if (mWriteErrorOccured) {
+ if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+ return TextToSpeech.ERROR;
+ }
if (mStopped) {
if (DBG) Log.d(TAG, "Request has been aborted.");
return TextToSpeech.ERROR;
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index f32474f..717dde8 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -51,6 +51,7 @@ public abstract class TextToSpeechService extends Service {
private static final String SYNTH_THREAD_NAME = "SynthThread";
private SynthHandler mSynthHandler;
+ private Handler mAudioTrackHandler;
private CallbackMap mCallbacks;
@@ -63,6 +64,10 @@ public abstract class TextToSpeechService extends Service {
synthThread.start();
mSynthHandler = new SynthHandler(synthThread.getLooper());
+ HandlerThread audioTrackThread = new HandlerThread("TTS.audioTrackThread");
+ audioTrackThread.start();
+ mAudioTrackHandler = new Handler(audioTrackThread.getLooper());
+
mCallbacks = new CallbackMap();
// Load default language
@@ -75,6 +80,7 @@ public abstract class TextToSpeechService extends Service {
// Tell the synthesizer to stop
mSynthHandler.quit();
+ mAudioTrackHandler.getLooper().quit();
// Unregister all callbacks.
mCallbacks.kill();
@@ -444,7 +450,7 @@ public abstract class TextToSpeechService extends Service {
protected SynthesisRequest createSynthesisRequest() {
return new PlaybackSynthesisRequest(mText, mParams,
- getStreamType(), getVolume(), getPan());
+ getStreamType(), getVolume(), getPan(), mAudioTrackHandler);
}
private void setRequestParams(SynthesisRequest request) {
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index d432dee..fe96565 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -234,7 +234,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
if (action == MotionEvent.ACTION_DOWN) {
boolean cap = isSelecting(buffer);
if (cap) {
- int offset = widget.getOffset((int) event.getX(), (int) event.getY());
+ int offset = widget.getOffsetForPosition(event.getX(), event.getY());
buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
@@ -259,7 +259,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
// Update selection as we're moving the selection area.
// Get the current touch position
- int offset = widget.getOffset((int) event.getX(), (int) event.getY());
+ int offset = widget.getOffsetForPosition(event.getX(), event.getY());
Selection.extendSelection(buffer, offset);
return true;
@@ -275,7 +275,7 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
return true;
}
- int offset = widget.getOffset((int) event.getX(), (int) event.getY());
+ int offset = widget.getOffsetForPosition(event.getX(), event.getY());
if (isSelecting(buffer)) {
buffer.removeSpan(LAST_TAP_DOWN);
Selection.extendSelection(buffer, offset);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 845fbc3..61a24a0 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,7 +17,6 @@
package android.view;
-import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
@@ -137,14 +136,14 @@ public abstract class HardwareRenderer {
*
* @param canvas The Canvas used to render the view.
*/
- void onHardwarePreDraw(Canvas canvas);
+ void onHardwarePreDraw(HardwareCanvas canvas);
/**
* Invoked after a view is drawn by a hardware renderer.
*
* @param canvas The Canvas used to render the view.
*/
- void onHardwarePostDraw(Canvas canvas);
+ void onHardwarePostDraw(HardwareCanvas canvas);
}
/**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 82fd581..3436cd1 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -883,8 +883,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* <p>
* <ul>
* <li>For a stylus, reports the distance of the stylus from the screen.
- * The value is normalized to a range from 0.0 (direct contact) to 1.0 (furthest measurable
- * distance).
+ * The value is nominally measured in millimeters where 0.0 indicates direct contact
+ * and larger values indicate increasing distance from the surface.
* </ul>
* </p>
*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d5f573c..98d07c4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6048,6 +6048,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * Set the horizontal scrolled position of your view. This will cause a call to
+ * {@link #onScrollChanged(int, int, int, int)} and the view will be
+ * invalidated.
+ * @param value the x position to scroll to
+ */
+ public void setScrollX(int value) {
+ scrollTo(value, mScrollY);
+ }
+
+ /**
+ * Set the vertical scrolled position of your view. This will cause a call to
+ * {@link #onScrollChanged(int, int, int, int)} and the view will be
+ * invalidated.
+ * @param value the y position to scroll to
+ */
+ public void setScrollY(int value) {
+ scrollTo(mScrollX, value);
+ }
+
+ /**
* Return the scrolled left position of this view. This is the left edge of
* the displayed part of your view. You do not need to draw any pixels
* farther left, since those are outside of the frame of your view on
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index 8085ea8..bf04502 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -17,6 +17,7 @@
package android.view;
import android.Manifest;
+import android.animation.LayoutTransition;
import android.app.ActivityManagerNative;
import android.content.ClipDescription;
import android.content.ComponentCallbacks;
@@ -25,7 +26,6 @@ import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PixelFormat;
@@ -45,9 +45,7 @@ import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -230,10 +228,11 @@ public final class ViewAncestor extends Handler implements ViewParent,
int mScrollY;
int mCurScrollY;
Scroller mScroller;
- Bitmap mResizeBitmap;
- long mResizeBitmapStartTime;
- int mResizeBitmapDuration;
+ HardwareLayer mResizeBuffer;
+ long mResizeBufferStartTime;
+ int mResizeBufferDuration;
static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
+ private ArrayList<LayoutTransition> mPendingTransitions;
final ViewConfiguration mViewConfiguration;
@@ -672,6 +671,7 @@ public final class ViewAncestor extends Handler implements ViewParent,
if (!mTraversalScheduled) {
mTraversalScheduled = true;
+ //noinspection ConstantConditions
if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
final long now = System.nanoTime();
Log.d(TAG, "Latency: Scheduled traversal, it has been "
@@ -694,10 +694,32 @@ public final class ViewAncestor extends Handler implements ViewParent,
return mAppVisible ? mView.getVisibility() : View.GONE;
}
- void disposeResizeBitmap() {
- if (mResizeBitmap != null) {
- mResizeBitmap.recycle();
- mResizeBitmap = null;
+ void disposeResizeBuffer() {
+ if (mResizeBuffer != null) {
+ mResizeBuffer.destroy();
+ mResizeBuffer = null;
+ }
+ }
+
+ /**
+ * Add LayoutTransition to the list of transitions to be started in the next traversal.
+ * This list will be cleared after the transitions on the list are start()'ed. These
+ * transitionsa re added by LayoutTransition itself when it sets up animations. The setup
+ * happens during the layout phase of traversal, which we want to complete before any of the
+ * animations are started (because those animations may side-effect properties that layout
+ * depends upon, like the bounding rectangles of the affected views). So we add the transition
+ * to the list and it is started just prior to starting the drawing phase of traversal.
+ *
+ * @param transition The LayoutTransition to be started on the next traversal.
+ *
+ * @hide
+ */
+ public void requestTransitionStart(LayoutTransition transition) {
+ if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) {
+ if (mPendingTransitions == null) {
+ mPendingTransitions = new ArrayList<LayoutTransition>();
+ }
+ mPendingTransitions.add(transition);
}
}
@@ -819,15 +841,25 @@ public final class ViewAncestor extends Handler implements ViewParent,
mAttachInfo.mHardwareRenderer.isEnabled() &&
lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
- disposeResizeBitmap();
+ disposeResizeBuffer();
boolean completed = false;
+ HardwareCanvas canvas = null;
try {
- mResizeBitmap = Bitmap.createBitmap(mWidth, mHeight,
- Bitmap.Config.ARGB_8888);
- mResizeBitmap.setHasAlpha(false);
- Canvas canvas = new Canvas(mResizeBitmap);
+ if (mResizeBuffer == null) {
+ mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
+ mWidth, mHeight, false);
+ } else if (mResizeBuffer.getWidth() != mWidth ||
+ mResizeBuffer.getHeight() != mHeight) {
+ mResizeBuffer.resize(mWidth, mHeight);
+ }
+ canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas);
+ canvas.setViewport(mWidth, mHeight);
+ canvas.onPreDraw(null);
+ final int restoreCount = canvas.save();
+
canvas.drawColor(0xff000000, PorterDuff.Mode.SRC);
+
int yoff;
final boolean scrolling = mScroller != null
&& mScroller.computeScrollOffset();
@@ -837,22 +869,32 @@ public final class ViewAncestor extends Handler implements ViewParent,
} else {
yoff = mScrollY;
}
+
canvas.translate(0, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
- canvas.setScreenDensity(mAttachInfo.mScalingRequired
- ? DisplayMetrics.DENSITY_DEVICE : 0);
+
mView.draw(canvas);
- mResizeBitmapStartTime = SystemClock.uptimeMillis();
- mResizeBitmapDuration = mView.getResources().getInteger(
+
+ mResizeBufferStartTime = SystemClock.uptimeMillis();
+ mResizeBufferDuration = mView.getResources().getInteger(
com.android.internal.R.integer.config_mediumAnimTime);
completed = true;
+
+ canvas.restoreToCount(restoreCount);
} catch (OutOfMemoryError e) {
Log.w(TAG, "Not enough memory for content change anim buffer", e);
} finally {
- if (!completed) {
- mResizeBitmap = null;
+ if (canvas != null) {
+ canvas.onPostDraw();
+ }
+ if (mResizeBuffer != null) {
+ mResizeBuffer.end(mAttachInfo.mHardwareCanvas);
+ if (!completed) {
+ mResizeBuffer.destroy();
+ mResizeBuffer = null;
+ }
}
}
}
@@ -1114,7 +1156,7 @@ public final class ViewAncestor extends Handler implements ViewParent,
if (mScroller != null) {
mScroller.abortAnimation();
}
- disposeResizeBitmap();
+ disposeResizeBuffer();
} else if (surfaceGenerationId != mSurface.getGenerationId() &&
mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) {
fullRedrawNeeded = true;
@@ -1396,6 +1438,12 @@ public final class ViewAncestor extends Handler implements ViewParent,
boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
if (!cancelDraw && !newSurface) {
+ if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
+ for (int i = 0; i < mPendingTransitions.size(); ++i) {
+ mPendingTransitions.get(i).startChangingAnimations();
+ }
+ mPendingTransitions.clear();
+ }
mFullRedrawNeeded = false;
final long drawStartTime;
@@ -1495,15 +1543,14 @@ public final class ViewAncestor extends Handler implements ViewParent,
int mResizeAlpha;
final Paint mResizePaint = new Paint();
- public void onHardwarePreDraw(Canvas canvas) {
+ public void onHardwarePreDraw(HardwareCanvas canvas) {
canvas.translate(0, -mHardwareYOffset);
}
- public void onHardwarePostDraw(Canvas canvas) {
- if (mResizeBitmap != null) {
- canvas.translate(0, mHardwareYOffset);
+ public void onHardwarePostDraw(HardwareCanvas canvas) {
+ if (mResizeBuffer != null) {
mResizePaint.setAlpha(mResizeAlpha);
- canvas.drawBitmap(mResizeBitmap, 0, 0, mResizePaint);
+ canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
}
}
@@ -1559,15 +1606,15 @@ public final class ViewAncestor extends Handler implements ViewParent,
boolean scalingRequired = mAttachInfo.mScalingRequired;
int resizeAlpha = 0;
- if (mResizeBitmap != null) {
- long deltaTime = SystemClock.uptimeMillis() - mResizeBitmapStartTime;
- if (deltaTime < mResizeBitmapDuration) {
- float amt = deltaTime/(float)mResizeBitmapDuration;
+ if (mResizeBuffer != null) {
+ long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime;
+ if (deltaTime < mResizeBufferDuration) {
+ float amt = deltaTime/(float) mResizeBufferDuration;
amt = mResizeInterpolator.getInterpolation(amt);
animating = true;
resizeAlpha = 255 - (int)(amt*255);
} else {
- disposeResizeBitmap();
+ disposeResizeBuffer();
}
}
@@ -1579,7 +1626,7 @@ public final class ViewAncestor extends Handler implements ViewParent,
if (mScroller != null) {
mScroller.abortAnimation();
}
- disposeResizeBitmap();
+ disposeResizeBuffer();
}
return;
}
@@ -1863,7 +1910,7 @@ public final class ViewAncestor extends Handler implements ViewParent,
if (scrollY != mScrollY) {
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
+ mScrollY + " , new=" + scrollY);
- if (!immediate && mResizeBitmap == null) {
+ if (!immediate && mResizeBuffer == null) {
if (mScroller == null) {
mScroller = new Scroller(mView.getContext());
}
@@ -1909,20 +1956,22 @@ public final class ViewAncestor extends Handler implements ViewParent,
public void focusableViewAvailable(View v) {
checkThread();
- if (mView != null && !mView.hasFocus()) {
- v.requestFocus();
- } else {
- // the one case where will transfer focus away from the current one
- // is if the current view is a view group that prefers to give focus
- // to its children first AND the view is a descendant of it.
- mFocusedView = mView.findFocus();
- boolean descendantsHaveDibsOnFocus =
- (mFocusedView instanceof ViewGroup) &&
- (((ViewGroup) mFocusedView).getDescendantFocusability() ==
- ViewGroup.FOCUS_AFTER_DESCENDANTS);
- if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
- // If a view gets the focus, the listener will be invoked from requestChildFocus()
+ if (mView != null) {
+ if (!mView.hasFocus()) {
v.requestFocus();
+ } else {
+ // the one case where will transfer focus away from the current one
+ // is if the current view is a view group that prefers to give focus
+ // to its children first AND the view is a descendant of it.
+ mFocusedView = mView.findFocus();
+ boolean descendantsHaveDibsOnFocus =
+ (mFocusedView instanceof ViewGroup) &&
+ (((ViewGroup) mFocusedView).getDescendantFocusability() ==
+ ViewGroup.FOCUS_AFTER_DESCENDANTS);
+ if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
+ // If a view gets the focus, the listener will be invoked from requestChildFocus()
+ v.requestFocus();
+ }
}
}
}
@@ -1986,9 +2035,7 @@ public final class ViewAncestor extends Handler implements ViewParent,
// At this point the resources have been updated to
// have the most recent config, whatever that is. Use
// the on in them which may be newer.
- if (mView != null) {
- config = mView.getResources().getConfiguration();
- }
+ config = mView.getResources().getConfiguration();
if (force || mLastConfiguration.diff(config) != 0) {
mLastConfiguration.setTo(config);
mView.dispatchConfigurationChanged(config);
@@ -2207,6 +2254,7 @@ public final class ViewAncestor extends Handler implements ViewParent,
if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
// The IME is trying to say this event is from the
// system! Bad bad bad!
+ //noinspection UnusedAssignment
event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
}
deliverKeyEventPostIme((KeyEvent)msg.obj, false);
@@ -2240,7 +2288,7 @@ public final class ViewAncestor extends Handler implements ViewParent,
}
}
- private void startInputEvent(InputEvent event, InputQueue.FinishedCallback finishedCallback) {
+ private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
if (mFinishedCallback != null) {
Slog.w(TAG, "Received a new input event from the input queue but there is "
+ "already an unfinished input event in progress.");
@@ -2454,9 +2502,6 @@ public final class ViewAncestor extends Handler implements ViewParent,
if (isDown) {
ensureTouchMode(true);
}
- if(false) {
- captureMotionLog("captureDispatchPointer", event);
- }
// Offset the scroll position.
if (mCurScrollY != 0) {
@@ -2534,6 +2579,7 @@ public final class ViewAncestor extends Handler implements ViewParent,
if (sendDone) {
finishInputEvent(event, handled);
}
+ //noinspection ConstantConditions
if (LOCAL_LOGV || WATCH_POINTER) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
Log.i(TAG, "Done dispatching!");
@@ -2859,52 +2905,6 @@ public final class ViewAncestor extends Handler implements ViewParent,
return false;
}
- /**
- * log motion events
- */
- private static void captureMotionLog(String subTag, MotionEvent ev) {
- //check dynamic switch
- if (ev == null ||
- SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) {
- return;
- }
-
- StringBuilder sb = new StringBuilder(subTag + ": ");
- sb.append(ev.getDownTime()).append(',');
- sb.append(ev.getEventTime()).append(',');
- sb.append(ev.getAction()).append(',');
- sb.append(ev.getX()).append(',');
- sb.append(ev.getY()).append(',');
- sb.append(ev.getPressure()).append(',');
- sb.append(ev.getSize()).append(',');
- sb.append(ev.getMetaState()).append(',');
- sb.append(ev.getXPrecision()).append(',');
- sb.append(ev.getYPrecision()).append(',');
- sb.append(ev.getDeviceId()).append(',');
- sb.append(ev.getEdgeFlags());
- Log.d(TAG, sb.toString());
- }
- /**
- * log motion events
- */
- private static void captureKeyLog(String subTag, KeyEvent ev) {
- //check dynamic switch
- if (ev == null ||
- SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) {
- return;
- }
- StringBuilder sb = new StringBuilder(subTag + ": ");
- sb.append(ev.getDownTime()).append(',');
- sb.append(ev.getEventTime()).append(',');
- sb.append(ev.getAction()).append(',');
- sb.append(ev.getKeyCode()).append(',');
- sb.append(ev.getRepeatCount()).append(',');
- sb.append(ev.getMetaState()).append(',');
- sb.append(ev.getDeviceId()).append(',');
- sb.append(ev.getScanCode());
- Log.d(TAG, sb.toString());
- }
-
int enqueuePendingEvent(Object event, boolean sendDone) {
int seq = mPendingEventSeq+1;
if (seq < 0) seq = 0;
@@ -2993,10 +2993,6 @@ public final class ViewAncestor extends Handler implements ViewParent,
return;
}
- if (false) {
- captureKeyLog("captureDispatchKeyEvent", event);
- }
-
// Make sure the fallback event policy sees all keys that will be delivered to the
// view hierarchy.
mFallbackEventHandler.preDispatchKeyEvent(event);
@@ -3392,12 +3388,12 @@ public final class ViewAncestor extends Handler implements ViewParent,
private final InputHandler mInputHandler = new InputHandler() {
public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
- startInputEvent(event, finishedCallback);
+ startInputEvent(finishedCallback);
dispatchKey(event, true);
}
public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
- startInputEvent(event, finishedCallback);
+ startInputEvent(finishedCallback);
dispatchMotion(event, true);
}
};
@@ -3429,10 +3425,6 @@ public final class ViewAncestor extends Handler implements ViewParent,
sendMessageAtTime(msg, event.getEventTime());
}
- public void dispatchMotion(MotionEvent event) {
- dispatchMotion(event, false);
- }
-
private void dispatchMotion(MotionEvent event, boolean sendDone) {
int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -3444,10 +3436,6 @@ public final class ViewAncestor extends Handler implements ViewParent,
}
}
- public void dispatchPointer(MotionEvent event) {
- dispatchPointer(event, false);
- }
-
private void dispatchPointer(MotionEvent event, boolean sendDone) {
Message msg = obtainMessage(DISPATCH_POINTER);
msg.obj = event;
@@ -3455,10 +3443,6 @@ public final class ViewAncestor extends Handler implements ViewParent,
sendMessageAtTime(msg, event.getEventTime());
}
- public void dispatchTrackball(MotionEvent event) {
- dispatchTrackball(event, false);
- }
-
private void dispatchTrackball(MotionEvent event, boolean sendDone) {
Message msg = obtainMessage(DISPATCH_TRACKBALL);
msg.obj = event;
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 4aa8727..f014070 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -92,12 +92,6 @@ public class ViewDebug {
public static final boolean TRACE_RECYCLER = false;
/**
- * The system property of dynamic switch for capturing event information
- * when it is set, we log key events, touch/motion and trackball events
- */
- static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent";
-
- /**
* Profiles drawing times in the events log.
*
* @hide
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6937573..f504b90 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4838,6 +4838,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * This method is called by LayoutTransition when there are 'changing' animations that need
+ * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
+ * starts all pending transitions prior to the drawing phase in the current traversal.
+ *
+ * @param transition The LayoutTransition to be started on the next traversal.
+ *
+ * @hide
+ */
+ public void requestTransitionStart(LayoutTransition transition) {
+ ViewAncestor viewAncestor = getViewAncestor();
+ viewAncestor.requestTransitionStart(transition);
+ }
+
+ /**
* Return true if the pressed state should be delayed for children or descendants of this
* ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
* This prevents the pressed state from appearing when the user is actually trying to scroll
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 1d56e9d..9eddf23 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -67,6 +67,19 @@ public class ViewPropertyAnimator {
private boolean mDurationSet = false;
/**
+ * The startDelay of the underlying Animator object. By default, we don't set the startDelay
+ * on the Animator and just use its default startDelay. If the startDelay is ever set on this
+ * Animator, then we use the startDelay that it was set to.
+ */
+ private long mStartDelay = 0;
+
+ /**
+ * A flag indicating whether the startDelay has been set on this object. If not, we don't set
+ * the startDelay on the underlying Animator, but instead just use its default startDelay.
+ */
+ private boolean mStartDelaySet = false;
+
+ /**
* The interpolator of the underlying Animator object. By default, we don't set the interpolator
* on the Animator and just use its default interpolator. If the interpolator is ever set on
* this Animator, then we use the interpolator that it was set to.
@@ -233,6 +246,60 @@ public class ViewPropertyAnimator {
}
/**
+ * Returns the current duration of property animations. If the duration was set on this
+ * object, that value is returned. Otherwise, the default value of the underlying Animator
+ * is returned.
+ *
+ * @see #setDuration(long)
+ * @return The duration of animations, in milliseconds.
+ */
+ public long getDuration() {
+ if (mStartDelaySet) {
+ return mStartDelay;
+ } else {
+ // Just return the default from ValueAnimator, since that's what we'd get if
+ // the value has not been set otherwise
+ return new ValueAnimator().getDuration();
+ }
+ }
+
+ /**
+ * Returns the current startDelay of property animations. If the startDelay was set on this
+ * object, that value is returned. Otherwise, the default value of the underlying Animator
+ * is returned.
+ *
+ * @see #setStartDelay(long)
+ * @return The startDelay of animations, in milliseconds.
+ */
+ public long getStartDelay() {
+ if (mStartDelaySet) {
+ return mStartDelay;
+ } else {
+ // Just return the default from ValueAnimator (0), since that's what we'd get if
+ // the value has not been set otherwise
+ return 0;
+ }
+ }
+
+ /**
+ * Sets the startDelay for the underlying animator that animates the requested properties.
+ * By default, the animator uses the default value for ValueAnimator. Calling this method
+ * will cause the declared value to be used instead.
+ * @param startDelay The delay of ensuing property animations, in milliseconds. The value
+ * cannot be negative.
+ * @return This object, allowing calls to methods in this class to be chained.
+ */
+ public ViewPropertyAnimator setStartDelay(long startDelay) {
+ if (startDelay < 0) {
+ throw new IllegalArgumentException("Animators cannot have negative duration: " +
+ startDelay);
+ }
+ mStartDelaySet = true;
+ mStartDelay = startDelay;
+ return this;
+ }
+
+ /**
* Sets the interpolator for the underlying animator that animates the requested properties.
* By default, the animator uses the default interpolator for ValueAnimator. Calling this method
* will cause the declared object to be used instead.
@@ -259,6 +326,33 @@ public class ViewPropertyAnimator {
}
/**
+ * Starts the currently pending property animations immediately. Calling <code>start()</code>
+ * is optional because all animations start automatically at the next opportunity. However,
+ * if the animations are needed to start immediately and synchronously (not at the time when
+ * the next event is processed by the hierarchy, which is when the animations would begin
+ * otherwise), then this method can be used.
+ */
+ public void start() {
+ startAnimation();
+ }
+
+ /**
+ * Cancels all property animations that are currently running or pending.
+ */
+ public void cancel() {
+ if (mAnimatorMap.size() > 0) {
+ HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
+ (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone();
+ Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
+ for (Animator runningAnim : animatorSet) {
+ runningAnim.cancel();
+ }
+ }
+ mPendingAnimations.clear();
+ mView.getHandler().removeCallbacks(mAnimationStarter);
+ }
+
+ /**
* This method will cause the View's <code>x</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
@@ -598,7 +692,7 @@ public class ViewPropertyAnimator {
// on a property will cancel a previous animation on that property, so
// there can only ever be one such animation running.
if (bundle.mPropertyMask == NONE) {
- // the animation is not longer changing anything - cancel it
+ // the animation is no longer changing anything - cancel it
animatorToCancel = runningAnim;
break;
}
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index 976e786..12391df 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -39,9 +39,6 @@ final class JWebCoreJavaBridge extends Handler {
// immediately.
private boolean mHasInstantTimer;
- // Reference count the pause/resume of timers
- private int mPauseTimerRefCount;
-
private boolean mTimerPaused;
private boolean mHasDeferredTimers;
@@ -136,7 +133,7 @@ final class JWebCoreJavaBridge extends Handler {
* Pause all timers.
*/
public void pause() {
- if (--mPauseTimerRefCount == 0) {
+ if (!mTimerPaused) {
mTimerPaused = true;
mHasDeferredTimers = false;
}
@@ -146,7 +143,7 @@ final class JWebCoreJavaBridge extends Handler {
* Resume all timers.
*/
public void resume() {
- if (++mPauseTimerRefCount == 1) {
+ if (mTimerPaused) {
mTimerPaused = false;
if (mHasDeferredTimers) {
mHasDeferredTimers = false;
diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java
index f59d7d0..5b4fb1d 100644
--- a/core/java/android/webkit/L10nUtils.java
+++ b/core/java/android/webkit/L10nUtils.java
@@ -69,7 +69,8 @@ public class L10nUtils {
com.android.internal.R.string.autofill_card_number_re, // IDS_AUTOFILL_CARD_NUMBER_RE
com.android.internal.R.string.autofill_expiration_month_re, // IDS_AUTOFILL_EXPIRATION_MONTH_RE
com.android.internal.R.string.autofill_expiration_date_re, // IDS_AUTOFILL_EXPIRATION_DATE_RE
- com.android.internal.R.string.autofill_card_ignored_re // IDS_AUTOFILL_CARD_IGNORED_RE
+ com.android.internal.R.string.autofill_card_ignored_re, // IDS_AUTOFILL_CARD_IGNORED_RE
+ com.android.internal.R.string.autofill_fax_re // IDS_AUTOFILL_FAX_RE
};
private static Context mApplicationContext;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 8ffbda2..66fcc27 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -183,7 +183,6 @@ public class WebSettings {
private boolean mJavaScriptCanOpenWindowsAutomatically = false;
private boolean mUseDoubleTree = false;
private boolean mUseWideViewport = false;
- private boolean mUseFixedViewport = false;
private boolean mSupportMultipleWindows = false;
private boolean mShrinksStandaloneImagesToFit = false;
private long mMaximumDecodedImageSize = 0; // 0 means default
@@ -361,10 +360,9 @@ public class WebSettings {
}
// User agent strings.
- private static final String DESKTOP_USERAGENT =
- "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)"
- + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0"
- + " Safari/530.17";
+ private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " +
+ "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " +
+ "Chrome/11.0.696.34 Safari/534.24";
private static final String IPHONE_USERAGENT =
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
+ " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
@@ -383,13 +381,6 @@ public class WebSettings {
mDefaultTextEncoding = context.getString(com.android.internal.
R.string.default_text_encoding);
- // Detect tablet device for fixed viewport mode.
- final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- final int landscapeWidth = Math.max(metrics.widthPixels, metrics.heightPixels);
- final int minTabletWidth = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.min_xlarge_screen_width);
- mUseFixedViewport = (metrics.density == 1.0f && landscapeWidth >= minTabletWidth);
-
if (sLockForLocaleSettings == null) {
sLockForLocaleSettings = new Object();
sLocale = Locale.getDefault();
@@ -1652,11 +1643,11 @@ public class WebSettings {
}
/**
- * Returns whether to use fixed viewport. Fixed viewport should operate only
- * when wide viewport is on.
+ * Returns whether to use fixed viewport. Use fixed viewport
+ * whenever wide viewport is on.
*/
/* package */ boolean getUseFixedViewport() {
- return getUseWideViewPort() && mUseFixedViewport;
+ return getUseWideViewPort();
}
/**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f774803..61a69ca 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -54,7 +54,9 @@ import android.net.http.SslCertificate;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
+import android.os.StrictMode;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.text.Selection;
@@ -8933,15 +8935,14 @@ public class WebView extends AbsoluteLayout
}
private static void checkThread() {
- if (!"main".equals(Thread.currentThread().getName())) {
- try {
- throw new RuntimeException("A WebView method was called on thread '" +
- Thread.currentThread().getName() + "'. " +
- "All WebView methods must be called on the UI thread. " +
- "Future versions of WebView may not support use on other threads.");
- } catch (RuntimeException e) {
- Log.e(LOGTAG, Log.getStackTraceString(e));
- }
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ RuntimeException exception = new RuntimeException(
+ "A WebView method was called on thread '" +
+ Thread.currentThread().getName() + "'. " +
+ "All WebView methods must be called on the UI thread. " +
+ "Future versions of WebView may not support use on other threads.");
+ Log.e(LOGTAG, Log.getStackTraceString(exception));
+ StrictMode.onWebViewMethodCalledOnWrongThread(exception);
}
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index e8083eb..db5ff54 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1865,7 +1865,7 @@ public final class WebViewCore {
Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
return;
}
- int width = calculateWindowWidth(w, textwrapWidth);
+ int width = calculateWindowWidth(w);
int height = h;
if (width != w) {
float heightWidthRatio = data.mHeightWidthRatio;
@@ -1891,41 +1891,18 @@ public final class WebViewCore {
}
// Calculate width to be used in webkit window.
- private int calculateWindowWidth(int viewWidth, int textwrapWidth) {
+ private int calculateWindowWidth(int viewWidth) {
int width = viewWidth;
if (mSettings.getUseWideViewPort()) {
if (mViewportWidth == -1) {
- if (mSettings.getLayoutAlgorithm() ==
- WebSettings.LayoutAlgorithm.NORMAL || mSettings.getUseFixedViewport()) {
- width = WebView.DEFAULT_VIEWPORT_WIDTH;
- } else {
- /*
- * if a page's minimum preferred width is wider than the
- * given "w", use it instead to get better layout result. If
- * we start a page with MAX_ZOOM_WIDTH, "w" will be always
- * wider. If we start a page with screen width, due to the
- * delay between {@link #didFirstLayout} and
- * {@link #viewSizeChanged},
- * {@link #nativeGetContentMinPrefWidth} will return a more
- * accurate value than initial 0 to result a better layout.
- * In the worse case, the native width will be adjusted when
- * next zoom or screen orientation change happens.
- */
- width = Math.min(WebView.sMaxViewportWidth, Math.max(viewWidth,
- Math.max(WebView.DEFAULT_VIEWPORT_WIDTH,
- nativeGetContentMinPrefWidth())));
- }
+ // Fixed viewport width.
+ width = WebView.DEFAULT_VIEWPORT_WIDTH;
} else if (mViewportWidth > 0) {
- if (mSettings.getUseFixedViewport()) {
- // Use website specified or desired fixed viewport width.
- width = mViewportWidth;
- } else {
- width = Math.max(viewWidth, mViewportWidth);
- }
- } else if (mSettings.getUseFixedViewport()) {
- width = mWebView.getViewWidth();
+ // Use website specified or desired fixed viewport width.
+ width = mViewportWidth;
} else {
- width = textwrapWidth;
+ // For mobile web site.
+ width = viewWidth;
}
}
return width;
@@ -2025,7 +2002,8 @@ public final class WebViewCore {
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mContentSize);
if (draw.mBaseLayer == 0) {
- if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message");
+ mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
return;
}
webkitDraw(draw);
@@ -2439,8 +2417,7 @@ public final class WebViewCore {
// in zoom overview mode.
tentativeScale = mInitialViewState.mTextWrapScale;
int tentativeViewWidth = Math.round(webViewWidth / tentativeScale);
- int windowWidth = calculateWindowWidth(tentativeViewWidth,
- tentativeViewWidth);
+ int windowWidth = calculateWindowWidth(tentativeViewWidth);
// In viewport setup time, since no content width is known, we assume
// the windowWidth will be the content width, to get a more likely
// zoom overview scale.
@@ -2449,8 +2426,7 @@ public final class WebViewCore {
// If user choose non-overview mode.
data.mScale = Math.max(data.mScale, tentativeScale);
}
- if (mSettings.isNarrowColumnLayout() &&
- mSettings.getUseFixedViewport()) {
+ if (mSettings.isNarrowColumnLayout()) {
// In case of automatic text reflow in fixed view port mode.
mInitialViewState.mTextWrapScale =
ZoomManager.computeReadingLevelScale(data.mScale);
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index f2a1ec3..e330737 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -1019,19 +1019,11 @@ class ZoomManager {
WebSettings settings = mWebView.getSettings();
int newZoomOverviewWidth = mZoomOverviewWidth;
if (settings.getUseWideViewPort()) {
- if (!settings.getUseFixedViewport()) {
- // limit mZoomOverviewWidth upper bound to
- // sMaxViewportWidth so that if the page doesn't behave
- // well, the WebView won't go insane. limit the lower
- // bound to match the default scale for mobile sites.
- newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth,
- Math.max((int) (viewWidth * mInvDefaultScale),
- Math.max(drawData.mMinPrefWidth, drawData.mViewSize.x)));
- } else if (drawData.mContentSize.x > 0) {
+ if (drawData.mContentSize.x > 0) {
// The webkitDraw for layers will not populate contentSize, and it'll be
// ignored for zoom overview width update.
- final int contentWidth = Math.max(drawData.mContentSize.x, drawData.mMinPrefWidth);
- newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth, contentWidth);
+ newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth,
+ drawData.mContentSize.x);
}
} else {
// If not use wide viewport, use view width as the zoom overview width.
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 86fefaf..6f30452 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -696,7 +696,8 @@ public class LinearLayout extends ViewGroup {
mTotalLength += mDividerHeight;
}
- if (useLargestChild && heightMode == MeasureSpec.AT_MOST) {
+ if (useLargestChild &&
+ (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
@@ -809,6 +810,31 @@ public class LinearLayout extends ViewGroup {
} else {
alternativeMaxWidth = Math.max(alternativeMaxWidth,
weightedMaxWidth);
+
+
+ // We have no limit, so make all weighted views as tall as the largest child.
+ // Children will have already been measured once.
+ if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) {
+ for (int i = 0; i < count; i++) {
+ final View child = getVirtualChildAt(i);
+
+ if (child == null || child.getVisibility() == View.GONE) {
+ continue;
+ }
+
+ final LinearLayout.LayoutParams lp =
+ (LinearLayout.LayoutParams) child.getLayoutParams();
+
+ float childExtra = lp.weight;
+ if (childExtra > 0) {
+ child.measure(
+ MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
+ MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(largestChildHeight,
+ MeasureSpec.EXACTLY));
+ }
+ }
+ }
}
if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
@@ -1044,7 +1070,8 @@ public class LinearLayout extends ViewGroup {
maxHeight = Math.max(maxHeight, ascent + descent);
}
- if (useLargestChild && widthMode == MeasureSpec.AT_MOST) {
+ if (useLargestChild &&
+ (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
@@ -1200,6 +1227,29 @@ public class LinearLayout extends ViewGroup {
}
} else {
alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
+
+ // We have no limit, so make all weighted views as wide as the largest child.
+ // Children will have already been measured once.
+ if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) {
+ for (int i = 0; i < count; i++) {
+ final View child = getVirtualChildAt(i);
+
+ if (child == null || child.getVisibility() == View.GONE) {
+ continue;
+ }
+
+ final LinearLayout.LayoutParams lp =
+ (LinearLayout.LayoutParams) child.getLayoutParams();
+
+ float childExtra = lp.weight;
+ if (childExtra > 0) {
+ child.measure(
+ MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
+ MeasureSpec.EXACTLY));
+ }
+ }
+ }
}
if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
@@ -1331,7 +1381,7 @@ public class LinearLayout extends ViewGroup {
void layoutVertical() {
final int paddingLeft = mPaddingLeft;
- int childTop = mPaddingTop;
+ int childTop;
int childLeft;
// Where right end of child should go
@@ -1346,19 +1396,21 @@ public class LinearLayout extends ViewGroup {
final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
- if (majorGravity != Gravity.TOP) {
- switch (majorGravity) {
- case Gravity.BOTTOM:
- // mTotalLength contains the padding already, we add the top
- // padding to compensate
- childTop = mBottom - mTop + mPaddingTop - mTotalLength;
- break;
-
- case Gravity.CENTER_VERTICAL:
- childTop += ((mBottom - mTop) - mTotalLength) / 2;
- break;
- }
-
+ switch (majorGravity) {
+ case Gravity.BOTTOM:
+ // mTotalLength contains the padding already
+ childTop = mPaddingTop + mBottom - mTop - mTotalLength;
+ break;
+
+ // mTotalLength contains the padding already
+ case Gravity.CENTER_VERTICAL:
+ childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2;
+ break;
+
+ case Gravity.TOP:
+ default:
+ childTop = mPaddingTop;
+ break;
}
for (int i = 0; i < count; i++) {
@@ -1376,12 +1428,8 @@ public class LinearLayout extends ViewGroup {
if (gravity < 0) {
gravity = minorGravity;
}
-
- switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- childLeft = paddingLeft + lp.leftMargin;
- break;
+ switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+ lp.leftMargin - lp.rightMargin;
@@ -1390,11 +1438,13 @@ public class LinearLayout extends ViewGroup {
case Gravity.RIGHT:
childLeft = childRight - childWidth - lp.rightMargin;
break;
+
+ case Gravity.LEFT:
default:
- childLeft = paddingLeft;
+ childLeft = paddingLeft + lp.leftMargin;
break;
}
-
+
if (hasDividerBeforeChildAt(i)) {
childTop += mDividerHeight;
}
@@ -1418,10 +1468,11 @@ public class LinearLayout extends ViewGroup {
* @see #onLayout(boolean, int, int, int, int)
*/
void layoutHorizontal() {
+ final boolean isLayoutRtl = isLayoutRtl();
final int paddingTop = mPaddingTop;
int childTop;
- int childLeft = mPaddingLeft;
+ int childLeft;
// Where bottom of child should go
final int height = mBottom - mTop;
@@ -1440,25 +1491,37 @@ public class LinearLayout extends ViewGroup {
final int[] maxAscent = mMaxAscent;
final int[] maxDescent = mMaxDescent;
- if (majorGravity != Gravity.LEFT) {
- switch (majorGravity) {
- case Gravity.RIGHT:
- // mTotalLength contains the padding already, we add the left
- // padding to compensate
- childLeft = mRight - mLeft + mPaddingLeft - mTotalLength;
- break;
-
- case Gravity.CENTER_HORIZONTAL:
- childLeft += ((mRight - mLeft) - mTotalLength) / 2;
- break;
- }
+ switch (majorGravity) {
+ case Gravity.RIGHT:
+ // mTotalLength contains the padding already
+ childLeft = mPaddingLeft + mRight - mLeft - mTotalLength;
+ break;
+
+ case Gravity.CENTER_HORIZONTAL:
+ // mTotalLength contains the padding already
+ childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2;
+ break;
+
+ case Gravity.LEFT:
+ default:
+ childLeft = mPaddingLeft;
+ break;
+ }
+
+ int start = 0;
+ int dir = 1;
+ //In case of RTL, start drawing from the last child.
+ if (isLayoutRtl) {
+ start = count - 1;
+ dir = -1;
}
for (int i = 0; i < count; i++) {
- final View child = getVirtualChildAt(i);
+ int childIndex = start + dir * i;
+ final View child = getVirtualChildAt(childIndex);
if (child == null) {
- childLeft += measureNullChild(i);
+ childLeft += measureNullChild(childIndex);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
@@ -1512,7 +1575,7 @@ public class LinearLayout extends ViewGroup {
break;
}
- if (hasDividerBeforeChildAt(i)) {
+ if (hasDividerBeforeChildAt(childIndex)) {
childLeft += mDividerWidth;
}
@@ -1522,7 +1585,7 @@ public class LinearLayout extends ViewGroup {
childLeft += childWidth + lp.rightMargin +
getNextLocationOffset(child);
- i += getChildrenSkipCount(child, i);
+ i += getChildrenSkipCount(child, childIndex);
}
}
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 1e1a043..563fc26 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1171,8 +1171,7 @@ public class PopupWindow {
int bottomEdge = displayFrame.bottom;
if (ignoreBottomDecorations) {
Resources res = anchor.getContext().getResources();
- bottomEdge = res.getDisplayMetrics().heightPixels -
- (int) res.getDimension(com.android.internal.R.dimen.screen_margin_bottom);
+ bottomEdge = res.getDisplayMetrics().heightPixels;
}
final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 71c91e1..c4ba7c8 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -116,7 +116,7 @@ public class StackView extends AdapterViewAnimator {
private static final int MIN_TIME_BETWEEN_INTERACTION_AND_AUTOADVANCE = 5000;
- private static long MIN_TIME_BETWEEN_SCROLLS = 100;
+ private static final long MIN_TIME_BETWEEN_SCROLLS = 100;
/**
* These variables are all related to the current state of touch interaction
@@ -213,8 +213,7 @@ public class StackView extends AdapterViewAnimator {
* Animate the views between different relative indexes within the {@link AdapterViewAnimator}
*/
void transformViewForTransition(int fromIndex, int toIndex, final View view, boolean animate) {
- ObjectAnimator alphaOa = null;
- ObjectAnimator oldAlphaOa = null;
+ ObjectAnimator alphaOa;
if (!animate) {
((StackFrame) view).cancelSliderAnimator();
@@ -1276,13 +1275,11 @@ public class StackView extends AdapterViewAnimator {
boolean firstPass = true;
parentRect.set(0, 0, 0, 0);
- int depth = 0;
while (p.getParent() != null && p.getParent() instanceof View
&& !parentRect.contains(globalInvalidateRect)) {
if (!firstPass) {
globalInvalidateRect.offset(p.getLeft() - p.getScrollX(), p.getTop()
- p.getScrollY());
- depth++;
}
firstPass = false;
p = (View) p.getParent();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d58c72b..3875765 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,11 +16,6 @@
package android.widget;
-import com.android.internal.util.FastMath;
-import com.android.internal.widget.EditableInputConnection;
-
-import org.xmlpull.v1.XmlPullParserException;
-
import android.R;
import android.content.ClipData;
import android.content.ClipData.Item;
@@ -129,6 +124,11 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.util.FastMath;
+import com.android.internal.widget.EditableInputConnection;
+
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.BreakIterator;
@@ -320,6 +320,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private int mTextEditSuggestionItemLayout;
private SuggestionsPopupWindow mSuggestionsPopupWindow;
private SuggestionRangeSpan mSuggestionRangeSpan;
+ private boolean mSuggestionsEnabled = true;
private int mCursorDrawableRes;
private final Drawable[] mCursorDrawable = new Drawable[2];
@@ -329,7 +330,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private Drawable mSelectHandleRight;
private Drawable mSelectHandleCenter;
- private int mLastDownPositionX, mLastDownPositionY;
+ private float mLastDownPositionX, mLastDownPositionY;
private Callback mCustomSelectionActionModeCallback;
private final int mSquaredTouchSlopDistance;
@@ -806,6 +807,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case com.android.internal.R.styleable.TextView_textIsSelectable:
mTextIsSelectable = a.getBoolean(attr, false);
break;
+
+ case com.android.internal.R.styleable.TextView_suggestionsEnabled:
+ mSuggestionsEnabled = a.getBoolean(attr, true);
+ break;
}
}
a.recycle();
@@ -2898,8 +2903,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setText(mCharWrapper, mBufferType, false, oldlen);
}
- private static class CharWrapper
- implements CharSequence, GetChars, GraphicsOperations {
+ private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations {
private char[] mChars;
private int mStart, mLength;
@@ -7323,8 +7327,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (action == MotionEvent.ACTION_DOWN) {
- mLastDownPositionX = (int) event.getX();
- mLastDownPositionY = (int) event.getY();
+ mLastDownPositionX = event.getX();
+ mLastDownPositionY = event.getY();
// Reset this state; it will be re-set if super.onTouchEvent
// causes focus to move to the view.
@@ -7758,16 +7762,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
hasPrimaryClip());
}
- private boolean isWordCharacter(int c, int type) {
- return (c == '\'' || c == '"' ||
- type == Character.UPPERCASE_LETTER ||
- type == Character.LOWERCASE_LETTER ||
- type == Character.TITLECASE_LETTER ||
- type == Character.MODIFIER_LETTER ||
- type == Character.OTHER_LETTER || // Should handle asian characters
- type == Character.DECIMAL_DIGIT_NUMBER);
- }
-
private static long packRangeInLong(int start, int end) {
return (((long) start) << 32) | end;
}
@@ -8140,7 +8134,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Long press in empty space moves cursor and shows the Paste affordance if available.
if (!isPositionOnText(mLastDownPositionX, mLastDownPositionY) &&
mInsertionControllerEnabled) {
- final int offset = getOffset(mLastDownPositionX, mLastDownPositionY);
+ final int offset = getOffsetForPosition(mLastDownPositionX, mLastDownPositionY);
stopSelectionActionMode();
Selection.setSelection((Spannable) mText, offset);
getInsertionController().showWithPaste();
@@ -8208,7 +8202,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private final ViewGroup[] mSuggestionViews = new ViewGroup[2];
private final int[] mSuggestionViewLayouts = new int[] {
mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout};
- private WordIterator mWordIterator;
+ private WordIterator mSuggestionWordIterator;
private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan[0];
public SuggestionsPopupWindow() {
@@ -8344,26 +8338,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private long[] getWordLimits(CharSequence text) {
- if (mWordIterator == null) mWordIterator = new WordIterator(); // TODO locale
- mWordIterator.setCharSequence(text);
+ // TODO locale for mSuggestionWordIterator
+ if (mSuggestionWordIterator == null) mSuggestionWordIterator = new WordIterator();
+ mSuggestionWordIterator.setCharSequence(text);
// First pass will simply count the number of words to be able to create an array
// Not too expensive since previous break positions are cached by the BreakIterator
int nbWords = 0;
- int position = mWordIterator.following(0);
+ int position = mSuggestionWordIterator.following(0);
while (position != BreakIterator.DONE) {
nbWords++;
- position = mWordIterator.following(position);
+ position = mSuggestionWordIterator.following(position);
}
int index = 0;
long[] result = new long[nbWords];
- position = mWordIterator.following(0);
+ position = mSuggestionWordIterator.following(0);
while (position != BreakIterator.DONE) {
- int wordStart = mWordIterator.getBeginning(position);
+ int wordStart = mSuggestionWordIterator.getBeginning(position);
result[index++] = packRangeInLong(wordStart, position);
- position = mWordIterator.following(position);
+ position = mSuggestionWordIterator.following(position);
}
return result;
@@ -8601,6 +8596,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
void showSuggestions() {
+ if (!mSuggestionsEnabled || !isTextEditable()) return;
+
if (mSuggestionsPopupWindow == null) {
mSuggestionsPopupWindow = new SuggestionsPopupWindow();
}
@@ -8615,6 +8612,31 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Some parts of the text can have alternate suggestion text attached. This is typically done by
+ * the IME by adding {@link SuggestionSpan}s to the text.
+ *
+ * When suggestions are enabled (default), this list of suggestions will be displayed when the
+ * user double taps on these parts of the text. No suggestions are displayed when this value is
+ * false. Use {@link #setSuggestionsEnabled(boolean)} to change this value.
+ *
+ * @return true if the suggestions popup window is enabled.
+ *
+ * @attr ref android.R.styleable#TextView_suggestionsEnabled
+ */
+ public boolean isSuggestionsEnabled() {
+ return mSuggestionsEnabled;
+ }
+
+ /**
+ * Enables or disables the suggestion popup. See {@link #isSuggestionsEnabled()}.
+ *
+ * @param enabled Whether or not suggestions are enabled.
+ */
+ public void setSuggestionsEnabled(boolean enabled) {
+ mSuggestionsEnabled = enabled;
+ }
+
+ /**
* If provided, this ActionMode.Callback will be used to create the ActionMode when text
* selection is initiated in this View.
*
@@ -9115,7 +9137,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public abstract void updateOffset(int offset);
- public abstract void updatePosition(int x, int y);
+ public abstract void updatePosition(float x, float y);
protected void positionAtCursorOffset(int offset) {
addPositionToTouchUpFilter(offset);
@@ -9215,7 +9237,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX;
final float newPosY = rawY - mTouchToWindowOffsetY + mTouchOffsetY;
- updatePosition(Math.round(newPosX), Math.round(newPosY));
+ updatePosition(newPosX, newPosY);
break;
}
@@ -9366,8 +9388,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
@Override
- public void updatePosition(int x, int y) {
- updateOffset(getOffset(x, y));
+ public void updatePosition(float x, float y) {
+ updateOffset(getOffsetForPosition(x, y));
}
void showPastePopupWindow() {
@@ -9421,11 +9443,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
@Override
- public void updatePosition(int x, int y) {
+ public void updatePosition(float x, float y) {
final int selectionStart = getSelectionStart();
final int selectionEnd = getSelectionEnd();
- int offset = getOffset(x, y);
+ int offset = getOffsetForPosition(x, y);
// No need to redraw when the offset is unchanged
if (offset == selectionStart) return;
@@ -9458,11 +9480,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
@Override
- public void updatePosition(int x, int y) {
+ public void updatePosition(float x, float y) {
final int selectionStart = getSelectionStart();
final int selectionEnd = getSelectionEnd();
- int offset = getOffset(x, y);
+ int offset = getOffsetForPosition(x, y);
// No need to redraw when the offset is unchanged
if (offset == selectionEnd) return;
@@ -9560,7 +9582,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Double tap detection
private long mPreviousTapUpTime = 0;
- private int mPreviousTapPositionX, mPreviousTapPositionY;
+ private float mPreviousTapPositionX, mPreviousTapPositionY;
SelectionModifierCursorController() {
resetTouchOffsets();
@@ -9593,19 +9615,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (isTextEditable() || mTextIsSelectable) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- final int x = (int) event.getX();
- final int y = (int) event.getY();
+ final float x = event.getX();
+ final float y = event.getY();
// Remember finger down position, to be able to start selection from there
- mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
+ mMinTouchOffset = mMaxTouchOffset = getOffsetForPosition(x, y);
// Double tap detection
long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
if (duration <= ViewConfiguration.getDoubleTapTimeout() &&
isPositionOnText(x, y)) {
- final int deltaX = x - mPreviousTapPositionX;
- final int deltaY = y - mPreviousTapPositionY;
- final int distanceSquared = deltaX * deltaX + deltaY * deltaY;
+ final float deltaX = x - mPreviousTapPositionX;
+ final float deltaY = y - mPreviousTapPositionY;
+ final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared < mSquaredTouchSlopDistance) {
showSuggestions();
mDiscardNextActionUp = true;
@@ -9641,9 +9663,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private void updateMinAndMaxOffsets(MotionEvent event) {
int pointerCount = event.getPointerCount();
for (int index = 0; index < pointerCount; index++) {
- final int x = (int) event.getX(index);
- final int y = (int) event.getY(index);
- int offset = getOffset(x, y);
+ int offset = getOffsetForPosition(event.getX(index), event.getY(index));
if (offset < mMinTouchOffset) mMinTouchOffset = offset;
if (offset > mMaxTouchOffset) mMaxTouchOffset = offset;
}
@@ -9701,41 +9721,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
- * Get the offset character closest to the specified absolute position.
+ * Get the character offset closest to the specified absolute position. A typical use case is to
+ * pass the result of {@link MotionEvent#getX()} and {@link MotionEvent#getY()} to this method.
*
* @param x The horizontal absolute position of a point on screen
* @param y The vertical absolute position of a point on screen
* @return the character offset for the character whose position is closest to the specified
* position. Returns -1 if there is no layout.
- *
- * @hide
*/
- public int getOffset(int x, int y) {
+ public int getOffsetForPosition(float x, float y) {
if (getLayout() == null) return -1;
final int line = getLineAtCoordinate(y);
final int offset = getOffsetAtCoordinate(line, x);
return offset;
}
- private int convertToLocalHorizontalCoordinate(int x) {
+ private float convertToLocalHorizontalCoordinate(float x) {
x -= getTotalPaddingLeft();
// Clamp the position to inside of the view.
- x = Math.max(0, x);
+ x = Math.max(0.0f, x);
x = Math.min(getWidth() - getTotalPaddingRight() - 1, x);
x += getScrollX();
return x;
}
- private int getLineAtCoordinate(int y) {
+ private int getLineAtCoordinate(float y) {
y -= getTotalPaddingTop();
// Clamp the position to inside of the view.
- y = Math.max(0, y);
+ y = Math.max(0.0f, y);
y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y);
y += getScrollY();
- return getLayout().getLineForVertical(y);
+ return getLayout().getLineForVertical((int) y);
}
- private int getOffsetAtCoordinate(int line, int x) {
+ private int getOffsetAtCoordinate(int line, float x) {
x = convertToLocalHorizontalCoordinate(x);
return getLayout().getOffsetForHorizontal(line, x);
}
@@ -9743,7 +9762,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/** Returns true if the screen coordinates position (x,y) corresponds to a character displayed
* in the view. Returns false when the position is in the empty space of left/right of text.
*/
- private boolean isPositionOnText(int x, int y) {
+ private boolean isPositionOnText(float x, float y) {
if (getLayout() == null) return false;
final int line = getLineAtCoordinate(y);
@@ -9765,7 +9784,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return true;
case DragEvent.ACTION_DRAG_LOCATION:
- final int offset = getOffset((int) event.getX(), (int) event.getY());
+ final int offset = getOffsetForPosition(event.getX(), event.getY());
Selection.setSelection((Spannable)mText, offset);
return true;
@@ -9789,7 +9808,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
content.append(item.coerceToText(TextView.this.mContext));
}
- final int offset = getOffset((int) event.getX(), (int) event.getY());
+ final int offset = getOffsetForPosition(event.getX(), event.getY());
Object localState = event.getLocalState();
DragLocalState dragLocalState = null;