diff options
Diffstat (limited to 'core/java/android')
33 files changed, 1077 insertions, 449 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 8f125d7..2b35cd4 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -269,45 +269,51 @@ public class ActivityManager { * all activities that are visible to the user. */ public static final int PROCESS_STATE_TOP = 2; + /** @hide Process is hosting a foreground service. */ + public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3; + + /** @hide Same as {@link #PROCESS_STATE_TOP} but while device is sleeping. */ + public static final int PROCESS_STATE_TOP_SLEEPING = 4; + /** @hide Process is important to the user, and something they are aware of. */ - public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 3; + public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 5; /** @hide Process is important to the user, but not something they are aware of. */ - public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 4; + public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 6; /** @hide Process is in the background running a backup/restore operation. */ - public static final int PROCESS_STATE_BACKUP = 5; + public static final int PROCESS_STATE_BACKUP = 7; /** @hide Process is in the background, but it can't restore its state so we want * to try to avoid killing it. */ - public static final int PROCESS_STATE_HEAVY_WEIGHT = 6; + public static final int PROCESS_STATE_HEAVY_WEIGHT = 8; /** @hide Process is in the background running a service. Unlike oom_adj, this level * is used for both the normal running in background state and the executing * operations state. */ - public static final int PROCESS_STATE_SERVICE = 7; + public static final int PROCESS_STATE_SERVICE = 9; /** @hide Process is in the background running a receiver. Note that from the * perspective of oom_adj receivers run at a higher foreground level, but for our * prioritization here that is not necessary and putting them below services means * many fewer changes in some process states as they receive broadcasts. */ - public static final int PROCESS_STATE_RECEIVER = 8; + public static final int PROCESS_STATE_RECEIVER = 10; /** @hide Process is in the background but hosts the home activity. */ - public static final int PROCESS_STATE_HOME = 9; + public static final int PROCESS_STATE_HOME = 11; /** @hide Process is in the background but hosts the last shown activity. */ - public static final int PROCESS_STATE_LAST_ACTIVITY = 10; + public static final int PROCESS_STATE_LAST_ACTIVITY = 12; /** @hide Process is being cached for later use and contains activities. */ - public static final int PROCESS_STATE_CACHED_ACTIVITY = 11; + public static final int PROCESS_STATE_CACHED_ACTIVITY = 13; /** @hide Process is being cached for later use and is a client of another cached * process that contains activities. */ - public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12; + public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 14; /** @hide Process is being cached for later use and is empty. */ - public static final int PROCESS_STATE_CACHED_EMPTY = 13; + public static final int PROCESS_STATE_CACHED_EMPTY = 15; /** @hide requestType for assist context: only basic information. */ public static final int ASSIST_CONTEXT_BASIC = 0; @@ -2064,7 +2070,7 @@ public class ActivityManager { return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE; } else if (procState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) { return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE; - } else if (procState >= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { + } else if (procState >= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; } else { return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ed05321..3b96fd5 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5322,19 +5322,25 @@ public final class ActivityThread { private DropBoxManager dropBox; - public DropBoxReporter() { - dropBox = (DropBoxManager) getSystemContext().getSystemService(Context.DROPBOX_SERVICE); - } + public DropBoxReporter() {} @Override public void addData(String tag, byte[] data, int flags) { + ensureInitialized(); dropBox.addData(tag, data, flags); } @Override public void addText(String tag, String data) { + ensureInitialized(); dropBox.addText(tag, data); } + + private synchronized void ensureInitialized() { + if (dropBox == null) { + dropBox = (DropBoxManager) getSystemContext().getSystemService(Context.DROPBOX_SERVICE); + } + } } public static void main(String[] args) { diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 5dd02ae..179957d 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -115,6 +115,40 @@ public class AlarmManager /** @hide */ public static final long WINDOW_HEURISTIC = -1; + /** + * Flag for alarms: this is to be a stand-alone alarm, that should not be batched with + * other alarms. + * @hide + */ + public static final int FLAG_STANDALONE = 1<<0; + + /** + * Flag for alarms: this alarm would like to wake the device even if it is idle. This + * is, for example, an alarm for an alarm clock. + * @hide + */ + public static final int FLAG_WAKE_FROM_IDLE = 1<<1; + + /** + * Flag for alarms: this alarm would like to still execute even if the device is + * idle. This won't bring the device out of idle, just allow this specific alarm to + * run. Note that this means the actual time this alarm goes off can be inconsistent + * with the time of non-allow-while-idle alarms (it could go earlier than the time + * requested by another alarm). + * + * @hide + */ + public static final int FLAG_ALLOW_WHILE_IDLE = 1<<2; + + /** + * Flag for alarms: this alarm marks the point where we would like to come out of idle + * mode. It may be moved by the alarm manager to match the first wake-from-idle alarm. + * Scheduling an alarm with this flag puts the alarm manager in to idle mode, where it + * avoids scheduling any further alarms until the marker alarm is executed. + * @hide + */ + public static final int FLAG_IDLE_UNTIL = 1<<3; + private final IAlarmManager mService; private final boolean mAlwaysExact; @@ -204,7 +238,7 @@ public class AlarmManager * @see #RTC_WAKEUP */ public void set(int type, long triggerAtMillis, PendingIntent operation) { - setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation, null, null); + setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null); } /** @@ -265,7 +299,8 @@ public class AlarmManager */ public void setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { - setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, operation, null, null); + setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation, null, + null); } /** @@ -315,7 +350,7 @@ public class AlarmManager */ public void setWindow(int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation) { - setImpl(type, windowStartMillis, windowLengthMillis, 0, operation, null, null); + setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, operation, null, null); } /** @@ -353,7 +388,16 @@ public class AlarmManager * @see #RTC_WAKEUP */ public void setExact(int type, long triggerAtMillis, PendingIntent operation) { - setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, operation, null, null); + setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null); + } + + /** + * Schedule an idle-until alarm, which will keep the alarm manager idle until + * the given time. + * @hide + */ + public void setIdleUntil(int type, long triggerAtMillis, PendingIntent operation) { + setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_IDLE_UNTIL, operation, null, null); } /** @@ -381,18 +425,19 @@ public class AlarmManager * @see android.content.Intent#filterEquals */ public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) { - setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, operation, null, info); + setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation, null, info); } /** @hide */ @SystemApi public void set(int type, long triggerAtMillis, long windowMillis, long intervalMillis, PendingIntent operation, WorkSource workSource) { - setImpl(type, triggerAtMillis, windowMillis, intervalMillis, operation, workSource, null); + setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, workSource, + null); } private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis, - PendingIntent operation, WorkSource workSource, AlarmClockInfo alarmClock) { + int flags, PendingIntent operation, WorkSource workSource, AlarmClockInfo alarmClock) { if (triggerAtMillis < 0) { /* NOTYET if (mAlwaysExact) { @@ -405,7 +450,7 @@ public class AlarmManager } try { - mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation, + mService.set(type, triggerAtMillis, windowMillis, intervalMillis, flags, operation, workSource, alarmClock); } catch (RemoteException ex) { } @@ -506,7 +551,7 @@ public class AlarmManager */ public void setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { - setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, operation, null, null); + setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null, null); } /** diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java index 3e545f9..2dbbc38 100644 --- a/core/java/android/app/AlertDialog.java +++ b/core/java/android/app/AlertDialog.java @@ -134,6 +134,7 @@ public class AlertDialog extends Dialog implements DialogInterface { * {@code context}'s theme. * * @param context the parent context + * @see android.R.styleable#Theme_alertDialogTheme */ protected AlertDialog(Context context) { this(context, 0); @@ -155,6 +156,7 @@ public class AlertDialog extends Dialog implements DialogInterface { * {@code context}'s theme. * * @param context the parent context + * @see android.R.styleable#Theme_alertDialogTheme */ protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) { this(context, 0); @@ -187,16 +189,24 @@ public class AlertDialog extends Dialog implements DialogInterface { * @param themeResId the resource ID of the theme against which to inflate * this dialog, or {@code 0} to use the parent * {@code context}'s default alert dialog theme + * @see android.R.styleable#Theme_alertDialogTheme */ protected AlertDialog(Context context, @AttrRes int themeResId) { - super(context, resolveDialogTheme(context, themeResId)); + this(context, themeResId, true); + } + + AlertDialog(Context context, @AttrRes int themeResId, boolean createContextThemeWrapper) { + super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0, + createContextThemeWrapper); mWindow.alwaysReadCloseOnTouchAttr(); mAlert = new AlertController(getContext(), this, getWindow()); } static int resolveDialogTheme(Context context, int themeResId) { - if (themeResId == THEME_TRADITIONAL) { + if (themeResId == 0) { + return 0; + } else if (themeResId == THEME_TRADITIONAL) { return R.style.Theme_Dialog_Alert; } else if (themeResId == THEME_HOLO_DARK) { return R.style.Theme_Holo_Dialog_Alert; @@ -428,7 +438,6 @@ public class AlertDialog extends Dialog implements DialogInterface { public static class Builder { private final AlertController.AlertParams P; - private int mThemeResId; /** * Creates a builder for an alert dialog that uses the default alert @@ -473,7 +482,6 @@ public class AlertDialog extends Dialog implements DialogInterface { public Builder(Context context, int themeResId) { P = new AlertController.AlertParams(new ContextThemeWrapper( context, resolveDialogTheme(context, themeResId))); - mThemeResId = themeResId; } /** @@ -1075,7 +1083,7 @@ public class AlertDialog extends Dialog implements DialogInterface { * create and display the dialog. */ public AlertDialog create() { - final AlertDialog dialog = new AlertDialog(P.mContext, mThemeResId); + final AlertDialog dialog = new AlertDialog(P.mContext); P.apply(dialog.mAlert); dialog.setCancelable(P.mCancelable); if (P.mCancelable) { diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 9defcbe..6a2d207 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -21,7 +21,6 @@ import android.annotation.DrawableRes; import android.annotation.IdRes; import android.annotation.LayoutRes; import android.annotation.StringRes; -import com.android.internal.app.WindowDecorActionBar; import android.annotation.Nullable; import android.content.ComponentName; @@ -56,6 +55,9 @@ import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; +import com.android.internal.R; +import com.android.internal.app.WindowDecorActionBar; + import java.lang.ref.WeakReference; /** @@ -130,27 +132,32 @@ public class Dialog implements DialogInterface, Window.Callback, }; /** - * Create a Dialog window that uses the default dialog frame style. - * - * @param context The Context the Dialog is to run it. In particular, it - * uses the window manager and theme in this context to - * present its UI. + * Creates a dialog window that uses the default dialog theme. + * <p> + * The supplied {@code context} is used to obtain the window manager and + * base theme used to present the dialog. + * + * @param context the context in which the dialog should run + * @see android.R.styleable#Theme_dialogTheme */ public Dialog(Context context) { this(context, 0, true); } /** - * Create a Dialog window that uses a custom dialog style. + * Creates a dialog window that uses a custom dialog style. + * <p> + * The supplied {@code context} is used to obtain the window manager and + * base theme used to present the dialog. + * <p> + * The supplied {@code theme} is applied on top of the context's theme. See + * <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes"> + * Style and Theme Resources</a> for more information about defining and + * using styles. * - * @param context The Context in which the Dialog should run. In particular, it - * uses the window manager and theme from this context to - * present its UI. - * @param theme A style resource describing the theme to use for the - * window. See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style - * and Theme Resources</a> for more information about defining and using - * styles. This theme is applied on top of the current theme in - * <var>context</var>. If 0, the default dialog theme will be used. + * @param context the context in which the dialog should run + * @param theme a style resource describing the theme to use for the + * window, or {@code 0} to use the default dialog theme */ public Dialog(Context context, int theme) { this(context, theme, true); @@ -159,9 +166,8 @@ public class Dialog implements DialogInterface, Window.Callback, Dialog(Context context, int theme, boolean createContextThemeWrapper) { if (createContextThemeWrapper) { if (theme == 0) { - TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme, - outValue, true); + final TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true); theme = outValue.resourceId; } mContext = new ContextThemeWrapper(context, theme); @@ -170,12 +176,14 @@ public class Dialog implements DialogInterface, Window.Callback, } mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); - Window w = new PhoneWindow(mContext); + + final Window w = new PhoneWindow(mContext); mWindow = w; w.setCallback(this); w.setOnWindowDismissedCallback(this); w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); + mListenersHandler = new ListenersHandler(this); } diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl index 194082e..d5719f5 100644 --- a/core/java/android/app/IAlarmManager.aidl +++ b/core/java/android/app/IAlarmManager.aidl @@ -28,7 +28,7 @@ import android.os.WorkSource; interface IAlarmManager { /** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */ void set(int type, long triggerAtTime, long windowLength, - long interval, in PendingIntent operation, in WorkSource workSource, + long interval, int flags, in PendingIntent operation, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock); boolean setTime(long millis); void setTimeZone(String zone); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 33262b3..e2230da 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -76,12 +76,10 @@ interface INotificationManager boolean matchesCallFilter(in Bundle extras); boolean isSystemConditionProviderEnabled(String path); + int getZenMode(); ZenModeConfig getZenModeConfig(); - boolean setZenModeConfig(in ZenModeConfig config); - oneway void setZenMode(int mode); + boolean setZenModeConfig(in ZenModeConfig config, String reason); + oneway void setZenMode(int mode, in Uri conditionId, String reason); oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions); oneway void requestZenModeConditions(in IConditionListener callback, int relevance); - oneway void setZenModeCondition(in Condition condition); - oneway void setAutomaticZenModeConditions(in Uri[] conditionIds); - Condition[] getAutomaticZenModeConditions(); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 479327d..fa61e18 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -20,6 +20,7 @@ import android.annotation.SdkConstant; import android.app.Notification.Builder; import android.content.ComponentName; import android.content.Context; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -27,7 +28,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; import android.os.UserHandle; -import android.service.notification.Condition; +import android.provider.Settings.Global; import android.service.notification.IConditionListener; import android.service.notification.ZenModeConfig; import android.util.Log; @@ -282,10 +283,10 @@ public class NotificationManager /** * @hide */ - public void setZenMode(int mode) { + public void setZenMode(int mode, Uri conditionId, String reason) { INotificationManager service = getService(); try { - service.setZenMode(mode); + service.setZenMode(mode, conditionId, reason); } catch (RemoteException e) { } } @@ -293,6 +294,18 @@ public class NotificationManager /** * @hide */ + public boolean setZenModeConfig(ZenModeConfig config, String reason) { + INotificationManager service = getService(); + try { + return service.setZenModeConfig(config, reason); + } catch (RemoteException e) { + return false; + } + } + + /** + * @hide + */ public void requestZenModeConditions(IConditionListener listener, int relevance) { INotificationManager service = getService(); try { @@ -304,24 +317,22 @@ public class NotificationManager /** * @hide */ - public void setZenModeCondition(Condition exitCondition) { + public int getZenMode() { INotificationManager service = getService(); try { - service.setZenModeCondition(exitCondition); + return service.getZenMode(); } catch (RemoteException e) { } + return Global.ZEN_MODE_OFF; } /** * @hide */ - public Condition getZenModeCondition() { + public ZenModeConfig getZenModeConfig() { INotificationManager service = getService(); try { - final ZenModeConfig config = service.getZenModeConfig(); - if (config != null) { - return config.exitCondition; - } + return service.getZenModeConfig(); } catch (RemoteException e) { } return null; diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index 7acf5f0..022a62c 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -318,7 +318,7 @@ public class VoiceInteractor { * @param label The label that will both be matched against what the user speaks * and displayed visually. * @param index The location of this option within the overall set of options. - * Can be used to help identify which the option when it is returned from the + * Can be used to help identify the option when it is returned from the * voice interactor. */ public Option(CharSequence label, int index) { diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 9151a16..8b79305 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -339,4 +339,30 @@ public class BackupManager { } } } + + /** + * Ask the framework which dataset, if any, the given package's data would be + * restored from if we were to install it right now. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @param packageName The name of the package whose most-suitable dataset we + * wish to look up + * @return The dataset token from which a restore should be attempted, or zero if + * no suitable data is available. + * + * @hide + */ + @SystemApi + public long getAvailableRestoreToken(String packageName) { + checkServiceBinder(); + if (sService != null) { + try { + return sService.getAvailableRestoreToken(packageName); + } catch (RemoteException e) { + Log.e(TAG, "getAvailableRestoreToken() couldn't connect"); + } + } + return 0; + } } diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index 8f36dc4..87e4ef1 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -313,4 +313,17 @@ interface IBackupManager { * is being queried. */ boolean isBackupServiceActive(int whichUser); + + /** + * Ask the framework which dataset, if any, the given package's data would be + * restored from if we were to install it right now. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @param packageName The name of the package whose most-suitable dataset we + * wish to look up + * @return The dataset token from which a restore should be attempted, or zero if + * no suitable data is available. + */ + long getAvailableRestoreToken(String packageName); } diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 590d791..044e3e3 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -517,6 +517,38 @@ public class IntentFilter implements Parcelable { } /** + * Return if this filter handle all HTTP or HTTPS data URI or not. + * + * @return True if the filter handle all HTTP or HTTPS data URI. False otherwise. + * + * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and + * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent + * data scheme is "http" or "https" and that there is no specific host defined. + * + * @hide + */ + public final boolean handleAllWebDataURI() { + return hasWebDataURI() && (countDataAuthorities() == 0); + } + + /** + * Return if this filter has any HTTP or HTTPS data URI or not. + * + * @return True if the filter has any HTTP or HTTPS data URI. False otherwise. + * + * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and + * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent + * data scheme is "http" or "https". + * + * @hide + */ + public final boolean hasWebDataURI() { + return hasAction(Intent.ACTION_VIEW) && + hasCategory(Intent.CATEGORY_BROWSABLE) && + (hasDataScheme(SCHEME_HTTP) || hasDataScheme(SCHEME_HTTPS)); + } + + /** * Return if this filter needs to be automatically verified again its data URIs or not. * * @return True if the filter needs to be automatically verified. False otherwise. @@ -530,10 +562,7 @@ public class IntentFilter implements Parcelable { * @hide */ public final boolean needsVerification() { - return hasAction(Intent.ACTION_VIEW) && - hasCategory(Intent.CATEGORY_BROWSABLE) && - (hasDataScheme(SCHEME_HTTP) || hasDataScheme(SCHEME_HTTPS)) && - getAutoVerify(); + return hasWebDataURI() && getAutoVerify(); } /** diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java index 28cbaa8..e50b0ff 100644 --- a/core/java/android/content/pm/IntentFilterVerificationInfo.java +++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java @@ -36,7 +36,7 @@ import java.util.ArrayList; /** * The {@link com.android.server.pm.PackageManagerService} maintains some - * {@link IntentFilterVerificationInfo}s for each domain / package / class name per user. + * {@link IntentFilterVerificationInfo}s for each domain / package name. * * @hide */ diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index bdbed75..7523675 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2778,7 +2778,7 @@ public class PackageParser { } /** - * Check if one of the IntentFilter as an action VIEW and a HTTP/HTTPS data URI + * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI */ private static boolean hasDomainURLs(Package pkg) { if (pkg == null || pkg.activities == null) return false; @@ -2792,8 +2792,10 @@ public class PackageParser { for (int m=0; m<countFilters; m++) { ActivityIntentInfo aii = filters.get(m); if (!aii.hasAction(Intent.ACTION_VIEW)) continue; + if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue; if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) || aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) { + Slog.d(TAG, "hasDomainURLs:true for package:" + pkg.packageName); return true; } } diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java index 7b141f0..05f5e90 100644 --- a/core/java/android/content/pm/ResolveInfo.java +++ b/core/java/android/content/pm/ResolveInfo.java @@ -144,9 +144,9 @@ public class ResolveInfo implements Parcelable { public boolean system; /** - * @hide Does the associated IntentFilter needs verification ? + * @hide Does the associated IntentFilter comes from a Browser ? */ - public boolean filterNeedsVerification; + public boolean handleAllWebDataURI; private ComponentInfo getComponentInfo() { if (activityInfo != null) return activityInfo; @@ -288,7 +288,7 @@ public class ResolveInfo implements Parcelable { resolvePackageName = orig.resolvePackageName; system = orig.system; targetUserId = orig.targetUserId; - filterNeedsVerification = orig.filterNeedsVerification; + handleAllWebDataURI = orig.handleAllWebDataURI; } public String toString() { @@ -350,7 +350,7 @@ public class ResolveInfo implements Parcelable { dest.writeInt(targetUserId); dest.writeInt(system ? 1 : 0); dest.writeInt(noResourceId ? 1 : 0); - dest.writeInt(filterNeedsVerification ? 1 : 0); + dest.writeInt(handleAllWebDataURI ? 1 : 0); } public static final Creator<ResolveInfo> CREATOR @@ -396,7 +396,7 @@ public class ResolveInfo implements Parcelable { targetUserId = source.readInt(); system = source.readInt() != 0; noResourceId = source.readInt() != 0; - filterNeedsVerification = source.readInt() != 0; + handleAllWebDataURI = source.readInt() != 0; } public static class DisplayNameComparator diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java index b5b89dd..581fe7f 100644 --- a/core/java/android/database/AbstractCursor.java +++ b/core/java/android/database/AbstractCursor.java @@ -35,34 +35,38 @@ public abstract class AbstractCursor implements CrossProcessCursor { private static final String TAG = "Cursor"; /** - * @deprecated This is never updated by this class and should not be used + * @removed This field should not be used. */ - @Deprecated protected HashMap<Long, Map<String, Object>> mUpdatedRows; - protected int mPos; - /** - * This must be set to the index of the row ID column by any - * subclass that wishes to support updates. - * - * @deprecated This field should not be used. + * @removed This field should not be used. */ - @Deprecated protected int mRowIdColumnIndex; /** - * If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of - * the column at {@link #mRowIdColumnIndex} for the current row this cursor is - * pointing at. - * - * @deprecated This field should not be used. + * @removed This field should not be used. */ - @Deprecated protected Long mCurrentRowID; + /** + * @deprecated Use {@link #getPosition()} instead. + */ + @Deprecated + protected int mPos; + + /** + * @deprecated Use {@link #isClosed()} instead. + */ + @Deprecated protected boolean mClosed; + + /** + * @deprecated Do not use. + */ + @Deprecated protected ContentResolver mContentResolver; + private Uri mNotifyUri; private final Object mSelfObserverLock = new Object(); @@ -76,18 +80,28 @@ public abstract class AbstractCursor implements CrossProcessCursor { /* -------------------------------------------------------- */ /* These need to be implemented by subclasses */ + @Override abstract public int getCount(); + @Override abstract public String[] getColumnNames(); + @Override abstract public String getString(int column); + @Override abstract public short getShort(int column); + @Override abstract public int getInt(int column); + @Override abstract public long getLong(int column); + @Override abstract public float getFloat(int column); + @Override abstract public double getDouble(int column); + @Override abstract public boolean isNull(int column); + @Override public int getType(int column) { // Reflects the assumption that all commonly used field types (meaning everything // but blobs) are convertible to strings so it should be safe to call @@ -96,6 +110,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { } // TODO implement getBlob in all cursor types + @Override public byte[] getBlob(int column) { throw new UnsupportedOperationException("getBlob is not supported"); } @@ -108,14 +123,17 @@ public abstract class AbstractCursor implements CrossProcessCursor { * * @return The pre-filled window that backs this cursor, or null if none. */ + @Override public CursorWindow getWindow() { return null; } + @Override public int getColumnCount() { return getColumnNames().length; } + @Override public void deactivate() { onDeactivateOrClose(); } @@ -129,6 +147,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { mDataSetObservable.notifyInvalidated(); } + @Override public boolean requery() { if (mSelfObserver != null && mSelfObserverRegistered == false) { mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver); @@ -138,10 +157,12 @@ public abstract class AbstractCursor implements CrossProcessCursor { return true; } + @Override public boolean isClosed() { return mClosed; } + @Override public void close() { mClosed = true; mContentObservable.unregisterAll(); @@ -158,11 +179,13 @@ public abstract class AbstractCursor implements CrossProcessCursor { * @param newPosition the position that we're moving to * @return true if the move is successful, false otherwise */ + @Override public boolean onMove(int oldPosition, int newPosition) { return true; } + @Override public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { // Default implementation, uses getString String result = getString(columnIndex); @@ -183,15 +206,14 @@ public abstract class AbstractCursor implements CrossProcessCursor { /* Implementation */ public AbstractCursor() { mPos = -1; - mRowIdColumnIndex = -1; - mCurrentRowID = null; - mUpdatedRows = new HashMap<Long, Map<String, Object>>(); } + @Override public final int getPosition() { return mPos; } + @Override public final boolean moveToPosition(int position) { // Make sure position isn't past the end of the cursor final int count = getCount(); @@ -216,9 +238,6 @@ public abstract class AbstractCursor implements CrossProcessCursor { mPos = -1; } else { mPos = position; - if (mRowIdColumnIndex != -1) { - mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex)); - } } return result; @@ -229,35 +248,43 @@ public abstract class AbstractCursor implements CrossProcessCursor { DatabaseUtils.cursorFillWindow(this, position, window); } + @Override public final boolean move(int offset) { return moveToPosition(mPos + offset); } + @Override public final boolean moveToFirst() { return moveToPosition(0); } + @Override public final boolean moveToLast() { return moveToPosition(getCount() - 1); } + @Override public final boolean moveToNext() { return moveToPosition(mPos + 1); } + @Override public final boolean moveToPrevious() { return moveToPosition(mPos - 1); } + @Override public final boolean isFirst() { return mPos == 0 && getCount() != 0; } + @Override public final boolean isLast() { int cnt = getCount(); return mPos == (cnt - 1) && cnt != 0; } + @Override public final boolean isBeforeFirst() { if (getCount() == 0) { return true; @@ -265,6 +292,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { return mPos == -1; } + @Override public final boolean isAfterLast() { if (getCount() == 0) { return true; @@ -272,6 +300,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { return mPos == getCount(); } + @Override public int getColumnIndex(String columnName) { // Hack according to bug 903852 final int periodIndex = columnName.lastIndexOf('.'); @@ -297,6 +326,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { return -1; } + @Override public int getColumnIndexOrThrow(String columnName) { final int index = getColumnIndex(columnName); if (index < 0) { @@ -305,14 +335,17 @@ public abstract class AbstractCursor implements CrossProcessCursor { return index; } + @Override public String getColumnName(int columnIndex) { return getColumnNames()[columnIndex]; } + @Override public void registerContentObserver(ContentObserver observer) { mContentObservable.registerObserver(observer); } + @Override public void unregisterContentObserver(ContentObserver observer) { // cursor will unregister all observers when it close if (!mClosed) { @@ -320,10 +353,12 @@ public abstract class AbstractCursor implements CrossProcessCursor { } } + @Override public void registerDataSetObserver(DataSetObserver observer) { mDataSetObservable.registerObserver(observer); } + @Override public void unregisterDataSetObserver(DataSetObserver observer) { mDataSetObservable.unregisterObserver(observer); } @@ -350,6 +385,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { * @param notifyUri The URI to watch for changes. This can be a * specific row URI, or a base URI for a whole class of content. */ + @Override public void setNotificationUri(ContentResolver cr, Uri notifyUri) { setNotificationUri(cr, notifyUri, UserHandle.myUserId()); } @@ -368,31 +404,29 @@ public abstract class AbstractCursor implements CrossProcessCursor { } } + @Override public Uri getNotificationUri() { synchronized (mSelfObserverLock) { return mNotifyUri; } } + @Override public boolean getWantsAllOnMoveCalls() { return false; } - /** - * Sets a {@link Bundle} that will be returned by {@link #getExtras()}. <code>null</code> will - * be converted into {@link Bundle#EMPTY}. - * - * @param extras {@link Bundle} to set. - * @hide - */ + @Override public void setExtras(Bundle extras) { mExtras = (extras == null) ? Bundle.EMPTY : extras; } + @Override public Bundle getExtras() { return mExtras; } + @Override public Bundle respond(Bundle extras) { return Bundle.EMPTY; } diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index 98c7043..8576715 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -41,7 +41,6 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { public void initialize(BulkCursorDescriptor d) { mBulkCursor = d.cursor; mColumns = d.columnNames; - mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns); mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls; mCount = d.count; if (d.window != null) { diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java index fc2a885..d10c9b8 100644 --- a/core/java/android/database/Cursor.java +++ b/core/java/android/database/Cursor.java @@ -444,6 +444,13 @@ public interface Cursor extends Closeable { boolean getWantsAllOnMoveCalls(); /** + * Sets a {@link Bundle} that will be returned by {@link #getExtras()}. + * + * @param extras {@link Bundle} to set, or null to set an empty bundle. + */ + void setExtras(Bundle extras); + + /** * Returns a bundle of extra values. This is an optional way for cursors to provide out-of-band * metadata to their users. One use of this is for reporting on the progress of network requests * that are required to fetch data for the cursor. diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java index d8fcb17..63a2792 100644 --- a/core/java/android/database/CursorWrapper.java +++ b/core/java/android/database/CursorWrapper.java @@ -45,163 +45,210 @@ public class CursorWrapper implements Cursor { return mCursor; } + @Override public void close() { mCursor.close(); } + @Override public boolean isClosed() { return mCursor.isClosed(); } + @Override public int getCount() { return mCursor.getCount(); } + @Override + @Deprecated public void deactivate() { mCursor.deactivate(); } + @Override public boolean moveToFirst() { return mCursor.moveToFirst(); } + @Override public int getColumnCount() { return mCursor.getColumnCount(); } + @Override public int getColumnIndex(String columnName) { return mCursor.getColumnIndex(columnName); } + @Override public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException { return mCursor.getColumnIndexOrThrow(columnName); } + @Override public String getColumnName(int columnIndex) { return mCursor.getColumnName(columnIndex); } + @Override public String[] getColumnNames() { return mCursor.getColumnNames(); } + @Override public double getDouble(int columnIndex) { return mCursor.getDouble(columnIndex); } + @Override + public void setExtras(Bundle extras) { + mCursor.setExtras(extras); + } + + @Override public Bundle getExtras() { return mCursor.getExtras(); } + @Override public float getFloat(int columnIndex) { return mCursor.getFloat(columnIndex); } + @Override public int getInt(int columnIndex) { return mCursor.getInt(columnIndex); } + @Override public long getLong(int columnIndex) { return mCursor.getLong(columnIndex); } + @Override public short getShort(int columnIndex) { return mCursor.getShort(columnIndex); } + @Override public String getString(int columnIndex) { return mCursor.getString(columnIndex); } + @Override public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { mCursor.copyStringToBuffer(columnIndex, buffer); } + @Override public byte[] getBlob(int columnIndex) { return mCursor.getBlob(columnIndex); } + @Override public boolean getWantsAllOnMoveCalls() { return mCursor.getWantsAllOnMoveCalls(); } + @Override public boolean isAfterLast() { return mCursor.isAfterLast(); } + @Override public boolean isBeforeFirst() { return mCursor.isBeforeFirst(); } + @Override public boolean isFirst() { return mCursor.isFirst(); } + @Override public boolean isLast() { return mCursor.isLast(); } + @Override public int getType(int columnIndex) { return mCursor.getType(columnIndex); } + @Override public boolean isNull(int columnIndex) { return mCursor.isNull(columnIndex); } + @Override public boolean moveToLast() { return mCursor.moveToLast(); } + @Override public boolean move(int offset) { return mCursor.move(offset); } + @Override public boolean moveToPosition(int position) { return mCursor.moveToPosition(position); } + @Override public boolean moveToNext() { return mCursor.moveToNext(); } + @Override public int getPosition() { return mCursor.getPosition(); } + @Override public boolean moveToPrevious() { return mCursor.moveToPrevious(); } + @Override public void registerContentObserver(ContentObserver observer) { - mCursor.registerContentObserver(observer); + mCursor.registerContentObserver(observer); } + @Override public void registerDataSetObserver(DataSetObserver observer) { - mCursor.registerDataSetObserver(observer); + mCursor.registerDataSetObserver(observer); } + @Override + @Deprecated public boolean requery() { return mCursor.requery(); } + @Override public Bundle respond(Bundle extras) { return mCursor.respond(extras); } + @Override public void setNotificationUri(ContentResolver cr, Uri uri) { - mCursor.setNotificationUri(cr, uri); + mCursor.setNotificationUri(cr, uri); } + @Override public Uri getNotificationUri() { return mCursor.getNotificationUri(); } + @Override public void unregisterContentObserver(ContentObserver observer) { - mCursor.unregisterContentObserver(observer); + mCursor.unregisterContentObserver(observer); } + @Override public void unregisterDataSetObserver(DataSetObserver observer) { mCursor.unregisterDataSetObserver(observer); } diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java index 5a1a8e2..2dc5ca4 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -105,7 +105,6 @@ public class SQLiteCursor extends AbstractWindowedCursor { mQuery = query; mColumns = query.getColumnNames(); - mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns); } /** diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index bd5a392..cccc4be 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -16,10 +16,12 @@ package android.os; +import android.content.Context; import android.os.BatteryProperty; import android.os.IBatteryPropertiesRegistrar; import android.os.RemoteException; import android.os.ServiceManager; +import com.android.internal.app.IBatteryStats; /** * The BatteryManager class contains strings and constants used for values @@ -128,6 +130,26 @@ public class BatteryManager { public static final int BATTERY_PLUGGED_ANY = BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS; + /** + * Sent when the device's battery has started charging (or has reached full charge + * and the device is on power). This is a good time to do work that you would like to + * avoid doing while on battery (that is to avoid draining the user's battery due to + * things they don't care enough about). + * + * This is paired with {@link #ACTION_DISCHARGING}. The current state can always + * be retrieved with {@link #isCharging()}. + */ + public static final String ACTION_CHARGING = "android.os.action.CHARGING"; + + /** + * Sent when the device's battery may be discharging, so apps should avoid doing + * extraneous work that would cause it to discharge faster. + * + * This is paired with {@link #ACTION_CHARGING}. The current state can always + * be retrieved with {@link #isCharging()}. + */ + public static final String ACTION_DISCHARGING = "android.os.action.DISCHARGING"; + /* * Battery property identifiers. These must match the values in * frameworks/native/include/batteryservice/BatteryService.h @@ -162,17 +184,34 @@ public class BatteryManager { */ public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; + private final IBatteryStats mBatteryStats; private final IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar; /** * @removed Was previously made visible by accident. */ public BatteryManager() { + mBatteryStats = IBatteryStats.Stub.asInterface( + ServiceManager.getService(BatteryStats.SERVICE_NAME)); mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface( ServiceManager.getService("batteryproperties")); } /** + * Return true if the battery is currently considered to be charging. This means that + * the device is plugged in and is supplying sufficient power that the battery level is + * going up (or the battery is fully charged). Changes in this state are matched by + * broadcasts of {@link #ACTION_CHARGING} and {@link #ACTION_DISCHARGING}. + */ + public boolean isCharging() { + try { + return mBatteryStats.isCharging(); + } catch (RemoteException e) { + return true; + } + } + + /** * Query a battery property from the batteryproperties service. * * Returns the requested value, or Long.MIN_VALUE if property not diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 3051926..1566985 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1065,6 +1065,7 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE_SCREEN_ON_FLAG = 1<<20; public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18; + public static final int STATE_CHARGING_FLAG = 1<<17; public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16; public static final int MOST_INTERESTING_STATES = @@ -1751,6 +1752,7 @@ public abstract class BatteryStats implements Parcelable { new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"), new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"), new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"), + new BitDescription(HistoryItem.STATE_CHARGING_FLAG, "charging", "ch"), new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth", "b"), new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK, HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn", "Pcn", diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 5ee8fb3..3087e1d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -880,6 +880,15 @@ public final class Settings { "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE"; /** + * Activity Action: Show Zen Mode schedule rule configuration settings. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_ZEN_MODE_SCHEDULE_RULE_SETTINGS + = "android.settings.ZEN_MODE_SCHEDULE_RULE_SETTINGS"; + + /** * Activity Action: Show the regulatory information screen for the device. * <p> * In some cases, a matching Activity may not exist, so ensure you safeguard @@ -7218,6 +7227,18 @@ public final class Settings { return "ZEN_MODE_OFF"; } + /** @hide */ public static boolean isValidZenMode(int value) { + switch (value) { + case Global.ZEN_MODE_OFF: + case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: + case Global.ZEN_MODE_ALARMS: + case Global.ZEN_MODE_NO_INTERRUPTIONS: + return true; + default: + return false; + } + } + /** * Opaque value, changes when persisted zen mode configuration changes. * diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 5e2accd..dc5770b 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -59,9 +59,6 @@ public final class KeymasterDefs { public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705; public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_LONG | 200; - public static final int KM_TAG_DSA_GENERATOR = KM_BIGNUM | 201; - public static final int KM_TAG_DSA_P = KM_BIGNUM | 202; - public static final int KM_TAG_DSA_Q = KM_BIGNUM | 203; public static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400; public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401; public static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402; @@ -91,22 +88,8 @@ public final class KeymasterDefs { // Algorithm values. public static final int KM_ALGORITHM_RSA = 1; - public static final int KM_ALGORITHM_DSA = 2; - public static final int KM_ALGORITHM_ECDSA = 3; - public static final int KM_ALGORITHM_ECIES = 4; + public static final int KM_ALGORITHM_EC = 3; public static final int KM_ALGORITHM_AES = 32; - public static final int KM_ALGORITHM_3DES = 33; - public static final int KM_ALGORITHM_SKIPJACK = 34; - public static final int KM_ALGORITHM_MARS = 48; - public static final int KM_ALGORITHM_RC6 = 49; - public static final int KM_ALGORITHM_SERPENT = 50; - public static final int KM_ALGORITHM_TWOFISH = 51; - public static final int KM_ALGORITHM_IDEA = 52; - public static final int KM_ALGORITHM_RC5 = 53; - public static final int KM_ALGORITHM_CAST5 = 54; - public static final int KM_ALGORITHM_BLOWFISH = 55; - public static final int KM_ALGORITHM_RC4 = 64; - public static final int KM_ALGORITHM_CHACHA20 = 65; public static final int KM_ALGORITHM_HMAC = 128; // Block modes. @@ -219,7 +202,6 @@ public final class KeymasterDefs { public static final int KM_ERROR_INVALID_TAG = -40; public static final int KM_ERROR_MEMORY_ALLOCATION_FAILED = -41; public static final int KM_ERROR_INVALID_RESCOPING = -42; - public static final int KM_ERROR_INVALID_DSA_PARAMS = -43; public static final int KM_ERROR_IMPORT_PARAMETER_MISMATCH = -44; public static final int KM_ERROR_SECURE_HW_ACCESS_DENIED = -45; public static final int KM_ERROR_OPERATION_CANCELLED = -46; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 2702457..5aaf2e7 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -22,22 +22,25 @@ import android.content.res.Resources; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.provider.Settings.Global; import android.text.TextUtils; import android.text.format.DateFormat; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; +import com.android.internal.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.Locale; import java.util.Objects; - -import com.android.internal.R; +import java.util.UUID; /** * Persisted configuration for zen mode. @@ -47,10 +50,6 @@ import com.android.internal.R; public class ZenModeConfig implements Parcelable { private static String TAG = "ZenModeConfig"; - public static final String SLEEP_MODE_NIGHTS = "nights"; - public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights"; - public static final String SLEEP_MODE_DAYS_PREFIX = "days:"; - public static final int SOURCE_ANYONE = 0; public static final int SOURCE_CONTACT = 1; public static final int SOURCE_STAR = 2; @@ -60,6 +59,7 @@ public class ZenModeConfig implements Parcelable { Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY }; public static final int[] WEEKNIGHT_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY, Calendar.THURSDAY }; + public static final int[] WEEKEND_DAYS = { Calendar.FRIDAY, Calendar.SATURDAY }; public static final int[] MINUTE_BUCKETS = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 }; private static final int SECONDS_MS = 1000; @@ -69,7 +69,7 @@ public class ZenModeConfig implements Parcelable { private static final boolean DEFAULT_ALLOW_REMINDERS = true; private static final boolean DEFAULT_ALLOW_EVENTS = true; - private static final int XML_VERSION = 1; + private static final int XML_VERSION = 2; private static final String ZEN_TAG = "zen"; private static final String ZEN_ATT_VERSION = "version"; private static final String ALLOW_TAG = "allow"; @@ -78,14 +78,6 @@ public class ZenModeConfig implements Parcelable { private static final String ALLOW_ATT_FROM = "from"; private static final String ALLOW_ATT_REMINDERS = "reminders"; private static final String ALLOW_ATT_EVENTS = "events"; - private static final String SLEEP_TAG = "sleep"; - private static final String SLEEP_ATT_MODE = "mode"; - private static final String SLEEP_ATT_NONE = "none"; - - private static final String SLEEP_ATT_START_HR = "startHour"; - private static final String SLEEP_ATT_START_MIN = "startMin"; - private static final String SLEEP_ATT_END_HR = "endHour"; - private static final String SLEEP_ATT_END_MIN = "endMin"; private static final String CONDITION_TAG = "condition"; private static final String CONDITION_ATT_COMPONENT = "component"; @@ -97,8 +89,16 @@ public class ZenModeConfig implements Parcelable { private static final String CONDITION_ATT_STATE = "state"; private static final String CONDITION_ATT_FLAGS = "flags"; - private static final String EXIT_CONDITION_TAG = "exitCondition"; - private static final String EXIT_CONDITION_ATT_COMPONENT = "component"; + private static final String MANUAL_TAG = "manual"; + private static final String AUTOMATIC_TAG = "automatic"; + + private static final String RULE_ATT_ID = "id"; + private static final String RULE_ATT_ENABLED = "enabled"; + private static final String RULE_ATT_SNOOZING = "snoozing"; + private static final String RULE_ATT_NAME = "name"; + private static final String RULE_ATT_COMPONENT = "component"; + private static final String RULE_ATT_ZEN = "zen"; + private static final String RULE_ATT_CONDITION_ID = "conditionId"; public boolean allowCalls; public boolean allowMessages; @@ -106,16 +106,8 @@ public class ZenModeConfig implements Parcelable { public boolean allowEvents = DEFAULT_ALLOW_EVENTS; public int allowFrom = SOURCE_ANYONE; - public String sleepMode; - public int sleepStartHour; // 0-23 - public int sleepStartMinute; // 0-59 - public int sleepEndHour; - public int sleepEndMinute; - public boolean sleepNone; // false = priority, true = none - public ComponentName[] conditionComponents; - public Uri[] conditionIds; - public Condition exitCondition; - public ComponentName exitConditionComponent; + public ZenRule manualRule; + public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>(); public ZenModeConfig() { } @@ -124,27 +116,18 @@ public class ZenModeConfig implements Parcelable { allowMessages = source.readInt() == 1; allowReminders = source.readInt() == 1; allowEvents = source.readInt() == 1; - if (source.readInt() == 1) { - sleepMode = source.readString(); - } - sleepStartHour = source.readInt(); - sleepStartMinute = source.readInt(); - sleepEndHour = source.readInt(); - sleepEndMinute = source.readInt(); - sleepNone = source.readInt() == 1; - int len = source.readInt(); - if (len > 0) { - conditionComponents = new ComponentName[len]; - source.readTypedArray(conditionComponents, ComponentName.CREATOR); - } - len = source.readInt(); + allowFrom = source.readInt(); + manualRule = source.readParcelable(null); + final int len = source.readInt(); if (len > 0) { - conditionIds = new Uri[len]; - source.readTypedArray(conditionIds, Uri.CREATOR); + final String[] ids = new String[len]; + final ZenRule[] rules = new ZenRule[len]; + source.readStringArray(ids); + source.readTypedArray(rules, ZenRule.CREATOR); + for (int i = 0; i < len; i++) { + automaticRules.put(ids[i], rules[i]); + } } - allowFrom = source.readInt(); - exitCondition = source.readParcelable(null); - exitConditionComponent = source.readParcelable(null); } @Override @@ -153,32 +136,22 @@ public class ZenModeConfig implements Parcelable { dest.writeInt(allowMessages ? 1 : 0); dest.writeInt(allowReminders ? 1 : 0); dest.writeInt(allowEvents ? 1 : 0); - if (sleepMode != null) { - dest.writeInt(1); - dest.writeString(sleepMode); - } else { - dest.writeInt(0); - } - dest.writeInt(sleepStartHour); - dest.writeInt(sleepStartMinute); - dest.writeInt(sleepEndHour); - dest.writeInt(sleepEndMinute); - dest.writeInt(sleepNone ? 1 : 0); - if (conditionComponents != null && conditionComponents.length > 0) { - dest.writeInt(conditionComponents.length); - dest.writeTypedArray(conditionComponents, 0); - } else { - dest.writeInt(0); - } - if (conditionIds != null && conditionIds.length > 0) { - dest.writeInt(conditionIds.length); - dest.writeTypedArray(conditionIds, 0); + dest.writeInt(allowFrom); + dest.writeParcelable(manualRule, 0); + if (!automaticRules.isEmpty()) { + final int len = automaticRules.size(); + final String[] ids = new String[len]; + final ZenRule[] rules = new ZenRule[len]; + for (int i = 0; i < len; i++) { + ids[i] = automaticRules.keyAt(i); + rules[i] = automaticRules.valueAt(i); + } + dest.writeInt(len); + dest.writeStringArray(ids); + dest.writeTypedArray(rules, 0); } else { dest.writeInt(0); } - dest.writeInt(allowFrom); - dest.writeParcelable(exitCondition, 0); - dest.writeParcelable(exitConditionComponent, 0); } @Override @@ -189,19 +162,38 @@ public class ZenModeConfig implements Parcelable { .append(",allowFrom=").append(sourceToString(allowFrom)) .append(",allowReminders=").append(allowReminders) .append(",allowEvents=").append(allowEvents) - .append(",sleepMode=").append(sleepMode) - .append(",sleepStart=").append(sleepStartHour).append('.').append(sleepStartMinute) - .append(",sleepEnd=").append(sleepEndHour).append('.').append(sleepEndMinute) - .append(",sleepNone=").append(sleepNone) - .append(",conditionComponents=") - .append(conditionComponents == null ? null : TextUtils.join(",", conditionComponents)) - .append(",conditionIds=") - .append(conditionIds == null ? null : TextUtils.join(",", conditionIds)) - .append(",exitCondition=").append(exitCondition) - .append(",exitConditionComponent=").append(exitConditionComponent) + .append(",automaticRules=").append(automaticRules) + .append(",manualRule=").append(manualRule) .append(']').toString(); } + public boolean isValid() { + if (!isValidManualRule(manualRule)) return false; + final int N = automaticRules.size(); + for (int i = 0; i < N; i++) { + if (!isValidAutomaticRule(automaticRules.valueAt(i))) return false; + } + return true; + } + + private static boolean isValidManualRule(ZenRule rule) { + return rule == null || Global.isValidZenMode(rule.zenMode) && sameCondition(rule); + } + + private static boolean isValidAutomaticRule(ZenRule rule) { + return rule != null && !TextUtils.isEmpty(rule.name) && Global.isValidZenMode(rule.zenMode) + && rule.conditionId != null && sameCondition(rule); + } + + private static boolean sameCondition(ZenRule rule) { + if (rule == null) return false; + if (rule.conditionId == null) { + return rule.condition == null; + } else { + return rule.condition == null || rule.conditionId.equals(rule.condition.id); + } + } + public static String sourceToString(int source) { switch (source) { case SOURCE_ANYONE: @@ -225,45 +217,29 @@ public class ZenModeConfig implements Parcelable { && other.allowFrom == allowFrom && other.allowReminders == allowReminders && other.allowEvents == allowEvents - && Objects.equals(other.sleepMode, sleepMode) - && other.sleepNone == sleepNone - && other.sleepStartHour == sleepStartHour - && other.sleepStartMinute == sleepStartMinute - && other.sleepEndHour == sleepEndHour - && other.sleepEndMinute == sleepEndMinute - && Objects.deepEquals(other.conditionComponents, conditionComponents) - && Objects.deepEquals(other.conditionIds, conditionIds) - && Objects.equals(other.exitCondition, exitCondition) - && Objects.equals(other.exitConditionComponent, exitConditionComponent); + && Objects.equals(other.automaticRules, automaticRules) + && Objects.equals(other.manualRule, manualRule); } @Override public int hashCode() { return Objects.hash(allowCalls, allowMessages, allowFrom, allowReminders, allowEvents, - sleepMode, sleepNone, sleepStartHour, sleepStartMinute, sleepEndHour, - sleepEndMinute, Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds), - exitCondition, exitConditionComponent); + automaticRules, manualRule); } - public boolean isValid() { - return isValidHour(sleepStartHour) && isValidMinute(sleepStartMinute) - && isValidHour(sleepEndHour) && isValidMinute(sleepEndMinute) - && isValidSleepMode(sleepMode); - } - - public static boolean isValidSleepMode(String sleepMode) { - return sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS) - || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS) || tryParseDays(sleepMode) != null; + private static String toDayList(int[] days) { + if (days == null || days.length == 0) return ""; + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < days.length; i++) { + if (i > 0) sb.append('.'); + sb.append(days[i]); + } + return sb.toString(); } - public static int[] tryParseDays(String sleepMode) { - if (sleepMode == null) return null; - sleepMode = sleepMode.trim(); - if (SLEEP_MODE_NIGHTS.equals(sleepMode)) return ALL_DAYS; - if (SLEEP_MODE_WEEKNIGHTS.equals(sleepMode)) return WEEKNIGHT_DAYS; - if (!sleepMode.startsWith(SLEEP_MODE_DAYS_PREFIX)) return null; - if (sleepMode.equals(SLEEP_MODE_DAYS_PREFIX)) return null; - final String[] tokens = sleepMode.substring(SLEEP_MODE_DAYS_PREFIX.length()).split(","); + private static int[] tryParseDayList(String dayList, String sep) { + if (dayList == null) return null; + final String[] tokens = dayList.split(sep); if (tokens.length == 0) return null; final int[] rt = new int[tokens.length]; for (int i = 0; i < tokens.length; i++) { @@ -283,7 +259,7 @@ public class ZenModeConfig implements Parcelable { } } - public static ZenModeConfig readXml(XmlPullParser parser) + public static ZenModeConfig readXml(XmlPullParser parser, Migration migration) throws XmlPullParserException, IOException { int type = parser.getEventType(); if (type != XmlPullParser.START_TAG) return null; @@ -291,16 +267,13 @@ public class ZenModeConfig implements Parcelable { if (!ZEN_TAG.equals(tag)) return null; final ZenModeConfig rt = new ZenModeConfig(); final int version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION); - final ArrayList<ComponentName> conditionComponents = new ArrayList<ComponentName>(); - final ArrayList<Uri> conditionIds = new ArrayList<Uri>(); + if (version == 1) { + final XmlV1 v1 = XmlV1.readXml(parser); + return migration.migrate(v1); + } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { tag = parser.getName(); if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) { - if (!conditionComponents.isEmpty()) { - rt.conditionComponents = conditionComponents - .toArray(new ComponentName[conditionComponents.size()]); - rt.conditionIds = conditionIds.toArray(new Uri[conditionIds.size()]); - } return rt; } if (type == XmlPullParser.START_TAG) { @@ -314,31 +287,13 @@ public class ZenModeConfig implements Parcelable { if (rt.allowFrom < SOURCE_ANYONE || rt.allowFrom > MAX_SOURCE) { throw new IndexOutOfBoundsException("bad source in config:" + rt.allowFrom); } - } else if (SLEEP_TAG.equals(tag)) { - final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE); - rt.sleepMode = isValidSleepMode(mode)? mode : null; - rt.sleepNone = safeBoolean(parser, SLEEP_ATT_NONE, false); - final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0); - final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0); - final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0); - final int endMinute = safeInt(parser, SLEEP_ATT_END_MIN, 0); - rt.sleepStartHour = isValidHour(startHour) ? startHour : 0; - rt.sleepStartMinute = isValidMinute(startMinute) ? startMinute : 0; - rt.sleepEndHour = isValidHour(endHour) ? endHour : 0; - rt.sleepEndMinute = isValidMinute(endMinute) ? endMinute : 0; - } else if (CONDITION_TAG.equals(tag)) { - final ComponentName component = - safeComponentName(parser, CONDITION_ATT_COMPONENT); - final Uri conditionId = safeUri(parser, CONDITION_ATT_ID); - if (component != null && conditionId != null) { - conditionComponents.add(component); - conditionIds.add(conditionId); - } - } else if (EXIT_CONDITION_TAG.equals(tag)) { - rt.exitCondition = readConditionXml(parser); - if (rt.exitCondition != null) { - rt.exitConditionComponent = - safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT); + } else if (MANUAL_TAG.equals(tag)) { + rt.manualRule = readRuleXml(parser); + } else if (AUTOMATIC_TAG.equals(tag)) { + final String id = parser.getAttributeValue(null, RULE_ATT_ID); + final ZenRule automaticRule = readRuleXml(parser); + if (id != null && automaticRule != null) { + rt.automaticRules.put(id, automaticRule); } } } @@ -358,39 +313,61 @@ public class ZenModeConfig implements Parcelable { out.attribute(null, ALLOW_ATT_FROM, Integer.toString(allowFrom)); out.endTag(null, ALLOW_TAG); - out.startTag(null, SLEEP_TAG); - if (sleepMode != null) { - out.attribute(null, SLEEP_ATT_MODE, sleepMode); - } - out.attribute(null, SLEEP_ATT_NONE, Boolean.toString(sleepNone)); - out.attribute(null, SLEEP_ATT_START_HR, Integer.toString(sleepStartHour)); - out.attribute(null, SLEEP_ATT_START_MIN, Integer.toString(sleepStartMinute)); - out.attribute(null, SLEEP_ATT_END_HR, Integer.toString(sleepEndHour)); - out.attribute(null, SLEEP_ATT_END_MIN, Integer.toString(sleepEndMinute)); - out.endTag(null, SLEEP_TAG); - - if (conditionComponents != null && conditionIds != null - && conditionComponents.length == conditionIds.length) { - for (int i = 0; i < conditionComponents.length; i++) { - out.startTag(null, CONDITION_TAG); - out.attribute(null, CONDITION_ATT_COMPONENT, - conditionComponents[i].flattenToString()); - out.attribute(null, CONDITION_ATT_ID, conditionIds[i].toString()); - out.endTag(null, CONDITION_TAG); - } + if (manualRule != null) { + out.startTag(null, MANUAL_TAG); + writeRuleXml(manualRule, out); + out.endTag(null, MANUAL_TAG); } - if (exitCondition != null && exitConditionComponent != null) { - out.startTag(null, EXIT_CONDITION_TAG); - out.attribute(null, EXIT_CONDITION_ATT_COMPONENT, - exitConditionComponent.flattenToString()); - writeConditionXml(exitCondition, out); - out.endTag(null, EXIT_CONDITION_TAG); + final int N = automaticRules.size(); + for (int i = 0; i < N; i++) { + final String id = automaticRules.keyAt(i); + final ZenRule automaticRule = automaticRules.valueAt(i); + out.startTag(null, AUTOMATIC_TAG); + out.attribute(null, RULE_ATT_ID, id); + writeRuleXml(automaticRule, out); + out.endTag(null, AUTOMATIC_TAG); } out.endTag(null, ZEN_TAG); } + public static ZenRule readRuleXml(XmlPullParser parser) { + final ZenRule rt = new ZenRule(); + rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true); + rt.snoozing = safeBoolean(parser, RULE_ATT_SNOOZING, false); + rt.name = parser.getAttributeValue(null, RULE_ATT_NAME); + final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN); + rt.zenMode = tryParseZenMode(zen, -1); + if (rt.zenMode == -1) { + Slog.w(TAG, "Bad zen mode in rule xml:" + zen); + return null; + } + rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID); + rt.component = safeComponentName(parser, RULE_ATT_COMPONENT); + rt.condition = readConditionXml(parser); + return rt.condition != null ? rt : null; + } + + public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException { + out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled)); + out.attribute(null, RULE_ATT_SNOOZING, Boolean.toString(rule.snoozing)); + if (rule.name != null) { + out.attribute(null, RULE_ATT_NAME, rule.name); + } + out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode)); + if (rule.component != null) { + out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString()); + } + if (rule.conditionId != null) { + out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString()); + } + if (rule.condition != null) { + writeConditionXml(rule.condition, out); + } + } + public static Condition readConditionXml(XmlPullParser parser) { final Uri id = safeUri(parser, CONDITION_ATT_ID); + if (id == null) return null; final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY); final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1); final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2); @@ -446,6 +423,14 @@ public class ZenModeConfig implements Parcelable { return Uri.parse(val); } + public ArraySet<String> getAutomaticRuleNames() { + final ArraySet<String> rt = new ArraySet<String>(); + for (int i = 0; i < automaticRules.size(); i++) { + rt.add(automaticRules.valueAt(i).name); + } + return rt; + } + @Override public int describeContents() { return 0; @@ -475,17 +460,6 @@ public class ZenModeConfig implements Parcelable { } }; - public DowntimeInfo toDowntimeInfo() { - final DowntimeInfo downtime = new DowntimeInfo(); - downtime.startHour = sleepStartHour; - downtime.startMinute = sleepStartMinute; - downtime.endHour = sleepEndHour; - downtime.endMinute = sleepEndMinute; - downtime.mode = sleepMode; - downtime.none = sleepNone; - return downtime; - } - public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) { final long now = System.currentTimeMillis(); final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; @@ -548,38 +522,77 @@ public class ZenModeConfig implements Parcelable { return tryParseCountdownConditionId(conditionId) != 0; } - // 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"; + // built-in schedule conditions + public static final String SCHEDULE_PATH = "schedule"; + + public static class ScheduleInfo { + public int[] days; + public int startHour; + public int startMinute; + public int endHour; + public int endMinute; + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof ScheduleInfo)) return false; + final ScheduleInfo other = (ScheduleInfo) o; + return toDayList(days).equals(toDayList(other.days)) + && startHour == other.startHour + && startMinute == other.startMinute + && endHour == other.endHour + && endMinute == other.endMinute; + } + + public ScheduleInfo copy() { + final ScheduleInfo rt = new ScheduleInfo(); + if (days != null) { + rt.days = new int[days.length]; + System.arraycopy(days, 0, rt.days, 0, days.length); + } + rt.startHour = startHour; + rt.startMinute = startMinute; + rt.endHour = endHour; + rt.endMinute = endMinute; + return rt; + } + } - public static Uri toDowntimeConditionId(DowntimeInfo downtime) { + public static Uri toScheduleConditionId(ScheduleInfo schedule) { return new Uri.Builder().scheme(Condition.SCHEME) .authority(SYSTEM_AUTHORITY) - .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)) + .appendPath(SCHEDULE_PATH) + .appendQueryParameter("days", toDayList(schedule.days)) + .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute) + .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute) .build(); } - public static DowntimeInfo tryParseDowntimeConditionId(Uri conditionId) { - if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY) - || conditionId.getPathSegments().size() != 1 - || !DOWNTIME_PATH.equals(conditionId.getPathSegments().get(0))) { - return null; - } + public static boolean isValidScheduleConditionId(Uri conditionId) { + return tryParseScheduleConditionId(conditionId) != null; + } + + public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) { + final boolean isSchedule = conditionId != null + && conditionId.getScheme().equals(Condition.SCHEME) + && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY) + && conditionId.getPathSegments().size() == 1 + && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH); + if (!isSchedule) return null; final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start")); final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end")); if (start == null || end == null) return null; - final DowntimeInfo downtime = new DowntimeInfo(); - downtime.startHour = start[0]; - 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; + final ScheduleInfo rt = new ScheduleInfo(); + rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\."); + rt.startHour = start[0]; + rt.startMinute = start[1]; + rt.endHour = end[0]; + rt.endMinute = end[1]; + return rt; } private static int[] tryParseHourAndMinute(String value) { @@ -591,36 +604,268 @@ public class ZenModeConfig implements Parcelable { return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null; } - public static boolean isValidDowntimeConditionId(Uri conditionId) { - return tryParseDowntimeConditionId(conditionId) != null; + private static int tryParseZenMode(String value, int defValue) { + final int rt = tryParseInt(value, defValue); + return Global.isValidZenMode(rt) ? rt : defValue; } - public static class DowntimeInfo { - public int startHour; // 0-23 - public int startMinute; // 0-59 - public int endHour; - public int endMinute; - public String mode; - public boolean none; + public String newRuleId() { + return UUID.randomUUID().toString().replace("-", ""); + } + + public static String getConditionLine1(Context context, ZenModeConfig config, + int userHandle) { + return getConditionLine(context, config, userHandle, true /*useLine1*/); + } + + public static String getConditionSummary(Context context, ZenModeConfig config, + int userHandle) { + return getConditionLine(context, config, userHandle, false /*useLine1*/); + } + + private static String getConditionLine(Context context, ZenModeConfig config, + int userHandle, boolean useLine1) { + if (config == null) return ""; + if (config.manualRule != null) { + final Uri id = config.manualRule.conditionId; + if (id == null) { + return context.getString(com.android.internal.R.string.zen_mode_forever); + } + final long time = tryParseCountdownConditionId(id); + Condition c = config.manualRule.condition; + if (time > 0) { + final long now = System.currentTimeMillis(); + final long span = time - now; + c = toTimeCondition(context, + time, Math.round(span / (float) MINUTES_MS), now, userHandle); + } + final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; + return TextUtils.isEmpty(rt) ? "" : rt; + } + String summary = ""; + for (ZenRule automaticRule : config.automaticRules.values()) { + if (automaticRule.enabled && !automaticRule.snoozing + && automaticRule.isTrueOrUnknown()) { + if (summary.isEmpty()) { + summary = automaticRule.name; + } else { + summary = context.getResources() + .getString(R.string.zen_mode_rule_name_combination, summary, + automaticRule.name); + } + } + } + return summary; + } + + public static class ZenRule implements Parcelable { + public boolean enabled; + public boolean snoozing; // user manually disabled this instance + public String name; // required for automatic (unique) + public int zenMode; + public Uri conditionId; // required for automatic + public Condition condition; // optional + public ComponentName component; // optional + + public ZenRule() { } + + public ZenRule(Parcel source) { + enabled = source.readInt() == 1; + snoozing = source.readInt() == 1; + if (source.readInt() == 1) { + name = source.readString(); + } + zenMode = source.readInt(); + conditionId = source.readParcelable(null); + condition = source.readParcelable(null); + component = source.readParcelable(null); + } @Override - public int hashCode() { + public int describeContents() { return 0; } @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(enabled ? 1 : 0); + dest.writeInt(snoozing ? 1 : 0); + if (name != null) { + dest.writeInt(1); + dest.writeString(name); + } else { + dest.writeInt(0); + } + dest.writeInt(zenMode); + dest.writeParcelable(conditionId, 0); + dest.writeParcelable(condition, 0); + dest.writeParcelable(component, 0); + } + + @Override + public String toString() { + return new StringBuilder(ZenRule.class.getSimpleName()).append('[') + .append("enabled=").append(enabled) + .append(",snoozing=").append(snoozing) + .append(",name=").append(name) + .append(",zenMode=").append(Global.zenModeToString(zenMode)) + .append(",conditionId=").append(conditionId) + .append(",condition=").append(condition) + .append(",component=").append(component) + .append(']').toString(); + } + + @Override public boolean equals(Object o) { - if (!(o instanceof DowntimeInfo)) return false; - final DowntimeInfo other = (DowntimeInfo) o; - return startHour == other.startHour - && startMinute == other.startMinute - && endHour == other.endHour - && endMinute == other.endMinute - && Objects.equals(mode, other.mode) - && none == other.none; + if (!(o instanceof ZenRule)) return false; + if (o == this) return true; + final ZenRule other = (ZenRule) o; + return other.enabled == enabled + && other.snoozing == snoozing + && Objects.equals(other.name, name) + && other.zenMode == zenMode + && Objects.equals(other.conditionId, conditionId) + && Objects.equals(other.condition, condition) + && Objects.equals(other.component, component); + } + + @Override + public int hashCode() { + return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, + component); + } + + public boolean isTrueOrUnknown() { + return condition == null || condition.state == Condition.STATE_TRUE + || condition.state == Condition.STATE_UNKNOWN; + } + + public static final Parcelable.Creator<ZenRule> CREATOR + = new Parcelable.Creator<ZenRule>() { + @Override + public ZenRule createFromParcel(Parcel source) { + return new ZenRule(source); + } + @Override + public ZenRule[] newArray(int size) { + return new ZenRule[size]; + } + }; + } + + // Legacy config + public static final class XmlV1 { + public static final String SLEEP_MODE_NIGHTS = "nights"; + public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights"; + public static final String SLEEP_MODE_DAYS_PREFIX = "days:"; + + private static final String EXIT_CONDITION_TAG = "exitCondition"; + private static final String EXIT_CONDITION_ATT_COMPONENT = "component"; + private static final String SLEEP_TAG = "sleep"; + private static final String SLEEP_ATT_MODE = "mode"; + private static final String SLEEP_ATT_NONE = "none"; + + private static final String SLEEP_ATT_START_HR = "startHour"; + private static final String SLEEP_ATT_START_MIN = "startMin"; + private static final String SLEEP_ATT_END_HR = "endHour"; + private static final String SLEEP_ATT_END_MIN = "endMin"; + + public boolean allowCalls; + public boolean allowMessages; + public boolean allowReminders = DEFAULT_ALLOW_REMINDERS; + public boolean allowEvents = DEFAULT_ALLOW_EVENTS; + public int allowFrom = SOURCE_ANYONE; + + public String sleepMode; // nights, weeknights, days:1,2,3 Calendar.days + public int sleepStartHour; // 0-23 + public int sleepStartMinute; // 0-59 + public int sleepEndHour; + public int sleepEndMinute; + public boolean sleepNone; // false = priority, true = none + public ComponentName[] conditionComponents; + public Uri[] conditionIds; + public Condition exitCondition; // manual exit condition + public ComponentName exitConditionComponent; // manual exit condition component + + private static boolean isValidSleepMode(String sleepMode) { + return sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS) + || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS) || tryParseDays(sleepMode) != null; + } + + public static int[] tryParseDays(String sleepMode) { + if (sleepMode == null) return null; + sleepMode = sleepMode.trim(); + if (SLEEP_MODE_NIGHTS.equals(sleepMode)) return ALL_DAYS; + if (SLEEP_MODE_WEEKNIGHTS.equals(sleepMode)) return WEEKNIGHT_DAYS; + if (!sleepMode.startsWith(SLEEP_MODE_DAYS_PREFIX)) return null; + if (sleepMode.equals(SLEEP_MODE_DAYS_PREFIX)) return null; + return tryParseDayList(sleepMode.substring(SLEEP_MODE_DAYS_PREFIX.length()), ","); + } + + public static XmlV1 readXml(XmlPullParser parser) + throws XmlPullParserException, IOException { + int type; + String tag; + XmlV1 rt = new XmlV1(); + final ArrayList<ComponentName> conditionComponents = new ArrayList<ComponentName>(); + final ArrayList<Uri> conditionIds = new ArrayList<Uri>(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + tag = parser.getName(); + if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) { + if (!conditionComponents.isEmpty()) { + rt.conditionComponents = conditionComponents + .toArray(new ComponentName[conditionComponents.size()]); + rt.conditionIds = conditionIds.toArray(new Uri[conditionIds.size()]); + } + return rt; + } + if (type == XmlPullParser.START_TAG) { + if (ALLOW_TAG.equals(tag)) { + rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false); + rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false); + rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS, + DEFAULT_ALLOW_REMINDERS); + rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, + DEFAULT_ALLOW_EVENTS); + rt.allowFrom = safeInt(parser, ALLOW_ATT_FROM, SOURCE_ANYONE); + if (rt.allowFrom < SOURCE_ANYONE || rt.allowFrom > MAX_SOURCE) { + throw new IndexOutOfBoundsException("bad source in config:" + + rt.allowFrom); + } + } else if (SLEEP_TAG.equals(tag)) { + final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE); + rt.sleepMode = isValidSleepMode(mode)? mode : null; + rt.sleepNone = safeBoolean(parser, SLEEP_ATT_NONE, false); + final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0); + final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0); + final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0); + final int endMinute = safeInt(parser, SLEEP_ATT_END_MIN, 0); + rt.sleepStartHour = isValidHour(startHour) ? startHour : 0; + rt.sleepStartMinute = isValidMinute(startMinute) ? startMinute : 0; + rt.sleepEndHour = isValidHour(endHour) ? endHour : 0; + rt.sleepEndMinute = isValidMinute(endMinute) ? endMinute : 0; + } else if (CONDITION_TAG.equals(tag)) { + final ComponentName component = + safeComponentName(parser, CONDITION_ATT_COMPONENT); + final Uri conditionId = safeUri(parser, CONDITION_ATT_ID); + if (component != null && conditionId != null) { + conditionComponents.add(component); + conditionIds.add(conditionId); + } + } else if (EXIT_CONDITION_TAG.equals(tag)) { + rt.exitCondition = readConditionXml(parser); + if (rt.exitCondition != null) { + rt.exitConditionComponent = + safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT); + } + } + } + } + throw new IllegalStateException("Failed to reach END_DOCUMENT"); } } - // built-in next alarm conditions - public static final String NEXT_ALARM_PATH = "next_alarm"; + public interface Migration { + ZenModeConfig migrate(XmlV1 v1); + } } diff --git a/core/java/android/text/method/AllCapsTransformationMethod.java b/core/java/android/text/method/AllCapsTransformationMethod.java index f9920dd..0cea821 100644 --- a/core/java/android/text/method/AllCapsTransformationMethod.java +++ b/core/java/android/text/method/AllCapsTransformationMethod.java @@ -19,6 +19,7 @@ import android.content.Context; import android.graphics.Rect; import android.util.Log; import android.view.View; +import android.widget.TextView; import java.util.Locale; @@ -39,11 +40,23 @@ public class AllCapsTransformationMethod implements TransformationMethod2 { @Override public CharSequence getTransformation(CharSequence source, View view) { - if (mEnabled) { - return source != null ? source.toString().toUpperCase(mLocale) : null; + if (!mEnabled) { + Log.w(TAG, "Caller did not enable length changes; not transforming text"); + return source; } - Log.w(TAG, "Caller did not enable length changes; not transforming text"); - return source; + + if (source == null) { + return null; + } + + Locale locale = null; + if (view instanceof TextView) { + locale = ((TextView)view).getTextLocale(); + } + if (locale == null) { + locale = mLocale; + } + return source.toString().toUpperCase(locale); } @Override diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java index 63607fa..07c1ec3 100644 --- a/core/java/android/text/method/BaseKeyListener.java +++ b/core/java/android/text/method/BaseKeyListener.java @@ -22,6 +22,8 @@ import android.text.*; import android.text.method.TextKeyListener.Capitalize; import android.widget.TextView; +import java.text.BreakIterator; + /** * Abstract base class for key listeners. * @@ -63,9 +65,9 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener private boolean backspaceOrForwardDelete(View view, Editable content, int keyCode, KeyEvent event, boolean isForwardDelete) { - // Ensure the key event does not have modifiers except ALT or SHIFT. + // Ensure the key event does not have modifiers except ALT or SHIFT or CTRL. if (!KeyEvent.metaStateHasNoModifiers(event.getMetaState() - & ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK))) { + & ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK))) { return false; } @@ -74,18 +76,28 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener return true; } - // Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible. - if (getMetaState(content, META_ALT_ON, event) == 1) { - if (deleteLine(view, content)) { - return true; + // MetaKeyKeyListener doesn't track control key state. Need to check the KeyEvent instead. + boolean isCtrlActive = ((event.getMetaState() & KeyEvent.META_CTRL_ON) != 0); + boolean isShiftActive = (getMetaState(content, META_SHIFT_ON, event) == 1); + boolean isAltActive = (getMetaState(content, META_ALT_ON, event) == 1); + + if (isCtrlActive) { + if (isAltActive || isShiftActive) { + // Ctrl+Alt, Ctrl+Shift, Ctrl+Alt+Shift should not delete any characters. + return false; } + return deleteUntilWordBoundary(view, content, isForwardDelete); + } + + // Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible. + if (isAltActive && deleteLine(view, content)) { + return true; } // Delete a character. final int start = Selection.getSelectionEnd(content); final int end; - if (isForwardDelete || event.isShiftPressed() - || getMetaState(content, META_SHIFT_ON) == 1) { + if (isForwardDelete || event.isShiftPressed() || isShiftActive) { end = TextUtils.getOffsetAfter(content, start); } else { end = TextUtils.getOffsetBefore(content, start); @@ -97,6 +109,54 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener return false; } + private boolean deleteUntilWordBoundary(View view, Editable content, boolean isForwardDelete) { + int currentCursorOffset = Selection.getSelectionStart(content); + + // If there is a selection, do nothing. + if (currentCursorOffset != Selection.getSelectionEnd(content)) { + return false; + } + + // Early exit if there is no contents to delete. + if ((!isForwardDelete && currentCursorOffset == 0) || + (isForwardDelete && currentCursorOffset == content.length())) { + return false; + } + + WordIterator wordIterator = null; + if (view instanceof TextView) { + wordIterator = ((TextView)view).getWordIterator(); + } + + if (wordIterator == null) { + // Default locale is used for WordIterator since the appropriate locale is not clear + // here. + // TODO: Use appropriate locale for WordIterator. + wordIterator = new WordIterator(); + } + + int deleteFrom; + int deleteTo; + + if (isForwardDelete) { + deleteFrom = currentCursorOffset; + wordIterator.setCharSequence(content, deleteFrom, content.length()); + deleteTo = wordIterator.following(currentCursorOffset); + if (deleteTo == BreakIterator.DONE) { + deleteTo = content.length(); + } + } else { + deleteTo = currentCursorOffset; + wordIterator.setCharSequence(content, 0, deleteTo); + deleteFrom = wordIterator.preceding(currentCursorOffset); + if (deleteFrom == BreakIterator.DONE) { + deleteFrom = 0; + } + } + content.delete(deleteFrom, deleteTo); + return true; + } + private boolean deleteSelection(View view, Editable content) { int selectionStart = Selection.getSelectionStart(content); int selectionEnd = Selection.getSelectionEnd(content); diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index 0b70fdb..5209f90 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -430,7 +430,6 @@ public class TransitionManager { * Ends all pending and ongoing transitions on the specified scene root. * * @param sceneRoot The root of the View hierarchy to end transitions on. - * @hide */ public static void endTransitions(final ViewGroup sceneRoot) { sPendingTransitions.remove(sceneRoot); diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 2eac549..1ee4780 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -49,7 +49,6 @@ public final class InputDevice implements Parcelable { private final String mName; private final int mVendorId; private final int mProductId; - private final String mUniqueId; private final String mDescriptor; private final InputDeviceIdentifier mIdentifier; private final boolean mIsExternal; @@ -57,6 +56,7 @@ public final class InputDevice implements Parcelable { private final int mKeyboardType; private final KeyCharacterMap mKeyCharacterMap; private final boolean mHasVibrator; + private final boolean mHasMic; private final boolean mHasButtonUnderPad; private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>(); @@ -357,8 +357,8 @@ public final class InputDevice implements Parcelable { // Called by native code. private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId, - int productId, String uniqueId, String descriptor, boolean isExternal, int sources, - int keyboardType, KeyCharacterMap keyCharacterMap, boolean hasVibrator, + int productId, String descriptor, boolean isExternal, int sources, int keyboardType, + KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasMic, boolean hasButtonUnderPad) { mId = id; mGeneration = generation; @@ -366,13 +366,13 @@ public final class InputDevice implements Parcelable { mName = name; mVendorId = vendorId; mProductId = productId; - mUniqueId = uniqueId; mDescriptor = descriptor; mIsExternal = isExternal; mSources = sources; mKeyboardType = keyboardType; mKeyCharacterMap = keyCharacterMap; mHasVibrator = hasVibrator; + mHasMic = hasMic; mHasButtonUnderPad = hasButtonUnderPad; mIdentifier = new InputDeviceIdentifier(descriptor, vendorId, productId); } @@ -384,13 +384,13 @@ public final class InputDevice implements Parcelable { mName = in.readString(); mVendorId = in.readInt(); mProductId = in.readInt(); - mUniqueId = in.readString(); mDescriptor = in.readString(); mIsExternal = in.readInt() != 0; mSources = in.readInt(); mKeyboardType = in.readInt(); mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in); mHasVibrator = in.readInt() != 0; + mHasMic = in.readInt() != 0; mHasButtonUnderPad = in.readInt() != 0; mIdentifier = new InputDeviceIdentifier(mDescriptor, mVendorId, mProductId); @@ -509,23 +509,6 @@ public final class InputDevice implements Parcelable { } /** - * Gets the vendor's unique id for the given device, if available. - * <p> - * A vendor may assign a unique id to a device (e.g., MAC address for - * Bluetooth devices). A null value will be assigned where a unique id is - * not available. - * </p><p> - * This method is dependent on the vendor, whereas {@link #getDescriptor} - * attempts to create a unique id even when the vendor has not provided one. - * </p> - * - * @return The unique id of a given device - */ - public String getUniqueId() { - return mUniqueId; - } - - /** * Gets the input device descriptor, which is a stable identifier for an input device. * <p> * An input device descriptor uniquely identifies an input device. Its value @@ -737,6 +720,14 @@ public final class InputDevice implements Parcelable { } /** + * Reports whether the device has a built-in microphone. + * @return Whether the device has a built-in microphone. + */ + public boolean hasMic() { + return mHasMic; + } + + /** * Reports whether the device has a button under its touchpad * @return Whether the device has a button under its touchpad * @hide @@ -864,13 +855,13 @@ public final class InputDevice implements Parcelable { out.writeString(mName); out.writeInt(mVendorId); out.writeInt(mProductId); - out.writeString(mUniqueId); out.writeString(mDescriptor); out.writeInt(mIsExternal ? 1 : 0); out.writeInt(mSources); out.writeInt(mKeyboardType); mKeyCharacterMap.writeToParcel(out, flags); out.writeInt(mHasVibrator ? 1 : 0); + out.writeInt(mHasMic ? 1 : 0); out.writeInt(mHasButtonUnderPad ? 1 : 0); final int numRanges = mMotionRanges.size(); @@ -916,6 +907,8 @@ public final class InputDevice implements Parcelable { description.append(" Has Vibrator: ").append(mHasVibrator).append("\n"); + description.append(" Has mic: ").append(mHasMic).append("\n"); + description.append(" Sources: 0x").append(Integer.toHexString(mSources)).append(" ("); appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard"); appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad"); diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java index 36bce0b..f951dc2 100644 --- a/core/java/android/widget/ActionMenuPresenter.java +++ b/core/java/android/widget/ActionMenuPresenter.java @@ -397,7 +397,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter public void updateMenuView(boolean cleared) { final ViewGroup menuViewParent = (ViewGroup) ((View) mMenuView).getParent(); if (menuViewParent != null) { - setupItemAnimations(); +// setupItemAnimations(); ActionBarTransition.beginDelayedTransition(menuViewParent); } super.updateMenuView(cleared); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index c0908b3..5fb0c92 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -557,7 +557,7 @@ public class Editor { } } - private void hideInsertionPointCursorController() { + void hideInsertionPointCursorController() { if (mInsertionPointCursorController != null) { mInsertionPointCursorController.hide(); } @@ -1672,7 +1672,8 @@ public class Editor { return false; } ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); - mSelectionActionMode = mTextView.startActionMode(actionModeCallback); + mSelectionActionMode = mTextView.startActionMode( + actionModeCallback, ActionMode.TYPE_FLOATING); return mSelectionActionMode != null; } @@ -1706,7 +1707,8 @@ public class Editor { // immediately hide the newly created action bar and would be visually distracting. if (!willExtract) { ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); - mSelectionActionMode = mTextView.startActionMode(actionModeCallback); + mSelectionActionMode = mTextView.startActionMode( + actionModeCallback, ActionMode.TYPE_FLOATING); } final boolean selectionStarted = mSelectionActionMode != null || willExtract; diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 21213ac..4b5407a 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -46,6 +46,8 @@ import android.view.ViewGroupOverlay; import android.widget.AbsListView.OnScrollListener; import android.widget.ImageView.ScaleType; +import com.android.internal.R; + /** * Helper class for AbsListView to draw and control the Fast Scroll thumb */ @@ -82,6 +84,10 @@ class FastScroller { private static final int OVERLAY_AT_THUMB = 1; private static final int OVERLAY_ABOVE_THUMB = 2; + // Positions for thumb in relation to track. + private static final int THUMB_POSITION_MIDPOINT = 0; + private static final int THUMB_POSITION_INSIDE = 1; + // Indices for mPreviewResId. private static final int PREVIEW_LEFT = 0; private static final int PREVIEW_RIGHT = 1; @@ -100,7 +106,6 @@ class FastScroller { private final ImageView mThumbImage; private final ImageView mTrackImage; private final View mPreviewImage; - /** * Preview image resource IDs for left- and right-aligned layouts. See * {@link #PREVIEW_LEFT} and {@link #PREVIEW_RIGHT}. @@ -130,6 +135,11 @@ class FastScroller { private Drawable mThumbDrawable; private Drawable mTrackDrawable; private int mTextAppearance; + private int mThumbPosition; + + // Used to convert between y-coordinate and thumb position within track. + private float mThumbOffset; + private float mThumbRange; /** Total width of decorations. */ private int mWidth; @@ -278,7 +288,6 @@ class FastScroller { } private void updateAppearance() { - final Context context = mList.getContext(); int width = 0; // Add track to overlay if it has an image. @@ -298,12 +307,9 @@ class FastScroller { // Account for minimum thumb width. mWidth = Math.max(width, mThumbMinWidth); - mPreviewImage.setMinimumWidth(mPreviewMinWidth); - mPreviewImage.setMinimumHeight(mPreviewMinHeight); - if (mTextAppearance != 0) { - mPrimaryText.setTextAppearance(context, mTextAppearance); - mSecondaryText.setTextAppearance(context, mTextAppearance); + mPrimaryText.setTextAppearance(mTextAppearance); + mSecondaryText.setTextAppearance(mTextAppearance); } if (mTextColor != null) { @@ -316,13 +322,11 @@ class FastScroller { mSecondaryText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize); } - final int textMinSize = Math.max(0, mPreviewMinHeight); - mPrimaryText.setMinimumWidth(textMinSize); - mPrimaryText.setMinimumHeight(textMinSize); + final int padding = mPreviewPadding; mPrimaryText.setIncludeFontPadding(false); - mSecondaryText.setMinimumWidth(textMinSize); - mSecondaryText.setMinimumHeight(textMinSize); + mPrimaryText.setPadding(padding, padding, padding, padding); mSecondaryText.setIncludeFontPadding(false); + mSecondaryText.setPadding(padding, padding, padding, padding); refreshDrawablePressedState(); } @@ -330,50 +334,53 @@ class FastScroller { public void setStyle(@StyleRes int resId) { final Context context = mList.getContext(); final TypedArray ta = context.obtainStyledAttributes(null, - com.android.internal.R.styleable.FastScroll, android.R.attr.fastScrollStyle, resId); + R.styleable.FastScroll, R.attr.fastScrollStyle, resId); final int N = ta.getIndexCount(); for (int i = 0; i < N; i++) { final int index = ta.getIndex(i); switch (index) { - case com.android.internal.R.styleable.FastScroll_position: + case R.styleable.FastScroll_position: mOverlayPosition = ta.getInt(index, OVERLAY_FLOATING); break; - case com.android.internal.R.styleable.FastScroll_backgroundLeft: + case R.styleable.FastScroll_backgroundLeft: mPreviewResId[PREVIEW_LEFT] = ta.getResourceId(index, 0); break; - case com.android.internal.R.styleable.FastScroll_backgroundRight: + case R.styleable.FastScroll_backgroundRight: mPreviewResId[PREVIEW_RIGHT] = ta.getResourceId(index, 0); break; - case com.android.internal.R.styleable.FastScroll_thumbDrawable: + case R.styleable.FastScroll_thumbDrawable: mThumbDrawable = ta.getDrawable(index); break; - case com.android.internal.R.styleable.FastScroll_trackDrawable: + case R.styleable.FastScroll_trackDrawable: mTrackDrawable = ta.getDrawable(index); break; - case com.android.internal.R.styleable.FastScroll_textAppearance: + case R.styleable.FastScroll_textAppearance: mTextAppearance = ta.getResourceId(index, 0); break; - case com.android.internal.R.styleable.FastScroll_textColor: + case R.styleable.FastScroll_textColor: mTextColor = ta.getColorStateList(index); break; - case com.android.internal.R.styleable.FastScroll_textSize: + case R.styleable.FastScroll_textSize: mTextSize = ta.getDimensionPixelSize(index, 0); break; - case com.android.internal.R.styleable.FastScroll_minWidth: + case R.styleable.FastScroll_minWidth: mPreviewMinWidth = ta.getDimensionPixelSize(index, 0); break; - case com.android.internal.R.styleable.FastScroll_minHeight: + case R.styleable.FastScroll_minHeight: mPreviewMinHeight = ta.getDimensionPixelSize(index, 0); break; - case com.android.internal.R.styleable.FastScroll_thumbMinWidth: + case R.styleable.FastScroll_thumbMinWidth: mThumbMinWidth = ta.getDimensionPixelSize(index, 0); break; - case com.android.internal.R.styleable.FastScroll_thumbMinHeight: + case R.styleable.FastScroll_thumbMinHeight: mThumbMinHeight = ta.getDimensionPixelSize(index, 0); break; - case com.android.internal.R.styleable.FastScroll_padding: + case R.styleable.FastScroll_padding: mPreviewPadding = ta.getDimensionPixelSize(index, 0); break; + case R.styleable.FastScroll_thumbPosition: + mThumbPosition = ta.getInt(index, THUMB_POSITION_MIDPOINT); + break; } } @@ -478,14 +485,16 @@ class FastScroller { final int previewResId = mPreviewResId[mLayoutFromRight ? PREVIEW_RIGHT : PREVIEW_LEFT]; mPreviewImage.setBackgroundResource(previewResId); - // Add extra padding for text. - final Drawable background = mPreviewImage.getBackground(); - if (background != null) { - final Rect padding = mTempBounds; - background.getPadding(padding); - padding.offset(mPreviewPadding, mPreviewPadding); - mPreviewImage.setPadding(padding.left, padding.top, padding.right, padding.bottom); - } + // Propagate padding to text min width/height. + final int textMinWidth = Math.max(0, mPreviewMinWidth - mPreviewImage.getPaddingLeft() + - mPreviewImage.getPaddingRight()); + mPrimaryText.setMinimumWidth(textMinWidth); + mSecondaryText.setMinimumWidth(textMinWidth); + + final int textMinHeight = Math.max(0, mPreviewMinHeight - mPreviewImage.getPaddingTop() + - mPreviewImage.getPaddingBottom()); + mPrimaryText.setMinimumHeight(textMinHeight); + mSecondaryText.setMinimumHeight(textMinHeight); // Requires re-layout. updateLayout(); @@ -560,6 +569,8 @@ class FastScroller { layoutThumb(); layoutTrack(); + updateOffsetAndRange(); + final Rect bounds = mTempBounds; measurePreview(mPrimaryText, bounds); applyLayout(mPrimaryText, bounds); @@ -758,15 +769,45 @@ class FastScroller { final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); track.measure(widthMeasureSpec, heightMeasureSpec); + final int top; + final int bottom; + if (mThumbPosition == THUMB_POSITION_INSIDE) { + top = container.top; + bottom = container.bottom; + } else { + final int thumbHalfHeight = thumb.getHeight() / 2; + top = container.top + thumbHalfHeight; + bottom = container.bottom - thumbHalfHeight; + } + final int trackWidth = track.getMeasuredWidth(); - final int thumbHalfHeight = thumb.getHeight() / 2; final int left = thumb.getLeft() + (thumb.getWidth() - trackWidth) / 2; final int right = left + trackWidth; - final int top = container.top + thumbHalfHeight; - final int bottom = container.bottom - thumbHalfHeight; track.layout(left, top, right, bottom); } + /** + * Updates the offset and range used to convert from absolute y-position to + * thumb position within the track. + */ + private void updateOffsetAndRange() { + final View trackImage = mTrackImage; + final View thumbImage = mThumbImage; + final float min; + final float max; + if (mThumbPosition == THUMB_POSITION_INSIDE) { + final float halfThumbHeight = thumbImage.getHeight() / 2f; + min = trackImage.getTop() + halfThumbHeight; + max = trackImage.getBottom() - halfThumbHeight; + } else{ + min = trackImage.getTop(); + max = trackImage.getBottom(); + } + + mThumbOffset = min; + mThumbRange = max - min; + } + private void setState(int state) { mList.removeCallbacks(mDeferHide); @@ -1145,18 +1186,8 @@ class FastScroller { * to place the thumb. */ private void setThumbPos(float position) { - final Rect container = mContainerRect; - final int top = container.top; - final int bottom = container.bottom; - - final View trackImage = mTrackImage; - final View thumbImage = mThumbImage; - final float min = trackImage.getTop(); - final float max = trackImage.getBottom(); - final float offset = min; - final float range = max - min; - final float thumbMiddle = position * range + offset; - thumbImage.setTranslationY(thumbMiddle - thumbImage.getHeight() / 2); + final float thumbMiddle = position * mThumbRange + mThumbOffset; + mThumbImage.setTranslationY(thumbMiddle - mThumbImage.getHeight() / 2f); final View previewImage = mPreviewImage; final float previewHalfHeight = previewImage.getHeight() / 2f; @@ -1175,6 +1206,9 @@ class FastScroller { } // Center the preview on the thumb, constrained to the list bounds. + final Rect container = mContainerRect; + final int top = container.top; + final int bottom = container.bottom; final float minP = top + previewHalfHeight; final float maxP = bottom - previewHalfHeight; final float previewMiddle = MathUtils.constrain(previewPos, minP, maxP); @@ -1186,19 +1220,13 @@ class FastScroller { } private float getPosFromMotionEvent(float y) { - final View trackImage = mTrackImage; - final float min = trackImage.getTop(); - final float max = trackImage.getBottom(); - final float offset = min; - final float range = max - min; - // If the list is the same height as the thumbnail or shorter, // effectively disable scrolling. - if (range <= 0) { + if (mThumbRange <= 0) { return 0f; } - return MathUtils.constrain((y - offset) / range, 0f, 1f); + return MathUtils.constrain((y - mThumbOffset) / mThumbRange, 0f, 1f); } /** diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 9caa584..11439e4 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -9067,6 +9067,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean selectAllText() { + // Need to hide insert point cursor controller before settings selection, otherwise insert + // point cursor controller obtains cursor update event and update cursor with cancelling + // selection. + if (mEditor != null) { + mEditor.hideInsertionPointCursorController(); + } final int length = mText.length(); Selection.setSelection((Spannable) mText, 0, length); return length > 0; |