diff options
Diffstat (limited to 'core/java/android')
20 files changed, 341 insertions, 114 deletions
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index bdcff38..88b9080 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -71,6 +71,7 @@ interface INotificationManager ComponentName getEffectsSuppressor(); boolean matchesCallFilter(in Bundle extras); + boolean isSystemConditionProviderEnabled(String path); ZenModeConfig getZenModeConfig(); boolean setZenModeConfig(in ZenModeConfig config); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 7dc1ad6..cf54107 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -264,5 +264,17 @@ public class NotificationManager } } + /** + * @hide + */ + public boolean isSystemConditionProviderEnabled(String path) { + INotificationManager service = getService(); + try { + return service.isSystemConditionProviderEnabled(path); + } catch (RemoteException e) { + return false; + } + } + private Context mContext; } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 8021210..d9921a6 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -68,9 +68,6 @@ interface IConnectivityManager boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress); - /** Policy control over specific {@link NetworkStateTracker}. */ - void setPolicyDataEnable(int networkType, boolean enabled); - int tether(String iface); int untether(String iface); diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index b7b8731..2c3881c 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -40,8 +40,12 @@ interface INetworkStatsService { /** Mark given UID as being in foreground for stats purposes. */ void setUidForeground(int uid, boolean uidForeground); + + /** Force update of ifaces. */ + void forceUpdateIfaces(); /** Force update of statistics. */ void forceUpdate(); + /** Advise persistance threshold; may be overridden internally. */ void advisePersistThreshold(long thresholdBytes); diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index d36707e..a9de23e 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -25,6 +25,7 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build; import android.telephony.TelephonyManager; +import android.util.Slog; import java.util.Objects; @@ -35,10 +36,16 @@ import java.util.Objects; * @hide */ public class NetworkIdentity implements Comparable<NetworkIdentity> { + private static final String TAG = "NetworkIdentity"; + /** * When enabled, combine all {@link #mSubType} together under * {@link #SUBTYPE_COMBINED}. + * + * @deprecated we no longer offer to collect statistics on a per-subtype + * basis; this is always disabled. */ + @Deprecated public static final boolean COMBINE_SUBTYPE_ENABLED = true; public static final int SUBTYPE_COMBINED = -1; @@ -133,6 +140,18 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } /** + * Scrub given IMSI on production builds. + */ + public static String[] scrubSubscriberId(String[] subscriberId) { + if (subscriberId == null) return null; + final String[] res = new String[subscriberId.length]; + for (int i = 0; i < res.length; i++) { + res[i] = NetworkIdentity.scrubSubscriberId(subscriberId[i]); + } + return res; + } + + /** * Build a {@link NetworkIdentity} from the given {@link NetworkState}, * assuming that any mobile networks are using the current IMSI. */ @@ -140,23 +159,18 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { final int type = state.networkInfo.getType(); final int subType = state.networkInfo.getSubtype(); - // TODO: consider moving subscriberId over to LinkCapabilities, so it - // comes from an authoritative source. - String subscriberId = null; String networkId = null; boolean roaming = false; if (isNetworkTypeMobile(type)) { - final TelephonyManager telephony = (TelephonyManager) context.getSystemService( - Context.TELEPHONY_SERVICE); - roaming = telephony.isNetworkRoaming(); - if (state.subscriberId != null) { - subscriberId = state.subscriberId; - } else { - subscriberId = telephony.getSubscriberId(); + if (state.subscriberId == null) { + Slog.w(TAG, "Active mobile network without subscriber!"); } + subscriberId = state.subscriberId; + roaming = state.networkInfo.isRoaming(); + } else if (type == TYPE_WIFI) { if (state.networkId != null) { networkId = state.networkId; diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java index 5d2a43d..b92c9e3 100644 --- a/core/java/android/net/NetworkMisc.java +++ b/core/java/android/net/NetworkMisc.java @@ -20,15 +20,18 @@ import android.os.Parcel; import android.os.Parcelable; /** - * A grab-bag of information (metadata, policies, properties, etc) about a {@link Network}. + * A grab-bag of information (metadata, policies, properties, etc) about a + * {@link Network}. Since this contains PII, it should not be sent outside the + * system. * * @hide */ public class NetworkMisc implements Parcelable { /** - * If the {@link Network} is a VPN, whether apps are allowed to bypass the VPN. This is set by - * a {@link VpnService} and used by {@link ConnectivityService} when creating a VPN. + * If the {@link Network} is a VPN, whether apps are allowed to bypass the + * VPN. This is set by a {@link VpnService} and used by + * {@link ConnectivityManager} when creating a VPN. */ public boolean allowBypass; @@ -41,6 +44,11 @@ public class NetworkMisc implements Parcelable { */ public boolean explicitlySelected; + /** + * For mobile networks, this is the subscriber ID (such as IMSI). + */ + public String subscriberId; + public NetworkMisc() { } @@ -48,6 +56,7 @@ public class NetworkMisc implements Parcelable { if (nm != null) { allowBypass = nm.allowBypass; explicitlySelected = nm.explicitlySelected; + subscriberId = nm.subscriberId; } } @@ -60,6 +69,7 @@ public class NetworkMisc implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeInt(allowBypass ? 1 : 0); out.writeInt(explicitlySelected ? 1 : 0); + out.writeString(subscriberId); } public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() { @@ -68,6 +78,7 @@ public class NetworkMisc implements Parcelable { NetworkMisc networkMisc = new NetworkMisc(); networkMisc.allowBypass = in.readInt() != 0; networkMisc.explicitlySelected = in.readInt() != 0; + networkMisc.subscriberId = in.readString(); return networkMisc; } diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index 10c686b..e88bc26 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -35,7 +35,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { public static final long LIMIT_DISABLED = -1; public static final long SNOOZE_NEVER = -1; - public final NetworkTemplate template; + public NetworkTemplate template; public int cycleDay; public String cycleTimezone; public long warningBytes; diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java index d26c70d..933287f 100644 --- a/core/java/android/net/NetworkState.java +++ b/core/java/android/net/NetworkState.java @@ -30,16 +30,10 @@ public class NetworkState implements Parcelable { public final LinkProperties linkProperties; public final NetworkCapabilities networkCapabilities; public final Network network; - /** Currently only used by testing. */ public final String subscriberId; public final String networkId; public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties, - NetworkCapabilities networkCapabilities, Network network) { - this(networkInfo, linkProperties, networkCapabilities, network, null, null); - } - - public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, Network network, String subscriberId, String networkId) { this.networkInfo = networkInfo; @@ -85,5 +79,4 @@ public class NetworkState implements Parcelable { return new NetworkState[size]; } }; - } diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index b839e0a..57eef83 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -22,7 +22,6 @@ import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI_P2P; import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED; -import static android.net.NetworkIdentity.scrubSubscriberId; import static android.net.wifi.WifiInfo.removeDoubleQuotes; import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G; import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G; @@ -36,7 +35,9 @@ import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; +import java.util.Arrays; import java.util.Objects; /** @@ -48,7 +49,9 @@ import java.util.Objects; public class NetworkTemplate implements Parcelable { public static final int MATCH_MOBILE_ALL = 1; + @Deprecated public static final int MATCH_MOBILE_3G_LOWER = 2; + @Deprecated public static final int MATCH_MOBILE_4G = 3; public static final int MATCH_WIFI = 4; public static final int MATCH_ETHERNET = 5; @@ -146,17 +149,35 @@ public class NetworkTemplate implements Parcelable { private final int mMatchRule; private final String mSubscriberId; + + /** + * Ugh, templates are designed to target a single subscriber, but we might + * need to match several "merged" subscribers. These are the subscribers + * that should be considered to match this template. + * <p> + * Since the merge set is dynamic, it should <em>not</em> be persisted or + * used for determining equality. + */ + private final String[] mMatchSubscriberIds; + private final String mNetworkId; public NetworkTemplate(int matchRule, String subscriberId, String networkId) { + this(matchRule, subscriberId, new String[] { subscriberId }, networkId); + } + + public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, + String networkId) { mMatchRule = matchRule; mSubscriberId = subscriberId; + mMatchSubscriberIds = matchSubscriberIds; mNetworkId = networkId; } private NetworkTemplate(Parcel in) { mMatchRule = in.readInt(); mSubscriberId = in.readString(); + mMatchSubscriberIds = in.createStringArray(); mNetworkId = in.readString(); } @@ -164,6 +185,7 @@ public class NetworkTemplate implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mMatchRule); dest.writeString(mSubscriberId); + dest.writeStringArray(mMatchSubscriberIds); dest.writeString(mNetworkId); } @@ -177,7 +199,12 @@ public class NetworkTemplate implements Parcelable { final StringBuilder builder = new StringBuilder("NetworkTemplate: "); builder.append("matchRule=").append(getMatchRuleName(mMatchRule)); if (mSubscriberId != null) { - builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId)); + builder.append(", subscriberId=").append( + NetworkIdentity.scrubSubscriberId(mSubscriberId)); + } + if (mMatchSubscriberIds != null) { + builder.append(", matchSubscriberIds=").append( + Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds))); } if (mNetworkId != null) { builder.append(", networkId=").append(mNetworkId); @@ -201,6 +228,18 @@ public class NetworkTemplate implements Parcelable { return false; } + public boolean isMatchRuleMobile() { + switch (mMatchRule) { + case MATCH_MOBILE_3G_LOWER: + case MATCH_MOBILE_4G: + case MATCH_MOBILE_ALL: + case MATCH_MOBILE_WILDCARD: + return true; + default: + return false; + } + } + public int getMatchRule() { return mMatchRule; } @@ -247,14 +286,16 @@ public class NetworkTemplate implements Parcelable { // TODO: consider matching against WiMAX subscriber identity return true; } else { - return ((sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType)) - && Objects.equals(mSubscriberId, ident.mSubscriberId)); + final boolean matchesType = (sForceAllNetworkTypes + || contains(DATA_USAGE_NETWORK_TYPES, ident.mType)); + return matchesType && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId); } } /** * Check if mobile network classified 3G or lower with matching IMSI. */ + @Deprecated private boolean matchesMobile3gLower(NetworkIdentity ident) { ensureSubtypeAvailable(); if (ident.mType == TYPE_WIMAX) { @@ -273,6 +314,7 @@ public class NetworkTemplate implements Parcelable { /** * Check if mobile network classified 4G with matching IMSI. */ + @Deprecated private boolean matchesMobile4g(NetworkIdentity ident) { ensureSubtypeAvailable(); if (ident.mType == TYPE_WIMAX) { @@ -368,6 +410,27 @@ public class NetworkTemplate implements Parcelable { } } + /** + * Examine the given template and normalize if it refers to a "merged" + * mobile subscriber. We pick the "lowest" merged subscriber as the primary + * for key purposes, and expand the template to match all other merged + * subscribers. + * <p> + * For example, given an incoming template matching B, and the currently + * active merge set [A,B], we'd return a new template that primarily matches + * A, but also matches B. + */ + public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) { + if (template.isMatchRuleMobile() && ArrayUtils.contains(merged, template.mSubscriberId)) { + // Requested template subscriber is part of the merge group; return + // a template that matches all merged subscribers. + return new NetworkTemplate(template.mMatchRule, merged[0], merged, + template.mNetworkId); + } else { + return template; + } + } + public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() { @Override public NetworkTemplate createFromParcel(Parcel in) { diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index a241c3a..a9deaf3 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -168,7 +168,7 @@ public final class Debug public static final int NUM_OTHER_STATS = 17; /** @hide */ - public static final int NUM_DVK_STATS = 5; + public static final int NUM_DVK_STATS = 8; /** @hide */ public static final int NUM_CATEGORIES = 7; @@ -314,6 +314,9 @@ public final class Debug case 19: return ".LinearAlloc"; case 20: return ".GC"; case 21: return ".JITCache"; + case 22: return ".Zygote"; + case 23: return ".NonMoving"; + case 24: return ".IndirectRef"; default: return "????"; } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0c036d51..0062eb2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4349,6 +4349,16 @@ public final class Settings { Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS; /** + * The number of milliseconds to hold on to a PendingIntent based request. This delay gives + * the receivers of the PendingIntent an opportunity to make a new network request before + * the Network satisfying the request is potentially removed. + * + * @hide + */ + public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS = + "connectivity_release_pending_intent_delay_ms"; + + /** * Whether background data usage is allowed. * * @deprecated As of {@link VERSION_CODES#ICE_CREAM_SANDWICH}, diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index ce28d0a..979a01b 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -64,7 +64,7 @@ public class ZenModeConfig implements Parcelable { public static final int[] MINUTE_BUCKETS = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 }; private static final int SECONDS_MS = 1000; private static final int MINUTES_MS = 60 * SECONDS_MS; - private static final int ZERO_VALUE_MS = 20 * SECONDS_MS; + private static final int ZERO_VALUE_MS = 10 * SECONDS_MS; private static final boolean DEFAULT_ALLOW_EVENTS = true; @@ -471,6 +471,8 @@ public class ZenModeConfig implements Parcelable { downtime.startMinute = sleepStartMinute; downtime.endHour = sleepEndHour; downtime.endMinute = sleepEndMinute; + downtime.mode = sleepMode; + downtime.none = sleepNone; return downtime; } @@ -510,7 +512,7 @@ public class ZenModeConfig implements Parcelable { public static final String SYSTEM_AUTHORITY = "android"; // Built-in countdown conditions, e.g. condition://android/countdown/1399917958951 - private static final String COUNTDOWN_PATH = "countdown"; + public static final String COUNTDOWN_PATH = "countdown"; public static Uri toCountdownConditionId(long time) { return new Uri.Builder().scheme(Condition.SCHEME) @@ -536,8 +538,9 @@ public class ZenModeConfig implements Parcelable { return tryParseCountdownConditionId(conditionId) != 0; } - // Built-in downtime conditions, e.g. condition://android/downtime?start=10.00&end=7.00 - private static final String DOWNTIME_PATH = "downtime"; + // Built-in downtime conditions + // e.g. condition://android/downtime?start=10.00&end=7.00&mode=days%3A5%2C6&none=false + public static final String DOWNTIME_PATH = "downtime"; public static Uri toDowntimeConditionId(DowntimeInfo downtime) { return new Uri.Builder().scheme(Condition.SCHEME) @@ -545,6 +548,8 @@ public class ZenModeConfig implements Parcelable { .appendPath(DOWNTIME_PATH) .appendQueryParameter("start", downtime.startHour + "." + downtime.startMinute) .appendQueryParameter("end", downtime.endHour + "." + downtime.endMinute) + .appendQueryParameter("mode", downtime.mode) + .appendQueryParameter("none", Boolean.toString(downtime.none)) .build(); } @@ -562,6 +567,8 @@ public class ZenModeConfig implements Parcelable { downtime.startMinute = start[1]; downtime.endHour = end[0]; downtime.endMinute = end[1]; + downtime.mode = conditionId.getQueryParameter("mode"); + downtime.none = Boolean.toString(true).equals(conditionId.getQueryParameter("none")); return downtime; } @@ -583,6 +590,8 @@ public class ZenModeConfig implements Parcelable { public int startMinute; // 0-59 public int endHour; public int endMinute; + public String mode; + public boolean none; @Override public int hashCode() { @@ -596,7 +605,12 @@ public class ZenModeConfig implements Parcelable { return startHour == other.startHour && startMinute == other.startMinute && endHour == other.endHour - && endMinute == other.endMinute; + && endMinute == other.endMinute + && Objects.equals(mode, other.mode) + && none == other.none; } } + + // built-in next alarm conditions + public static final String NEXT_ALARM_PATH = "next_alarm"; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b54d462..2bb1ebc 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5938,9 +5938,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * layer. * * @param outRects List to which to add clickable areas. + * + * @hide */ - void addClickableRectsForAccessibility(List<RectF> outRects) { - if (isClickable() || isLongClickable()) { + public void addClickableRectsForAccessibility(List<RectF> outRects) { + if (isClickable() || isLongClickable() + || (mListenerInfo != null && mListenerInfo.mOnTouchListener != null)) { RectF bounds = new RectF(); bounds.set(0, 0, getWidth(), getHeight()); outRects.add(bounds); @@ -16059,7 +16062,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * This function is called whenever the view hotspot changes and needs to - * be propagated to drawables managed by the view. + * be propagated to drawables or child views managed by the view. + * <p> + * Dispatching to child views is handled by + * {@link #dispatchDrawableHotspotChanged(float, float)}. * <p> * Be sure to call through to the superclass when overriding this function. * @@ -16070,6 +16076,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mBackground != null) { mBackground.setHotspot(x, y); } + + dispatchDrawableHotspotChanged(x, y); + } + + /** + * Dispatches drawableHotspotChanged to all of this View's children. + * + * @param x hotspot x coordinate + * @param y hotspot y coordinate + * @see #drawableHotspotChanged(float, float) + */ + public void dispatchDrawableHotspotChanged(float x, float y) { } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 1551504..25a70eb 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -161,6 +161,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Used during drag dispatch private PointF mLocalPoint; + // Lazily-created holder for point computations. + private float[] mTempPoint; + // Layout animation private LayoutAnimationController mLayoutAnimationController; private Animation.AnimationListener mAnimationListener; @@ -880,8 +883,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return true; } + /** + * @hide + */ @Override - void addClickableRectsForAccessibility(List<RectF> outRects) { + public void addClickableRectsForAccessibility(List<RectF> outRects) { int sizeBefore = outRects.size(); super.addClickableRectsForAccessibility(outRects); @@ -2442,6 +2448,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager || child.getAnimation() != null; } + private float[] getTempPoint() { + if (mTempPoint == null) { + mTempPoint = new float[2]; + } + return mTempPoint; + } + /** * Returns true if a child view contains the specified point when transformed * into its coordinate space. @@ -2450,24 +2463,30 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ protected boolean isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint) { - float localX = x + mScrollX - child.mLeft; - float localY = y + mScrollY - child.mTop; - if (! child.hasIdentityMatrix() && mAttachInfo != null) { - final float[] localXY = mAttachInfo.mTmpTransformLocation; - localXY[0] = localX; - localXY[1] = localY; - child.getInverseMatrix().mapPoints(localXY); - localX = localXY[0]; - localY = localXY[1]; - } - final boolean isInView = child.pointInView(localX, localY); + final float[] point = getTempPoint(); + point[0] = x; + point[1] = y; + transformPointToViewLocal(point, child); + final boolean isInView = child.pointInView(point[0], point[1]); if (isInView && outLocalPoint != null) { - outLocalPoint.set(localX, localY); + outLocalPoint.set(point[0], point[1]); } return isInView; } /** + * @hide + */ + public void transformPointToViewLocal(float[] point, View child) { + point[0] += mScrollX - child.mLeft; + point[1] += mScrollY - child.mTop; + + if (!child.hasIdentityMatrix()) { + child.getInverseMatrix().mapPoints(point); + } + } + + /** * Transforms a motion event into the coordinate space of a particular child view, * filters out irrelevant pointer ids, and overrides its action if necessary. * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. @@ -3606,6 +3625,44 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + /** + * Dispatches drawable hotspot changes to child views that meet at least + * one of the following criteria: + * <ul> + * <li>Returns {@code false} from both {@link View#isClickable()} and + * {@link View#isLongClickable()}</li> + * <li>Requests duplication of parent state via + * {@link View#setDuplicateParentStateEnabled(boolean)}</li> + * </ul> + * + * @param x hotspot x coordinate + * @param y hotspot y coordinate + * @see #drawableHotspotChanged(float, float) + */ + @Override + public void dispatchDrawableHotspotChanged(float x, float y) { + final int count = mChildrenCount; + if (count == 0) { + return; + } + + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + final View child = children[i]; + // Children that are clickable on their own should not + // receive hotspots when their parent view does. + final boolean nonActionable = !child.isClickable() && !child.isLongClickable(); + final boolean duplicatesState = (child.mViewFlags & DUPLICATE_PARENT_STATE) != 0; + if (nonActionable || duplicatesState) { + final float[] point = getTempPoint(); + point[0] = x; + point[1] = y; + transformPointToViewLocal(point, child); + child.drawableHotspotChanged(point[0], point[1]); + } + } + } + @Override void dispatchCancelPendingInputEvents() { super.dispatchCancelPendingInputEvents(); @@ -5961,28 +6018,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - public void drawableHotspotChanged(float x, float y) { - super.drawableHotspotChanged(x, y); - - if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) { - if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) { - throw new IllegalStateException("addStateFromChildren cannot be enabled if a" - + " child has duplicateParentState set to true"); - } - - final View[] children = mChildren; - final int count = mChildrenCount; - - for (int i = 0; i < count; i++) { - final View child = children[i]; - if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) { - child.drawableHotspotChanged(x, y); - } - } - } - } - - @Override protected int[] onCreateDrawableState(int extraSpace) { if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) { return super.onCreateDrawableState(extraSpace); diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java index a218e4d..52912b1 100644 --- a/core/java/android/view/accessibility/AccessibilityCache.java +++ b/core/java/android/view/accessibility/AccessibilityCache.java @@ -78,6 +78,7 @@ final class AccessibilityCache { case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: case AccessibilityEvent.TYPE_VIEW_SELECTED: case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: + case AccessibilityEvent.TYPE_VIEW_CLICKED: case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: { refreshCachedNodeLocked(event.getWindowId(), event.getSourceNodeId()); } break; diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 6927660..d80ad6a 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; @@ -611,6 +612,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te private final int[] mScrollOffset = new int[2]; private final int[] mScrollConsumed = new int[2]; + private final float[] mTmpPoint = new float[2]; + // Used for offsetting MotionEvents that we feed to the VelocityTracker. // In the future it would be nice to be able to give this to the VelocityTracker // directly, or alternatively put a VT into absolute-positioning mode that only @@ -2509,38 +2512,29 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * Positions the selector in a way that mimics touch. */ void positionSelectorLikeTouch(int position, View sel, float x, float y) { - positionSelectorLikeFocus(position, sel); - - if (mSelector != null && position != INVALID_POSITION) { - mSelector.setHotspot(x, y); - } + positionSelector(position, sel, true, x, y); } /** * Positions the selector in a way that mimics keyboard focus. */ void positionSelectorLikeFocus(int position, View sel) { - // If we're changing position, update the visibility since the selector - // is technically being detached from the previous selection. - final Drawable selector = mSelector; - final boolean manageState = selector != null && mSelectorPosition != position - && position != INVALID_POSITION; - if (manageState) { - selector.setVisible(false, false); - } - - positionSelector(position, sel); - - if (manageState) { + if (mSelector != null && mSelectorPosition != position && position != INVALID_POSITION) { final Rect bounds = mSelectorRect; final float x = bounds.exactCenterX(); final float y = bounds.exactCenterY(); - selector.setVisible(getVisibility() == VISIBLE, false); - selector.setHotspot(x, y); + positionSelector(position, sel, true, x, y); + } else { + positionSelector(position, sel); } } void positionSelector(int position, View sel) { + positionSelector(position, sel, false, -1, -1); + } + + private void positionSelector(int position, View sel, boolean manageHotspot, float x, float y) { + final boolean positionChanged = position != mSelectorPosition; if (position != INVALID_POSITION) { mSelectorPosition = position; } @@ -2560,7 +2554,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Update the selector drawable. final Drawable selector = mSelector; if (selector != null) { + if (positionChanged) { + // Wipe out the current selector state so that we can start + // over in the new position with a fresh state. + selector.setVisible(false, false); + selector.setState(StateSet.NOTHING); + } selector.setBounds(selectorRect); + if (positionChanged) { + if (getVisibility() == VISIBLE) { + selector.setVisible(true, false); + } + selector.setState(getDrawableState()); + } + if (manageHotspot) { + selector.setHotspot(x, y); + } } final boolean isChildViewEnabled = mIsChildViewEnabled; @@ -3198,6 +3207,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // get the selector in the right state, but we don't want to press each child. } + @Override + public void dispatchDrawableHotspotChanged(float x, float y) { + // Don't dispatch hotspot changes to children. We'll manually handle + // calling drawableHotspotChanged on the correct child. + } + /** * Maps a point to a position in the list. * @@ -3256,6 +3271,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLayoutMode = LAYOUT_NORMAL; if (!mDataChanged) { + final float[] point = mTmpPoint; + point[0] = x; + point[1] = y; + transformPointToViewLocal(point, child); + child.drawableHotspotChanged(point[0], point[1]); child.setPressed(true); setPressed(true); layoutChildren(); @@ -3756,10 +3776,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } // Otherwise, check containment within list bounds. If we're // outside bounds, cancel any active presses. + final View motionView = getChildAt(mMotionPosition - mFirstPosition); final float x = ev.getX(pointerIndex); if (!pointInView(x, y, mTouchSlop)) { setPressed(false); - final View motionView = getChildAt(mMotionPosition - mFirstPosition); if (motionView != null) { motionView.setPressed(false); } @@ -3767,6 +3787,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mPendingCheckForTap : mPendingCheckForLongPress); mTouchMode = TOUCH_MODE_DONE_WAITING; updateSelectorState(); + } else if (motionView != null) { + // Still within bounds, update the hotspot. + final float[] point = mTmpPoint; + point[0] = x; + point[1] = y; + transformPointToViewLocal(point, motionView); + motionView.drawableHotspotChanged(point[0], point[1]); } break; case TOUCH_MODE_SCROLL: @@ -6416,6 +6443,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views. // However, we will NOT place them into scrap views. activeViews[i] = child; + // Remember the position so that setupChild() doesn't reset state. + lp.scrappedFromPosition = firstActivePosition + i; } } } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 0c65c50..371b480 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Build; import android.os.Bundle; import android.os.Parcel; @@ -757,10 +758,22 @@ public class HorizontalScrollView extends FrameLayout { } else { super.scrollTo(scrollX, scrollY); } - + awakenScrollBars(); } + /** + * @hide + */ + @Override + public void addClickableRectsForAccessibility(List<RectF> outRects) { + // This class always consumes touch events, therefore if it + // covers a view we do not want to send a click over it. + RectF bounds = new RectF(); + bounds.set(0, 0, getWidth(), getHeight()); + outRects.add(bounds); + } + @Override public boolean performAccessibilityAction(int action, Bundle arguments) { if (super.performAccessibilityAction(action, arguments)) { diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index a31d37e..fe8b08b 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -1385,7 +1385,9 @@ public class ListPopupWindow { clearCallbacks(); final View src = mSrc; - if (!src.isEnabled()) { + if (!src.isEnabled() || src.isLongClickable()) { + // Ignore long-press if the view is disabled or has its own + // handler. return; } @@ -1394,12 +1396,12 @@ public class ListPopupWindow { } // Don't let the parent intercept our events. - mSrc.getParent().requestDisallowInterceptTouchEvent(true); + src.getParent().requestDisallowInterceptTouchEvent(true); // Make sure we cancel any ongoing source event stream. final long now = SystemClock.uptimeMillis(); final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0); - mSrc.onTouchEvent(e); + src.onTouchEvent(e); e.recycle(); mForwarding = true; diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java index 04b5616..75c6184 100644 --- a/core/java/android/widget/RadialTimePickerView.java +++ b/core/java/android/widget/RadialTimePickerView.java @@ -1246,37 +1246,40 @@ public class RadialTimePickerView extends View implements View.OnTouchListener { } final int[] selectionDegrees = mSelectionDegrees; - int type = -1; - int newValue = -1; + final int type; + final int newValue; + final boolean valueChanged; if (mShowHours) { final int snapDegrees = snapOnly30s(degrees, 0) % 360; - if (forceSelection - || selectionDegrees[HOURS] != snapDegrees + valueChanged = selectionDegrees[HOURS] != snapDegrees || selectionDegrees[HOURS_INNER] != snapDegrees - || wasOnInnerCircle != mIsOnInnerCircle) { - selectionDegrees[HOURS] = snapDegrees; - selectionDegrees[HOURS_INNER] = snapDegrees; + || wasOnInnerCircle != mIsOnInnerCircle; - type = HOURS; - newValue = getCurrentHour(); - } + selectionDegrees[HOURS] = snapDegrees; + selectionDegrees[HOURS_INNER] = snapDegrees; + type = HOURS; + newValue = getCurrentHour(); } else { final int snapDegrees = snapPrefer30s(degrees) % 360; - if (forceSelection || selectionDegrees[MINUTES] != snapDegrees) { - selectionDegrees[MINUTES] = snapDegrees; + valueChanged = selectionDegrees[MINUTES] != snapDegrees; - type = MINUTES; - newValue = getCurrentMinute(); - } + selectionDegrees[MINUTES] = snapDegrees; + type = MINUTES; + newValue = getCurrentMinute(); } - if (newValue != -1) { + if (valueChanged || forceSelection || autoAdvance) { + // Fire the listener even if we just need to auto-advance. if (mListener != null) { mListener.onValueSelected(type, newValue, autoAdvance); } - performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); - invalidate(); + + // Only provide feedback if the value actually changed. + if (valueChanged || forceSelection) { + performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); + invalidate(); + } return true; } diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 4c8aa51..7a22224 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -689,6 +689,10 @@ public class Switch extends CompoundButton { * @return true if (x, y) is within the target area of the switch thumb */ private boolean hitThumb(float x, float y) { + if (mThumbDrawable == null) { + return false; + } + // Relies on mTempRect, MUST be called first! final int thumbOffset = getThumbOffset(); |