diff options
Diffstat (limited to 'core/java/android')
21 files changed, 497 insertions, 180 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 9874b0b..83acb4d 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1783,6 +1783,12 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case REQUEST_BUG_REPORT_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + requestBugReport(); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -4066,5 +4072,15 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public void requestBugReport() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(REQUEST_BUG_REPORT_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 9454636..3124671 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -361,6 +361,8 @@ public interface IActivityManager extends IInterface { public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException; public void unregisterUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException; + public void requestBugReport() throws RemoteException; + /* * Private non-Binder interfaces */ @@ -613,4 +615,5 @@ public interface IActivityManager extends IInterface { int REGISTER_USER_SWITCH_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+154; int UNREGISTER_USER_SWITCH_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+155; int GET_RUNNING_USER_IDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+156; + int REQUEST_BUG_REPORT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+157; } diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java index cfc8bbd..a9ccef0 100644 --- a/core/java/android/app/MediaRouteButton.java +++ b/core/java/android/app/MediaRouteButton.java @@ -221,21 +221,28 @@ public class MediaRouteButton extends View { void updateRouteCount() { final int N = mRouter.getRouteCount(); int count = 0; + boolean hasVideoRoutes = false; for (int i = 0; i < N; i++) { final RouteInfo route = mRouter.getRouteAt(i); - if ((route.getSupportedTypes() & mRouteTypes) != 0) { + final int routeTypes = route.getSupportedTypes(); + if ((routeTypes & mRouteTypes) != 0) { if (route instanceof RouteGroup) { count += ((RouteGroup) route).getRouteCount(); } else { count++; } + if ((routeTypes & MediaRouter.ROUTE_TYPE_LIVE_VIDEO) != 0) { + hasVideoRoutes = true; + } } } setEnabled(count != 0); - // Only allow toggling if we have more than just user routes - mToggleMode = count == 2 && (mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_AUDIO) != 0; + // Only allow toggling if we have more than just user routes. + // Don't toggle if we support video routes, we may have to let the dialog scan. + mToggleMode = count == 2 && (mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_AUDIO) != 0 && + !hasVideoRoutes; } @Override diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 1e61e10..3d656c7 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -97,13 +97,13 @@ public class StatusBarManager { } /** - * Expand the notifications. + * Expand the notifications panel. */ - public void expandNotifications() { + public void expandNotificationsPanel() { try { final IStatusBarService svc = getService(); if (svc != null) { - svc.expandNotifications(); + svc.expandNotificationsPanel(); } } catch (RemoteException ex) { // system process is dead anyway. @@ -112,13 +112,13 @@ public class StatusBarManager { } /** - * Collapse the notifications. + * Collapse the notifications and settings panels. */ - public void collapseNotifications() { + public void collapsePanels() { try { final IStatusBarService svc = getService(); if (svc != null) { - svc.collapseNotifications(); + svc.collapsePanels(); } } catch (RemoteException ex) { // system process is dead anyway. @@ -127,28 +127,13 @@ public class StatusBarManager { } /** - * Expand the quick settings. + * Expand the settings panel. */ - public void expandQuickSettings() { + public void expandSettingsPanel() { try { final IStatusBarService svc = getService(); if (svc != null) { - svc.expandQuickSettings(); - } - } catch (RemoteException ex) { - // system process is dead anyway. - throw new RuntimeException(ex); - } - } - - /** - * Collapse the quick settings. - */ - public void collapseQuickSettings() { - try { - final IStatusBarService svc = getService(); - if (svc != null) { - svc.collapseQuickSettings(); + svc.expandSettingsPanel(); } } catch (RemoteException ex) { // system process is dead anyway. diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index b9518b8..c301c5c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1305,6 +1305,14 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.REFERRER"; /** + * Used as an int extra field with {@link #ACTION_INSTALL_PACKAGE} and + * {@link} #ACTION_VIEW} to indicate the uid of the package that initiated the install + * @hide + */ + public static final String EXTRA_ORIGINATING_UID + = "android.intent.extra.ORIGINATING_UID"; + + /** * Used as a boolean extra field with {@link #ACTION_INSTALL_PACKAGE} to install a * package. Tells the installer UI to skip the confirmation with the user * if the .apk is replacing an existing one. diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 0b320f0..b884b98 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -172,10 +172,16 @@ public class ActivityInfo extends ComponentInfo */ public static final int FLAG_IMMERSIVE = 0x0400; /** + * @hide Bit in {@link #flags}: If set, this component will only be seen + * by the primary user. Only works with broadcast receivers. Set from the + * {@link android.R.attr#primaryUserOnly} attribute. + */ + public static final int FLAG_PRIMARY_USER_ONLY = 0x20000000; + /** * Bit in {@link #flags}: If set, a single instance of the receiver will * run for all users on the device. Set from the * {@link android.R.attr#singleUser} attribute. Note that this flag is - * only relevent for ActivityInfo structures that are describiner receiver + * only relevant for ActivityInfo structures that are describing receiver * components; it is not applied to activities. */ public static final int FLAG_SINGLE_USER = 0x40000000; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 0a22fca..c2b75f4 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2193,7 +2193,7 @@ public class PackageParser { if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestActivity_singleUser, false)) { - a.info.flags |= ServiceInfo.FLAG_SINGLE_USER; + a.info.flags |= ActivityInfo.FLAG_SINGLE_USER; if (a.info.exported) { Slog.w(TAG, "Activity exported request ignored due to singleUser: " + a.className + " at " + mArchiveSourcePath + " " @@ -2202,6 +2202,11 @@ public class PackageParser { } setExported = true; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestActivity_primaryUserOnly, + false)) { + a.info.flags |= ActivityInfo.FLAG_PRIMARY_USER_ONLY; + } } sa.recycle(); diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 3579977..7b3d8cd 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -34,6 +34,14 @@ public class PackageUserState { public HashSet<String> enabledComponents; public PackageUserState() { + this(true); + } + + /** @hide */ + public PackageUserState(boolean isSystem) { + if (!isSystem) { + stopped = notLaunched = true; + } installed = true; enabled = COMPONENT_ENABLED_STATE_DEFAULT; } diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index ab32523..593f826 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -71,6 +71,9 @@ public class UserInfo implements Parcelable { public long creationTime; public long lastLoggedInTime; + /** User is only partially created. */ + public boolean partial; + public UserInfo(int id, String name, int flags) { this(id, name, null, flags); } @@ -105,6 +108,7 @@ public class UserInfo implements Parcelable { serialNumber = orig.serialNumber; creationTime = orig.creationTime; lastLoggedInTime = orig.lastLoggedInTime; + partial = orig.partial; } public UserHandle getUserHandle() { @@ -128,6 +132,7 @@ public class UserInfo implements Parcelable { dest.writeInt(serialNumber); dest.writeLong(creationTime); dest.writeLong(lastLoggedInTime); + dest.writeInt(partial ? 1 : 0); } public static final Parcelable.Creator<UserInfo> CREATOR @@ -148,5 +153,6 @@ public class UserInfo implements Parcelable { serialNumber = source.readInt(); creationTime = source.readLong(); lastLoggedInTime = source.readLong(); + partial = source.readInt() != 0; } } diff --git a/core/java/android/content/pm/VerificationParams.java b/core/java/android/content/pm/VerificationParams.java index 6454de0..22e1a85 100644 --- a/core/java/android/content/pm/VerificationParams.java +++ b/core/java/android/content/pm/VerificationParams.java @@ -27,6 +27,9 @@ import android.os.Parcelable; * @hide */ public class VerificationParams implements Parcelable { + /** A constant used to indicate that a uid value is not present. */ + public static final int NO_UID = -1; + /** What we print out first when toString() is called. */ private static final String TO_STRING_PREFIX = "VerificationParams{"; @@ -39,6 +42,9 @@ public class VerificationParams implements Parcelable { /** HTTP referrer URI associated with the originatingURI. */ private final Uri mReferrer; + /** UID of the application that the install request originated from. */ + private final int mOriginatingUid; + /** UID of application requesting the install */ private int mInstallerUid; @@ -57,16 +63,19 @@ public class VerificationParams implements Parcelable { * from. May be {@code null}. * @param referrer HTTP referrer URI associated with the originatingURI. * May be {@code null}. + * @param originatingUid UID of the application that the install request originated + * from, or NO_UID if not present * @param manifestDigest an object that holds the digest of the package * which can be used to verify ownership. May be {@code null}. */ public VerificationParams(Uri verificationURI, Uri originatingURI, Uri referrer, - ManifestDigest manifestDigest) { + int originatingUid, ManifestDigest manifestDigest) { mVerificationURI = verificationURI; mOriginatingURI = originatingURI; mReferrer = referrer; + mOriginatingUid = originatingUid; mManifestDigest = manifestDigest; - mInstallerUid = -1; + mInstallerUid = NO_UID; } public Uri getVerificationURI() { @@ -81,11 +90,16 @@ public class VerificationParams implements Parcelable { return mReferrer; } + /** return NO_UID if not available */ + public int getOriginatingUid() { + return mOriginatingUid; + } + public ManifestDigest getManifestDigest() { return mManifestDigest; } - /** @return -1 when not set */ + /** @return NO_UID when not set */ public int getInstallerUid() { return mInstallerUid; } @@ -111,31 +125,39 @@ public class VerificationParams implements Parcelable { final VerificationParams other = (VerificationParams) o; - if (mVerificationURI == null && other.mVerificationURI != null) { - return false; - } - if (!mVerificationURI.equals(other.mVerificationURI)) { + if (mVerificationURI == null) { + if (other.mVerificationURI != null) { + return false; + } + } else if (!mVerificationURI.equals(other.mVerificationURI)) { return false; } - if (mOriginatingURI == null && other.mOriginatingURI != null) { - return false; - } - if (!mOriginatingURI.equals(other.mOriginatingURI)) { + if (mOriginatingURI == null) { + if (other.mOriginatingURI != null) { + return false; + } + } else if (!mOriginatingURI.equals(other.mOriginatingURI)) { return false; } - if (mReferrer == null && other.mReferrer != null) { - return false; - } - if (!mReferrer.equals(other.mReferrer)) { + if (mReferrer == null) { + if (other.mReferrer != null) { + return false; + } + } else if (!mReferrer.equals(other.mReferrer)) { return false; } - if (mManifestDigest == null && other.mManifestDigest != null) { + if (mOriginatingUid != other.mOriginatingUid) { return false; } - if (mManifestDigest != null && !mManifestDigest.equals(other.mManifestDigest)) { + + if (mManifestDigest == null) { + if (other.mManifestDigest != null) { + return false; + } + } else if (!mManifestDigest.equals(other.mManifestDigest)) { return false; } @@ -150,11 +172,12 @@ public class VerificationParams implements Parcelable { public int hashCode() { int hash = 3; - hash += 5 * (mVerificationURI==null?1:mVerificationURI.hashCode()); - hash += 7 * (mOriginatingURI==null?1:mOriginatingURI.hashCode()); - hash += 11 * (mReferrer==null?1:mReferrer.hashCode()); - hash += 13 * (mManifestDigest==null?1:mManifestDigest.hashCode()); - hash += 17 * mInstallerUid; + hash += 5 * (mVerificationURI == null ? 1 : mVerificationURI.hashCode()); + hash += 7 * (mOriginatingURI == null ? 1 : mOriginatingURI.hashCode()); + hash += 11 * (mReferrer == null ? 1 : mReferrer.hashCode()); + hash += 13 * mOriginatingUid; + hash += 17 * (mManifestDigest == null ? 1 : mManifestDigest.hashCode()); + hash += 19 * mInstallerUid; return hash; } @@ -169,6 +192,8 @@ public class VerificationParams implements Parcelable { sb.append(mOriginatingURI.toString()); sb.append(",mReferrer="); sb.append(mReferrer.toString()); + sb.append(",mOriginatingUid="); + sb.append(mOriginatingUid); sb.append(",mManifestDigest="); sb.append(mManifestDigest.toString()); sb.append(",mInstallerUid="); @@ -183,6 +208,7 @@ public class VerificationParams implements Parcelable { dest.writeParcelable(mVerificationURI, 0); dest.writeParcelable(mOriginatingURI, 0); dest.writeParcelable(mReferrer, 0); + dest.writeInt(mOriginatingUid); dest.writeParcelable(mManifestDigest, 0); dest.writeInt(mInstallerUid); } @@ -192,6 +218,7 @@ public class VerificationParams implements Parcelable { mVerificationURI = source.readParcelable(Uri.class.getClassLoader()); mOriginatingURI = source.readParcelable(Uri.class.getClassLoader()); mReferrer = source.readParcelable(Uri.class.getClassLoader()); + mOriginatingUid = source.readInt(); mManifestDigest = source.readParcelable(ManifestDigest.class.getClassLoader()); mInstallerUid = source.readInt(); } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b3ab385..5b49ba3 100755 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -7659,6 +7659,54 @@ public final class ContactsContract { public static final int MODE_LARGE = 3; /** + * Constructs the QuickContacts intent with a view's rect. + * @hide + */ + public static Intent composeQuickContactsIntent(Context context, View target, Uri lookupUri, + int mode, String[] excludeMimes) { + // Find location and bounds of target view, adjusting based on the + // assumed local density. + final float appScale = context.getResources().getCompatibilityInfo().applicationScale; + final int[] pos = new int[2]; + target.getLocationOnScreen(pos); + + final Rect rect = new Rect(); + rect.left = (int) (pos[0] * appScale + 0.5f); + rect.top = (int) (pos[1] * appScale + 0.5f); + rect.right = (int) ((pos[0] + target.getWidth()) * appScale + 0.5f); + rect.bottom = (int) ((pos[1] + target.getHeight()) * appScale + 0.5f); + + return composeQuickContactsIntent(context, rect, lookupUri, mode, excludeMimes); + } + + /** + * Constructs the QuickContacts intent. + * @hide + */ + public static Intent composeQuickContactsIntent(Context context, Rect target, + Uri lookupUri, int mode, String[] excludeMimes) { + // When launching from an Activiy, we don't want to start a new task, but otherwise + // we *must* start a new task. (Otherwise startActivity() would crash.) + Context actualContext = context; + while ((actualContext instanceof ContextWrapper) + && !(actualContext instanceof Activity)) { + actualContext = ((ContextWrapper) actualContext).getBaseContext(); + } + final int intentFlags = (actualContext instanceof Activity) + ? Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET + : Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK; + + // Launch pivot dialog through intent for now + final Intent intent = new Intent(ACTION_QUICK_CONTACT).addFlags(intentFlags); + + intent.setData(lookupUri); + intent.setSourceBounds(target); + intent.putExtra(EXTRA_MODE, mode); + intent.putExtra(EXTRA_EXCLUDE_MIMES, excludeMimes); + return intent; + } + + /** * Trigger a dialog that lists the various methods of interacting with * the requested {@link Contacts} entry. This may be based on available * {@link ContactsContract.Data} rows under that contact, and may also @@ -7683,20 +7731,10 @@ public final class ContactsContract { */ public static void showQuickContact(Context context, View target, Uri lookupUri, int mode, String[] excludeMimes) { - // Find location and bounds of target view, adjusting based on the - // assumed local density. - final float appScale = context.getResources().getCompatibilityInfo().applicationScale; - final int[] pos = new int[2]; - target.getLocationOnScreen(pos); - - final Rect rect = new Rect(); - rect.left = (int) (pos[0] * appScale + 0.5f); - rect.top = (int) (pos[1] * appScale + 0.5f); - rect.right = (int) ((pos[0] + target.getWidth()) * appScale + 0.5f); - rect.bottom = (int) ((pos[1] + target.getHeight()) * appScale + 0.5f); - // Trigger with obtained rectangle - showQuickContact(context, rect, lookupUri, mode, excludeMimes); + Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode, + excludeMimes); + context.startActivity(intent); } /** @@ -7727,25 +7765,9 @@ public final class ContactsContract { */ public static void showQuickContact(Context context, Rect target, Uri lookupUri, int mode, String[] excludeMimes) { - // When launching from an Activiy, we don't want to start a new task, but otherwise - // we *must* start a new task. (Otherwise startActivity() would crash.) - Context actualContext = context; - while ((actualContext instanceof ContextWrapper) - && !(actualContext instanceof Activity)) { - actualContext = ((ContextWrapper) actualContext).getBaseContext(); - } - final int intentFlags = (actualContext instanceof Activity) - ? Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET - : Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK; - - // Launch pivot dialog through intent for now - final Intent intent = new Intent(ACTION_QUICK_CONTACT).addFlags(intentFlags); - - intent.setData(lookupUri); - intent.setSourceBounds(target); - intent.putExtra(EXTRA_MODE, mode); - intent.putExtra(EXTRA_EXCLUDE_MIMES, excludeMimes); - context.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode, + excludeMimes); + context.startActivity(intent); } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 550713d..8825f58 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3141,6 +3141,12 @@ public final class Settings { public static final String DEVICE_PROVISIONED = Global.DEVICE_PROVISIONED; /** + * Whether the current user has been set up via setup wizard (0 = false, 1 = true) + * @hide + */ + public static final String USER_SETUP_COMPLETE = "user_setup_complete"; + + /** * List of input methods that are currently enabled. This is a string * containing the IDs of all enabled input methods, each ID separated * by ':'. @@ -4619,13 +4625,6 @@ public final class Settings { public static final String SMS_SHORT_CODE_RULE = "sms_short_code_rule"; /** - * Prefix for SMS short code regex patterns (country code is appended). - * @see com.android.internal.telephony.SmsUsageMonitor - * @hide - */ - public static final String SMS_SHORT_CODES_PREFIX = "sms_short_codes_"; - - /** * Used to disable Tethering on a device - defaults to true * @hide */ diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index b0a2711..4873860 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -19,6 +19,7 @@ package android.view; import android.content.Context; import android.os.SystemClock; import android.util.FloatMath; +import android.util.Log; import java.util.Arrays; @@ -223,10 +224,14 @@ public class ScaleGestureDetector { * @param id pointer id to clear * @see #addTouchHistory(MotionEvent) */ - private void removeTouchHistoryForId(int id) { + private boolean removeTouchHistoryForId(int id) { + if (id >= mTouchHistoryLastAccepted.length) { + return false; + } mTouchHistoryLastAccepted[id] = Float.NaN; mTouchHistoryDirection[id] = 0; mTouchHistoryLastAcceptedTime[id] = 0; + return true; } /** @@ -236,6 +241,11 @@ public class ScaleGestureDetector { * @see #addTouchHistory(MotionEvent) */ private float getAdjustedTouchHistory(int id) { + if (id >= mTouchHistoryLastAccepted.length) { + Log.e(TAG, "Error retrieving adjusted touch history for id=" + id + + " - incomplete event stream?"); + return 0; + } return mTouchHistoryLastAccepted[id]; } @@ -244,6 +254,10 @@ public class ScaleGestureDetector { * @see #addTouchHistory(MotionEvent) */ private void clearTouchHistory() { + if (mTouchHistoryLastAccepted == null) { + // All three arrays will be null if this is the case; nothing to do. + return; + } Arrays.fill(mTouchHistoryLastAccepted, Float.NaN); Arrays.fill(mTouchHistoryDirection, 0); Arrays.fill(mTouchHistoryLastAcceptedTime, 0); @@ -333,7 +347,11 @@ public class ScaleGestureDetector { final float focusY = sumY / div; if (pointerUp) { - removeTouchHistoryForId(event.getPointerId(event.getActionIndex())); + final int id = event.getPointerId(event.getActionIndex()); + if (!removeTouchHistoryForId(id)) { + Log.e(TAG, "Got ACTION_POINTER_UP for previously unknown id=" + id + + " - incomplete event stream?"); + } } else { addTouchHistory(event); } diff --git a/core/java/android/view/SimulatedTrackball.java b/core/java/android/view/SimulatedTrackball.java new file mode 100644 index 0000000..1e152d8 --- /dev/null +++ b/core/java/android/view/SimulatedTrackball.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +/** + * This class creates trackball events from touchpad events. + * @see ViewRootImpl + */ +class SimulatedTrackball { + + //The position of the previous touchpad event + private float mLastTouchpadXPosition; + private float mLastTouchpadYPosition; + //Where the touchpad was initially pressed + private float mTouchpadEnterXPosition; + private float mTouchpadEnterYPosition; + //When the last touchpad event occurred + private long mLastTouchPadStartTimeMs = 0; + + //Change in position allowed during tap events + private float mTouchSlop; + private float mTouchSlopSquared; + //Has the TouchSlop constraint been invalidated + private boolean mAlwaysInTapRegion = true; + + //Maximum difference in milliseconds between the down and up of a touch event + //for it to be considered a tap + //TODO:Read this value from a config file + private static final int MAX_TAP_TIME = 250; + + public SimulatedTrackball(){ + mTouchSlop = ViewConfiguration.getTouchSlop(); + mTouchSlopSquared = mTouchSlop * mTouchSlop; + } + + public void updateTrackballDirection(ViewRootImpl viewroot, MotionEvent event){ + //Store what time the touchpad event occurred + final long time = event.getEventTime(); + MotionEvent trackballEvent; + switch (event.getAction()) { + case MotionEvent.ACTION_HOVER_ENTER: + mLastTouchPadStartTimeMs = time; + mAlwaysInTapRegion = true; + mTouchpadEnterXPosition = event.getX(); + mTouchpadEnterYPosition = event.getY(); + break; + case MotionEvent.ACTION_HOVER_MOVE: + //Find the difference in position between the two most recent touchpad events + float deltaX = event.getX() - mLastTouchpadXPosition; + float deltaY = event.getY() - mLastTouchpadYPosition; + + //TODO: Get simulated trackball configuration parameters + //Create a trackball event from recorded touchpad event data + trackballEvent = MotionEvent.obtain(mLastTouchPadStartTimeMs, time, + MotionEvent.ACTION_MOVE, deltaX / 50, + deltaY / 50, 0, 0, event.getMetaState(), + 10f, 10f, event.getDeviceId(), 0); + trackballEvent.setSource(InputDevice.SOURCE_CLASS_TRACKBALL); + //Add the new event to event queue + viewroot.enqueueInputEvent(trackballEvent); + + deltaX = event.getX() - mTouchpadEnterXPosition; + deltaY = event.getY() - mTouchpadEnterYPosition; + if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY){ + mAlwaysInTapRegion = false; + } + break; + case MotionEvent.ACTION_HOVER_EXIT: + if (time-mLastTouchPadStartTimeMs<MAX_TAP_TIME && mAlwaysInTapRegion){ + //Trackball Down + trackballEvent = MotionEvent.obtain(mLastTouchPadStartTimeMs, time, + MotionEvent.ACTION_DOWN, 0, 0, 0, 0, event.getMetaState(), + 10f, 10f, event.getDeviceId(), 0); + trackballEvent.setSource(InputDevice.SOURCE_CLASS_TRACKBALL); + //Add the new event to event queue + viewroot.enqueueInputEvent(trackballEvent); + + //Trackball Release + trackballEvent = MotionEvent.obtain(mLastTouchPadStartTimeMs, time, + MotionEvent.ACTION_UP, 0, 0, 0, 0, event.getMetaState(), + 10f, 10f, event.getDeviceId(), 0); + trackballEvent.setSource(InputDevice.SOURCE_CLASS_TRACKBALL); + //Add the new event to event queue + viewroot.enqueueInputEvent(trackballEvent); + } + break; + } + //Store touch event position + mLastTouchpadXPosition = event.getX(); + mLastTouchpadYPosition = event.getY(); + } +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b5bb98a..6a35d51 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5887,6 +5887,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * layout attribute and/or the inherited value from the parent * * @return true if the layout is right-to-left. + * + * @hide */ @ViewDebug.ExportedProperty(category = "layout") public boolean isLayoutRtl() { @@ -11628,9 +11630,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing * that the parent directionality can and will be resolved before its children. * + * @return true if resolution has been done, false otherwise. + * * @hide */ - public void resolveLayoutDirection() { + public boolean resolveLayoutDirection() { // Clear any previous layout direction resolution mPrivateFlags2 &= ~PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK; @@ -11641,15 +11645,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, case LAYOUT_DIRECTION_INHERIT: // We cannot resolve yet. LTR is by default and let the resolution happen again // later to get the correct resolved value - if (!canResolveLayoutDirection()) return; - - ViewGroup viewGroup = ((ViewGroup) mParent); + if (!canResolveLayoutDirection()) return false; - // We cannot resolve yet on the parent too. LTR is by default and let the - // resolution happen again later - if (!viewGroup.canResolveLayoutDirection()) return; + View parent = ((View) mParent); + // Parent has not yet resolved, LTR is still the default + if (!parent.isLayoutDirectionResolved()) return false; - if (viewGroup.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { + if (parent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL; } break; @@ -11669,6 +11671,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Set to resolved mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED; + return true; } /** @@ -11679,10 +11682,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ public boolean canResolveLayoutDirection() { - switch ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_MASK) >> - PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) { + switch (getRawLayoutDirection()) { case LAYOUT_DIRECTION_INHERIT: - return (mParent != null) && (mParent instanceof ViewGroup); + return (mParent != null) && (mParent instanceof ViewGroup) && + ((ViewGroup) mParent).canResolveLayoutDirection(); default: return true; } @@ -16697,9 +16700,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Resolve the text direction. * + * @return true if resolution has been done, false otherwise. + * * @hide */ - public void resolveTextDirection() { + public boolean resolveTextDirection() { // Reset any previous text direction resolution mPrivateFlags2 &= ~(PFLAG2_TEXT_DIRECTION_RESOLVED | PFLAG2_TEXT_DIRECTION_RESOLVED_MASK); @@ -16708,29 +16713,35 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int textDirection = getRawTextDirection(); switch(textDirection) { case TEXT_DIRECTION_INHERIT: - if (canResolveTextDirection()) { - ViewGroup viewGroup = ((ViewGroup) mParent); - - // Set current resolved direction to the same value as the parent's one - final int parentResolvedDirection = viewGroup.getTextDirection(); - switch (parentResolvedDirection) { - case TEXT_DIRECTION_FIRST_STRONG: - case TEXT_DIRECTION_ANY_RTL: - case TEXT_DIRECTION_LTR: - case TEXT_DIRECTION_RTL: - case TEXT_DIRECTION_LOCALE: - mPrivateFlags2 |= - (parentResolvedDirection << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT); - break; - default: - // Default resolved direction is "first strong" heuristic - mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; - } - } else { + if (!canResolveTextDirection()) { // We cannot do the resolution if there is no parent, so use the default one mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; // Resolution will need to happen again later - return; + return false; + } + + View parent = ((View) mParent); + // Parent has not yet resolved, so we still return the default + if (!parent.isTextDirectionResolved()) { + mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; + // Resolution will need to happen again later + return false; + } + + // Set current resolved direction to the same value as the parent's one + final int parentResolvedDirection = parent.getTextDirection(); + switch (parentResolvedDirection) { + case TEXT_DIRECTION_FIRST_STRONG: + case TEXT_DIRECTION_ANY_RTL: + case TEXT_DIRECTION_LTR: + case TEXT_DIRECTION_RTL: + case TEXT_DIRECTION_LOCALE: + mPrivateFlags2 |= + (parentResolvedDirection << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT); + break; + default: + // Default resolved direction is "first strong" heuristic + mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT; } break; case TEXT_DIRECTION_FIRST_STRONG: @@ -16752,6 +16763,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Set to resolved mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED; + return true; } /** @@ -16762,7 +16774,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private boolean canResolveTextDirection() { switch (getRawTextDirection()) { case TEXT_DIRECTION_INHERIT: - return (mParent != null) && (mParent instanceof ViewGroup); + return (mParent != null) && (mParent instanceof View) && + ((View) mParent).canResolveTextDirection(); default: return true; } @@ -16892,9 +16905,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Resolve the text alignment. * + * @return true if resolution has been done, false otherwise. + * * @hide */ - public void resolveTextAlignment() { + public boolean resolveTextAlignment() { // Reset any previous text alignment resolution mPrivateFlags2 &= ~(PFLAG2_TEXT_ALIGNMENT_RESOLVED | PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK); @@ -16904,32 +16919,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback, switch (textAlignment) { case TEXT_ALIGNMENT_INHERIT: // Check if we can resolve the text alignment - if (canResolveTextAlignment() && mParent instanceof View) { - View view = (View) mParent; - - final int parentResolvedTextAlignment = view.getTextAlignment(); - switch (parentResolvedTextAlignment) { - case TEXT_ALIGNMENT_GRAVITY: - case TEXT_ALIGNMENT_TEXT_START: - case TEXT_ALIGNMENT_TEXT_END: - case TEXT_ALIGNMENT_CENTER: - case TEXT_ALIGNMENT_VIEW_START: - case TEXT_ALIGNMENT_VIEW_END: - // Resolved text alignment is the same as the parent resolved - // text alignment - mPrivateFlags2 |= - (parentResolvedTextAlignment << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT); - break; - default: - // Use default resolved text alignment - mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; - } - } - else { + if (!canResolveTextAlignment()) { // We cannot do the resolution if there is no parent so use the default mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; // Resolution will need to happen again later - return; + return false; + } + View parent = (View) mParent; + + // Parent has not yet resolved, so we still return the default + if (!parent.isTextAlignmentResolved()) { + mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; + // Resolution will need to happen again later + return false; + } + + final int parentResolvedTextAlignment = parent.getTextAlignment(); + switch (parentResolvedTextAlignment) { + case TEXT_ALIGNMENT_GRAVITY: + case TEXT_ALIGNMENT_TEXT_START: + case TEXT_ALIGNMENT_TEXT_END: + case TEXT_ALIGNMENT_CENTER: + case TEXT_ALIGNMENT_VIEW_START: + case TEXT_ALIGNMENT_VIEW_END: + // Resolved text alignment is the same as the parent resolved + // text alignment + mPrivateFlags2 |= + (parentResolvedTextAlignment << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT); + break; + default: + // Use default resolved text alignment + mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT; } break; case TEXT_ALIGNMENT_GRAVITY: @@ -16952,6 +16972,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Set the resolved mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED; + return true; } /** @@ -16962,7 +16983,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private boolean canResolveTextAlignment() { switch (getRawTextAlignment()) { case TEXT_DIRECTION_INHERIT: - return (mParent != null); + return (mParent != null) && (mParent instanceof View) && + ((View) mParent).canResolveTextAlignment(); default: return true; } @@ -17243,7 +17265,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return the measure specification based on size and mode */ public static int makeMeasureSpec(int size, int mode) { - return size + mode; + return (size & ~MODE_MASK) | (mode & MODE_MASK); } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 343db86..9af8f2c 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3476,6 +3476,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager ai.mKeepScreenOn = lastKeepOn; } + if (child.isLayoutDirectionInherited()) { + child.resetResolvedLayoutDirection(); + child.resolveRtlPropertiesIfNeeded(); + } + onViewAdded(child); if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { @@ -5347,48 +5352,54 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @hide */ @Override - public void resolveLayoutDirection() { - super.resolveLayoutDirection(); - - int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.isLayoutDirectionInherited()) { - child.resolveLayoutDirection(); + public boolean resolveLayoutDirection() { + final boolean result = super.resolveLayoutDirection(); + if (result) { + int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.isLayoutDirectionInherited()) { + child.resolveLayoutDirection(); + } } } + return result; } /** * @hide */ @Override - public void resolveTextDirection() { - super.resolveTextDirection(); - - int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.isTextDirectionInherited()) { - child.resolveTextDirection(); + public boolean resolveTextDirection() { + final boolean result = super.resolveTextDirection(); + if (result) { + int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.isTextDirectionInherited()) { + child.resolveTextDirection(); + } } } + return result; } /** * @hide */ @Override - public void resolveTextAlignment() { - super.resolveTextAlignment(); - - int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.isTextAlignmentInherited()) { - child.resolveTextAlignment(); + public boolean resolveTextAlignment() { + final boolean result = super.resolveTextAlignment(); + if (result) { + int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.isTextAlignmentInherited()) { + child.resolveTextAlignment(); + } } } + return result; } /** @@ -5984,7 +5995,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - protected boolean isLayoutRtl() { + /** + * @hide + */ + public boolean isLayoutRtl() { return (layoutDirection == View.LAYOUT_DIRECTION_RTL); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index c2e884d..73019b2 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -145,6 +145,8 @@ public final class ViewRootImpl implements ViewParent, final TrackballAxis mTrackballAxisX = new TrackballAxis(); final TrackballAxis mTrackballAxisY = new TrackballAxis(); + final SimulatedTrackball mSimulatedTrackball = new SimulatedTrackball(); + int mLastJoystickXDirection; int mLastJoystickYDirection; int mLastJoystickXKeyCode; @@ -3438,7 +3440,6 @@ public final class ViewRootImpl implements ViewParent, if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0); } - if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) { if (LOCAL_LOGV) Log.v(TAG, "Dispatching generic motion " + event + " to " + mView); @@ -3465,12 +3466,17 @@ public final class ViewRootImpl implements ViewParent, private void deliverGenericMotionEventPostIme(QueuedInputEvent q) { final MotionEvent event = (MotionEvent) q.mEvent; - final boolean isJoystick = (event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; + final int source = event.getSource(); + final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; + final boolean isTouchPad = (source & InputDevice.SOURCE_CLASS_POSITION) != 0; // If there is no view, then the event will not be handled. if (mView == null || !mAdded) { if (isJoystick) { updateJoystickDirection(event, false); + } else if (isTouchPad) { + //Convert TouchPad motion into a TrackBall event + mSimulatedTrackball.updateTrackballDirection(this, event); } finishInputEvent(q, false); return; @@ -3480,6 +3486,9 @@ public final class ViewRootImpl implements ViewParent, if (mView.dispatchGenericMotionEvent(event)) { if (isJoystick) { updateJoystickDirection(event, false); + } else if (isTouchPad) { + //Convert TouchPad motion into a TrackBall event + mSimulatedTrackball.updateTrackballDirection(this, event); } finishInputEvent(q, true); return; @@ -3490,6 +3499,10 @@ public final class ViewRootImpl implements ViewParent, // those. updateJoystickDirection(event, true); finishInputEvent(q, true); + } else if (isTouchPad) { + //Convert TouchPad motion into a TrackBall event + mSimulatedTrackball.updateTrackballDirection(this, event); + finishInputEvent(q, true); } else { finishInputEvent(q, false); } diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index bc78adc..cea1fd5 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -46,6 +46,9 @@ public class CheckedTextView extends TextView implements Checkable { private int mCheckMarkWidth; private boolean mNeedRequestlayout; + private int initialPaddingLeft = -1; + private int initialPaddingRight = -1; + private static final int[] CHECKED_STATE_SET = { R.attr.state_checked }; @@ -176,10 +179,22 @@ public class CheckedTextView extends TextView implements Checkable { protected void internalSetPadding(int left, int top, int right, int bottom) { super.internalSetPadding(left, top, right, bottom); setBasePadding(isLayoutRtl()); + initialPaddingLeft = mPaddingLeft; + initialPaddingRight = mPaddingRight; } @Override public void onPaddingChanged(int layoutDirection) { + updatePadding(); + } + + @Override + public void onRtlPropertiesChanged() { + super.onRtlPropertiesChanged(); + updatePadding(); + } + + private void updatePadding() { int newPadding = (mCheckMarkDrawable != null) ? mCheckMarkWidth + mBasePadding : mBasePadding; mNeedRequestlayout |= (mPaddingRight != newPadding); @@ -193,7 +208,7 @@ public class CheckedTextView extends TextView implements Checkable { mNeedRequestlayout = false; } } - + @Override public void setPadding(int left, int top, int right, int bottom) { super.setPadding(left, top, right, bottom); diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index b72b8cb..e0c5bbd 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -182,6 +182,12 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList */ private boolean mIsRtl = true; + /** + * Offset between the center of the selected child view and the center of the Gallery. + * Used to reset position correctly during layout. + */ + private int mSelectedCenterOffset; + public Gallery(Context context) { this(context, null); } @@ -395,6 +401,14 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList setSelectionToCenterChild(); + final View selChild = mSelectedChild; + if (selChild != null) { + final int childLeft = selChild.getLeft(); + final int childCenter = selChild.getWidth() / 2; + final int galleryCenter = getWidth() / 2; + mSelectedCenterOffset = childLeft + childCenter - galleryCenter; + } + onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these. invalidate(); @@ -537,6 +551,7 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList // We haven't been callbacking during the fling, so do it now super.selectionChanged(); } + mSelectedCenterOffset = 0; invalidate(); } @@ -650,7 +665,8 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList View sel = makeAndAddView(mSelectedPosition, 0, 0, true); // Put the selected child in the center - int selectedOffset = childrenLeft + (childrenWidth / 2) - (sel.getWidth() / 2); + int selectedOffset = childrenLeft + (childrenWidth / 2) - (sel.getWidth() / 2) + + mSelectedCenterOffset; sel.offsetLeftAndRight(selectedOffset); fillToGalleryRight(); diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 87396fb..1d465ce 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -789,6 +789,12 @@ public class ImageView extends View { if (resizeWidth) { int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; + + // Allow the width to outgrow its original estimate if height is fixed. + if (!resizeHeight) { + widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); + } + if (newWidth <= widthSize) { widthSize = newWidth; done = true; @@ -799,6 +805,13 @@ public class ImageView extends View { if (!done && resizeHeight) { int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; + + // Allow the height to outgrow its original estimate if width is fixed. + if (!resizeWidth) { + heightSize = resolveAdjustedSize(newHeight, mMaxHeight, + heightMeasureSpec); + } + if (newHeight <= heightSize) { heightSize = newHeight; } diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 704f6b6..4918e48 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -1361,6 +1361,14 @@ public class NumberPicker extends LinearLayout { // Allow text entry rather than strictly numeric entry. mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + // Make sure the min, max, respect the size of the displayed + // values. This will take care of the current value as well. + if (getMinValue() >= displayedValues.length) { + setMinValue(0); + } + if (getMaxValue() >= displayedValues.length) { + setMaxValue(displayedValues.length - 1); + } } else { mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER); } |