diff --git a/Android.mk b/Android.mk index 1802239d25bde0d8155f198211adc25a4b57b8e8..3d52f44f3f93ff43413962cc4165cf8a3235d448 100644 --- a/Android.mk +++ b/Android.mk @@ -165,6 +165,7 @@ LOCAL_SRC_FILES += \ core/java/android/print/ILayoutResultCallback.aidl \ core/java/android/print/IPrinterDiscoveryObserver.aidl \ core/java/android/print/IPrintDocumentAdapter.aidl \ + core/java/android/print/IPrintDocumentAdapterObserver.aidl \ core/java/android/print/IPrintJobStateChangeListener.aidl \ core/java/android/print/IPrintManager.aidl \ core/java/android/print/IPrintSpooler.aidl \ @@ -250,10 +251,14 @@ LOCAL_SRC_FILES += \ media/java/android/media/IAudioService.aidl \ media/java/android/media/IAudioFocusDispatcher.aidl \ media/java/android/media/IAudioRoutesObserver.aidl \ + media/java/android/media/IMediaRouterClient.aidl \ + media/java/android/media/IMediaRouterService.aidl \ media/java/android/media/IMediaScannerListener.aidl \ media/java/android/media/IMediaScannerService.aidl \ media/java/android/media/IRemoteControlClient.aidl \ media/java/android/media/IRemoteControlDisplay.aidl \ + media/java/android/media/IRemoteDisplayCallback.aidl \ + media/java/android/media/IRemoteDisplayProvider.aidl \ media/java/android/media/IRemoteVolumeObserver.aidl \ media/java/android/media/IRingtonePlayer.aidl \ telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \ @@ -537,31 +542,89 @@ framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \ framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \ frameworks/base/docs/knowntags.txt -sample_dir := development/samples +sample_dir := development/samples/browseable new_sample_dir := developers/samples/android # Whitelist of valid groups, used for default TOC grouping. Each sample must # belong to one (and only one) group. Assign samples to groups by setting # a sample.group var to one of these groups in the sample's _index.jd. -sample_groups := -samplegroup Input \ - -samplegroup Sensors \ - -samplegroup Connectivity +sample_groups := -samplegroup Background \ + -samplegroup Connectivity \ + -samplegroup Content \ + -samplegroup Input \ + -samplegroup Media \ + -samplegroup Security \ + -samplegroup Testing \ + -samplegroup UI \ + -samplegroup Views # the list here should match the list of samples included in the sdk samples package # (see development/build/sdk.atree) # remove htmlified samples for now -- samples are still available through the SDK web_docs_sample_code_flags := \ -hdf android.hasSamples 1 \ - -samplecode $(new_sample_dir)/input/gestures/BasicGestureDetect/BasicGestureDetect \ - samples/BasicGestureDetect/ "Basic Gestures" \ - -samplecode $(sample_dir)/AccelerometerPlay \ - samples/AccelerometerPlay "Accelerometer Play" \ - -samplecode $(sample_dir)/ActionBarCompat \ - samples/ActionBarCompat "Action Bar Compatibility" \ - -samplecode $(sample_dir)/BluetoothHDP \ - samples/BluetoothHDP "Bluetooth HDP Demo" \ - -samplecode $(sample_dir)/BluetoothLeGatt \ - samples/BluetoothLeGatt "Bluetooth HDP Demo" + -samplecode $(sample_dir)/BasicAccessibility \ + samples/BasicAccessibility "" \ + -samplecode $(sample_dir)/HorizontalPaging \ + samples/HorizontalPaging "" \ + -samplecode $(sample_dir)/ShareActionProvider \ + samples/ShareActionProvider "" \ + -samplecode $(sample_dir)/Styled \ + samples/Styled "" \ + -samplecode $(sample_dir)/BasicAndroidKeyStore \ + samples/BasicAndroidKeyStore "" \ + -samplecode $(sample_dir)/Basic \ + samples/Basic "" \ + -samplecode $(sample_dir)/ImmersiveMode \ + samples/ImmersiveMode "" \ + -samplecode $(sample_dir)/repeatingAlarm \ + samples/repeatingAlarm "" \ + -samplecode $(sample_dir)/TextLinkify \ + samples/TextLinkify "" \ + -samplecode $(sample_dir)/BasicMediaRouter \ + samples/BasicMediaRouter "" \ + -samplecode $(sample_dir)/BasicMultitouch \ + samples/BasicMultitouch "" \ + -samplecode $(sample_dir)/TextSwitcher \ + samples/TextSwitcher "" \ + -samplecode $(sample_dir)/ActivityInstrumentation \ + samples/ActivityInstrumentation "" \ + -samplecode $(sample_dir)/BorderlessButtons \ + samples/BorderlessButtons "" \ + -samplecode $(sample_dir)/BasicNotifications \ + samples/BasicNotifications "" \ + -samplecode $(sample_dir)/AdvancedImmersiveMode \ + samples/AdvancedImmersiveMode "" \ + -samplecode $(sample_dir)/BluetoothLeGatt \ + samples/BluetoothLeGatt "" \ + -samplecode $(sample_dir)/NetworkConnect \ + samples/NetworkConnect "" \ + -samplecode $(sample_dir)/BasicNetworking \ + samples/BasicNetworking "" \ + -samplecode $(sample_dir)/BasicMediaDecoder \ + samples/BasicMediaDecoder "" \ + -samplecode $(sample_dir)/BasicImmersiveMode \ + samples/BasicImmersiveMode "" \ + -samplecode $(sample_dir)/CustomChoiceList \ + samples/CustomChoiceList "" \ + -samplecode $(sample_dir)/BasicContactables \ + samples/BasicContactables "" \ + -samplecode $(sample_dir)/BasicGestureDetect \ + samples/BasicGestureDetect "" \ + -samplecode $(sample_dir)/DoneBar \ + samples/DoneBar "" \ + -samplecode $(sample_dir)/ListPopupMenu \ + samples/ListPopupMenu "" \ + -samplecode $(sample_dir)/AppRestrictions \ + samples/AppRestrictions "" \ + -samplecode $(sample_dir)/CustomNotifications \ + samples/CustomNotifications "" \ + -samplecode $(sample_dir)/BasicSyncAdapter \ + samples/BasicSyncAdapter "" \ + -samplecode $(sample_dir)/StorageClient \ + samples/StorageClient "" \ + -samplecode $(sample_dir)/StorageProvider \ + samples/StorageProvider "" # -samplecode $(sample_dir)/AndroidBeamDemo \ # samples/AndroidBeamDemo "Android Beam Demo" \ # -samplecode $(sample_dir)/ApiDemos \ @@ -780,9 +843,9 @@ LOCAL_MODULE := online-sdk LOCAL_DROIDDOC_OPTIONS:= \ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ -toroot / \ - -hdf android.whichdoc online -# $(sample_groups) \ -# $(web_docs_sample_code_flags) + -hdf android.whichdoc online \ + $(sample_groups) \ + $(web_docs_sample_code_flags) LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk diff --git a/api/current.txt b/api/current.txt index ff48db0efdbe97b68e226ce947165fb1e3b0cbbf..8e69592ecabcd8acba60595de330372955bc8344 100644 --- a/api/current.txt +++ b/api/current.txt @@ -22,6 +22,7 @@ package android { field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"; field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD"; + field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; @@ -3083,18 +3084,18 @@ package android.app { method public void cancel(android.app.PendingIntent); method public void set(int, long, android.app.PendingIntent); method public void setExact(int, long, android.app.PendingIntent); - method public deprecated void setInexactRepeating(int, long, long, android.app.PendingIntent); + method public void setInexactRepeating(int, long, long, android.app.PendingIntent); method public void setRepeating(int, long, long, android.app.PendingIntent); method public void setTime(long); method public void setTimeZone(java.lang.String); method public void setWindow(int, long, long, android.app.PendingIntent); field public static final int ELAPSED_REALTIME = 3; // 0x3 field public static final int ELAPSED_REALTIME_WAKEUP = 2; // 0x2 - field public static final deprecated long INTERVAL_DAY = 86400000L; // 0x5265c00L - field public static final deprecated long INTERVAL_FIFTEEN_MINUTES = 900000L; // 0xdbba0L - field public static final deprecated long INTERVAL_HALF_DAY = 43200000L; // 0x2932e00L - field public static final deprecated long INTERVAL_HALF_HOUR = 1800000L; // 0x1b7740L - field public static final deprecated long INTERVAL_HOUR = 3600000L; // 0x36ee80L + field public static final long INTERVAL_DAY = 86400000L; // 0x5265c00L + field public static final long INTERVAL_FIFTEEN_MINUTES = 900000L; // 0xdbba0L + field public static final long INTERVAL_HALF_DAY = 43200000L; // 0x2932e00L + field public static final long INTERVAL_HALF_HOUR = 1800000L; // 0x1b7740L + field public static final long INTERVAL_HOUR = 3600000L; // 0x36ee80L field public static final int RTC = 1; // 0x1 field public static final int RTC_WAKEUP = 0; // 0x0 } @@ -13227,6 +13228,7 @@ package android.media.audiofx { field public static final java.util.UUID EFFECT_TYPE_BASS_BOOST; field public static final java.util.UUID EFFECT_TYPE_ENV_REVERB; field public static final java.util.UUID EFFECT_TYPE_EQUALIZER; + field public static final java.util.UUID EFFECT_TYPE_LOUDNESS_ENHANCER; field public static final java.util.UUID EFFECT_TYPE_NS; field public static final java.util.UUID EFFECT_TYPE_PRESET_REVERB; field public static final java.util.UUID EFFECT_TYPE_VIRTUALIZER; @@ -13384,6 +13386,7 @@ package android.media.audiofx { } public class LoudnessEnhancer extends android.media.audiofx.AudioEffect { + ctor public LoudnessEnhancer(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.RuntimeException, java.lang.UnsupportedOperationException; method public float getTargetGain() throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException; method public void setTargetGain(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.UnsupportedOperationException; field public static final int PARAM_TARGET_GAIN_MB = 0; // 0x0 @@ -25632,10 +25635,8 @@ package android.transition { ctor public TransitionManager(); method public static void beginDelayedTransition(android.view.ViewGroup); method public static void beginDelayedTransition(android.view.ViewGroup, android.transition.Transition); - method public static android.transition.Transition getDefaultTransition(); method public static void go(android.transition.Scene); method public static void go(android.transition.Scene, android.transition.Transition); - method public void setDefaultTransition(android.transition.Transition); method public void setTransition(android.transition.Scene, android.transition.Transition); method public void setTransition(android.transition.Scene, android.transition.Scene, android.transition.Transition); method public void transitionTo(android.transition.Scene); @@ -25794,6 +25795,7 @@ package android.util { method public boolean equals(android.util.DisplayMetrics); method public void setTo(android.util.DisplayMetrics); method public void setToDefaults(); + field public static final int DENSITY_400 = 400; // 0x190 field public static final int DENSITY_DEFAULT = 160; // 0xa0 field public static final int DENSITY_HIGH = 240; // 0xf0 field public static final int DENSITY_LOW = 120; // 0x78 diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index c18f54259678e8ac4893e20c7083dd78d0ef15d4..0344d261fd35f75591c022acbd71ef17d67f7f0c 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -107,7 +107,7 @@ public class Am extends BaseCommand { " am switch-user \n" + " am stop-user \n" + " am stack create \n" + - " am stack movetask [true|false]\n" + + " am stack movetask [true|false]\n" + " am stack resize \n" + " am stack boxes\n" + " am stack box \n" + diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index e29f8ea636478478f75b9cf40781853c1998b8b9..d6db8c2a0d13917149bc26092a5eb95746600209 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -58,6 +58,7 @@ import android.text.method.TextKeyListener; import android.util.AttributeSet; import android.util.EventLog; import android.util.Log; +import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; import android.view.ActionMode; @@ -4846,36 +4847,23 @@ public class Activity extends ContextThemeWrapper writer.println(mChangingConfigurations); writer.print(innerPrefix); writer.print("mCurrentConfig="); writer.println(mCurrentConfig); + if (mLoaderManager != null) { writer.print(prefix); writer.print("Loader Manager "); writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager))); writer.println(":"); mLoaderManager.dump(prefix + " ", fd, writer, args); } + mFragments.dump(prefix, fd, writer, args); - writer.print(prefix); writer.println("View Hierarchy:"); - dumpViewHierarchy(prefix + " ", writer, getWindow().getDecorView()); - } - private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) { - writer.print(prefix); - if (view == null) { - writer.println("null"); - return; - } - writer.println(view.toString()); - if (!(view instanceof ViewGroup)) { - return; - } - ViewGroup grp = (ViewGroup)view; - final int N = grp.getChildCount(); - if (N <= 0) { - return; - } - prefix = prefix + " "; - for (int i=0; i results) { ResultData res = new ResultData(); res.token = token; res.results = results; - queueOrSendMessage(H.SEND_RESULT, res); + sendMessage(H.SEND_RESULT, res); } // we use token to identify this activity without having to send the @@ -626,7 +619,7 @@ public final class ActivityThread { updatePendingConfiguration(curConfig); - queueOrSendMessage(H.LAUNCH_ACTIVITY, r); + sendMessage(H.LAUNCH_ACTIVITY, r); } public final void scheduleRelaunchActivity(IBinder token, @@ -641,12 +634,12 @@ public final class ActivityThread { data.intents = intents; data.token = token; - queueOrSendMessage(H.NEW_INTENT, data); + sendMessage(H.NEW_INTENT, data); } public final void scheduleDestroyActivity(IBinder token, boolean finishing, int configChanges) { - queueOrSendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0, + sendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0, configChanges); } @@ -658,7 +651,7 @@ public final class ActivityThread { sync, false, mAppThread.asBinder(), sendingUser); r.info = info; r.compatInfo = compatInfo; - queueOrSendMessage(H.RECEIVER, r); + sendMessage(H.RECEIVER, r); } public final void scheduleCreateBackupAgent(ApplicationInfo app, @@ -668,7 +661,7 @@ public final class ActivityThread { d.compatInfo = compatInfo; d.backupMode = backupMode; - queueOrSendMessage(H.CREATE_BACKUP_AGENT, d); + sendMessage(H.CREATE_BACKUP_AGENT, d); } public final void scheduleDestroyBackupAgent(ApplicationInfo app, @@ -677,7 +670,7 @@ public final class ActivityThread { d.appInfo = app; d.compatInfo = compatInfo; - queueOrSendMessage(H.DESTROY_BACKUP_AGENT, d); + sendMessage(H.DESTROY_BACKUP_AGENT, d); } public final void scheduleCreateService(IBinder token, @@ -688,7 +681,7 @@ public final class ActivityThread { s.info = info; s.compatInfo = compatInfo; - queueOrSendMessage(H.CREATE_SERVICE, s); + sendMessage(H.CREATE_SERVICE, s); } public final void scheduleBindService(IBinder token, Intent intent, @@ -702,7 +695,7 @@ public final class ActivityThread { if (DEBUG_SERVICE) Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid=" + Binder.getCallingUid() + " pid=" + Binder.getCallingPid()); - queueOrSendMessage(H.BIND_SERVICE, s); + sendMessage(H.BIND_SERVICE, s); } public final void scheduleUnbindService(IBinder token, Intent intent) { @@ -710,7 +703,7 @@ public final class ActivityThread { s.token = token; s.intent = intent; - queueOrSendMessage(H.UNBIND_SERVICE, s); + sendMessage(H.UNBIND_SERVICE, s); } public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId, @@ -722,11 +715,11 @@ public final class ActivityThread { s.flags = flags; s.args = args; - queueOrSendMessage(H.SERVICE_ARGS, s); + sendMessage(H.SERVICE_ARGS, s); } public final void scheduleStopService(IBinder token) { - queueOrSendMessage(H.STOP_SERVICE, token); + sendMessage(H.STOP_SERVICE, token); } public final void bindApplication(String processName, @@ -763,24 +756,24 @@ public final class ActivityThread { data.initProfileFile = profileFile; data.initProfileFd = profileFd; data.initAutoStopProfiler = false; - queueOrSendMessage(H.BIND_APPLICATION, data); + sendMessage(H.BIND_APPLICATION, data); } public final void scheduleExit() { - queueOrSendMessage(H.EXIT_APPLICATION, null); + sendMessage(H.EXIT_APPLICATION, null); } public final void scheduleSuicide() { - queueOrSendMessage(H.SUICIDE, null); + sendMessage(H.SUICIDE, null); } public void requestThumbnail(IBinder token) { - queueOrSendMessage(H.REQUEST_THUMBNAIL, token); + sendMessage(H.REQUEST_THUMBNAIL, token); } public void scheduleConfigurationChanged(Configuration config) { updatePendingConfiguration(config); - queueOrSendMessage(H.CONFIGURATION_CHANGED, config); + sendMessage(H.CONFIGURATION_CHANGED, config); } public void updateTimeZone() { @@ -807,7 +800,7 @@ public final class ActivityThread { data.fd = ParcelFileDescriptor.dup(fd); data.token = servicetoken; data.args = args; - queueOrSendMessage(H.DUMP_SERVICE, data); + sendMessage(H.DUMP_SERVICE, data, 0, 0, true /*async*/); } catch (IOException e) { Slog.w(TAG, "dumpService failed", e); } @@ -825,11 +818,11 @@ public final class ActivityThread { } public void scheduleLowMemory() { - queueOrSendMessage(H.LOW_MEMORY, null); + sendMessage(H.LOW_MEMORY, null); } public void scheduleActivityConfigurationChanged(IBinder token) { - queueOrSendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token); + sendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token); } public void profilerControl(boolean start, String path, ParcelFileDescriptor fd, @@ -837,14 +830,14 @@ public final class ActivityThread { ProfilerControlData pcd = new ProfilerControlData(); pcd.path = path; pcd.fd = fd; - queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0, profileType); + sendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0, profileType); } public void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) { DumpHeapData dhd = new DumpHeapData(); dhd.path = path; dhd.fd = fd; - queueOrSendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0); + sendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0, 0, true /*async*/); } public void setSchedulingGroup(int group) { @@ -860,11 +853,11 @@ public final class ActivityThread { } public void dispatchPackageBroadcast(int cmd, String[] packages) { - queueOrSendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd); + sendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd); } public void scheduleCrash(String msg) { - queueOrSendMessage(H.SCHEDULE_CRASH, msg); + sendMessage(H.SCHEDULE_CRASH, msg); } public void dumpActivity(FileDescriptor fd, IBinder activitytoken, @@ -875,7 +868,7 @@ public final class ActivityThread { data.token = activitytoken; data.prefix = prefix; data.args = args; - queueOrSendMessage(H.DUMP_ACTIVITY, data); + sendMessage(H.DUMP_ACTIVITY, data, 0, 0, true /*async*/); } catch (IOException e) { Slog.w(TAG, "dumpActivity failed", e); } @@ -888,7 +881,7 @@ public final class ActivityThread { data.fd = ParcelFileDescriptor.dup(fd); data.token = providertoken; data.args = args; - queueOrSendMessage(H.DUMP_PROVIDER, data); + sendMessage(H.DUMP_PROVIDER, data, 0, 0, true /*async*/); } catch (IOException e) { Slog.w(TAG, "dumpProvider failed", e); } @@ -929,82 +922,14 @@ public final class ActivityThread { long openSslSocketCount = Debug.countInstancesOfClass(OpenSSLSocketImpl.class); SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo(); - // For checkin, we print one long comma-separated list of values + dumpMemInfoTable(pw, memInfo, checkin, dumpFullInfo, dumpDalvik, Process.myPid(), + (mBoundApplication != null) ? mBoundApplication.processName : "unknown", + nativeMax, nativeAllocated, nativeFree, + dalvikMax, dalvikAllocated, dalvikFree); + if (checkin) { // NOTE: if you change anything significant below, also consider changing // ACTIVITY_THREAD_CHECKIN_VERSION. - String processName = (mBoundApplication != null) - ? mBoundApplication.processName : "unknown"; - - // Header - pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(','); - pw.print(Process.myPid()); pw.print(','); - pw.print(processName); pw.print(','); - - // Heap info - max - pw.print(nativeMax); pw.print(','); - pw.print(dalvikMax); pw.print(','); - pw.print("N/A,"); - pw.print(nativeMax + dalvikMax); pw.print(','); - - // Heap info - allocated - pw.print(nativeAllocated); pw.print(','); - pw.print(dalvikAllocated); pw.print(','); - pw.print("N/A,"); - pw.print(nativeAllocated + dalvikAllocated); pw.print(','); - - // Heap info - free - pw.print(nativeFree); pw.print(','); - pw.print(dalvikFree); pw.print(','); - pw.print("N/A,"); - pw.print(nativeFree + dalvikFree); pw.print(','); - - // Heap info - proportional set size - pw.print(memInfo.nativePss); pw.print(','); - pw.print(memInfo.dalvikPss); pw.print(','); - pw.print(memInfo.otherPss); pw.print(','); - pw.print(memInfo.getTotalPss()); pw.print(','); - - // Heap info - swappable set size - pw.print(memInfo.nativeSwappablePss); pw.print(','); - pw.print(memInfo.dalvikSwappablePss); pw.print(','); - pw.print(memInfo.otherSwappablePss); pw.print(','); - pw.print(memInfo.getTotalSwappablePss()); pw.print(','); - - // Heap info - shared dirty - pw.print(memInfo.nativeSharedDirty); pw.print(','); - pw.print(memInfo.dalvikSharedDirty); pw.print(','); - pw.print(memInfo.otherSharedDirty); pw.print(','); - pw.print(memInfo.getTotalSharedDirty()); pw.print(','); - - // Heap info - shared clean - pw.print(memInfo.nativeSharedClean); pw.print(','); - pw.print(memInfo.dalvikSharedClean); pw.print(','); - pw.print(memInfo.otherSharedClean); pw.print(','); - pw.print(memInfo.getTotalSharedClean()); pw.print(','); - - // Heap info - private Dirty - pw.print(memInfo.nativePrivateDirty); pw.print(','); - pw.print(memInfo.dalvikPrivateDirty); pw.print(','); - pw.print(memInfo.otherPrivateDirty); pw.print(','); - pw.print(memInfo.getTotalPrivateDirty()); pw.print(','); - - // Heap info - private Clean - pw.print(memInfo.nativePrivateClean); pw.print(','); - pw.print(memInfo.dalvikPrivateClean); pw.print(','); - pw.print(memInfo.otherPrivateClean); pw.print(','); - pw.print(memInfo.getTotalPrivateClean()); pw.print(','); - - // Heap info - other areas - for (int i=0; i * + *

Note: Beginning with API 19 + * ({@link android.os.Build.VERSION_CODES#KITKAT}) alarm delivery is inexact: + * the OS will shift alarms in order to minimize wakeups and battery use. There are + * new APIs to support applications which need strict delivery guarantees; see + * {@link #setWindow(int, long, long, PendingIntent)} and + * {@link #setExact(int, long, PendingIntent)}. Applications whose {@code targetSdkVersion} + * is earlier than API 19 will continue to see the previous behavior in which all + * alarms are delivered exactly when requested. + * *

You do not * instantiate this class directly; instead, retrieve it through * {@link android.content.Context#getSystemService @@ -109,21 +118,19 @@ public class AlarmManager } /** - * TBW: discussion of fuzzy nature of alarms in KLP+. - * *

Schedule an alarm. Note: for timing operations (ticks, timeouts, - * etc) it is easier and much more efficient to use - * {@link android.os.Handler}. If there is already an alarm scheduled - * for the same IntentSender, it will first be canceled. + * etc) it is easier and much more efficient to use {@link android.os.Handler}. + * If there is already an alarm scheduled for the same IntentSender, that previous + * alarm will first be canceled. * - *

If the time occurs in the past, the alarm will be triggered + *

If the stated trigger time is in the past, the alarm will be triggered * immediately. If there is already an alarm for this Intent * scheduled (with the equality of two intents being defined by * {@link Intent#filterEquals}), then it will be removed and replaced by * this one. * *

- * The alarm is an intent broadcast that goes to a broadcast receiver that + * The alarm is an Intent broadcast that goes to a broadcast receiver that * you registered with {@link android.content.Context#registerReceiver} * or through the <receiver> tag in an AndroidManifest.xml file. * @@ -133,9 +140,34 @@ public class AlarmManager * how many past alarm events have been accumulated into this intent * broadcast. Recurring alarms that have gone undelivered because the * phone was asleep may have a count greater than one when delivered. - * - * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC or - * RTC_WAKEUP. + * + *

+ *

+ * Note: Beginning in API 19, the trigger time passed to this method + * is treated as inexact: the alarm will not be delivered before this time, but + * may be deferred and delivered some time later. The OS will use + * this policy in order to "batch" alarms together across the entire system, + * minimizing the number of times the device needs to "wake up" and minimizing + * battery use. In general, alarms scheduled in the near future will not + * be deferred as long as alarms scheduled far in the future. + * + *

+ * With the new batching policy, delivery ordering guarantees are not as + * strong as they were previously. If the application sets multiple alarms, + * it is possible that these alarms' actual delivery ordering may not match + * the order of their requested delivery times. If your application has + * strong ordering requirements there are other APIs that you can use to get + * the necessary behavior; see {@link #setWindow(int, long, long, PendingIntent)} + * and {@link #setExact(int, long, PendingIntent)}. + * + *

+ * Applications whose {@code targetSdkVersion} is before API 19 will + * continue to get the previous alarm behavior: all of their scheduled alarms + * will be treated as exact. + *

+ * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. * @param triggerAtMillis time in milliseconds that the alarm should go * off, using the appropriate clock (depending on the alarm type). * @param operation Action to perform when the alarm goes off; @@ -165,10 +197,10 @@ public class AlarmManager * {@link android.os.Handler}. If there is already an alarm scheduled * for the same IntentSender, it will first be canceled. * - *

Like {@link #set}, except you can also - * supply a rate at which the alarm will repeat. This alarm continues - * repeating until explicitly removed with {@link #cancel}. If the time - * occurs in the past, the alarm will be triggered immediately, with an + *

Like {@link #set}, except you can also supply a period at which + * the alarm will automatically repeat. This alarm continues + * repeating until explicitly removed with {@link #cancel}. If the stated + * trigger time is in the past, the alarm will be triggered immediately, with an * alarm count depending on how far in the past the trigger time is relative * to the repeat interval. * @@ -185,8 +217,15 @@ public class AlarmManager * between alarms, then the approach to take is to use one-time alarms, * scheduling the next one yourself when handling each alarm delivery. * - * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP}, RTC or - * RTC_WAKEUP. + *

+ * Note: as of API 19, all repeating alarms are inexact. If your + * application needs precise delivery times then it must use one-time + * exact alarms, rescheduling each time as described above. Legacy applications + * whose {@code targetSdkVersion} is earlier than API 19 will continue to have all + * of their alarms, including repeating alarms, treated as exact. + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. * @param triggerAtMillis time in milliseconds that the alarm should first * go off, using the appropriate clock (depending on the alarm type). * @param intervalMillis interval in milliseconds between subsequent repeats @@ -214,18 +253,33 @@ public class AlarmManager } /** - * Schedule an alarm to be delivered within a given window of time. + * Schedule an alarm to be delivered within a given window of time. This method + * is similar to {@link #set(int, long, PendingIntent)}, but allows the + * application to precisely control the degree to which its delivery might be + * adjusted by the OS. This method allows an application to take advantage of the + * battery optimizations that arise from delivery batching even when it has + * modest timeliness requirements for its alarms. * - * TBW: clean up these docs + *

+ * This method can also be used to achieve strict ordering guarantees among + * multiple alarms by ensuring that the windows requested for each alarm do + * not intersect. + * + *

+ * When precise delivery is not required, applications should use the standard + * {@link #set(int, long, PendingIntent)} method. This will give the OS the most + * flexibility to minimize wakeups and battery use. For alarms that must be delivered + * at precisely-specified times with no acceptable variation, applications can use + * {@link #setExact(int, long, PendingIntent)}. * - * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC or - * RTC_WAKEUP. + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. * @param windowStartMillis The earliest time, in milliseconds, that the alarm should * be delivered, expressed in the appropriate clock's units (depending on the alarm * type). * @param windowLengthMillis The length of the requested delivery window, * in milliseconds. The alarm will be delivered no later than this many - * milliseconds after the windowStartMillis time. Note that this parameter + * milliseconds after {@code windowStartMillis}. Note that this parameter * is a duration, not the timestamp of the end of the window. * @param operation Action to perform when the alarm goes off; * typically comes from {@link PendingIntent#getBroadcast @@ -249,8 +303,38 @@ public class AlarmManager } /** - * TBW: new 'exact' alarm that must be delivered as nearly as possible - * to the precise time specified. + * Schedule an alarm to be delivered precisely at the stated time. + * + *

+ * This method is like {@link #set(int, long, PendingIntent)}, but does not permit + * the OS to adjust the delivery time. The alarm will be delivered as nearly as + * possible to the requested trigger time. + * + *

+ * Note: only alarms for which there is a strong demand for exact-time + * delivery (such as an alarm clock ringing at the requested time) should be + * scheduled as exact. Applications are strongly discouraged from using exact + * alarms unnecessarily as they reduce the OS's ability to minimize battery use. + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. + * @param triggerAtMillis time in milliseconds that the alarm should go + * off, using the appropriate clock (depending on the alarm type). + * @param operation Action to perform when the alarm goes off; + * typically comes from {@link PendingIntent#getBroadcast + * IntentSender.getBroadcast()}. + * + * @see #set + * @see #setRepeating + * @see #setWindow + * @see #cancel + * @see android.content.Context#sendBroadcast + * @see android.content.Context#registerReceiver + * @see android.content.Intent#filterEquals + * @see #ELAPSED_REALTIME + * @see #ELAPSED_REALTIME_WAKEUP + * @see #RTC + * @see #RTC_WAKEUP */ public void setExact(int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, operation, null); @@ -283,74 +367,82 @@ public class AlarmManager } /** - * @deprecated setInexactRepeating() is deprecated; as of API 19 all - * repeating alarms are inexact. + * Available inexact recurrence interval recognized by + * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * when running on Android prior to API 19. */ - @Deprecated public static final long INTERVAL_FIFTEEN_MINUTES = 15 * 60 * 1000; /** - * @deprecated setInexactRepeating() is deprecated; as of API 19 all - * repeating alarms are inexact. + * Available inexact recurrence interval recognized by + * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * when running on Android prior to API 19. */ - @Deprecated public static final long INTERVAL_HALF_HOUR = 2*INTERVAL_FIFTEEN_MINUTES; /** - * @deprecated setInexactRepeating() is deprecated; as of API 19 all - * repeating alarms are inexact. + * Available inexact recurrence interval recognized by + * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * when running on Android prior to API 19. */ - @Deprecated public static final long INTERVAL_HOUR = 2*INTERVAL_HALF_HOUR; /** - * @deprecated setInexactRepeating() is deprecated; as of API 19 all - * repeating alarms are inexact. + * Available inexact recurrence interval recognized by + * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * when running on Android prior to API 19. */ - @Deprecated public static final long INTERVAL_HALF_DAY = 12*INTERVAL_HOUR; /** - * @deprecated setInexactRepeating() is deprecated; as of API 19 all - * repeating alarms are inexact. + * Available inexact recurrence interval recognized by + * {@link #setInexactRepeating(int, long, long, PendingIntent)} + * when running on Android prior to API 19. */ - @Deprecated public static final long INTERVAL_DAY = 2*INTERVAL_HALF_DAY; /** * Schedule a repeating alarm that has inexact trigger time requirements; * for example, an alarm that repeats every hour, but not necessarily at * the top of every hour. These alarms are more power-efficient than - * the strict recurrences supplied by {@link #setRepeating}, since the - * system can adjust alarms' phase to cause them to fire simultaneously, + * the strict recurrences traditionally supplied by {@link #setRepeating}, since the + * system can adjust alarms' delivery times to cause them to fire simultaneously, * avoiding waking the device from sleep more than necessary. - * + * *

Your alarm's first trigger will not be before the requested time, * but it might not occur for almost a full interval after that time. In * addition, while the overall period of the repeating alarm will be as * requested, the time between any two successive firings of the alarm * may vary. If your application demands very low jitter, use - * {@link #setRepeating} instead. + * one-shot alarms with an appropriate window instead; see {@link + * #setWindow(int, long, long, PendingIntent)} and + * {@link #setExact(int, long, PendingIntent)}. * - * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP}, RTC or - * RTC_WAKEUP. + *

+ * As of API 19, all repeating alarms are inexact. Because this method has + * been available since API 3, your application can safely call it and be + * assured that it will get similar behavior on both current and older versions + * of Android. + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. * @param triggerAtMillis time in milliseconds that the alarm should first * go off, using the appropriate clock (depending on the alarm type). This * is inexact: the alarm will not fire before this time, but there may be a * delay of almost an entire alarm interval before the first invocation of * the alarm. * @param intervalMillis interval in milliseconds between subsequent repeats - * of the alarm. If this is one of INTERVAL_FIFTEEN_MINUTES, + * of the alarm. Prior to API 19, if this is one of INTERVAL_FIFTEEN_MINUTES, * INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_HALF_DAY, or INTERVAL_DAY * then the alarm will be phase-aligned with other alarms to reduce the * number of wakeups. Otherwise, the alarm will be set as though the - * application had called {@link #setRepeating}. + * application had called {@link #setRepeating}. As of API 19, all repeating + * alarms will be inexact and subject to batching with other alarms regardless + * of their stated repeat interval. * @param operation Action to perform when the alarm goes off; * typically comes from {@link PendingIntent#getBroadcast * IntentSender.getBroadcast()}. * - * @deprecated As of API 19, all repeating alarms are inexact. - * * @see android.os.Handler * @see #set * @see #cancel @@ -367,7 +459,6 @@ public class AlarmManager * @see #INTERVAL_HALF_DAY * @see #INTERVAL_DAY */ - @Deprecated public void setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, operation, null); diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index b741cc5c85b35c64e6089f1f6e9eec2e6b532dd5..2ed8b0fbe24ce64ec190a59eb19c2253d3f64c52 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -1011,15 +1011,15 @@ public class DownloadManager { } /** - * Returns {@link Uri} for the given downloaded file id, if the file is - * downloaded successfully. otherwise, null is returned. + * Returns the {@link Uri} of the given downloaded file id, if the file is + * downloaded successfully. Otherwise, null is returned. *

* If the specified downloaded file is in external storage (for example, /sdcard dir), * then it is assumed to be safe for anyone to read and the returned {@link Uri} corresponds * to the filepath on sdcard. * * @param id the id of the downloaded file. - * @return the {@link Uri} for the given downloaded file id, if download was successful. null + * @return the {@link Uri} of the given downloaded file id, if download was successful. null * otherwise. */ public Uri getUriForDownloadedFile(long id) { @@ -1064,15 +1064,11 @@ public class DownloadManager { } /** - * Returns {@link Uri} for the given downloaded file id, if the file is - * downloaded successfully. otherwise, null is returned. - *

- * If the specified downloaded file is in external storage (for example, /sdcard dir), - * then it is assumed to be safe for anyone to read and the returned {@link Uri} corresponds - * to the filepath on sdcard. + * Returns the media type of the given downloaded file id, if the file was + * downloaded successfully. Otherwise, null is returned. * * @param id the id of the downloaded file. - * @return the {@link Uri} for the given downloaded file id, if download was successful. null + * @return the media type of the given downloaded file id, if download was successful. null * otherwise. */ public String getMimeTypeForDownloadedFile(long id) { diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index a307a73d52b3a7eba3c8b334d32708d8310a9c57..028fa6816b96a933de8674f83d92cc89e7cbd1c9 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1213,8 +1213,8 @@ public class Instrumentation { } /** - * Perform calling of an activity's {@link Activity#onPause} method. The - * default implementation simply calls through to that method. + * Perform calling of an activity's {@link Activity#onSaveInstanceState} + * method. The default implementation simply calls through to that method. * * @param activity The activity being saved. * @param outState The bundle to pass to the call. diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 22a21cdf829da37cbb6580688e68e112fd7a0269..aab6ed83b11fd30bb4c6fe75a9be5ec587874b05 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -205,7 +205,9 @@ public class KeyguardManager { try { mWM.exitKeyguardSecurely(new IOnKeyguardExitResult.Stub() { public void onKeyguardExitResult(boolean success) throws RemoteException { - callback.onKeyguardExitResult(success); + if (callback != null) { + callback.onKeyguardExitResult(success); + } } }); } catch (RemoteException e) { diff --git a/core/java/android/app/MediaRouteActionProvider.java b/core/java/android/app/MediaRouteActionProvider.java index 63b641cb708e9d184b3e81b93efa013eec0260a2..dffa969a7148adaad2167d63406f96c833ba8295 100644 --- a/core/java/android/app/MediaRouteActionProvider.java +++ b/core/java/android/app/MediaRouteActionProvider.java @@ -16,10 +16,7 @@ package android.app; -import com.android.internal.app.MediaRouteChooserDialogFragment; - import android.content.Context; -import android.content.ContextWrapper; import android.media.MediaRouter; import android.media.MediaRouter.RouteInfo; import android.util.Log; @@ -30,22 +27,38 @@ import android.view.ViewGroup; import java.lang.ref.WeakReference; +/** + * The media route action provider displays a {@link MediaRouteButton media route button} + * in the application's {@link ActionBar} to allow the user to select routes and + * to control the currently selected route. + *

+ * The application must specify the kinds of routes that the user should be allowed + * to select by specifying the route types with the {@link #setRouteTypes} method. + *

+ * Refer to {@link MediaRouteButton} for a description of the button that will + * appear in the action bar menu. Note that instead of disabling the button + * when no routes are available, the action provider will instead make the + * menu item invisible. In this way, the button will only be visible when it + * is possible for the user to discover and select a matching route. + *

+ */ public class MediaRouteActionProvider extends ActionProvider { private static final String TAG = "MediaRouteActionProvider"; - private Context mContext; - private MediaRouter mRouter; - private MenuItem mMenuItem; - private MediaRouteButton mView; + private final Context mContext; + private final MediaRouter mRouter; + private final MediaRouterCallback mCallback; + private int mRouteTypes; + private MediaRouteButton mButton; private View.OnClickListener mExtendedSettingsListener; - private RouterCallback mCallback; public MediaRouteActionProvider(Context context) { super(context); + mContext = context; mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); - mCallback = new RouterCallback(this); + mCallback = new MediaRouterCallback(this); // Start with live audio by default. // TODO Update this when new route types are added; segment by API level @@ -53,80 +66,74 @@ public class MediaRouteActionProvider extends ActionProvider { setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_AUDIO); } + /** + * Sets the types of routes that will be shown in the media route chooser dialog + * launched by this button. + * + * @param types The route types to match. + */ public void setRouteTypes(int types) { - if (mRouteTypes == types) return; - if (mRouteTypes != 0) { - mRouter.removeCallback(mCallback); - } - mRouteTypes = types; - if (types != 0) { - mRouter.addCallback(types, mCallback); + if (mRouteTypes != types) { + // FIXME: We currently have no way of knowing whether the action provider + // is still needed by the UI. Unfortunately this means the action provider + // may leak callbacks until garbage collection occurs. This may result in + // media route providers doing more work than necessary in the short term + // while trying to discover routes that are no longer of interest to the + // application. To solve this problem, the action provider will need some + // indication from the framework that it is being destroyed. + if (mRouteTypes != 0) { + mRouter.removeCallback(mCallback); + } + mRouteTypes = types; + if (types != 0) { + mRouter.addCallback(types, mCallback, + MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY); + } + refreshRoute(); + + if (mButton != null) { + mButton.setRouteTypes(mRouteTypes); + } } - if (mView != null) { - mView.setRouteTypes(mRouteTypes); + } + + public void setExtendedSettingsClickListener(View.OnClickListener listener) { + mExtendedSettingsListener = listener; + if (mButton != null) { + mButton.setExtendedSettingsClickListener(listener); } } @Override + @SuppressWarnings("deprecation") public View onCreateActionView() { throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead."); } @Override public View onCreateActionView(MenuItem item) { - if (mMenuItem != null || mView != null) { + if (mButton != null) { Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " + "with a menu item. Don't reuse MediaRouteActionProvider instances! " + "Abandoning the old one..."); } - mMenuItem = item; - mView = new MediaRouteButton(mContext); - mView.setCheatSheetEnabled(true); - mView.setRouteTypes(mRouteTypes); - mView.setExtendedSettingsClickListener(mExtendedSettingsListener); - mView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + + mButton = new MediaRouteButton(mContext); + mButton.setCheatSheetEnabled(true); + mButton.setRouteTypes(mRouteTypes); + mButton.setExtendedSettingsClickListener(mExtendedSettingsListener); + mButton.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)); - return mView; + return mButton; } @Override public boolean onPerformDefaultAction() { - final FragmentManager fm = getActivity().getFragmentManager(); - // See if one is already attached to this activity. - MediaRouteChooserDialogFragment dialogFragment = - (MediaRouteChooserDialogFragment) fm.findFragmentByTag( - MediaRouteChooserDialogFragment.FRAGMENT_TAG); - if (dialogFragment != null) { - Log.w(TAG, "onPerformDefaultAction(): Chooser dialog already showing!"); - return false; - } - - dialogFragment = new MediaRouteChooserDialogFragment(); - dialogFragment.setExtendedSettingsClickListener(mExtendedSettingsListener); - dialogFragment.setRouteTypes(mRouteTypes); - dialogFragment.show(fm, MediaRouteChooserDialogFragment.FRAGMENT_TAG); - return true; - } - - private Activity getActivity() { - // Gross way of unwrapping the Activity so we can get the FragmentManager - Context context = mContext; - while (context instanceof ContextWrapper && !(context instanceof Activity)) { - context = ((ContextWrapper) context).getBaseContext(); - } - if (!(context instanceof Activity)) { - throw new IllegalStateException("The MediaRouteActionProvider's Context " + - "is not an Activity."); - } - - return (Activity) context; - } - - public void setExtendedSettingsClickListener(View.OnClickListener listener) { - mExtendedSettingsListener = listener; - if (mView != null) { - mView.setExtendedSettingsClickListener(listener); + if (mButton != null) { + return mButton.showDialogInternal(); } + return false; } @Override @@ -136,36 +143,43 @@ public class MediaRouteActionProvider extends ActionProvider { @Override public boolean isVisible() { - return mRouter.getRouteCount() > 1; + return mRouter.isRouteAvailable(mRouteTypes, + MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); + } + + private void refreshRoute() { + refreshVisibility(); } - private static class RouterCallback extends MediaRouter.SimpleCallback { - private WeakReference mAp; + private static class MediaRouterCallback extends MediaRouter.SimpleCallback { + private final WeakReference mProviderWeak; - RouterCallback(MediaRouteActionProvider ap) { - mAp = new WeakReference(ap); + public MediaRouterCallback(MediaRouteActionProvider provider) { + mProviderWeak = new WeakReference(provider); } @Override public void onRouteAdded(MediaRouter router, RouteInfo info) { - final MediaRouteActionProvider ap = mAp.get(); - if (ap == null) { - router.removeCallback(this); - return; - } - - ap.refreshVisibility(); + refreshRoute(router); } @Override public void onRouteRemoved(MediaRouter router, RouteInfo info) { - final MediaRouteActionProvider ap = mAp.get(); - if (ap == null) { + refreshRoute(router); + } + + @Override + public void onRouteChanged(MediaRouter router, RouteInfo info) { + refreshRoute(router); + } + + private void refreshRoute(MediaRouter router) { + MediaRouteActionProvider provider = mProviderWeak.get(); + if (provider != null) { + provider.refreshRoute(); + } else { router.removeCallback(this); - return; } - - ap.refreshVisibility(); } } } diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java index 7e0a27aca508d8da524934bdd64410932700c2f9..a7982f4584fc1d4c379d8e5729c5c9352798a0c2 100644 --- a/core/java/android/app/MediaRouteButton.java +++ b/core/java/android/app/MediaRouteButton.java @@ -17,7 +17,7 @@ package android.app; import com.android.internal.R; -import com.android.internal.app.MediaRouteChooserDialogFragment; +import com.android.internal.app.MediaRouteDialogPresenter; import android.content.Context; import android.content.ContextWrapper; @@ -30,7 +30,6 @@ import android.media.MediaRouter.RouteGroup; import android.media.MediaRouter.RouteInfo; import android.text.TextUtils; import android.util.AttributeSet; -import android.util.Log; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.SoundEffectConstants; @@ -38,17 +37,15 @@ import android.view.View; import android.widget.Toast; public class MediaRouteButton extends View { - private static final String TAG = "MediaRouteButton"; + private final MediaRouter mRouter; + private final MediaRouterCallback mCallback; - private MediaRouter mRouter; - private final MediaRouteCallback mRouterCallback = new MediaRouteCallback(); private int mRouteTypes; private boolean mAttachedToWindow; private Drawable mRemoteIndicator; private boolean mRemoteActive; - private boolean mToggleMode; private boolean mCheatSheetEnabled; private boolean mIsConnecting; @@ -56,12 +53,13 @@ public class MediaRouteButton extends View { private int mMinHeight; private OnClickListener mExtendedSettingsClickListener; - private MediaRouteChooserDialogFragment mDialogFragment; + // The checked state is used when connected to a remote route. private static final int[] CHECKED_STATE_SET = { R.attr.state_checked }; + // The activated state is used while connecting to a remote route. private static final int[] ACTIVATED_STATE_SET = { R.attr.state_activated }; @@ -78,6 +76,7 @@ public class MediaRouteButton extends View { super(context, attrs, defStyleAttr); mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE); + mCallback = new MediaRouterCallback(); TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.MediaRouteButton, defStyleAttr, 0); @@ -98,53 +97,99 @@ public class MediaRouteButton extends View { setRouteTypes(routeTypes); } - private void setRemoteIndicatorDrawable(Drawable d) { - if (mRemoteIndicator != null) { - mRemoteIndicator.setCallback(null); - unscheduleDrawable(mRemoteIndicator); - } - mRemoteIndicator = d; - if (d != null) { - d.setCallback(this); - d.setState(getDrawableState()); - d.setVisible(getVisibility() == VISIBLE, false); + /** + * Gets the media route types for filtering the routes that the user can + * select using the media route chooser dialog. + * + * @return The route types. + */ + public int getRouteTypes() { + return mRouteTypes; + } + + /** + * Sets the types of routes that will be shown in the media route chooser dialog + * launched by this button. + * + * @param types The route types to match. + */ + public void setRouteTypes(int types) { + if (mRouteTypes != types) { + if (mAttachedToWindow && mRouteTypes != 0) { + mRouter.removeCallback(mCallback); + } + + mRouteTypes = types; + + if (mAttachedToWindow && types != 0) { + mRouter.addCallback(types, mCallback, + MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY); + } + + refreshRoute(); } + } - refreshDrawableState(); + public void setExtendedSettingsClickListener(OnClickListener listener) { + mExtendedSettingsClickListener = listener; } - @Override - public boolean performClick() { - // Send the appropriate accessibility events and call listeners - boolean handled = super.performClick(); - if (!handled) { - playSoundEffect(SoundEffectConstants.CLICK); + /** + * Show the route chooser or controller dialog. + *

+ * If the default route is selected or if the currently selected route does + * not match the {@link #getRouteTypes route types}, then shows the route chooser dialog. + * Otherwise, shows the route controller dialog to offer the user + * a choice to disconnect from the route or perform other control actions + * such as setting the route's volume. + *

+ * This will attach a {@link DialogFragment} to the containing Activity. + *

+ */ + public void showDialog() { + showDialogInternal(); + } + + boolean showDialogInternal() { + if (!mAttachedToWindow) { + return false; } - if (mToggleMode) { - if (mRemoteActive) { - mRouter.selectRouteInt(mRouteTypes, mRouter.getDefaultRoute()); - } else { - final int N = mRouter.getRouteCount(); - for (int i = 0; i < N; i++) { - final RouteInfo route = mRouter.getRouteAt(i); - if ((route.getSupportedTypes() & mRouteTypes) != 0 && - route != mRouter.getDefaultRoute()) { - mRouter.selectRouteInt(mRouteTypes, route); - } - } + DialogFragment f = MediaRouteDialogPresenter.showDialogFragment(getActivity(), + mRouteTypes, mExtendedSettingsClickListener); + return f != null; + } + + private Activity getActivity() { + // Gross way of unwrapping the Activity so we can get the FragmentManager + Context context = getContext(); + while (context instanceof ContextWrapper) { + if (context instanceof Activity) { + return (Activity)context; } - } else { - showDialog(); + context = ((ContextWrapper)context).getBaseContext(); } - - return handled; + throw new IllegalStateException("The MediaRouteButton's Context is not an Activity."); } + /** + * Sets whether to enable showing a toast with the content descriptor of the + * button when the button is long pressed. + */ void setCheatSheetEnabled(boolean enable) { mCheatSheetEnabled = enable; } + @Override + public boolean performClick() { + // Send the appropriate accessibility events and call listeners + boolean handled = super.performClick(); + if (!handled) { + playSoundEffect(SoundEffectConstants.CLICK); + } + return showDialogInternal() || handled; + } + @Override public boolean performLongClick() { if (super.performLongClick()) { @@ -183,85 +228,9 @@ public class MediaRouteButton extends View { } cheatSheet.show(); performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - return true; } - public void setRouteTypes(int types) { - if (types == mRouteTypes) { - // Already registered; nothing to do. - return; - } - - if (mAttachedToWindow && mRouteTypes != 0) { - mRouter.removeCallback(mRouterCallback); - } - - mRouteTypes = types; - - if (mAttachedToWindow) { - updateRouteInfo(); - mRouter.addCallback(types, mRouterCallback); - } - } - - private void updateRouteInfo() { - updateRemoteIndicator(); - updateRouteCount(); - } - - public int getRouteTypes() { - return mRouteTypes; - } - - void updateRemoteIndicator() { - final RouteInfo selected = mRouter.getSelectedRoute(mRouteTypes); - final boolean isRemote = selected != mRouter.getDefaultRoute(); - final boolean isConnecting = selected != null && - selected.getStatusCode() == RouteInfo.STATUS_CONNECTING; - - boolean needsRefresh = false; - if (mRemoteActive != isRemote) { - mRemoteActive = isRemote; - needsRefresh = true; - } - if (mIsConnecting != isConnecting) { - mIsConnecting = isConnecting; - needsRefresh = true; - } - - if (needsRefresh) { - refreshDrawableState(); - } - } - - void updateRouteCount() { - final int N = mRouter.getRouteCount(); - int count = 0; - boolean hasVideoRoutes = false; - for (int i = 0; i < N; i++) { - final RouteInfo route = mRouter.getRouteAt(i); - final int routeTypes = route.getSupportedTypes(); - if ((routeTypes & mRouteTypes) != 0) { - if (route instanceof RouteGroup) { - count += ((RouteGroup) route).getRouteCount(); - } else { - count++; - } - if ((routeTypes & MediaRouter.ROUTE_TYPE_LIVE_VIDEO) != 0) { - hasVideoRoutes = true; - } - } - } - - setEnabled(count != 0); - - // Only allow toggling if we have more than just user routes. - // Don't toggle if we support video routes, we may have to let the dialog scan. - mToggleMode = count == 2 && (mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_AUDIO) != 0 && - !hasVideoRoutes; - } - @Override protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); @@ -289,6 +258,21 @@ public class MediaRouteButton extends View { } } + private void setRemoteIndicatorDrawable(Drawable d) { + if (mRemoteIndicator != null) { + mRemoteIndicator.setCallback(null); + unscheduleDrawable(mRemoteIndicator); + } + mRemoteIndicator = d; + if (d != null) { + d.setCallback(this); + d.setState(getDrawableState()); + d.setVisible(getVisibility() == VISIBLE, false); + } + + refreshDrawableState(); + } + @Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || who == mRemoteIndicator; @@ -297,12 +281,16 @@ public class MediaRouteButton extends View { @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); - if (mRemoteIndicator != null) mRemoteIndicator.jumpToCurrentState(); + + if (mRemoteIndicator != null) { + mRemoteIndicator.jumpToCurrentState(); + } } @Override public void setVisibility(int visibility) { super.setVisibility(visibility); + if (mRemoteIndicator != null) { mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false); } @@ -311,19 +299,22 @@ public class MediaRouteButton extends View { @Override public void onAttachedToWindow() { super.onAttachedToWindow(); + mAttachedToWindow = true; if (mRouteTypes != 0) { - mRouter.addCallback(mRouteTypes, mRouterCallback); - updateRouteInfo(); + mRouter.addCallback(mRouteTypes, mCallback, + MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY); } + refreshRoute(); } @Override public void onDetachedFromWindow() { + mAttachedToWindow = false; if (mRouteTypes != 0) { - mRouter.removeCallback(mRouterCallback); + mRouter.removeCallback(mCallback); } - mAttachedToWindow = false; + super.onDetachedFromWindow(); } @@ -386,93 +377,71 @@ public class MediaRouteButton extends View { final int drawLeft = left + (right - left - drawWidth) / 2; final int drawTop = top + (bottom - top - drawHeight) / 2; - mRemoteIndicator.setBounds(drawLeft, drawTop, drawLeft + drawWidth, drawTop + drawHeight); + mRemoteIndicator.setBounds(drawLeft, drawTop, + drawLeft + drawWidth, drawTop + drawHeight); mRemoteIndicator.draw(canvas); } - public void setExtendedSettingsClickListener(OnClickListener listener) { - mExtendedSettingsClickListener = listener; - if (mDialogFragment != null) { - mDialogFragment.setExtendedSettingsClickListener(listener); - } - } - - /** - * Asynchronously show the route chooser dialog. - * This will attach a {@link DialogFragment} to the containing Activity. - */ - public void showDialog() { - final FragmentManager fm = getActivity().getFragmentManager(); - if (mDialogFragment == null) { - // See if one is already attached to this activity. - mDialogFragment = (MediaRouteChooserDialogFragment) fm.findFragmentByTag( - MediaRouteChooserDialogFragment.FRAGMENT_TAG); - } - if (mDialogFragment != null) { - Log.w(TAG, "showDialog(): Already showing!"); - return; - } + private void refreshRoute() { + if (mAttachedToWindow) { + final MediaRouter.RouteInfo route = mRouter.getSelectedRoute(); + final boolean isRemote = !route.isDefault() && route.matchesTypes(mRouteTypes); + final boolean isConnecting = isRemote && route.isConnecting(); + + boolean needsRefresh = false; + if (mRemoteActive != isRemote) { + mRemoteActive = isRemote; + needsRefresh = true; + } + if (mIsConnecting != isConnecting) { + mIsConnecting = isConnecting; + needsRefresh = true; + } - mDialogFragment = new MediaRouteChooserDialogFragment(); - mDialogFragment.setExtendedSettingsClickListener(mExtendedSettingsClickListener); - mDialogFragment.setLauncherListener(new MediaRouteChooserDialogFragment.LauncherListener() { - @Override - public void onDetached(MediaRouteChooserDialogFragment detachedFragment) { - mDialogFragment = null; + if (needsRefresh) { + refreshDrawableState(); } - }); - mDialogFragment.setRouteTypes(mRouteTypes); - mDialogFragment.show(fm, MediaRouteChooserDialogFragment.FRAGMENT_TAG); - } - private Activity getActivity() { - // Gross way of unwrapping the Activity so we can get the FragmentManager - Context context = getContext(); - while (context instanceof ContextWrapper && !(context instanceof Activity)) { - context = ((ContextWrapper) context).getBaseContext(); + setEnabled(mRouter.isRouteAvailable(mRouteTypes, + MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE)); } - if (!(context instanceof Activity)) { - throw new IllegalStateException("The MediaRouteButton's Context is not an Activity."); - } - - return (Activity) context; } - private class MediaRouteCallback extends MediaRouter.SimpleCallback { + private final class MediaRouterCallback extends MediaRouter.SimpleCallback { @Override - public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { - updateRemoteIndicator(); + public void onRouteAdded(MediaRouter router, RouteInfo info) { + refreshRoute(); } @Override - public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { - updateRemoteIndicator(); + public void onRouteRemoved(MediaRouter router, RouteInfo info) { + refreshRoute(); } @Override public void onRouteChanged(MediaRouter router, RouteInfo info) { - updateRemoteIndicator(); + refreshRoute(); } @Override - public void onRouteAdded(MediaRouter router, RouteInfo info) { - updateRouteCount(); + public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { + refreshRoute(); } @Override - public void onRouteRemoved(MediaRouter router, RouteInfo info) { - updateRouteCount(); + public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { + refreshRoute(); } @Override public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, int index) { - updateRouteCount(); + refreshRoute(); } @Override public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) { - updateRouteCount(); + refreshRoute(); } } } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 7bcf43eb8d038867ffb05f196dd2539cebe874b1..2045ed8411a30a3e7673b7edbe9c6ec48fc36d0e 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -58,10 +58,7 @@ public class StatusBarManager { | DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK | DISABLE_SEARCH; - public static final int NAVIGATION_HINT_BACK_NOP = 1 << 0; - public static final int NAVIGATION_HINT_HOME_NOP = 1 << 1; - public static final int NAVIGATION_HINT_RECENT_NOP = 1 << 2; - public static final int NAVIGATION_HINT_BACK_ALT = 1 << 3; + public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0; public static final int WINDOW_STATUS_BAR = 1; public static final int WINDOW_NAVIGATION_BAR = 2; diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 607930c0be440ec4527ebc464cc2df7106646322..91b0d7cd68baac932d4c727f7ba1f84959b01621 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -146,7 +146,9 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { @Override public void shutdown() { synchronized (mLock) { - throwIfCalledByNotTrustedUidLocked(); + if (isConnectedLocked()) { + throwIfCalledByNotTrustedUidLocked(); + } throwIfShutdownLocked(); mIsShutdown = true; if (isConnectedLocked()) { diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 5822e468b36b0e9ee59f8713d022f24b3ebd37bb..d789a944f5f2641eec630fe6c887b3b4b4d2558a 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -322,7 +322,7 @@ public final class BluetoothDevice implements Parcelable { /** * Broadcast Action: This intent is used to broadcast PAIRING REQUEST - *

Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} to + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to * receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @@ -465,7 +465,7 @@ public final class BluetoothDevice implements Parcelable { /** * The user will be prompted to enter a pin or - * a privileged app will enter a pin for user. + * an app will enter a pin for user. */ public static final int PAIRING_VARIANT_PIN = 0; @@ -477,7 +477,7 @@ public final class BluetoothDevice implements Parcelable { /** * The user will be prompted to confirm the passkey displayed on the screen or - * a privileged app will confirm the passkey for the user. + * an app will confirm the passkey for the user. */ public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; @@ -725,7 +725,7 @@ public final class BluetoothDevice implements Parcelable { * the bonding process completes, and its result. *

Android system services will handle the necessary user interactions * to confirm and complete the bonding process. - *

Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return false on immediate error, true if bonding will begin */ @@ -965,7 +965,7 @@ public final class BluetoothDevice implements Parcelable { /** * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} - *

Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true pin has been set * false for error @@ -993,7 +993,7 @@ public final class BluetoothDevice implements Parcelable { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - *

Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true confirmation has been sent out * false for error diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index a9d055988862e49164bf99b47b1c565b5ad1cf99..ddde3fb00d69dd42ae160a987d1df2e234fb0084 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -398,135 +398,137 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return AppOpsManager.MODE_ALLOWED; } - private void enforceReadPermissionInner(Uri uri) throws SecurityException { - final Context context = getContext(); - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - String missingPerm = null; - - if (UserHandle.isSameApp(uid, mMyUid)) { - return; + private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException { + enforceWritePermissionInner(uri); + if (mWriteOp != AppOpsManager.OP_NONE) { + return mAppOpsManager.noteOp(mWriteOp, Binder.getCallingUid(), callingPkg); } + return AppOpsManager.MODE_ALLOWED; + } + } - if (mExported) { - final String componentPerm = getReadPermission(); - if (componentPerm != null) { - if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) { - return; - } else { - missingPerm = componentPerm; - } + /** {@hide} */ + protected void enforceReadPermissionInner(Uri uri) throws SecurityException { + final Context context = getContext(); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + String missingPerm = null; + + if (UserHandle.isSameApp(uid, mMyUid)) { + return; + } + + if (mExported) { + final String componentPerm = getReadPermission(); + if (componentPerm != null) { + if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) { + return; + } else { + missingPerm = componentPerm; } + } - // track if unprotected read is allowed; any denied - // below removes this ability - boolean allowDefaultRead = (componentPerm == null); - - final PathPermission[] pps = getPathPermissions(); - if (pps != null) { - final String path = uri.getPath(); - for (PathPermission pp : pps) { - final String pathPerm = pp.getReadPermission(); - if (pathPerm != null && pp.match(path)) { - if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) { - return; - } else { - // any denied means we lose - // default access. - allowDefaultRead = false; - missingPerm = pathPerm; - } + // track if unprotected read is allowed; any denied + // below removes this ability + boolean allowDefaultRead = (componentPerm == null); + + final PathPermission[] pps = getPathPermissions(); + if (pps != null) { + final String path = uri.getPath(); + for (PathPermission pp : pps) { + final String pathPerm = pp.getReadPermission(); + if (pathPerm != null && pp.match(path)) { + if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) { + return; + } else { + // any denied means we lose + // default access. + allowDefaultRead = false; + missingPerm = pathPerm; } } } - - // if we passed checks above, and no default - // permission, then allow access. - if (allowDefaultRead) return; - } - - // last chance, check against any uri grants - if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION) - == PERMISSION_GRANTED) { - return; } - final String failReason = mExported - ? " requires " + missingPerm + ", or grantUriPermission()" - : " requires the provider be exported, or grantUriPermission()"; - throw new SecurityException("Permission Denial: reading " - + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid - + ", uid=" + uid + failReason); + // if we passed checks above, and no default + // permission, then allow access. + if (allowDefaultRead) return; } - private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException { - enforceWritePermissionInner(uri); - if (mWriteOp != AppOpsManager.OP_NONE) { - return mAppOpsManager.noteOp(mWriteOp, Binder.getCallingUid(), callingPkg); - } - return AppOpsManager.MODE_ALLOWED; + // last chance, check against any uri grants + if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION) + == PERMISSION_GRANTED) { + return; } - private void enforceWritePermissionInner(Uri uri) throws SecurityException { - final Context context = getContext(); - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - String missingPerm = null; + final String failReason = mExported + ? " requires " + missingPerm + ", or grantUriPermission()" + : " requires the provider be exported, or grantUriPermission()"; + throw new SecurityException("Permission Denial: reading " + + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid + + ", uid=" + uid + failReason); + } - if (UserHandle.isSameApp(uid, mMyUid)) { - return; - } + /** {@hide} */ + protected void enforceWritePermissionInner(Uri uri) throws SecurityException { + final Context context = getContext(); + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + String missingPerm = null; - if (mExported) { - final String componentPerm = getWritePermission(); - if (componentPerm != null) { - if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) { - return; - } else { - missingPerm = componentPerm; - } + if (UserHandle.isSameApp(uid, mMyUid)) { + return; + } + + if (mExported) { + final String componentPerm = getWritePermission(); + if (componentPerm != null) { + if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) { + return; + } else { + missingPerm = componentPerm; } + } - // track if unprotected write is allowed; any denied - // below removes this ability - boolean allowDefaultWrite = (componentPerm == null); - - final PathPermission[] pps = getPathPermissions(); - if (pps != null) { - final String path = uri.getPath(); - for (PathPermission pp : pps) { - final String pathPerm = pp.getWritePermission(); - if (pathPerm != null && pp.match(path)) { - if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) { - return; - } else { - // any denied means we lose - // default access. - allowDefaultWrite = false; - missingPerm = pathPerm; - } + // track if unprotected write is allowed; any denied + // below removes this ability + boolean allowDefaultWrite = (componentPerm == null); + + final PathPermission[] pps = getPathPermissions(); + if (pps != null) { + final String path = uri.getPath(); + for (PathPermission pp : pps) { + final String pathPerm = pp.getWritePermission(); + if (pathPerm != null && pp.match(path)) { + if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) { + return; + } else { + // any denied means we lose + // default access. + allowDefaultWrite = false; + missingPerm = pathPerm; } } } - - // if we passed checks above, and no default - // permission, then allow access. - if (allowDefaultWrite) return; } - // last chance, check against any uri grants - if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - == PERMISSION_GRANTED) { - return; - } + // if we passed checks above, and no default + // permission, then allow access. + if (allowDefaultWrite) return; + } - final String failReason = mExported - ? " requires " + missingPerm + ", or grantUriPermission()" - : " requires the provider be exported, or grantUriPermission()"; - throw new SecurityException("Permission Denial: writing " - + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid - + ", uid=" + uid + failReason); + // last chance, check against any uri grants + if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + == PERMISSION_GRANTED) { + return; } + + final String failReason = mExported + ? " requires " + missingPerm + ", or grantUriPermission()" + : " requires the provider be exported, or grantUriPermission()"; + throw new SecurityException("Permission Denial: writing " + + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid + + ", uid=" + uid + failReason); } /** diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java index 0284882963c7218f2b1e0e73fe3dc16d217aa76f..cffc653e44ef7e7b9f1325109f1e53fd906b623c 100644 --- a/core/java/android/content/SyncInfo.java +++ b/core/java/android/content/SyncInfo.java @@ -54,6 +54,14 @@ public class SyncInfo implements Parcelable { this.startTime = startTime; } + /** @hide */ + public SyncInfo(SyncInfo other) { + this.authorityId = other.authorityId; + this.account = new Account(other.account.name, other.account.type); + this.authority = other.authority; + this.startTime = other.startTime; + } + /** @hide */ public int describeContents() { return 0; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 267fb2af2dd16c276b1baa2f696d8a8b81626c49..20002ad9d47140a3fb39adc9df28aa3a98df99aa 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -53,6 +53,7 @@ import android.content.IntentSender; * {@hide} */ interface IPackageManager { + boolean isPackageAvailable(String packageName, int userId); PackageInfo getPackageInfo(String packageName, int flags, int userId); int getPackageUid(String packageName, int userId); int[] getPackageGids(String packageName); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 17d13e500e8e8a26d86a7f1e9b744d284d91ded3..e6da288fedbdb14fbe47a70f1eef795068947dbc 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -282,6 +282,10 @@ public class PackageParser { || (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0; } + public static boolean isAvailable(PackageUserState state) { + return checkUseInstalledOrBlocked(0, state); + } + public static PackageInfo generatePackageInfo(PackageParser.Package p, int gids[], int flags, long firstInstallTime, long lastUpdateTime, HashSet grantedPermissions, PackageUserState state, int userId) { diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 14f67c58289432d561117d9d2e61644c3e35f101..50fdb4170c60c535a00aea76b5b52abbcc643483 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -358,14 +358,20 @@ public class SystemSensorManager extends SensorManager { mListener = listener; } + @Override public void addSensorEvent(Sensor sensor) { SensorEvent t = new SensorEvent(Sensor.getMaxLengthValuesArray(sensor, mManager.mTargetSdkLevel)); - mSensorsEvents.put(sensor.getHandle(), t); + synchronized (mSensorsEvents) { + mSensorsEvents.put(sensor.getHandle(), t); + } } + @Override public void removeSensorEvent(Sensor sensor) { - mSensorsEvents.delete(sensor.getHandle()); + synchronized (mSensorsEvents) { + mSensorsEvents.delete(sensor.getHandle()); + } } // Called from native code. @@ -374,9 +380,14 @@ public class SystemSensorManager extends SensorManager { protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy, long timestamp) { final Sensor sensor = sHandleToSensor.get(handle); - SensorEvent t = mSensorsEvents.get(handle); + SensorEvent t = null; + synchronized (mSensorsEvents) { + t = mSensorsEvents.get(handle); + } + if (t == null) { - Log.e(TAG, "Error: Sensor Event is null for Sensor: " + sensor); + // This may happen if the client has unregistered and there are pending events in + // the queue waiting to be delivered. Ignore. return; } // Copy from the values array. @@ -427,14 +438,20 @@ public class SystemSensorManager extends SensorManager { mListener = listener; } + @Override public void addSensorEvent(Sensor sensor) { TriggerEvent t = new TriggerEvent(Sensor.getMaxLengthValuesArray(sensor, mManager.mTargetSdkLevel)); - mTriggerEvents.put(sensor.getHandle(), t); + synchronized (mTriggerEvents) { + mTriggerEvents.put(sensor.getHandle(), t); + } } + @Override public void removeSensorEvent(Sensor sensor) { - mTriggerEvents.delete(sensor.getHandle()); + synchronized (mTriggerEvents) { + mTriggerEvents.delete(sensor.getHandle()); + } } // Called from native code. @@ -443,7 +460,10 @@ public class SystemSensorManager extends SensorManager { protected void dispatchSensorEvent(int handle, float[] values, int accuracy, long timestamp) { final Sensor sensor = sHandleToSensor.get(handle); - TriggerEvent t = mTriggerEvents.get(handle); + TriggerEvent t = null; + synchronized (mTriggerEvents) { + t = mTriggerEvents.get(handle); + } if (t == null) { Log.e(TAG, "Error: Trigger Event is null for Sensor: " + sensor); return; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 4fe2c4d3669b5d68b1d8a06082f423732dca008b..a38beec44f935ee25da32e0cf00d0205063a7ef4 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -332,6 +332,27 @@ public final class CameraCharacteristics extends CameraMetadata { public static final Key LENS_FACING = new Key("android.lens.facing", int.class); + /** + *

+ * If set to 1, the HAL will always split result + * metadata for a single capture into multiple buffers, + * returned using multiple process_capture_result calls. + *

+ *

+ * Does not need to be listed in static + * metadata. Support for partial results will be reworked in + * future versions of camera service. This quirk will stop + * working at that point; DO NOT USE without careful + * consideration of future support. + *

+ * + * Optional - This value may be null on some devices. + * + * @hide + */ + public static final Key QUIRKS_USE_PARTIAL_RESULT = + new Key("android.quirks.usePartialResult", byte.class); + /** *

* How many output streams can be allocated at diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 7095e4d498ab6d7f8aacfb48a0e8674b0a05d283..9e8d7d19e288bec6cb21767e9b0aaa841a73366b 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -630,6 +630,36 @@ public interface CameraDevice extends AutoCloseable { // default empty implementation } + /** + * This method is called when some results from an image capture are + * available. + * + *

The result provided here will contain some subset of the fields of + * a full result. Multiple onCapturePartial calls may happen per + * capture; a given result field will only be present in one partial + * capture at most. The final onCaptureCompleted call will always + * contain all the fields, whether onCapturePartial was called or + * not.

+ * + *

The default implementation of this method does nothing.

+ * + * @param camera The CameraDevice sending the callback. + * @param request The request that was given to the CameraDevice + * @param result The partial output metadata from the capture, which + * includes a subset of the CaptureResult fields. + * + * @see #capture + * @see #captureBurst + * @see #setRepeatingRequest + * @see #setRepeatingBurst + * + * @hide + */ + public void onCapturePartial(CameraDevice camera, + CaptureRequest request, CaptureResult result) { + // default empty implementation + } + /** * This method is called when an image capture has completed and the * result metadata is available. diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index dbd0457e525f5c372b65508a6de47020531e4d80..535b9636769dbf21b3a8284de3a36e1f49c72104 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -589,6 +589,32 @@ public final class CaptureResult extends CameraMetadata { public static final Key NOISE_REDUCTION_MODE = new Key("android.noiseReduction.mode", int.class); + /** + *

+ * Whether a result given to the framework is the + * final one for the capture, or only a partial that contains a + * subset of the full set of dynamic metadata + * values. + *

+ *

+ * The entries in the result metadata buffers for a + * single capture may not overlap, except for this entry. The + * FINAL buffers must retain FIFO ordering relative to the + * requests that generate them, so the FINAL buffer for frame 3 must + * always be sent to the framework after the FINAL buffer for frame 2, and + * before the FINAL buffer for frame 4. PARTIAL buffers may be returned + * in any order relative to other frames, but all PARTIAL buffers for a given + * capture must arrive before the FINAL buffer for that capture. This entry may + * only be used by the HAL if quirks.usePartialResult is set to 1. + *

+ * + * Optional - This value may be null on some devices. + * + * @hide + */ + public static final Key QUIRKS_PARTIAL_RESULT = + new Key("android.quirks.partialResult", boolean.class); + /** *

* A frame counter set by the framework. This value monotonically diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java index c5d0999213068cbe0439cda4b6915e4ae4b419c0..40586f0efcd966298bd7cc0e7876b0f64506e101 100644 --- a/core/java/android/hardware/camera2/impl/CameraDevice.java +++ b/core/java/android/hardware/camera2/impl/CameraDevice.java @@ -19,27 +19,24 @@ package android.hardware.camera2.impl; import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; import android.hardware.camera2.CameraAccessException; -import android.hardware.camera2.CameraMetadata; -import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.utils.CameraBinderDecorator; import android.hardware.camera2.utils.CameraRuntimeException; -import android.os.IBinder; -import android.os.RemoteException; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; +import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; import android.view.Surface; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; -import java.util.Stack; /** * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate @@ -49,6 +46,8 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { private final String TAG; private final boolean DEBUG; + private static final int REQUEST_ID_NONE = -1; + // TODO: guard every function with if (!mRemoteDevice) check (if it was closed) private ICameraDeviceUser mRemoteDevice; @@ -63,7 +62,8 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { private final SparseArray mCaptureListenerMap = new SparseArray(); - private final Stack mRepeatingRequestIdStack = new Stack(); + private int mRepeatingRequestId = REQUEST_ID_NONE; + private final ArrayList mRepeatingRequestIdDeletedList = new ArrayList(); // Map stream IDs to Surfaces private final SparseArray mConfiguredOutputs = new SparseArray(); @@ -186,7 +186,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { stopRepeating(); try { - mRemoteDevice.waitUntilIdle(); + waitUntilIdle(); // TODO: mRemoteDevice.beginConfigure // Delete all streams first (to free up HW resources) @@ -279,6 +279,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { checkIfCameraClosed(); int requestId; + if (repeating) { + stopRepeating(); + } + try { requestId = mRemoteDevice.submitRequest(request, repeating); } catch (CameraRuntimeException e) { @@ -293,7 +297,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } if (repeating) { - mRepeatingRequestIdStack.add(requestId); + mRepeatingRequestId = requestId; } if (mIdle) { @@ -327,8 +331,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { synchronized (mLock) { checkIfCameraClosed(); - while (!mRepeatingRequestIdStack.isEmpty()) { - int requestId = mRepeatingRequestIdStack.pop(); + if (mRepeatingRequestId != REQUEST_ID_NONE) { + + int requestId = mRepeatingRequestId; + mRepeatingRequestId = REQUEST_ID_NONE; + + // Queue for deletion after in-flight requests finish + mRepeatingRequestIdDeletedList.add(requestId); try { mRemoteDevice.cancelRequest(requestId); @@ -347,7 +356,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { synchronized (mLock) { checkIfCameraClosed(); - if (!mRepeatingRequestIdStack.isEmpty()) { + if (mRepeatingRequestId != REQUEST_ID_NONE) { throw new IllegalStateException("Active repeating request ongoing"); } @@ -359,6 +368,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { // impossible return; } + + mRepeatingRequestId = REQUEST_ID_NONE; + mRepeatingRequestIdDeletedList.clear(); + mCaptureListenerMap.clear(); } } @@ -564,6 +577,9 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } final CaptureListenerHolder holder; + Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT); + boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial); + synchronized (mLock) { // TODO: move this whole map into this class to make it more testable, // exposing the methods necessary like subscribeToRequest, unsubscribe.. @@ -572,13 +588,28 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { holder = CameraDevice.this.mCaptureListenerMap.get(requestId); // Clean up listener once we no longer expect to see it. - - // TODO: how to handle repeating listeners? - // we probably want cancelRequest to return # of times it already enqueued and - // keep a counter. - if (holder != null && !holder.isRepeating()) { + if (holder != null && !holder.isRepeating() && !quirkIsPartialResult) { CameraDevice.this.mCaptureListenerMap.remove(requestId); } + + // TODO: add 'capture sequence completed' callback to the + // service, and clean up repeating requests there instead. + + // If we received a result for a repeating request and have + // prior repeating requests queued for deletion, remove those + // requests from mCaptureListenerMap. + if (holder != null && holder.isRepeating() && !quirkIsPartialResult + && mRepeatingRequestIdDeletedList.size() > 0) { + Iterator iter = mRepeatingRequestIdDeletedList.iterator(); + while (iter.hasNext()) { + int deletedRequestId = iter.next(); + if (deletedRequestId < requestId) { + CameraDevice.this.mCaptureListenerMap.remove(deletedRequestId); + iter.remove(); + } + } + } + } // Check if we have a listener for this @@ -591,8 +622,25 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { final CaptureRequest request = holder.getRequest(); final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId); - holder.getHandler().post( - new Runnable() { + Runnable resultDispatch = null; + + // Either send a partial result or the final capture completed result + if (quirkIsPartialResult) { + // Partial result + resultDispatch = new Runnable() { + @Override + public void run() { + if (!CameraDevice.this.isClosed()){ + holder.getListener().onCapturePartial( + CameraDevice.this, + request, + resultAsCapture); + } + } + }; + } else { + // Final capture result + resultDispatch = new Runnable() { @Override public void run() { if (!CameraDevice.this.isClosed()){ @@ -602,7 +650,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { resultAsCapture); } } - }); + }; + } + + holder.getHandler().post(resultDispatch); } } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index f12be5f404a9abecac1e7b7036111abee15bcb3d..d5208d97bd41d952ae0194b9445e75c42945e88d 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -299,6 +299,10 @@ public final class DisplayManager { /** * Initiates a fresh scan of availble Wifi displays. * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. + *

+ * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. + *

+ * * @hide */ public void scanWifiDisplays() { @@ -312,8 +316,7 @@ public final class DisplayManager { * Automatically remembers the display after a successful connection, if not * already remembered. *

- * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY} to connect - * to unknown displays. No permissions are required to connect to already known displays. + * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. *

* * @param deviceAddress The MAC address of the device to which we should connect. diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java index b674324197ed5c945affa360dbf233217c9f1e98..8a2c2b6c675fed792b652f8020a4274fb6a89e6a 100644 --- a/core/java/android/net/PacProxySelector.java +++ b/core/java/android/net/PacProxySelector.java @@ -97,7 +97,7 @@ public class PacProxySelector extends ProxySelector { } catch (Exception e) { port = 8080; } - ret.add(new Proxy(Type.HTTP, new InetSocketAddress(host, port))); + ret.add(new Proxy(Type.HTTP, InetSocketAddress.createUnresolved(host, port))); } } if (ret.size() == 0) { diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java index 44cfa94d5cd186dd543aeb4380462f3fa80772cd..010e5277074c09406aac3e23dc438adc51dd8f06 100644 --- a/core/java/android/net/ProxyProperties.java +++ b/core/java/android/net/ProxyProperties.java @@ -140,6 +140,7 @@ public class ProxyProperties implements Parcelable { } public boolean isValid() { + if (!TextUtils.isEmpty(mPacFileUrl)) return true; try { Proxy.validate(mHost == null ? "" : mHost, mPort == 0 ? "" : Integer.toString(mPort), mExclusionList == null ? "" : mExclusionList); diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 486e75a5ca30cb62a04c8e4d0eb8208a2fbdde1c..6743c6cf67f8d62f379a5c223b63f671dce3d887 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -49,6 +49,8 @@ import android.util.Log; *

Developer Guides

*

For more information about using NFC, read the * Near Field Communication developer guide.

+ *

To perform basic file sharing between devices, read + * Sharing Files with NFC. * */ public final class NfcAdapter { @@ -309,8 +311,12 @@ public final class NfcAdapter { final Context mContext; /** - * A callback to be invoked when the system has found a tag in - * reader mode. + * A callback to be invoked when the system finds a tag while the foreground activity is + * operating in reader mode. + *

Register your {@code ReaderCallback} implementation with {@link + * NfcAdapter#enableReaderMode} and disable it with {@link + * NfcAdapter#disableReaderMode}. + * @see NfcAdapter#enableReaderMode */ public interface ReaderCallback { public void onTagDiscovered(Tag tag); diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index bc98a0b504255b93db540c000c9bc9e4efca05e8..b1a9ea300d741dfbe5aa6cb7f7dbc7e01da2b0fc 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -105,6 +105,11 @@ public abstract class BatteryStats implements Parcelable { */ public static final int FOREGROUND_ACTIVITY = 10; + /** + * A constant indicating a wifi batched scan is active + */ + public static final int WIFI_BATCHED_SCAN = 11; + /** * Include all of the data in the stats, including previously saved data. */ @@ -270,6 +275,8 @@ public abstract class BatteryStats implements Parcelable { public abstract void noteFullWifiLockReleasedLocked(); public abstract void noteWifiScanStartedLocked(); public abstract void noteWifiScanStoppedLocked(); + public abstract void noteWifiBatchedScanStartedLocked(int csph); + public abstract void noteWifiBatchedScanStoppedLocked(); public abstract void noteWifiMulticastEnabledLocked(); public abstract void noteWifiMulticastDisabledLocked(); public abstract void noteAudioTurnedOnLocked(); @@ -281,6 +288,7 @@ public abstract class BatteryStats implements Parcelable { public abstract long getWifiRunningTime(long batteryRealtime, int which); public abstract long getFullWifiLockTime(long batteryRealtime, int which); public abstract long getWifiScanTime(long batteryRealtime, int which); + public abstract long getWifiBatchedScanTime(int csphBin, long batteryRealtime, int which); public abstract long getWifiMulticastTime(long batteryRealtime, int which); public abstract long getAudioTurnedOnTime(long batteryRealtime, int which); @@ -288,6 +296,8 @@ public abstract class BatteryStats implements Parcelable { public abstract Timer getForegroundActivityTimer(); public abstract Timer getVibratorOnTimer(); + public static final int NUM_WIFI_BATCHED_SCAN_BINS = 5; + /** * Note that these must match the constants in android.os.PowerManager. * Also, if the user activity types change, the BatteryStatsImpl.VERSION must @@ -844,12 +854,13 @@ public abstract class BatteryStats implements Parcelable { public static final int DATA_CONNECTION_EVDO_B = 12; public static final int DATA_CONNECTION_LTE = 13; public static final int DATA_CONNECTION_EHRPD = 14; - public static final int DATA_CONNECTION_OTHER = 15; + public static final int DATA_CONNECTION_HSPAP = 15; + public static final int DATA_CONNECTION_OTHER = 16; static final String[] DATA_CONNECTION_NAMES = { "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A", "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte", - "ehrpd", "other" + "ehrpd", "hspap", "other" }; public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1; @@ -2080,9 +2091,11 @@ public abstract class BatteryStats implements Parcelable { TimeUtils.formatDuration(ew.usedTime, pw); pw.print(" over "); TimeUtils.formatDuration(ew.overTime, pw); - pw.print(" ("); - pw.print((ew.usedTime*100)/ew.overTime); - pw.println("%)"); + if (ew.overTime != 0) { + pw.print(" ("); + pw.print((ew.usedTime*100)/ew.overTime); + pw.println("%)"); + } } } uidActivity = true; diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 114a1ea69035c990071a49f5337df836b78a9f3a..bc51a60992805fff2afe069bfd5e40758e77dd8d 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -457,6 +457,13 @@ public class Build { * margins correctly. *

  • {@link android.app.ActionBar}'s window content overlay is allowed to be * drawn.
  • + *
  • The {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} + * permission is now always enforced.
  • + *
  • Access to package-specific external storage directories belonging + * to the calling app no longer requires the + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} or + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} + * permissions.
  • * */ public static final int KITKAT = 19; diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 5de365f5335c012367637f9d8f3f8eec3fe95200..af57507b878235bb600fa2d69ce28c4680648306 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -35,10 +35,12 @@ public final class Bundle implements Parcelable, Cloneable { public static final Bundle EMPTY; static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L' + static final Parcel EMPTY_PARCEL; static { EMPTY = new Bundle(); EMPTY.mMap = ArrayMap.EMPTY; + EMPTY_PARCEL = Parcel.obtain(); } // Invariant - exactly one of mMap / mParcelledData will be null @@ -115,9 +117,13 @@ public final class Bundle implements Parcelable, Cloneable { */ public Bundle(Bundle b) { if (b.mParcelledData != null) { - mParcelledData = Parcel.obtain(); - mParcelledData.appendFrom(b.mParcelledData, 0, b.mParcelledData.dataSize()); - mParcelledData.setDataPosition(0); + if (b.mParcelledData == EMPTY_PARCEL) { + mParcelledData = EMPTY_PARCEL; + } else { + mParcelledData = Parcel.obtain(); + mParcelledData.appendFrom(b.mParcelledData, 0, b.mParcelledData.dataSize()); + mParcelledData.setDataPosition(0); + } } else { mParcelledData = null; } @@ -216,6 +222,18 @@ public final class Bundle implements Parcelable, Cloneable { return; } + if (mParcelledData == EMPTY_PARCEL) { + if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + + ": empty"); + if (mMap == null) { + mMap = new ArrayMap(1); + } else { + mMap.erase(); + } + mParcelledData = null; + return; + } + int N = mParcelledData.readInt(); if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + ": reading " + N + " maps"); @@ -1652,11 +1670,20 @@ public final class Bundle implements Parcelable, Cloneable { final boolean oldAllowFds = parcel.pushAllowFds(mAllowFds); try { if (mParcelledData != null) { - int length = mParcelledData.dataSize(); - parcel.writeInt(length); - parcel.writeInt(BUNDLE_MAGIC); - parcel.appendFrom(mParcelledData, 0, length); + if (mParcelledData == EMPTY_PARCEL) { + parcel.writeInt(0); + } else { + int length = mParcelledData.dataSize(); + parcel.writeInt(length); + parcel.writeInt(BUNDLE_MAGIC); + parcel.appendFrom(mParcelledData, 0, length); + } } else { + // Special case for empty bundles. + if (mMap == null || mMap.size() <= 0) { + parcel.writeInt(0); + return; + } int lengthPos = parcel.dataPosition(); parcel.writeInt(-1); // dummy, will hold length parcel.writeInt(BUNDLE_MAGIC); @@ -1690,6 +1717,13 @@ public final class Bundle implements Parcelable, Cloneable { } void readFromParcelInner(Parcel parcel, int length) { + if (length == 0) { + // Empty Bundle or end of data. + mParcelledData = EMPTY_PARCEL; + mHasFds = false; + mFdsKnown = true; + return; + } int magic = parcel.readInt(); if (magic != BUNDLE_MAGIC) { //noinspection ThrowableInstanceNeverThrown @@ -1716,8 +1750,12 @@ public final class Bundle implements Parcelable, Cloneable { @Override public synchronized String toString() { if (mParcelledData != null) { - return "Bundle[mParcelledData.dataSize=" + - mParcelledData.dataSize() + "]"; + if (mParcelledData == EMPTY_PARCEL) { + return "Bundle[EMPTY_PARCEL]"; + } else { + return "Bundle[mParcelledData.dataSize=" + + mParcelledData.dataSize() + "]"; + } } return "Bundle[" + mMap.toString() + "]"; } diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 4d48fd46dd297813a7b8d94730fb4ef81d9eea19..ff3e27785b74ffd72f6fd07c5cbe09344e5d5cc4 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -144,13 +144,6 @@ public class FileUtils { } } - /** returns the FAT file system volume ID for the volume mounted - * at the given mount point, or -1 for failure - * @param mountPoint point for FAT volume - * @return volume ID or -1 - */ - public static native int getFatVolumeId(String mountPoint); - /** * Perform an fsync on the given FileOutputStream. The stream at this * point must be flushed but not yet closed. diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 4c7bbb48f52b225ffa4fcd7fb1b6fac0edb5093b..56176a479fb37c43c684881866518bef8c143ed0 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -23,11 +23,12 @@ import android.os.WorkSource; interface IPowerManager { - // WARNING: The first three methods must remain the first three methods because their + // WARNING: The first four methods must remain the first three methods because their // transaction numbers must not change unless IPowerManager.cpp is also updated. void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws); void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName, int uidtoblame); void releaseWakeLock(IBinder lock, int flags); + void updateWakeLockUids(IBinder lock, in int[] uids); void updateWakeLockWorkSource(IBinder lock, in WorkSource ws); boolean isWakeLockLevelSupported(int level); diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index 78c859ee06efe8b3e0b9fa251b99b9727b872fa2..21e9f6b0ef93f45be01df6152b9687124d16fd19 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -59,7 +59,6 @@ public final class Looper { final MessageQueue mQueue; final Thread mThread; - volatile boolean mRun; private Printer mLogging; @@ -187,7 +186,6 @@ public final class Looper { private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); - mRun = true; mThread = Thread.currentThread(); } @@ -300,27 +298,12 @@ public final class Looper { } public void dump(Printer pw, String prefix) { - pw = PrefixPrinter.create(pw, prefix); - pw.println(this.toString()); - pw.println("mRun=" + mRun); - pw.println("mThread=" + mThread); - pw.println("mQueue=" + ((mQueue != null) ? mQueue : "(null")); - if (mQueue != null) { - synchronized (mQueue) { - long now = SystemClock.uptimeMillis(); - Message msg = mQueue.mMessages; - int n = 0; - while (msg != null) { - pw.println(" Message " + n + ": " + msg.toString(now)); - n++; - msg = msg.next; - } - pw.println("(Total messages: " + n + ")"); - } - } + pw.println(prefix + toString()); + mQueue.dump(pw, prefix + " "); } public String toString() { - return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}"; + return "Looper (" + mThread.getName() + ", tid " + mThread.getId() + + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}"; } } diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index 0abc149c842803587921cd939185afeb0ebe479b..51203a48740a0f4f5bef3d278c35e24e411a7fd8 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -428,36 +428,48 @@ public final class Message implements Parcelable { public Message() { } + @Override public String toString() { return toString(SystemClock.uptimeMillis()); } String toString(long now) { - StringBuilder b = new StringBuilder(); - - b.append("{ what="); - b.append(what); + StringBuilder b = new StringBuilder(); + b.append("{ when="); + TimeUtils.formatDuration(when - now, b); + + if (target != null) { + if (callback != null) { + b.append(" callback="); + b.append(callback.getClass().getName()); + } else { + b.append(" what="); + b.append(what); + } - b.append(" when="); - TimeUtils.formatDuration(when-now, b); + if (arg1 != 0) { + b.append(" arg1="); + b.append(arg1); + } - if (arg1 != 0) { - b.append(" arg1="); - b.append(arg1); - } + if (arg2 != 0) { + b.append(" arg2="); + b.append(arg2); + } - if (arg2 != 0) { - b.append(" arg2="); - b.append(arg2); - } + if (obj != null) { + b.append(" obj="); + b.append(obj); + } - if (obj != null) { - b.append(" obj="); - b.append(obj); + b.append(" target="); + b.append(target.getClass().getName()); + } else { + b.append(" barrier="); + b.append(arg1); } b.append(" }"); - return b.toString(); } diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index d1b8213d95fcc8437610e604f3cd53c1dfd6e92e..799de5c656b2d57686e7d95f34094f8c0681d7de 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -18,6 +18,7 @@ package android.os; import android.util.AndroidRuntimeException; import android.util.Log; +import android.util.Printer; import java.util.ArrayList; @@ -252,6 +253,7 @@ public final class MessageQueue { synchronized (this) { final int token = mNextBarrierToken++; final Message msg = Message.obtain(); + msg.when = when; msg.arg1 = token; Message prev = null; @@ -393,12 +395,16 @@ public final class MessageQueue { boolean isIdling() { synchronized (this) { - // If the loop is quitting then it must not be idling. - // We can assume mPtr != 0 when mQuitting is false. - return !mQuitting && nativeIsIdling(mPtr); + return isIdlingLocked(); } } + private boolean isIdlingLocked() { + // If the loop is quitting then it must not be idling. + // We can assume mPtr != 0 when mQuitting is false. + return !mQuitting && nativeIsIdling(mPtr); + } + void removeMessages(Handler h, int what, Object object) { if (h == null) { return; @@ -537,4 +543,17 @@ public final class MessageQueue { } } } + + void dump(Printer pw, String prefix) { + synchronized (this) { + long now = SystemClock.uptimeMillis(); + int n = 0; + for (Message msg = mMessages; msg != null; msg = msg.next) { + pw.println(prefix + "Message " + n + ": " + msg.toString(now)); + n++; + } + pw.println(prefix + "(Total messages: " + n + ", idling=" + isIdlingLocked() + + ", quitting=" + mQuitting + ")"); + } + } } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 02b1998264a2e218da66e771fe80c5b6db67b0bf..94b961793e2674fbdc6e842f81566aa855146500 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -611,11 +611,15 @@ public final class Parcel { here.fillInStackTrace(); Log.d(TAG, "Writing " + N + " ArrayMap entries", here); } + int startPos; for (int i=0; i 0) { + if (DEBUG_ARRAY_MAP) startPos = dataPosition(); Object key = readValue(loader); - if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + ": key=0x" - + (key != null ? key.hashCode() : 0) + " " + key); Object value = readValue(loader); + if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + " " + + (dataPosition()-startPos) + " bytes: key=0x" + + Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key); outVal.append(key, value); N--; } diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 1456387ea83d3440fabb20b4a45f7e1069e6eeed..5273c20abf9d496403e36af1b634af49dae8c630 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -231,10 +231,11 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { final FileDescriptor fd = openInternal(file, mode); if (fd == null) return null; - final FileDescriptor[] comm = createCommSocketPair(true); + final FileDescriptor[] comm = createCommSocketPair(); final ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd, comm[0]); // Kick off thread to watch for status updates + IoUtils.setBlocking(comm[1], true); final ListenerBridge bridge = new ListenerBridge(comm[1], handler.getLooper(), listener); bridge.start(); @@ -378,7 +379,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { */ public static ParcelFileDescriptor[] createReliablePipe() throws IOException { try { - final FileDescriptor[] comm = createCommSocketPair(false); + final FileDescriptor[] comm = createCommSocketPair(); final FileDescriptor[] fds = Libcore.os.pipe(); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fds[0], comm[0]), @@ -416,7 +417,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { */ public static ParcelFileDescriptor[] createReliableSocketPair() throws IOException { try { - final FileDescriptor[] comm = createCommSocketPair(false); + final FileDescriptor[] comm = createCommSocketPair(); final FileDescriptor fd0 = new FileDescriptor(); final FileDescriptor fd1 = new FileDescriptor(); Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); @@ -428,13 +429,13 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } } - private static FileDescriptor[] createCommSocketPair(boolean blocking) throws IOException { + private static FileDescriptor[] createCommSocketPair() throws IOException { try { final FileDescriptor comm1 = new FileDescriptor(); final FileDescriptor comm2 = new FileDescriptor(); Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2); - IoUtils.setBlocking(comm1, blocking); - IoUtils.setBlocking(comm2, blocking); + IoUtils.setBlocking(comm1, false); + IoUtils.setBlocking(comm2, false); return new FileDescriptor[] { comm1, comm2 }; } catch (ErrnoException e) { throw e.rethrowAsIOException(); @@ -670,34 +671,35 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } try { - try { - if (status != Status.SILENCE) { - final byte[] buf = getOrCreateStatusBuffer(); - int writePtr = 0; + if (status == Status.SILENCE) return; + + // Since we're about to close, read off any remote status. It's + // okay to remember missing here. + mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer()); - Memory.pokeInt(buf, writePtr, status, ByteOrder.BIG_ENDIAN); - writePtr += 4; + // Skip writing status when other end has already gone away. + if (mStatus != null) return; + + try { + final byte[] buf = getOrCreateStatusBuffer(); + int writePtr = 0; - if (msg != null) { - final byte[] rawMsg = msg.getBytes(); - final int len = Math.min(rawMsg.length, buf.length - writePtr); - System.arraycopy(rawMsg, 0, buf, writePtr, len); - writePtr += len; - } + Memory.pokeInt(buf, writePtr, status, ByteOrder.BIG_ENDIAN); + writePtr += 4; - Libcore.os.write(mCommFd, buf, 0, writePtr); + if (msg != null) { + final byte[] rawMsg = msg.getBytes(); + final int len = Math.min(rawMsg.length, buf.length - writePtr); + System.arraycopy(rawMsg, 0, buf, writePtr, len); + writePtr += len; } + + Libcore.os.write(mCommFd, buf, 0, writePtr); } catch (ErrnoException e) { // Reporting status is best-effort Log.w(TAG, "Failed to report status: " + e); } - if (status != Status.SILENCE) { - // Since we're about to close, read off any remote status. It's - // okay to remember missing here. - mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer()); - } - } finally { IoUtils.closeQuietly(mCommFd); mCommFd = null; diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index bb3d296acd7999451a5fe5cd55017d93bc9dea3c..3249bcb517433f6cf2320e2133260a2fd6637744 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -22,9 +22,12 @@ import android.util.Log; * Writes trace events to the system trace buffer. These trace events can be * collected and visualized using the Systrace tool. * - * This tracing mechanism is independent of the method tracing mechanism + *

    This tracing mechanism is independent of the method tracing mechanism * offered by {@link Debug#startMethodTracing}. In particular, it enables * tracing of events that occur across multiple processes. + *

    For information about using the Systrace tool, read Analyzing Display and Performance + * with Systrace. */ public final class Trace { /* diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index 177a9554191aa079029f7c9f3cb44c0bd1679c55..0285cb93211425fbbce843286eb3dd6c89d5a407 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -21,6 +21,9 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import com.android.internal.util.IndentingPrintWriter; + +import java.io.CharArrayWriter; import java.io.File; /** @@ -46,6 +49,10 @@ public class StorageVolume implements Parcelable { /** When set, indicates exclusive ownership of this volume */ private final UserHandle mOwner; + private String mUuid; + private String mUserLabel; + private String mState; + // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING, // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED, // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts. @@ -76,6 +83,9 @@ public class StorageVolume implements Parcelable { mAllowMassStorage = in.readInt() != 0; mMaxFileSize = in.readLong(); mOwner = in.readParcelable(null); + mUuid = in.readString(); + mUserLabel = in.readString(); + mState = in.readString(); } public static StorageVolume fromTemplate(StorageVolume template, File path, UserHandle owner) { @@ -189,6 +199,45 @@ public class StorageVolume implements Parcelable { return mOwner; } + public void setUuid(String uuid) { + mUuid = uuid; + } + + public String getUuid() { + return mUuid; + } + + /** + * Parse and return volume UUID as FAT volume ID, or return -1 if unable to + * parse or UUID is unknown. + */ + public int getFatVolumeId() { + if (mUuid == null || mUuid.length() != 9) { + return -1; + } + try { + return Integer.parseInt(mUuid.replace("-", ""), 16); + } catch (NumberFormatException e) { + return -1; + } + } + + public void setUserLabel(String userLabel) { + mUserLabel = userLabel; + } + + public String getUserLabel() { + return mUserLabel; + } + + public void setState(String state) { + mState = state; + } + + public String getState() { + return mState; + } + @Override public boolean equals(Object obj) { if (obj instanceof StorageVolume && mPath != null) { @@ -205,19 +254,28 @@ public class StorageVolume implements Parcelable { @Override public String toString() { - final StringBuilder builder = new StringBuilder("StorageVolume ["); - builder.append("mStorageId=").append(mStorageId); - builder.append(" mPath=").append(mPath); - builder.append(" mDescriptionId=").append(mDescriptionId); - builder.append(" mPrimary=").append(mPrimary); - builder.append(" mRemovable=").append(mRemovable); - builder.append(" mEmulated=").append(mEmulated); - builder.append(" mMtpReserveSpace=").append(mMtpReserveSpace); - builder.append(" mAllowMassStorage=").append(mAllowMassStorage); - builder.append(" mMaxFileSize=").append(mMaxFileSize); - builder.append(" mOwner=").append(mOwner); - builder.append("]"); - return builder.toString(); + final CharArrayWriter writer = new CharArrayWriter(); + dump(new IndentingPrintWriter(writer, " ", 80)); + return writer.toString(); + } + + public void dump(IndentingPrintWriter pw) { + pw.println("StorageVolume:"); + pw.increaseIndent(); + pw.printPair("mStorageId", mStorageId); + pw.printPair("mPath", mPath); + pw.printPair("mDescriptionId", mDescriptionId); + pw.printPair("mPrimary", mPrimary); + pw.printPair("mRemovable", mRemovable); + pw.printPair("mEmulated", mEmulated); + pw.printPair("mMtpReserveSpace", mMtpReserveSpace); + pw.printPair("mAllowMassStorage", mAllowMassStorage); + pw.printPair("mMaxFileSize", mMaxFileSize); + pw.printPair("mOwner", mOwner); + pw.printPair("mUuid", mUuid); + pw.printPair("mUserLabel", mUserLabel); + pw.printPair("mState", mState); + pw.decreaseIndent(); } public static final Creator CREATOR = new Creator() { @@ -249,5 +307,8 @@ public class StorageVolume implements Parcelable { parcel.writeInt(mAllowMassStorage ? 1 : 0); parcel.writeLong(mMaxFileSize); parcel.writeParcelable(mOwner, flags); + parcel.writeString(mUuid); + parcel.writeString(mUserLabel); + parcel.writeString(mState); } } diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index 37a810236832141201fdeaf9fa93db02c34e7b00..f7d1eb72f080203f8fa0aabd3c853af5b34a5723 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -1069,11 +1069,11 @@ public class Preference implements Comparable { * @return 0 if the same; less than 0 if this Preference sorts ahead of another; * greater than 0 if this Preference sorts after another. */ + @Override public int compareTo(Preference another) { - if (mOrder != DEFAULT_ORDER - || (mOrder == DEFAULT_ORDER && another.mOrder != DEFAULT_ORDER)) { + if (mOrder != another.mOrder) { // Do order comparison - return mOrder - another.mOrder; + return mOrder - another.mOrder; } else if (mTitle == another.mTitle) { // If titles are null or share same object comparison return 0; diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 2ab5a91a7013e9719fd366a596494567329db858..7ddfa874155bf7a90dfa8ff4ff73c2bd8d4b671c 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -33,7 +33,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.AttributeSet; -import android.util.Log; import android.util.TypedValue; import android.util.Xml; import android.view.LayoutInflater; @@ -125,8 +124,6 @@ public abstract class PreferenceActivity extends ListActivity implements PreferenceManager.OnPreferenceTreeClickListener, PreferenceFragment.OnPreferenceStartFragmentCallback { - private static final String TAG = "PreferenceActivity"; - // Constants for state save/restore private static final String HEADERS_TAG = ":android:headers"; private static final String CUR_HEADER_TAG = ":android:cur_header"; @@ -524,7 +521,9 @@ public abstract class PreferenceActivity extends ListActivity implements int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0); int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0); - if (savedInstanceState != null) { + // Restore from headers only if they are supported which + // is in multi-pane mode. + if (savedInstanceState != null && !mSinglePane) { // We are restarting from a previous saved state; used that to // initialize, instead of starting fresh. ArrayList

    headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG); diff --git a/core/java/android/print/IPrintDocumentAdapter.aidl b/core/java/android/print/IPrintDocumentAdapter.aidl index b12c922cca6e3370aaf6e8d2353d6a3832d9dd90..2b95c1244f795eb14f7dd641cbb0d69863999de3 100644 --- a/core/java/android/print/IPrintDocumentAdapter.aidl +++ b/core/java/android/print/IPrintDocumentAdapter.aidl @@ -19,6 +19,7 @@ package android.print; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.print.ILayoutResultCallback; +import android.print.IPrintDocumentAdapterObserver; import android.print.IWriteResultCallback; import android.print.PageRange; import android.print.PrintAttributes; @@ -29,10 +30,12 @@ import android.print.PrintAttributes; * @hide */ oneway interface IPrintDocumentAdapter { + void setObserver(in IPrintDocumentAdapterObserver observer); void start(); void layout(in PrintAttributes oldAttributes, in PrintAttributes newAttributes, ILayoutResultCallback callback, in Bundle metadata, int sequence); void write(in PageRange[] pages, in ParcelFileDescriptor fd, IWriteResultCallback callback, int sequence); void finish(); + void cancel(); } diff --git a/core/java/android/print/IPrintDocumentAdapterObserver.aidl b/core/java/android/print/IPrintDocumentAdapterObserver.aidl new file mode 100644 index 0000000000000000000000000000000000000000..4443df09ae385806176a56244e049806814ab446 --- /dev/null +++ b/core/java/android/print/IPrintDocumentAdapterObserver.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.print; + +/** + * Interface for observing the state of a print document adapter. + * + * @hide + */ +oneway interface IPrintDocumentAdapterObserver { + void onDestroy(); +} diff --git a/core/java/android/print/PageRange.java b/core/java/android/print/PageRange.java index cdcd0c7d2e6ab27a44a619fa7a6228fbf8808043..d6320f0f849d66e51767318771b7c0dc75e9f905 100644 --- a/core/java/android/print/PageRange.java +++ b/core/java/android/print/PageRange.java @@ -39,9 +39,8 @@ public final class PageRange implements Parcelable { * @param start The start page index (zero based and inclusive). * @param end The end page index (zero based and inclusive). * - * @throws IllegalArgumentException If start is less than zero. - * @throws IllegalArgumentException If end is less than zero. - * @throws IllegalArgumentException If start greater than end. + * @throws IllegalArgumentException If start is less than zero or end + * is less than zero or start greater than end. */ public PageRange(int start, int end) { if (start < 0) { diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java index e1a9cb7592e5111c00ab624613190007a481d8b8..c6254e06bbb8f208f2ce14937043857526e828ac 100644 --- a/core/java/android/print/PrintAttributes.java +++ b/core/java/android/print/PrintAttributes.java @@ -30,7 +30,11 @@ import com.android.internal.R; import java.util.Map; /** - * This class represents the attributes of a print job. + * This class represents the attributes of a print job. These attributes + * describe how the printed content should be laid out. For example, the + * print attributes may state that the content should be laid out on a + * letter size with 300 DPI (dots per inch) resolution, have a margin of + * 10 mills (thousand of an inch) on all sides, and be black and white. */ public final class PrintAttributes implements Parcelable { /** Color mode: Monochrome color scheme, for example one color is used. */ @@ -277,7 +281,7 @@ public final class PrintAttributes implements Parcelable { * Unknown media size in portrait mode. *

    * Note: This is for specifying orientation without media - * size. You should not use the dimensions reported by this class. + * size. You should not use the dimensions reported by this instance. *

    */ public static final MediaSize UNKNOWN_PORTRAIT = @@ -288,7 +292,7 @@ public final class PrintAttributes implements Parcelable { * Unknown media size in landscape mode. *

    * Note: This is for specifying orientation without media - * size. You should not use the dimensions reported by this class. + * size. You should not use the dimensions reported by this instance. *

    */ public static final MediaSize UNKNOWN_LANDSCAPE = @@ -615,9 +619,7 @@ public final class PrintAttributes implements Parcelable { private final int mHeightMils; /** - * Creates a new instance. This is the preferred constructor since - * it enables the media size label to be shown in a localized fashion - * on a locale change. + * Creates a new instance. * * @param id The unique media size id. * @param packageName The name of the creating package. @@ -625,10 +627,9 @@ public final class PrintAttributes implements Parcelable { * @param widthMils The width in mils (thousands of an inch). * @param heightMils The height in mils (thousands of an inch). * - * @throws IllegalArgumentException If the id is empty. - * @throws IllegalArgumentException If the label is empty. - * @throws IllegalArgumentException If the widthMils is less than or equal to zero. - * @throws IllegalArgumentException If the heightMils is less than or equal to zero. + * @throws IllegalArgumentException If the id is empty or the label + * is empty or the widthMils is less than or equal to zero or the + * heightMils is less than or equal to zero. * * @hide */ @@ -667,14 +668,13 @@ public final class PrintAttributes implements Parcelable { * * @param id The unique media size id. It is unique amongst other media sizes * supported by the printer. - * @param label The internationalized human readable label. + * @param label The localized human readable label. * @param widthMils The width in mils (thousands of an inch). * @param heightMils The height in mils (thousands of an inch). * - * @throws IllegalArgumentException If the id is empty. - * @throws IllegalArgumentException If the label is empty. - * @throws IllegalArgumentException If the widthMils is less than or equal to zero. - * @throws IllegalArgumentException If the heightMils is less than or equal to zero. + * @throws IllegalArgumentException If the id is empty or the label is empty + * or the widthMils is less than or equal to zero or the heightMils is less + * than or equal to zero. */ public MediaSize(String id, String label, int widthMils, int heightMils) { if (TextUtils.isEmpty(id)) { @@ -776,12 +776,16 @@ public final class PrintAttributes implements Parcelable { } /** - * Returns a new media size in a portrait orientation + * Returns a new media size instance in a portrait orientation, * which is the height is the greater dimension. * - * @return New instance in landscape orientation. + * @return New instance in landscape orientation if this one + * is in landscape, otherwise this instance. */ public MediaSize asPortrait() { + if (isPortrait()) { + return this; + } return new MediaSize(mId, mLabel, mPackageName, Math.min(mWidthMils, mHeightMils), Math.max(mWidthMils, mHeightMils), @@ -789,12 +793,16 @@ public final class PrintAttributes implements Parcelable { } /** - * Returns a new media size in a landscape orientation + * Returns a new media size instance in a landscape orientation, * which is the height is the lesser dimension. * - * @return New instance in landscape orientation. + * @return New instance in landscape orientation if this one + * is in portrait, otherwise this instance. */ public MediaSize asLandscape() { + if (!isPortrait()) { + return this; + } return new MediaSize(mId, mLabel, mPackageName, Math.max(mWidthMils, mHeightMils), Math.min(mWidthMils, mHeightMils), @@ -881,8 +889,8 @@ public final class PrintAttributes implements Parcelable { * This class specifies a supported resolution in DPI (dots per inch). * Resolution defines how many points with different color can be placed * on one inch in horizontal or vertical direction of the target media. - * For example, a printer with 600DIP can produce higher quality images - * the one with 300DPI resolution. + * For example, a printer with 600 DPI can produce higher quality images + * the one with 300 DPI resolution. */ public static final class Resolution { private final String mId; @@ -895,14 +903,13 @@ public final class PrintAttributes implements Parcelable { * * @param id The unique resolution id. It is unique amongst other resolutions * supported by the printer. - * @param label The internationalized human readable label. + * @param label The localized human readable label. * @param horizontalDpi The horizontal resolution in DPI (dots per inch). * @param verticalDpi The vertical resolution in DPI (dots per inch). * - * @throws IllegalArgumentException If the id is empty. - * @throws IllegalArgumentException If the label is empty. - * @throws IllegalArgumentException If the horizontalDpi is less than or equal to zero. - * @throws IllegalArgumentException If the verticalDpi is less than or equal to zero. + * @throws IllegalArgumentException If the id is empty or the label is empty + * or the horizontalDpi is less than or equal to zero or the verticalDpi is + * less than or equal to zero. */ public Resolution(String id, String label, int horizontalDpi, int verticalDpi) { if (TextUtils.isEmpty(id)) { diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java index 4113ac737c1a43aa3349fd89379618b5b6a36cd2..1f59bef853a3047a529e652a842e43834a45d04e 100644 --- a/core/java/android/print/PrintDocumentAdapter.java +++ b/core/java/android/print/PrintDocumentAdapter.java @@ -38,15 +38,46 @@ import android.os.ParcelFileDescriptor; * *
  • * After every call to {@link #onLayout(PrintAttributes, PrintAttributes, - * CancellationSignal, LayoutResultCallback, Bundle)}, you may get a call to - * {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, WriteResultCallback)} - * asking you to write a PDF file with the content for specific pages. + * CancellationSignal, LayoutResultCallback, Bundle)}, you may get + * a call to {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, + * WriteResultCallback)} asking you to write a PDF file with the content for + * specific pages. *
  • *
  • * Finally, you will receive a call to {@link #onFinish()}. You can use this * callback to release resources allocated in {@link #onStart()}. *
  • * + *

    + * The {@link #onStart()} callback is always the first call you will receive and + * is useful for doing one time setup or resource allocation before printing. You + * will not receive a subsequent call here. + *

    + *

    + * The {@link #onLayout(PrintAttributes, PrintAttributes, CancellationSignal, + * LayoutResultCallback, Bundle)} callback requires that you layout the content + * based on the current {@link PrintAttributes}. The execution of this method is + * not considered completed until you invoke one of the methods on the passed in + * callback instance. Hence, you will not receive a subsequent call to any other + * method of this class until the execution of this method is complete by invoking + * one of the callback methods. + *

    + *

    + * The {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, + * WriteResultCallback)} requires that you render and write the content of some + * pages to the provided destination. The execution of this method is not + * considered complete until you invoke one of the methods on the passed in + * callback instance. Hence, you will not receive a subsequent call to any other + * method of this class until the execution of this method is complete by invoking + * one of the callback methods. You will never receive a sequence of one or more + * calls to this method without a previous call to {@link #onLayout(PrintAttributes, + * PrintAttributes, CancellationSignal, LayoutResultCallback, Bundle)}. + *

    + *

    + * The {@link #onFinish()} callback is always the last call you will receive and + * is useful for doing one time cleanup or resource deallocation after printing. + * You will not receive a subsequent call here. + *

    *

    *

    Implementation

    *

    @@ -54,7 +85,11 @@ import android.os.ParcelFileDescriptor; * of the work on an arbitrary thread. For example, if the printed content * does not depend on the UI state, i.e. on what is shown on the screen, then * you can offload the entire work on a dedicated thread, thus making your - * application interactive while the print work is being performed. + * application interactive while the print work is being performed. Note that + * while your activity is covered by the system print UI and a user cannot + * interact with it, doing the printing work on the main application thread + * may affect the performance of your other application components as they + * are also executed on that thread. *

    *

    * You can also do work on different threads, for example if you print UI @@ -64,7 +99,7 @@ import android.os.ParcelFileDescriptor; * This will ensure that the UI does not change while you are laying out the * printed content. Then you can handle {@link #onWrite(PageRange[], ParcelFileDescriptor, * CancellationSignal, WriteResultCallback)} and {@link #onFinish()} on another - * thread. This will ensure that the UI is frozen for the minimal amount of + * thread. This will ensure that the main thread is busy for a minimal amount of * time. Also this assumes that you will generate the printed content in * {@link #onLayout(PrintAttributes, PrintAttributes, CancellationSignal, * LayoutResultCallback, Bundle)} which is not mandatory. If you use multiple @@ -76,6 +111,12 @@ public abstract class PrintDocumentAdapter { /** * Extra: mapped to a boolean value that is true if * the current layout is for a print preview, false otherwise. + * This extra is provided in the {@link Bundle} argument of the {@link + * #onLayout(PrintAttributes, PrintAttributes, CancellationSignal, + * LayoutResultCallback, Bundle)} callback. + * + * @see #onLayout(PrintAttributes, PrintAttributes, CancellationSignal, + * LayoutResultCallback, Bundle) */ public static final String EXTRA_PRINT_PREVIEW = "EXTRA_PRINT_PREVIEW"; @@ -95,17 +136,41 @@ public abstract class PrintDocumentAdapter { * After you are done laying out, you must invoke: {@link * LayoutResultCallback#onLayoutFinished(PrintDocumentInfo, boolean)} with * the last argument true or false depending on - * whether the layout changed the content or not, respectively; and {@link - * LayoutResultCallback#onLayoutFailed(CharSequence)}, if an error occurred. - * Note that you must call one of the methods of the given callback. + * whether the layout changed the content or not, respectively; or {@link + * LayoutResultCallback#onLayoutFailed(CharSequence)}, if an error occurred; + * or {@link LayoutResultCallback#onLayoutCancelled()} if layout was + * cancelled in a response to a cancellation request via the passed in + * {@link CancellationSignal}. Note that you must call one of + * the methods of the given callback for this method to be considered complete + * which is you will not receive any calls to this adapter until the current + * layout operation is complete by invoking a method on the callback instance. + * The callback methods can be invoked from an arbitrary thread. + *

    + *

    + * One of the arguments passed to this method is a {@link CancellationSignal} + * which is used to propagate requests from the system to your application for + * canceling the current layout operation. For example, a cancellation may be + * requested if the user changes a print option that may affect layout while + * you are performing a layout operation. In such a case the system will make + * an attempt to cancel the current layout as another one will have to be performed. + * Typically, you should register a cancellation callback in the cancellation + * signal. The cancellation callback will not be made on the + * main thread and can be registered as follows: *

    + *
    +     * cancellationSignal.setOnCancelListener(new OnCancelListener() {
    +     *     @Override
    +     *     public void onCancel() {
    +     *         // Cancel layout
    +     *     }
    +     * });
    +     * 
    *

    * Note: If the content is large and a layout will be * performed, it is a good practice to schedule the work on a dedicated * thread and register an observer in the provided {@link * CancellationSignal} upon invocation of which you should stop the - * layout. The cancellation callback will not be made on the main - * thread. + * layout. *

    * * @param oldAttributes The old print attributes. @@ -128,17 +193,41 @@ public abstract class PrintDocumentAdapter { * on the main thread. *

    * After you are done writing, you should close the file descriptor and - * invoke {@link WriteResultCallback #onWriteFinished(PageRange[]), if writing + * invoke {@link WriteResultCallback#onWriteFinished(PageRange[])}, if writing * completed successfully; or {@link WriteResultCallback#onWriteFailed( - * CharSequence)}, if an error occurred. Note that you must call one of - * the methods of the given callback. + * CharSequence)}, if an error occurred; or {@link WriteResultCallback#onWriteCancelled()}, + * if writing was cancelled in a response to a cancellation request via the passed + * in {@link CancellationSignal}. Note that you must call one of + * the methods of the given callback for this method to be considered complete which + * is you will not receive any calls to this adapter until the current write + * operation is complete by invoking a method on the callback instance. The callback + * methods can be invoked from an arbitrary thread. + *

    + *

    + * One of the arguments passed to this method is a {@link CancellationSignal} + * which is used to propagate requests from the system to your application for + * canceling the current write operation. For example, a cancellation may be + * requested if the user changes a print option that may affect layout while + * you are performing a write operation. In such a case the system will make + * an attempt to cancel the current write as a layout will have to be performed + * which then may be followed by a write. Typically, you should register a + * cancellation callback in the cancellation signal. The cancellation callback + * will not be made on the main thread and can be registered + * as follows: *

    + *
    +     * cancellationSignal.setOnCancelListener(new OnCancelListener() {
    +     *     @Override
    +     *     public void onCancel() {
    +     *         // Cancel write
    +     *     }
    +     * });
    +     * 
    *

    * Note: If the printed content is large, it is a good * practice to schedule writing it on a dedicated thread and register an * observer in the provided {@link CancellationSignal} upon invocation of - * which you should stop writing. The cancellation callback will not be - * made on the main thread. + * which you should stop writing. *

    * * @param pages The pages whose content to print - non-overlapping in ascending order. @@ -178,7 +267,8 @@ public abstract class PrintDocumentAdapter { /** * Notifies that all the data was written. * - * @param pages The pages that were written. Cannot be null or empty. + * @param pages The pages that were written. Cannot be null + * or empty. */ public void onWriteFinished(PageRange[] pages) { /* do nothing - stub */ @@ -187,7 +277,8 @@ public abstract class PrintDocumentAdapter { /** * Notifies that an error occurred while writing the data. * - * @param error Error message. May be null if error is unknown. + * @param error The localized error message. + * shown to the user. May be null if error is unknown. */ public void onWriteFailed(CharSequence error) { /* do nothing - stub */ @@ -218,7 +309,7 @@ public abstract class PrintDocumentAdapter { /** * Notifies that the layout finished and whether the content changed. * - * @param info An info object describing the document. Cannot be null. + * @param info An info object describing the document. Cannot be null. * @param changed Whether the layout changed. * * @see PrintDocumentInfo @@ -230,7 +321,8 @@ public abstract class PrintDocumentAdapter { /** * Notifies that an error occurred while laying out the document. * - * @param error Error message. May be null if error is unknown. + * @param error The localized error message. + * shown to the user. May be null if error is unknown. */ public void onLayoutFailed(CharSequence error) { /* do nothing - stub */ diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java index b721ef4e0dfeff4994209afce71195fb5a4b363f..928be6cae90ad7f805e6cfced7c9559da369a444 100644 --- a/core/java/android/print/PrintDocumentInfo.java +++ b/core/java/android/print/PrintDocumentInfo.java @@ -21,12 +21,56 @@ import android.os.Parcelable; import android.text.TextUtils; /** - * This class encapsulates information about a printed document. + * This class encapsulates information about a document for printing + * purposes. This meta-data is used by the platform and print services, + * components that interact with printers. For example, this class + * contains the number of pages contained in the document it describes and + * this number of pages is shown to the user allowing him/her to select + * the range to print. Also a print service may optimize the printing + * process based on the content type, such as document or photo. + *

    + * Instances of this class are created by the printing application and + * passed to the {@link PrintDocumentAdapter.LayoutResultCallback#onLayoutFinished( + * PrintDocumentInfo, boolean) PrintDocumentAdapter.LayoutResultCallback.onLayoutFinished( + * PrintDocumentInfo, boolean)} callback after successfully laying out the + * content which is performed in {@link PrintDocumentAdapter#onLayout(PrintAttributes, + * PrintAttributes, android.os.CancellationSignal, PrintDocumentAdapter.LayoutResultCallback, + * android.os.Bundle) PrintDocumentAdapter.onLayout(PrintAttributes, + * PrintAttributes, android.os.CancellationSignal, + * PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle)}. + *

    + *

    + * An example usage looks like this: + *

    + *
    + * . . .
    + *
    + * public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
    + *         CancellationSignal cancellationSignal, LayoutResultCallback callback,
    + *         Bundle metadata) {
    + *
    + *        // Assume the app defined a LayoutResult class which contains
    + *        // the layout result data and that the content is a document.
    + *        LayoutResult result = doSomeLayoutWork();
    + *
    + *        PrintDocumentInfo info = new PrintDocumentInfo
    + *                .Builder("printed_file.pdf")
    + *                .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
    + *                .setPageCount(result.getPageCount())
    + *                .build();
    + *
    + *       callback.onLayoutFinished(info, result.getContentChanged());
    + *   }
    + *
    + *   . . .
    + *
    + * 
    + *

    */ public final class PrintDocumentInfo implements Parcelable { /** - * Constant for unknown page count.. + * Constant for unknown page count. */ public static final int PAGE_COUNT_UNKNOWN = -1; @@ -37,11 +81,23 @@ public final class PrintDocumentInfo implements Parcelable { /** * Content type: document. + *

    + * A print service may use normal paper to print the content instead + * of dedicated photo paper. Also it may use a lower quality printing + * process as the content is not as sensitive to print quality variation + * as a photo is. + *

    */ public static final int CONTENT_TYPE_DOCUMENT = 0; /** * Content type: photo. + *

    + * A print service may use dedicated photo paper to print the content + * instead of normal paper. Also it may use a higher quality printing + * process as the content is more sensitive to print quality variation + * than a document. + *

    */ public static final int CONTENT_TYPE_PHOTO = 1; @@ -82,7 +138,8 @@ public final class PrintDocumentInfo implements Parcelable { } /** - * Gets the document name. + * Gets the document name. This name may be shown to + * the user. * * @return The document name. */ @@ -213,20 +270,23 @@ public final class PrintDocumentInfo implements Parcelable { } /** - * Builder for creating an {@link PrintDocumentInfo}. + * Builder for creating a {@link PrintDocumentInfo}. */ public static final class Builder { private final PrintDocumentInfo mPrototype; /** * Constructor. + * *

    - * The values of the relevant properties are initialized with default - * values. Please refer to the documentation of the individual setters - * for information about the default values. + * The values of the relevant properties are initialized with defaults. + * Please refer to the documentation of the individual setters for + * information about the default values. *

    * - * @param name The document name. Cannot be empty. + * @param name The document name which may be shown to the user and + * is the file name if the content it describes is saved as a PDF. + * Cannot be empty. */ public Builder(String name) { if (TextUtils.isEmpty(name)) { diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java index 535ae43354fa07809e41b424962e11b1d5c3dac9..0abe2193249ee8bafefc15a03b98bf581ba4974e 100644 --- a/core/java/android/print/PrintJob.java +++ b/core/java/android/print/PrintJob.java @@ -17,8 +17,13 @@ package android.print; /** - * This class represents a print job from the perspective of - * an application. + * This class represents a print job from the perspective of an + * application. It contains behavior methods for performing operations + * on it as well as methods for querying its state. A snapshot of the + * print job state is represented by the {@link PrintJobInfo} class. + * The state of a print job may change over time. An application receives + * instances of this class when creating a print job or querying for + * its print jobs. */ public final class PrintJob { @@ -145,11 +150,12 @@ public final class PrintJob { /** * Gets whether this print job is failed. Such a print job is * not successfully printed due to an error. You can request - * a restart via {@link #restart()}. + * a restart via {@link #restart()} or cancel via {@link #cancel()}. * * @return Whether the print job is failed. * * @see #restart() + * @see #cancel() */ public boolean isFailed() { return getInfo().getState() == PrintJobInfo.STATE_FAILED; diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java index c6f0a6848f91a7514fcf97c5854db42530fc526c..63f94fe642fb2ccd3d8455fb40c9ad82eb31c2c9 100644 --- a/core/java/android/print/PrintJobInfo.java +++ b/core/java/android/print/PrintJobInfo.java @@ -16,13 +16,17 @@ package android.print; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import java.util.Arrays; /** - * This class represents the description of a print job. + * This class represents the description of a print job. The print job + * state includes properties such as its id, print attributes used for + * generating the content, and so on. Note that the print jobs state may + * change over time and this class represents a snapshot of this state. */ public final class PrintJobInfo implements Parcelable { @@ -93,7 +97,7 @@ public final class PrintJobInfo implements Parcelable { public static final int STATE_BLOCKED = 4; /** - * Print job state: The print job was successfully printed. + * Print job state: The print job is successfully printed. * This is a terminal state. *

    * Next valid states: None @@ -103,15 +107,14 @@ public final class PrintJobInfo implements Parcelable { /** * Print job state: The print job was printing but printing failed. - * This is a terminal state. *

    - * Next valid states: None + * Next valid states: {@link #STATE_CANCELED}, {@link #STATE_STARTED} *

    */ public static final int STATE_FAILED = 6; /** - * Print job state: The print job was canceled. + * Print job state: The print job is canceled. * This is a terminal state. *

    * Next valid states: None @@ -158,6 +161,9 @@ public final class PrintJobInfo implements Parcelable { /** Information about the printed document. */ private PrintDocumentInfo mDocumentInfo; + /** Advanced printer specific options. */ + private Bundle mAdvancedOptions; + /** Whether we are trying to cancel this print job. */ private boolean mCanceling; @@ -182,6 +188,7 @@ public final class PrintJobInfo implements Parcelable { mAttributes = other.mAttributes; mDocumentInfo = other.mDocumentInfo; mCanceling = other.mCanceling; + mAdvancedOptions = other.mAdvancedOptions; } private PrintJobInfo(Parcel parcel) { @@ -195,20 +202,17 @@ public final class PrintJobInfo implements Parcelable { mCreationTime = parcel.readLong(); mCopies = parcel.readInt(); mStateReason = parcel.readString(); - if (parcel.readInt() == 1) { - Parcelable[] parcelables = parcel.readParcelableArray(null); + Parcelable[] parcelables = parcel.readParcelableArray(null); + if (parcelables != null) { mPageRanges = new PageRange[parcelables.length]; for (int i = 0; i < parcelables.length; i++) { mPageRanges[i] = (PageRange) parcelables[i]; } } - if (parcel.readInt() == 1) { - mAttributes = PrintAttributes.CREATOR.createFromParcel(parcel); - } - if (parcel.readInt() == 1) { - mDocumentInfo = PrintDocumentInfo.CREATOR.createFromParcel(parcel); - } + mAttributes = (PrintAttributes) parcel.readParcelable(null); + mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null); mCanceling = (parcel.readInt() == 1); + mAdvancedOptions = parcel.readBundle(); } /** @@ -297,6 +301,14 @@ public final class PrintJobInfo implements Parcelable { * Gets the current job state. * * @return The job state. + * + * @see #STATE_CREATED + * @see #STATE_QUEUED + * @see #STATE_STARTED + * @see #STATE_COMPLETED + * @see #STATE_BLOCKED + * @see #STATE_FAILED + * @see #STATE_CANCELED */ public int getState() { return mState; @@ -511,6 +523,71 @@ public final class PrintJobInfo implements Parcelable { mCanceling = cancelling; } + /** + * Gets whether this job has a given advanced (printer specific) print + * option. + * + * @param key The option key. + * @return Whether the option is present. + * + * @hide + */ + public boolean hasAdvancedOption(String key) { + return mAdvancedOptions != null && mAdvancedOptions.containsKey(key); + } + + /** + * Gets the value of an advanced (printer specific) print option. + * + * @param key The option key. + * @return The option value. + * + * @hide + */ + public String getAdvancedStringOption(String key) { + if (mAdvancedOptions != null) { + return mAdvancedOptions.getString(key); + } + return null; + } + + /** + * Gets the value of an advanced (printer specific) print option. + * + * @param key The option key. + * @return The option value. + * + * @hide + */ + public int getAdvancedIntOption(String key) { + if (mAdvancedOptions != null) { + return mAdvancedOptions.getInt(key); + } + return 0; + } + + /** + * Gets the advanced options. + * + * @return The advanced options. + * + * @hide + */ + public Bundle getAdvancedOptions() { + return mAdvancedOptions; + } + + /** + * Sets the advanced options. + * + * @param options The advanced options. + * + * @hide + */ + public void setAdvancedOptions(Bundle options) { + mAdvancedOptions = options; + } + @Override public int describeContents() { return 0; @@ -528,25 +605,11 @@ public final class PrintJobInfo implements Parcelable { parcel.writeLong(mCreationTime); parcel.writeInt(mCopies); parcel.writeString(mStateReason); - if (mPageRanges != null) { - parcel.writeInt(1); - parcel.writeParcelableArray(mPageRanges, flags); - } else { - parcel.writeInt(0); - } - if (mAttributes != null) { - parcel.writeInt(1); - mAttributes.writeToParcel(parcel, flags); - } else { - parcel.writeInt(0); - } - if (mDocumentInfo != null) { - parcel.writeInt(1); - mDocumentInfo.writeToParcel(parcel, flags); - } else { - parcel.writeInt(0); - } + parcel.writeParcelableArray(mPageRanges, flags); + parcel.writeParcelable(mAttributes, flags); + parcel.writeParcelable(mDocumentInfo, 0); parcel.writeInt(mCanceling ? 1 : 0); + parcel.writeBundle(mAdvancedOptions); } @Override @@ -567,6 +630,7 @@ public final class PrintJobInfo implements Parcelable { builder.append(", cancelling: " + mCanceling); builder.append(", pages: " + (mPageRanges != null ? Arrays.toString(mPageRanges) : null)); + builder.append(", hasAdvancedOptions: " + (mAdvancedOptions != null)); builder.append("}"); return builder.toString(); } @@ -611,7 +675,7 @@ public final class PrintJobInfo implements Parcelable { * Constructor. * * @param prototype Prototype to use as a starting point. - * Can be null. + * Can be null. */ public Builder(PrintJobInfo prototype) { mPrototype = (prototype != null) @@ -653,7 +717,10 @@ public final class PrintJobInfo implements Parcelable { * @param value The option value. */ public void putAdvancedOption(String key, String value) { - + if (mPrototype.mAdvancedOptions == null) { + mPrototype.mAdvancedOptions = new Bundle(); + } + mPrototype.mAdvancedOptions.putString(key, value); } /** @@ -663,7 +730,10 @@ public final class PrintJobInfo implements Parcelable { * @param value The option value. */ public void putAdvancedOption(String key, int value) { - + if (mPrototype.mAdvancedOptions == null) { + mPrototype.mAdvancedOptions = new Bundle(); + } + mPrototype.mAdvancedOptions.putInt(key, value); } /** diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index dbd8278d3a1fbee47410826d32fc8a6a30878a78..d1bb8fd20a4c9cf613feee11b5763427fb2c6152 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -16,6 +16,8 @@ package android.print; +import android.app.Activity; +import android.app.Application.ActivityLifecycleCallbacks; import android.content.Context; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; @@ -53,6 +55,49 @@ import java.util.Map; * PrintManager printManager = * (PrintManager) context.getSystemService(Context.PRINT_SERVICE); * + * + *

    Print mechanics

    + *

    + * The key idea behind printing on the platform is that the content to be printed + * should be laid out for the currently selected print options resulting in an + * optimized output and higher user satisfaction. To achieve this goal the platform + * declares a contract that the printing application has to follow which is defined + * by the {@link PrintDocumentAdapter} class. At a higher level the contract is that + * when the user selects some options from the print UI that may affect the way + * content is laid out, for example page size, the application receives a callback + * allowing it to layout the content to better fit these new constraints. After a + * layout pass the system may ask the application to render one or more pages one + * or more times. For example, an application may produce a single column list for + * smaller page sizes and a multi-column table for larger page sizes. + *

    + *

    Print jobs

    + *

    + * Print jobs are started by calling the {@link #print(String, PrintDocumentAdapter, + * PrintAttributes)} from an activity which results in bringing up the system print + * UI. Once the print UI is up, when the user changes a selected print option that + * affects the way content is laid out the system starts to interact with the + * application following the mechanics described the section above. + *

    + *

    + * Print jobs can be in {@link PrintJobInfo#STATE_CREATED created}, {@link + * PrintJobInfo#STATE_QUEUED queued}, {@link PrintJobInfo#STATE_STARTED started}, + * {@link PrintJobInfo#STATE_BLOCKED blocked}, {@link PrintJobInfo#STATE_COMPLETED + * completed}, {@link PrintJobInfo#STATE_FAILED failed}, and {@link + * PrintJobInfo#STATE_CANCELED canceled} state. Print jobs are stored in dedicated + * system spooler until they are handled which is they are cancelled or completed. + * Active print jobs, ones that are not cancelled or completed, are considered failed + * if the device reboots as the new boot may be after a very long time. The user may + * choose to restart such print jobs. Once a print job is queued all relevant content + * is stored in the system spooler and its lifecycle becomes detached from this of + * the application that created it. + *

    + *

    + * An applications can query the print spooler for current print jobs it created + * but not print jobs created by other applications. + *

    + * + * @see PrintJob + * @see PrintJobInfo */ public final class PrintManager { @@ -290,20 +335,54 @@ public final class PrintManager { /** * Creates a print job for printing a {@link PrintDocumentAdapter} with * default print attributes. - * - * @param printJobName A name for the new print job. + *

    + * Calling this method brings the print UI allowing the user to customize + * the print job and returns a {@link PrintJob} object without waiting for the + * user to customize or confirm the print job. The returned print job instance + * is in a {@link PrintJobInfo#STATE_CREATED created} state. + *

    + * This method can be called only from an {@link Activity}. The rationale is that + * printing from a service will create an inconsistent user experience as the print + * UI would appear without any context. + *

    + *

    + * Also the passed in {@link PrintDocumentAdapter} will be considered invalid if + * your activity is finished. The rationale is that once the activity that + * initiated printing is finished, the provided adapter may be in an inconsistent + * state as it may depend on the UI presented by the activity. + *

    + *

    + * The default print attributes are a hint to the system how the data is to + * be printed. For example, a photo editor may look at the photo aspect ratio + * to determine the default orientation and provide a hint whether the printing + * should be in portrait or landscape. The system will do a best effort to + * selected the hinted options in the print dialog, given the current printer + * supports them. + *

    + * + * @param printJobName A name for the new print job which is shown to the user. * @param documentAdapter An adapter that emits the document to print. - * @param attributes The default print job attributes. + * @param attributes The default print job attributes or null. * @return The created print job on success or null on failure. + * @throws IllegalStateException If not called from an {@link Activity}. + * @throws IllegalArgumentException If the print job name is empty or the + * document adapter is null. + * * @see PrintJob */ public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, PrintAttributes attributes) { + if (!(mContext instanceof Activity)) { + throw new IllegalStateException("Can print only from an activity"); + } if (TextUtils.isEmpty(printJobName)) { - throw new IllegalArgumentException("priintJobName cannot be empty"); + throw new IllegalArgumentException("printJobName cannot be empty"); + } + if (documentAdapter == null) { + throw new IllegalArgumentException("documentAdapter cannot be null"); } - PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter, - mContext.getMainLooper()); + PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate( + (Activity) mContext, documentAdapter); try { Bundle result = mService.print(printJobName, delegate, attributes, mContext.getPackageName(), mAppId, mUserId); @@ -369,17 +448,21 @@ public final class PrintManager { return new PrinterDiscoverySession(mService, mContext, mUserId); } - private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub { + private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub + implements ActivityLifecycleCallbacks { private final Object mLock = new Object(); private CancellationSignal mLayoutOrWriteCancellation; - private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - - // cleared in finish() + private Activity mActivity; // Strong reference OK - cleared in finish() + + private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish private Handler mHandler; // Strong reference OK - cleared in finish() + private IPrintDocumentAdapterObserver mObserver; // Strong reference OK - cleared in finish + private LayoutSpec mLastLayoutSpec; private WriteSpec mLastWriteSpec; @@ -390,16 +473,39 @@ public final class PrintManager { private boolean mFinishRequested; private boolean mFinished; - public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) { + private boolean mDestroyed; + + public PrintDocumentAdapterDelegate(Activity activity, + PrintDocumentAdapter documentAdapter) { + mActivity = activity; mDocumentAdapter = documentAdapter; - mHandler = new MyHandler(looper); + mHandler = new MyHandler(mActivity.getMainLooper()); + mActivity.getApplication().registerActivityLifecycleCallbacks(this); + } + + @Override + public void setObserver(IPrintDocumentAdapterObserver observer) { + final boolean destroyed; + synchronized (mLock) { + if (!mDestroyed) { + mObserver = observer; + } + destroyed = mDestroyed; + } + if (destroyed) { + try { + observer.onDestroy(); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error announcing destroyed state", re); + } + } } @Override public void start() { synchronized (mLock) { - // Started or finished - nothing to do. - if (mStartReqeusted || mFinishRequested) { + // Started called or finish called or destroyed - nothing to do. + if (mStartReqeusted || mFinishRequested || mDestroyed) { return; } @@ -412,71 +518,85 @@ public final class PrintManager { @Override public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes, ILayoutResultCallback callback, Bundle metadata, int sequence) { + final boolean destroyed; synchronized (mLock) { - // Start not called or finish called - nothing to do. - if (!mStartReqeusted || mFinishRequested) { - return; - } - - // Layout cancels write and overrides layout. - if (mLastWriteSpec != null) { - IoUtils.closeQuietly(mLastWriteSpec.fd); - mLastWriteSpec = null; - } + destroyed = mDestroyed; + // If start called and not finished called and not destroyed - do some work. + if (mStartReqeusted && !mFinishRequested && !mDestroyed) { + // Layout cancels write and overrides layout. + if (mLastWriteSpec != null) { + IoUtils.closeQuietly(mLastWriteSpec.fd); + mLastWriteSpec = null; + } - mLastLayoutSpec = new LayoutSpec(); - mLastLayoutSpec.callback = callback; - mLastLayoutSpec.oldAttributes = oldAttributes; - mLastLayoutSpec.newAttributes = newAttributes; - mLastLayoutSpec.metadata = metadata; - mLastLayoutSpec.sequence = sequence; + mLastLayoutSpec = new LayoutSpec(); + mLastLayoutSpec.callback = callback; + mLastLayoutSpec.oldAttributes = oldAttributes; + mLastLayoutSpec.newAttributes = newAttributes; + mLastLayoutSpec.metadata = metadata; + mLastLayoutSpec.sequence = sequence; + + // Cancel the previous cancellable operation.When the + // cancellation completes we will do the pending work. + if (cancelPreviousCancellableOperationLocked()) { + return; + } - // Cancel the previous cancellable operation.When the - // cancellation completes we will do the pending work. - if (cancelPreviousCancellableOperationLocked()) { - return; + doPendingWorkLocked(); + } + } + if (destroyed) { + try { + callback.onLayoutFailed(null, sequence); + } catch (RemoteException re) { + Log.i(LOG_TAG, "Error notifying for cancelled layout", re); } - - doPendingWorkLocked(); } } @Override public void write(PageRange[] pages, ParcelFileDescriptor fd, IWriteResultCallback callback, int sequence) { + final boolean destroyed; synchronized (mLock) { - // Start not called or finish called - nothing to do. - if (!mStartReqeusted || mFinishRequested) { - return; - } + destroyed = mDestroyed; + // If start called and not finished called and not destroyed - do some work. + if (mStartReqeusted && !mFinishRequested && !mDestroyed) { + // Write cancels previous writes. + if (mLastWriteSpec != null) { + IoUtils.closeQuietly(mLastWriteSpec.fd); + mLastWriteSpec = null; + } - // Write cancels previous writes. - if (mLastWriteSpec != null) { - IoUtils.closeQuietly(mLastWriteSpec.fd); - mLastWriteSpec = null; - } + mLastWriteSpec = new WriteSpec(); + mLastWriteSpec.callback = callback; + mLastWriteSpec.pages = pages; + mLastWriteSpec.fd = fd; + mLastWriteSpec.sequence = sequence; - mLastWriteSpec = new WriteSpec(); - mLastWriteSpec.callback = callback; - mLastWriteSpec.pages = pages; - mLastWriteSpec.fd = fd; - mLastWriteSpec.sequence = sequence; + // Cancel the previous cancellable operation.When the + // cancellation completes we will do the pending work. + if (cancelPreviousCancellableOperationLocked()) { + return; + } - // Cancel the previous cancellable operation.When the - // cancellation completes we will do the pending work. - if (cancelPreviousCancellableOperationLocked()) { - return; + doPendingWorkLocked(); + } + } + if (destroyed) { + try { + callback.onWriteFailed(null, sequence); + } catch (RemoteException re) { + Log.i(LOG_TAG, "Error notifying for cancelled write", re); } - - doPendingWorkLocked(); } } @Override public void finish() { synchronized (mLock) { - // Start not called or finish called - nothing to do. - if (!mStartReqeusted || mFinishRequested) { + // Start not called or finish called or destroyed - nothing to do. + if (!mStartReqeusted || mFinishRequested || mDestroyed) { return; } @@ -495,15 +615,90 @@ public final class PrintManager { } } + @Override + public void cancel() { + // Start not called or finish called or destroyed - nothing to do. + if (!mStartReqeusted || mFinishRequested || mDestroyed) { + return; + } + // Request cancellation of pending work if needed. + synchronized (mLock) { + cancelPreviousCancellableOperationLocked(); + } + } + + @Override + public void onActivityPaused(Activity activity) { + /* do nothing */ + } + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + /* do nothing */ + } + + @Override + public void onActivityStarted(Activity activity) { + /* do nothing */ + } + + @Override + public void onActivityResumed(Activity activity) { + /* do nothing */ + } + + @Override + public void onActivityStopped(Activity activity) { + /* do nothing */ + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + /* do nothing */ + } + + @Override + public void onActivityDestroyed(Activity activity) { + // We really care only if the activity is being destroyed to + // notify the the print spooler so it can close the print dialog. + // Note the the spooler has a death recipient that observes if + // this process gets killed so we cover the case of onDestroy not + // being called due to this process being killed to reclaim memory. + final IPrintDocumentAdapterObserver observer; + synchronized (mLock) { + if (activity == mActivity) { + mDestroyed = true; + observer = mObserver; + clearLocked(); + } else { + observer = null; + activity = null; + } + } + if (observer != null) { + activity.getApplication().unregisterActivityLifecycleCallbacks( + PrintDocumentAdapterDelegate.this); + try { + observer.onDestroy(); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error announcing destroyed state", re); + } + } + } + private boolean isFinished() { return mDocumentAdapter == null; } - private void doFinish() { + private void clearLocked() { + mActivity = null; mDocumentAdapter = null; mHandler = null; - synchronized (mLock) { - mLayoutOrWriteCancellation = null; + mLayoutOrWriteCancellation = null; + mLastLayoutSpec = null; + if (mLastWriteSpec != null) { + IoUtils.closeQuietly(mLastWriteSpec.fd); + mLastWriteSpec = null; } } @@ -564,63 +759,81 @@ public final class PrintManager { } switch (message.what) { case MSG_START: { - mDocumentAdapter.onStart(); - } - break; + final PrintDocumentAdapter adapter; + synchronized (mLock) { + adapter = mDocumentAdapter; + } + if (adapter != null) { + adapter.onStart(); + } + } break; case MSG_LAYOUT: { + final PrintDocumentAdapter adapter; final CancellationSignal cancellation; final LayoutSpec layoutSpec; synchronized (mLock) { + adapter = mDocumentAdapter; layoutSpec = mLastLayoutSpec; mLastLayoutSpec = null; cancellation = new CancellationSignal(); mLayoutOrWriteCancellation = cancellation; } - if (layoutSpec != null) { + if (layoutSpec != null && adapter != null) { if (DEBUG) { Log.i(LOG_TAG, "Performing layout"); } - mDocumentAdapter.onLayout(layoutSpec.oldAttributes, + adapter.onLayout(layoutSpec.oldAttributes, layoutSpec.newAttributes, cancellation, new MyLayoutResultCallback(layoutSpec.callback, layoutSpec.sequence), layoutSpec.metadata); } - } - break; + } break; case MSG_WRITE: { + final PrintDocumentAdapter adapter; final CancellationSignal cancellation; final WriteSpec writeSpec; synchronized (mLock) { + adapter = mDocumentAdapter; writeSpec = mLastWriteSpec; mLastWriteSpec = null; cancellation = new CancellationSignal(); mLayoutOrWriteCancellation = cancellation; } - if (writeSpec != null) { + if (writeSpec != null && adapter != null) { if (DEBUG) { Log.i(LOG_TAG, "Performing write"); } - mDocumentAdapter.onWrite(writeSpec.pages, writeSpec.fd, + adapter.onWrite(writeSpec.pages, writeSpec.fd, cancellation, new MyWriteResultCallback(writeSpec.callback, writeSpec.fd, writeSpec.sequence)); } - } - break; + } break; case MSG_FINISH: { if (DEBUG) { Log.i(LOG_TAG, "Performing finish"); } - mDocumentAdapter.onFinish(); - doFinish(); - } - break; + final PrintDocumentAdapter adapter; + final Activity activity; + synchronized (mLock) { + adapter = mDocumentAdapter; + activity = mActivity; + clearLocked(); + } + if (adapter != null) { + adapter.onFinish(); + } + if (activity != null) { + activity.getApplication().unregisterActivityLifecycleCallbacks( + PrintDocumentAdapterDelegate.this); + } + } break; default: { throw new IllegalArgumentException("Unknown message: " @@ -647,6 +860,11 @@ public final class PrintManager { } final ILayoutResultCallback callback; synchronized (mLock) { + if (mDestroyed) { + Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you " + + "finish the printing activity before print completion?"); + return; + } callback = mCallback; clearLocked(); } @@ -663,6 +881,11 @@ public final class PrintManager { public void onLayoutFailed(CharSequence error) { final ILayoutResultCallback callback; synchronized (mLock) { + if (mDestroyed) { + Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you " + + "finish the printing activity before print completion?"); + return; + } callback = mCallback; clearLocked(); } @@ -678,6 +901,11 @@ public final class PrintManager { @Override public void onLayoutCancelled() { synchronized (mLock) { + if (mDestroyed) { + Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you " + + "finish the printing activity before print completion?"); + return; + } clearLocked(); } } @@ -705,6 +933,11 @@ public final class PrintManager { public void onWriteFinished(PageRange[] pages) { final IWriteResultCallback callback; synchronized (mLock) { + if (mDestroyed) { + Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you " + + "finish the printing activity before print completion?"); + return; + } callback = mCallback; clearLocked(); } @@ -727,6 +960,11 @@ public final class PrintManager { public void onWriteFailed(CharSequence error) { final IWriteResultCallback callback; synchronized (mLock) { + if (mDestroyed) { + Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you " + + "finish the printing activity before print completion?"); + return; + } callback = mCallback; clearLocked(); } @@ -742,6 +980,11 @@ public final class PrintManager { @Override public void onWriteCancelled() { synchronized (mLock) { + if (mDestroyed) { + Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you " + + "finish the printing activity before print completion?"); + return; + } clearLocked(); } } diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java index df51ec10fb7900bfdd6cf776f30772584d1cb23b..b615600d33ae83319f81e420f24009f715bffab2 100644 --- a/core/java/android/print/PrinterCapabilitiesInfo.java +++ b/core/java/android/print/PrinterCapabilitiesInfo.java @@ -24,10 +24,17 @@ import android.print.PrintAttributes.Resolution; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; /** - * This class represents the capabilities of a printer. + * This class represents the capabilities of a printer. Instances + * of this class are created by a print service to report the + * capabilities of a printer it manages. The capabilities of a + * printer specify how it can print content. For example, what + * are the media sizes supported by the printer, what are the + * minimal margins of the printer based on its technical design, + * etc. */ public final class PrinterCapabilitiesInfo implements Parcelable { /** @@ -112,7 +119,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable { * @return The media sizes. */ public List getMediaSizes() { - return mMediaSizes; + return Collections.unmodifiableList(mMediaSizes); } /** @@ -121,7 +128,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable { * @return The resolutions. */ public List getResolutions() { - return mResolutions; + return Collections.unmodifiableList(mResolutions); } /** @@ -135,9 +142,9 @@ public final class PrinterCapabilitiesInfo implements Parcelable { } /** - * Gets the supported color modes. + * Gets the bit mask of supported color modes. * - * @return The color modes. + * @return The bit mask of supported color modes. * * @see PrintAttributes#COLOR_MODE_COLOR * @see PrintAttributes#COLOR_MODE_MONOCHROME @@ -355,9 +362,10 @@ public final class PrinterCapabilitiesInfo implements Parcelable { } /** - * Builder for creating of a {@link PrinterInfo}. This class is responsible - * to enforce that all required attributes have at least one default value. - * In other words, this class creates only well-formed {@link PrinterInfo}s. + * Builder for creating of a {@link PrinterCapabilitiesInfo}. This class is + * responsible to enforce that all required attributes have at least one + * default value. In other words, this class creates only well-formed {@link + * PrinterCapabilitiesInfo}s. *

    * Look at the individual methods for a reference whether a property is * required or if it is optional. @@ -369,9 +377,9 @@ public final class PrinterCapabilitiesInfo implements Parcelable { /** * Creates a new instance. * - * @param printerId The printer id. Cannot be null. + * @param printerId The printer id. Cannot be null. * - * @throws IllegalArgumentException If the printer id is null. + * @throws IllegalArgumentException If the printer id is null. */ public Builder(PrinterId printerId) { if (printerId == null) { @@ -492,7 +500,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable { /** * Crates a new {@link PrinterCapabilitiesInfo} enforcing that all - * required properties have need specified. See individual methods + * required properties have been specified. See individual methods * in this class for reference about required attributes. * * @return A new {@link PrinterCapabilitiesInfo}. @@ -521,7 +529,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable { if (mPrototype.mMinMargins == null) { throw new IllegalArgumentException("margins cannot be null"); } - return new PrinterCapabilitiesInfo(mPrototype); + return mPrototype; } private void throwIfDefaultAlreadySpecified(int propertyIndex) { diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java index ad79a38acb97a96595f1a0377f7c042a1c3a91d2..7fcc81f410068d28f37f4742345615ef4f905a62 100644 --- a/core/java/android/print/PrinterInfo.java +++ b/core/java/android/print/PrinterInfo.java @@ -21,7 +21,12 @@ import android.os.Parcelable; import android.text.TextUtils; /** - * This class represents the description of a printer. + * This class represents the description of a printer. Instances of + * this class are created by print services to report to the system + * the printers they manage. The information of this class has two + * major components, printer properties such as name, id, status, + * description and printer capabilities which describe the various + * print modes a printer supports such as media sizes, margins, etc. */ public final class PrinterInfo implements Parcelable { @@ -96,6 +101,10 @@ public final class PrinterInfo implements Parcelable { * Gets the printer status. * * @return The status. + * + * @see #STATUS_BUSY + * @see #STATUS_IDLE + * @see #STATUS_UNAVAILABLE */ public int getStatus() { return mStatus; @@ -216,6 +225,8 @@ public final class PrinterInfo implements Parcelable { * @param printerId The printer id. Cannot be null. * @param name The printer name. Cannot be empty. * @param status The printer status. Must be a valid status. + * @throws IllegalArgumentException If the printer id is null, or the + * printer name is empty or the status is not a valid one. */ public Builder(PrinterId printerId, String name, int status) { if (printerId == null) { @@ -259,7 +270,8 @@ public final class PrinterInfo implements Parcelable { } /** - * Sets the printer name. + * Sets the localized printer name which + * is shown to the user * * @param name The name. * @return This builder. @@ -270,7 +282,8 @@ public final class PrinterInfo implements Parcelable { } /** - * Sets the printer description. + * Sets the localized printer description + * which is shown to the user * * @param description The description. * @return This builder. @@ -292,12 +305,12 @@ public final class PrinterInfo implements Parcelable { } /** - * Crates a new {@link PrinterInfo}. + * Creates a new {@link PrinterInfo}. * * @return A new {@link PrinterInfo}. */ public PrinterInfo build() { - return new PrinterInfo(mPrototype); + return mPrototype; } private boolean isValidStatus(int status) { diff --git a/core/java/android/print/package.html b/core/java/android/print/package.html new file mode 100644 index 0000000000000000000000000000000000000000..579567d686bb90b4c52f499ce4bae19ee3a52613 --- /dev/null +++ b/core/java/android/print/package.html @@ -0,0 +1,46 @@ + + +

    Overview

    +

    +Provides classes for implementing print support in applications and also contains all +base classes and abstractions involved in printing. These base classes are also used +by other more specialized printing related packages. +

    +

    +The entry point for interacting with the print system is the {@link android.print.PrintManager} +which is a system service that can be obtained from the current context. The print manager +provides APIs for printing, querying the state of print jobs, etc. +

    +

    Print contract

    +

    +An application that wants to implement printing must extend +{@link android.print.PrintDocumentAdapter} which defines the contract between the system +and the application.The key idea behind this adapter is that the printed content may change +based on the selected print options, such as media size, orientation, which +requires the content to be re-laid out. The constraints according to which the content has +to be laid out are encapsulated in the {@link android.print.PrintAttributes} class. Once +layout is completed the application calls back to the system passing a +{@link android.print.PrintDocumentInfo} instance which describes the generated content. After +the content has been laid out the application may be asked to render some pages of that content +for preview or printing. The range of pages that have to be rendered is abstracted by the +{@link android.print.PageRange} class. +

    +

    Print jobs

    +

    +A print job is represented by the {@link android.print.PrintJob} class which has behavior +methods as well as methods for querying its state. Each print job has a unique id represented +by the {@link android.print.PrintJobId} class and exposes APIs for obtaining a {@link +android.print.PrintJobInfo} which is a snapshot of its state. The print job state may +change over time. +

    +

    Printers

    +

    +An available printer represented by the {@link android.print.PrinterInfo} class has a +unique id which is abstracted by the {@link android.print.PrinterId} class. The {@link +android.print.PrinterInfo} contains printer properties such as id, name, description, status, +and printer capabilities encapsulated in the {@link android.print.PrinterCapabilitiesInfo} +class. Printer capabilities describe how a printer can print content, for example what are +the supported media sizes, color modes, resolutions, etc. +

    + + diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java index fdeb3730b90e1c244928fd18bf3ab5a2a4a0b07d..6fa0bddb43f48e574c37111a67ed39f5e6a1c4bb 100644 --- a/core/java/android/printservice/PrintJob.java +++ b/core/java/android/printservice/PrintJob.java @@ -321,7 +321,7 @@ public final class PrintJob { */ public String getAdvancedStringOption(String key) { PrintService.throwIfNotCalledOnMainThread(); - return null; + return getInfo().getAdvancedStringOption(key); } /** @@ -333,7 +333,7 @@ public final class PrintJob { */ public boolean hasAdvancedOption(String key) { PrintService.throwIfNotCalledOnMainThread(); - return false; + return getInfo().hasAdvancedOption(key); } /** @@ -344,7 +344,7 @@ public final class PrintJob { */ public int getAdvancedIntOption(String key) { PrintService.throwIfNotCalledOnMainThread(); - return 0; + return getInfo().getAdvancedIntOption(key); } @Override diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index 0fc5f7f035a6a78658e6412af65d871964577a88..eb0ac2e631b8486f2594913aaed97aaf70c4a63c 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -209,6 +209,14 @@ public abstract class PrintService extends Service { * PrintJob#getAdvancedStringOption(String) PrintJob.getAdvancedStringOption(String)} * and {@link PrintJob#getAdvancedIntOption(String) PrintJob.getAdvancedIntOption(String)}. *

    + *

    + * If the advanced print options activity offers changes to the standard print + * options, you can get the current {@link android.print.PrinterInfo} using the + * "android.intent.extra.print.EXTRA_PRINTER_INFO" extra which will allow you to + * present the user with UI options supported by the current printer. For example, + * if the current printer does not support a give media size, you should not + * offer it in the advanced print options dialog. + *

    */ public static final String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO"; diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java index 8e9636c2b7335016500f5e64b8cfba173e87a7bb..a2c6c09e4932f1c65a766c278a43ed9adaf02a05 100644 --- a/core/java/android/printservice/PrintServiceInfo.java +++ b/core/java/android/printservice/PrintServiceInfo.java @@ -60,6 +60,8 @@ public final class PrintServiceInfo implements Parcelable { private final String mAddPrintersActivityName; + private final String mAdvancedPrintOptionsActivityName; + /** * Creates a new instance. * @@ -70,6 +72,7 @@ public final class PrintServiceInfo implements Parcelable { mResolveInfo = parcel.readParcelable(null); mSettingsActivityName = parcel.readString(); mAddPrintersActivityName = parcel.readString(); + mAdvancedPrintOptionsActivityName = parcel.readString(); } /** @@ -78,14 +81,16 @@ public final class PrintServiceInfo implements Parcelable { * @param resolveInfo The service resolve info. * @param settingsActivityName Optional settings activity name. * @param addPrintersActivityName Optional add printers activity name. + * @param advancedPrintOptionsActivityName Optional advanced print options activity. */ public PrintServiceInfo(ResolveInfo resolveInfo, String settingsActivityName, - String addPrintersActivityName) { + String addPrintersActivityName, String advancedPrintOptionsActivityName) { mId = new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name).flattenToString(); mResolveInfo = resolveInfo; mSettingsActivityName = settingsActivityName; mAddPrintersActivityName = addPrintersActivityName; + mAdvancedPrintOptionsActivityName = advancedPrintOptionsActivityName; } /** @@ -99,6 +104,7 @@ public final class PrintServiceInfo implements Parcelable { public static PrintServiceInfo create(ResolveInfo resolveInfo, Context context) { String settingsActivityName = null; String addPrintersActivityName = null; + String advancedPrintOptionsActivityName = null; XmlResourceParser parser = null; PackageManager packageManager = context.getPackageManager(); @@ -128,6 +134,9 @@ public final class PrintServiceInfo implements Parcelable { addPrintersActivityName = attributes.getString( com.android.internal.R.styleable.PrintService_addPrintersActivity); + advancedPrintOptionsActivityName = attributes.getString(com.android.internal + .R.styleable.PrintService_advancedPrintOptionsActivity); + attributes.recycle(); } } catch (IOException ioe) { @@ -144,7 +153,8 @@ public final class PrintServiceInfo implements Parcelable { } } - return new PrintServiceInfo(resolveInfo, settingsActivityName, addPrintersActivityName); + return new PrintServiceInfo(resolveInfo, settingsActivityName, + addPrintersActivityName, advancedPrintOptionsActivityName); } /** @@ -194,6 +204,19 @@ public final class PrintServiceInfo implements Parcelable { return mAddPrintersActivityName; } + /** + * The advanced print options activity name. + *

    + * Statically set from + * {@link PrintService#SERVICE_META_DATA meta-data}. + *

    + * + * @return The advanced print options activity name. + */ + public String getAdvancedOptionsActivityName() { + return mAdvancedPrintOptionsActivityName; + } + /** * {@inheritDoc} */ @@ -206,6 +229,7 @@ public final class PrintServiceInfo implements Parcelable { parcel.writeParcelable(mResolveInfo, 0); parcel.writeString(mSettingsActivityName); parcel.writeString(mAddPrintersActivityName); + parcel.writeString(mAdvancedPrintOptionsActivityName); } @Override @@ -243,6 +267,8 @@ public final class PrintServiceInfo implements Parcelable { builder.append(", resolveInfo=").append(mResolveInfo); builder.append(", settingsActivityName=").append(mSettingsActivityName); builder.append(", addPrintersActivityName=").append(mAddPrintersActivityName); + builder.append(", advancedPrintOptionsActivityName=") + .append(mAdvancedPrintOptionsActivityName); builder.append("}"); return builder.toString(); } diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 7f8dca2f42408a2cbe1f10890e0f7dcdd8c560a9..86d3cf864a42c5556f1e2dc5b56adb0c0f121a29 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -34,6 +34,7 @@ import android.media.ExifInterface; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; +import android.os.OperationCanceledException; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.OnCloseListener; import android.os.RemoteException; @@ -81,6 +82,9 @@ public final class DocumentsContract { /** {@hide} */ public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME"; + /** {@hide} */ + public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED"; + /** * Included in {@link AssetFileDescriptor#getExtras()} when returned * thumbnail should be rotated. @@ -667,7 +671,9 @@ public final class DocumentsContract { try { return getDocumentThumbnail(client, documentUri, size, signal); } catch (Exception e) { - Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); + if (!(e instanceof OperationCanceledException)) { + Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e); + } return null; } finally { ContentProviderClient.releaseQuietly(client); diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index c9efb53a959d9b9652e30107ce3c4b74b4516c6f..49816f8091b6da19547dfcfc6b80fad45d8a09aa 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -62,7 +62,8 @@ import java.io.FileNotFoundException; * android:authorities="com.example.mycloudprovider" * android:exported="true" * android:grantUriPermissions="true" - * android:permission="android.permission.MANAGE_DOCUMENTS"> + * android:permission="android.permission.MANAGE_DOCUMENTS" + * android:enabled="@bool/isAtLeastKitKat"> * <intent-filter> * <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> * </intent-filter> @@ -76,9 +77,8 @@ import java.io.FileNotFoundException; * only the system can obtain. Applications cannot use a documents provider * directly; they must go through {@link Intent#ACTION_OPEN_DOCUMENT} or * {@link Intent#ACTION_CREATE_DOCUMENT} which requires a user to actively - * navigate and select documents. When a user selects documents through that - * UI, the system issues narrow URI permission grants to the requesting - * application. + * navigate and select documents. When a user selects documents through that UI, + * the system issues narrow URI permission grants to the requesting application. *

    *

    Documents

    *

    @@ -91,8 +91,8 @@ import java.io.FileNotFoundException; *

    * Each document can have different capabilities, as described by * {@link Document#COLUMN_FLAGS}. For example, if a document can be represented - * as a thumbnail, a provider can set {@link Document#FLAG_SUPPORTS_THUMBNAIL} - * and implement + * as a thumbnail, your provider can set + * {@link Document#FLAG_SUPPORTS_THUMBNAIL} and implement * {@link #openDocumentThumbnail(String, Point, CancellationSignal)} to return * that thumbnail. *

    @@ -102,7 +102,7 @@ import java.io.FileNotFoundException; * single document can be included in multiple directories when responding to * {@link #queryChildDocuments(String, String[], String)}. For example, a * provider might surface a single photo in multiple locations: once in a - * directory of locations, and again in a directory of dates. + * directory of geographic locations, and again in a directory of dates. *

    *

    Roots

    *

    @@ -162,7 +162,7 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Create a new document and return its newly generated - * {@link Document#COLUMN_DOCUMENT_ID}. A provider must allocate a new + * {@link Document#COLUMN_DOCUMENT_ID}. You must allocate a new * {@link Document#COLUMN_DOCUMENT_ID} to represent the document, which must * not change once returned. * @@ -194,16 +194,17 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Return all roots currently provided. A provider must define at least one - * root to display to users, and it should avoid making network requests to - * keep this request fast. + * Return all roots currently provided. To display to users, you must define + * at least one root. You should avoid making network requests to keep this + * request fast. *

    * Each root is defined by the metadata columns described in {@link Root}, * including {@link Root#COLUMN_DOCUMENT_ID} which points to a directory * representing a tree of documents to display under that root. *

    * If this set of roots changes, you must call {@link ContentResolver#notifyChange(Uri, - * android.database.ContentObserver)} to notify the system. + * android.database.ContentObserver, boolean)} with + * {@link DocumentsContract#buildRootsUri(String)} to notify the system. * * @param projection list of {@link Root} columns to put into the cursor. If * {@code null} all supported columns should be included. @@ -216,6 +217,8 @@ public abstract class DocumentsProvider extends ContentProvider { * {@link Root#FLAG_SUPPORTS_RECENTS}. The returned documents should be * sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order, and * limited to only return the 64 most recently modified documents. + *

    + * Recent documents do not support change notifications. * * @param projection list of {@link Document} columns to put into the * cursor. If {@code null} all supported columns should be @@ -229,8 +232,8 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Return metadata for the single requested document. A provider should - * avoid making network requests to keep this request fast. + * Return metadata for the single requested document. You should avoid + * making network requests to keep this request fast. * * @param documentId the document to return. * @param projection list of {@link Document} columns to put into the @@ -248,10 +251,18 @@ public abstract class DocumentsProvider extends ContentProvider { * If your provider is cloud-based, and you have some data cached or pinned * locally, you may return the local data immediately, setting * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that - * your provider is still fetching additional data. Then, when the network - * data is available, you can call {@link ContentResolver#notifyChange(Uri, - * android.database.ContentObserver)} to trigger a requery and return the - * complete contents. + * you are still fetching additional data. Then, when the network data is + * available, you can send a change notification to trigger a requery and + * return the complete contents. To return a Cursor with extras, you need to + * extend and override {@link Cursor#getExtras()}. + *

    + * To support change notifications, you must + * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant + * Uri, such as + * {@link DocumentsContract#buildChildDocumentsUri(String, String)}. Then + * you can call {@link ContentResolver#notifyChange(Uri, + * android.database.ContentObserver, boolean)} with that Uri to send change + * notifications. * * @param parentDocumentId the directory to return children for. * @param projection list of {@link Document} columns to put into the @@ -289,6 +300,20 @@ public abstract class DocumentsProvider extends ContentProvider { *

    * Only documents may be returned; directories are not supported in search * results. + *

    + * If your provider is cloud-based, and you have some data cached or pinned + * locally, you may return the local data immediately, setting + * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that + * you are still fetching additional data. Then, when the network data is + * available, you can send a change notification to trigger a requery and + * return the complete contents. + *

    + * To support change notifications, you must + * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant + * Uri, such as {@link DocumentsContract#buildSearchDocumentsUri(String, + * String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri, + * android.database.ContentObserver, boolean)} with that Uri to send change + * notifications. * * @param rootId the root to search under. * @param query string to match documents against. @@ -327,22 +352,19 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Open and return the requested document. *

    - * A provider should return a reliable {@link ParcelFileDescriptor} to + * Your provider should return a reliable {@link ParcelFileDescriptor} to * detect when the remote caller has finished reading or writing the - * document. A provider may return a pipe or socket pair if the mode is - * exclusively {@link ParcelFileDescriptor#MODE_READ_ONLY} or - * {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, but complex modes like - * {@link ParcelFileDescriptor#MODE_READ_WRITE} require a normal file on - * disk. + * document. You may return a pipe or socket pair if the mode is exclusively + * "r" or "w", but complex modes like "rw" imply a normal file on disk that + * supports seeking. *

    - * If a provider blocks while downloading content, it should periodically - * check {@link CancellationSignal#isCanceled()} to abort abandoned open - * requests. + * If you block while downloading content, you should periodically check + * {@link CancellationSignal#isCanceled()} to abort abandoned open requests. * * @param documentId the document to return. * @param mode the mode to open with, such as 'r', 'w', or 'rw'. * @param signal used by the caller to signal if the request should be - * cancelled. + * cancelled. May be null. * @see ParcelFileDescriptor#open(java.io.File, int, android.os.Handler, * OnCloseListener) * @see ParcelFileDescriptor#createReliablePipe() @@ -359,15 +381,14 @@ public abstract class DocumentsProvider extends ContentProvider { * attempting to serve from a local cache if possible. A provider should * never return images more than double the hinted size. *

    - * If a provider performs expensive operations to download or generate a - * thumbnail, it should periodically check - * {@link CancellationSignal#isCanceled()} to abort abandoned thumbnail - * requests. + * If you perform expensive operations to download or generate a thumbnail, + * you should periodically check {@link CancellationSignal#isCanceled()} to + * abort abandoned thumbnail requests. * * @param documentId the document to return. * @param sizeHint hint of the optimal thumbnail dimensions. * @param signal used by the caller to signal if the request should be - * cancelled. + * cancelled. May be null. * @see Document#FLAG_SUPPORTS_THUMBNAIL */ @SuppressWarnings("unused") @@ -495,10 +516,7 @@ public abstract class DocumentsProvider extends ContentProvider { final boolean callerHasManage = context.checkCallingOrSelfPermission(android.Manifest.permission.MANAGE_DOCUMENTS) == PackageManager.PERMISSION_GRANTED; - if (!callerHasManage) { - getContext().enforceCallingOrSelfUriPermission( - documentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, method); - } + enforceWritePermissionInner(documentUri); final Bundle out = new Bundle(); try { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7f245394d246223cc943ceef7e542dee3190b90a..04f3f0aad84dc1d1f5588a8612983f9e5e054777 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3252,9 +3252,14 @@ public final class Settings { /** * A 64-bit number (as a hex string) that is randomly - * generated on the device's first boot and should remain - * constant for the lifetime of the device. (The value may - * change if a factory reset is performed on the device.) + * generated when the user first sets up the device and should remain + * constant for the lifetime of the user's device. The value may + * change if a factory reset is performed on the device. + *

    Note: When a device has multiple users + * (available on certain devices running Android 4.2 or higher), each user appears as a + * completely separate device, so the {@code ANDROID_ID} value is unique to each + * user.

    */ public static final String ANDROID_ID = "android_id"; diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index b8083632bc4fcba716c9ec88b721180fce86ce35..2752085a427e06c6849088f9b29f87610098bb3d 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -993,8 +993,16 @@ public class TextToSpeech { return runAction(new Action>() { @Override public Set run(ITextToSpeechService service) throws RemoteException { - String[] features = service.getFeaturesForLanguage( + String[] features = null; + try { + features = service.getFeaturesForLanguage( locale.getISO3Language(), locale.getISO3Country(), locale.getVariant()); + } catch(MissingResourceException e) { + Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 " + + "country code for locale: " + locale, e); + return null; + } + if (features != null) { final Set featureSet = new HashSet(); Collections.addAll(featureSet, features); diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java index 5fbd22e273e8446bea3847dfe1a8aac96d8bdf39..4f996cd7b91e662d35593752d95bfd6d220db428 100644 --- a/core/java/android/speech/tts/TtsEngines.java +++ b/core/java/android/speech/tts/TtsEngines.java @@ -44,6 +44,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Locale; +import java.util.MissingResourceException; /** * Support class for querying the list of available engines @@ -369,28 +370,34 @@ public class TtsEngines { public String getDefaultLocale() { final Locale locale = Locale.getDefault(); - // Note that the default locale might have an empty variant - // or language, and we take care that the construction is - // the same as {@link #getV1Locale} i.e no trailing delimiters - // or spaces. - String defaultLocale = locale.getISO3Language(); - if (TextUtils.isEmpty(defaultLocale)) { - Log.w(TAG, "Default locale is empty."); - return ""; - } + try { + // Note that the default locale might have an empty variant + // or language, and we take care that the construction is + // the same as {@link #getV1Locale} i.e no trailing delimiters + // or spaces. + String defaultLocale = locale.getISO3Language(); + if (TextUtils.isEmpty(defaultLocale)) { + Log.w(TAG, "Default locale is empty."); + return ""; + } + + if (!TextUtils.isEmpty(locale.getISO3Country())) { + defaultLocale += LOCALE_DELIMITER + locale.getISO3Country(); + } else { + // Do not allow locales of the form lang--variant with + // an empty country. + return defaultLocale; + } + if (!TextUtils.isEmpty(locale.getVariant())) { + defaultLocale += LOCALE_DELIMITER + locale.getVariant(); + } - if (!TextUtils.isEmpty(locale.getISO3Country())) { - defaultLocale += LOCALE_DELIMITER + locale.getISO3Country(); - } else { - // Do not allow locales of the form lang--variant with - // an empty country. return defaultLocale; + } catch (MissingResourceException e) { + // Default locale does not have a ISO 3166 and/or ISO 639-2/T codes. Return the + // default "eng-usa" (that would be the result of Locale.getDefault() == Locale.US). + return "eng-usa"; } - if (!TextUtils.isEmpty(locale.getVariant())) { - defaultLocale += LOCALE_DELIMITER + locale.getVariant(); - } - - return defaultLocale; } /** diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java index 160c630bf527afbe7d6402899b4e90c6cbfad17e..f839d52b012697979adce52100d3e0515a3dd171 100644 --- a/core/java/android/text/Html.java +++ b/core/java/android/text/Html.java @@ -391,6 +391,15 @@ public class Html { out.append(">"); } else if (c == '&') { out.append("&"); + } else if (c >= 0xD800 && c <= 0xDFFF) { + if (c < 0xDC00 && i + 1 < end) { + char d = text.charAt(i + 1); + if (d >= 0xDC00 && d <= 0xDFFF) { + i++; + int codepoint = 0x010000 | (int) c - 0xD800 << 10 | (int) d - 0xDC00; + out.append("&#").append(codepoint).append(";"); + } + } } else if (c > 0x7E || c < ' ') { out.append("&#").append((int) c).append(";"); } else if (c == ' ') { diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java index 6d066d6a733de9895b74779e488246498dd26aeb..c5963882e2bf0cffa762ece1cfccece40e5fe32d 100644 --- a/core/java/android/text/InputType.java +++ b/core/java/android/text/InputType.java @@ -46,9 +46,9 @@ public interface InputType { * of text being given. Currently supported classes are: * {@link #TYPE_CLASS_TEXT}, {@link #TYPE_CLASS_NUMBER}, * {@link #TYPE_CLASS_PHONE}, {@link #TYPE_CLASS_DATETIME}. - * If the class is not one you + *

    IME authors: If the class is not one you * understand, assume {@link #TYPE_CLASS_TEXT} with NO variation - * or flags. + * or flags.

    */ public static final int TYPE_MASK_CLASS = 0x0000000f; @@ -69,7 +69,10 @@ public interface InputType { * This should be interpreted to mean that the target input connection * is not rich, it can not process and show things like candidate text nor * retrieve the current text, so the input method will need to run in a - * limited "generate key events" mode. + * limited "generate key events" mode, if it supports it. Note that some + * input methods may not support it, for example a voice-based input + * method will likely not be able to generate key events even if this + * flag is set. */ public static final int TYPE_NULL = 0x00000000; @@ -94,48 +97,70 @@ public interface InputType { * Flag for {@link #TYPE_CLASS_TEXT}: capitalize all characters. Overrides * {@link #TYPE_TEXT_FLAG_CAP_WORDS} and * {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. This value is explicitly defined - * to be the same as {@link TextUtils#CAP_MODE_CHARACTERS}. + * to be the same as {@link TextUtils#CAP_MODE_CHARACTERS}. Of course, + * this only affects languages where there are upper-case and lower-case letters. */ public static final int TYPE_TEXT_FLAG_CAP_CHARACTERS = 0x00001000; /** - * Flag for {@link #TYPE_CLASS_TEXT}: capitalize first character of - * all words. Overrides {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. This + * Flag for {@link #TYPE_CLASS_TEXT}: capitalize the first character of + * every word. Overrides {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. This * value is explicitly defined - * to be the same as {@link TextUtils#CAP_MODE_WORDS}. + * to be the same as {@link TextUtils#CAP_MODE_WORDS}. Of course, + * this only affects languages where there are upper-case and lower-case letters. */ public static final int TYPE_TEXT_FLAG_CAP_WORDS = 0x00002000; /** - * Flag for {@link #TYPE_CLASS_TEXT}: capitalize first character of + * Flag for {@link #TYPE_CLASS_TEXT}: capitalize the first character of * each sentence. This value is explicitly defined - * to be the same as {@link TextUtils#CAP_MODE_SENTENCES}. + * to be the same as {@link TextUtils#CAP_MODE_SENTENCES}. For example + * in English it means to capitalize after a period and a space (note that other + * languages may have different characters for period, or not use spaces, + * or use different grammatical rules). Of course, + * this only affects languages where there are upper-case and lower-case letters. */ public static final int TYPE_TEXT_FLAG_CAP_SENTENCES = 0x00004000; /** * Flag for {@link #TYPE_CLASS_TEXT}: the user is entering free-form - * text that should have auto-correction applied to it. + * text that should have auto-correction applied to it. Without this flag, + * the IME will not try to correct typos. You should always set this flag + * unless you really expect users to type non-words in this field, for + * example to choose a name for a character in a game. + * Contrast this with {@link #TYPE_TEXT_FLAG_AUTO_COMPLETE} and + * {@link #TYPE_TEXT_FLAG_NO_SUGGESTIONS}: + * {@code TYPE_TEXT_FLAG_AUTO_CORRECT} means that the IME will try to + * auto-correct typos as the user is typing, but does not define whether + * the IME offers an interface to show suggestions. */ public static final int TYPE_TEXT_FLAG_AUTO_CORRECT = 0x00008000; /** - * Flag for {@link #TYPE_CLASS_TEXT}: the text editor is performing - * auto-completion of the text being entered based on its own semantics, - * which it will present to the user as they type. This generally means - * that the input method should not be showing candidates itself, but can - * expect for the editor to supply its own completions/candidates from + * Flag for {@link #TYPE_CLASS_TEXT}: the text editor (which means + * the application) is performing auto-completion of the text being entered + * based on its own semantics, which it will present to the user as they type. + * This generally means that the input method should not be showing + * candidates itself, but can expect the editor to supply its own + * completions/candidates from * {@link android.view.inputmethod.InputMethodSession#displayCompletions * InputMethodSession.displayCompletions()} as a result of the editor calling * {@link android.view.inputmethod.InputMethodManager#displayCompletions * InputMethodManager.displayCompletions()}. + * Note the contrast with {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} and + * {@link #TYPE_TEXT_FLAG_NO_SUGGESTIONS}: + * {@code TYPE_TEXT_FLAG_AUTO_COMPLETE} means the editor should show an + * interface for displaying suggestions, but instead of supplying its own + * it will rely on the Editor to pass completions/corrections. */ public static final int TYPE_TEXT_FLAG_AUTO_COMPLETE = 0x00010000; /** * Flag for {@link #TYPE_CLASS_TEXT}: multiple lines of text can be * entered into the field. If this flag is not set, the text field - * will be constrained to a single line. + * will be constrained to a single line. The IME may also choose not to + * display an enter key when this flag is not set, as there should be no + * need to create new lines. */ public static final int TYPE_TEXT_FLAG_MULTI_LINE = 0x00020000; @@ -152,6 +177,16 @@ public interface InputType { * do not contain words from the language and do not benefit from any * dictionary-based completions or corrections. It overrides the * {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} value when set. + * Please avoid using this unless you are certain this is what you want. + * Many input methods need suggestions to work well, for example the ones + * based on gesture typing. Consider clearing + * {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} instead if you just do not + * want the IME to correct typos. + * Note the contrast with {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} and + * {@link #TYPE_TEXT_FLAG_AUTO_COMPLETE}: + * {@code TYPE_TEXT_FLAG_NO_SUGGESTIONS} means the IME should never + * show an interface to display suggestions. Most IMEs will also take this to + * mean they should not try to auto-correct what the user is typing. */ public static final int TYPE_TEXT_FLAG_NO_SUGGESTIONS = 0x00080000; @@ -224,7 +259,9 @@ public interface InputType { /** * Variation of {@link #TYPE_CLASS_TEXT}: entering text for phonetic - * pronunciation, such as a phonetic name field in contacts. + * pronunciation, such as a phonetic name field in contacts. This is mostly + * useful for languages where one spelling may have several phonetic + * readings, like Japanese. */ public static final int TYPE_TEXT_VARIATION_PHONETIC = 0x000000c0; @@ -255,12 +292,13 @@ public interface InputType { // ---------------------------------------------------------------------- /** - * Class for numeric text. This class supports the following flag: + * Class for numeric text. This class supports the following flags: * {@link #TYPE_NUMBER_FLAG_SIGNED} and * {@link #TYPE_NUMBER_FLAG_DECIMAL}. It also supports the following * variations: {@link #TYPE_NUMBER_VARIATION_NORMAL} and - * {@link #TYPE_NUMBER_VARIATION_PASSWORD}. If you do not recognize - * the variation, normal should be assumed. + * {@link #TYPE_NUMBER_VARIATION_PASSWORD}. + *

    IME authors: If you do not recognize + * the variation, normal should be assumed.

    */ public static final int TYPE_CLASS_NUMBER = 0x00000002; @@ -318,7 +356,7 @@ public interface InputType { * following variations: * {@link #TYPE_DATETIME_VARIATION_NORMAL} * {@link #TYPE_DATETIME_VARIATION_DATE}, and - * {@link #TYPE_DATETIME_VARIATION_TIME},. + * {@link #TYPE_DATETIME_VARIATION_TIME}. */ public static final int TYPE_CLASS_DATETIME = 0x00000004; diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java index d798abec5a33b2eabd0d4cc48d0feab8054b1006..e1f1896f5e9da2c0fe1bec9b17d0956ccd9d1243 100644 --- a/core/java/android/transition/Scene.java +++ b/core/java/android/transition/Scene.java @@ -36,27 +36,28 @@ public final class Scene { private ViewGroup mSceneRoot; private ViewGroup mLayout; // alternative to layoutId Runnable mEnterAction, mExitAction; - private static ThreadLocal> sScenes = new ThreadLocal>(); /** * Returns a Scene described by the resource file associated with the given - * layoutId parameter. If such a Scene has already been created, - * that same Scene will be returned. This caching of layoutId-based scenes enables - * sharing of common scenes between those created in code and those referenced - * by {@link TransitionManager} XML resource files. + * layoutId parameter. If such a Scene has already been created for + * the given sceneRoot, that same Scene will be returned. + * This caching of layoutId-based scenes enables sharing of common scenes + * between those created in code and those referenced by {@link TransitionManager} + * XML resource files. * * @param sceneRoot The root of the hierarchy in which scene changes * and transitions will take place. * @param layoutId The id of a standard layout resource file. * @param context The context used in the process of inflating * the layout resource. - * @return + * @return The scene for the given root and layout id */ public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) { - SparseArray scenes = sScenes.get(); + SparseArray scenes = (SparseArray) sceneRoot.getTag( + com.android.internal.R.id.scene_layoutid_cache); if (scenes == null) { scenes = new SparseArray(); - sScenes.set(scenes); + sceneRoot.setTagInternal(com.android.internal.R.id.scene_layoutid_cache, scenes); } Scene scene = scenes.get(layoutId); if (scene != null) { diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index dcf668b6c443d9a3d70450b4a3eedacf7b6ab5fc..da9ba5a7fd750c32127a8ad13d64bc5cbfbb2701 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -994,15 +994,7 @@ public abstract class Transition implements Cloneable { * false otherwise */ void captureValues(ViewGroup sceneRoot, boolean start) { - if (start) { - mStartValues.viewValues.clear(); - mStartValues.idValues.clear(); - mStartValues.itemIdValues.clear(); - } else { - mEndValues.viewValues.clear(); - mEndValues.idValues.clear(); - mEndValues.itemIdValues.clear(); - } + clearValues(start); if (mTargetIds.size() > 0 || mTargets.size() > 0) { if (mTargetIds.size() > 0) { for (int i = 0; i < mTargetIds.size(); ++i) { @@ -1054,6 +1046,23 @@ public abstract class Transition implements Cloneable { } } + /** + * Clear valuesMaps for specified start/end state + * + * @param start true if the start values should be cleared, false otherwise + */ + void clearValues(boolean start) { + if (start) { + mStartValues.viewValues.clear(); + mStartValues.idValues.clear(); + mStartValues.itemIdValues.clear(); + } else { + mEndValues.viewValues.clear(); + mEndValues.idValues.clear(); + mEndValues.itemIdValues.clear(); + } + } + /** * Recursive method which captures values for an entire view hierarchy, * starting at some root view. Transitions without targetIDs will use this @@ -1246,7 +1255,8 @@ public abstract class Transition implements Cloneable { Animator anim = runningAnimators.keyAt(i); if (anim != null) { AnimationInfo oldInfo = runningAnimators.get(anim); - if (oldInfo != null) { + if (oldInfo != null && oldInfo.view != null && + oldInfo.view.getContext() == sceneRoot.getContext()) { boolean cancel = false; TransitionValues oldValues = oldInfo.values; View oldView = oldInfo.view; diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java index 4af0f51a41e2a0bc04e635185fe1d27d0686830e..9f77d5ee7f9f6d8c421b32d01b60b0bcbe16d0f1 100644 --- a/core/java/android/transition/TransitionInflater.java +++ b/core/java/android/transition/TransitionInflater.java @@ -20,9 +20,7 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.util.ArrayMap; import android.util.AttributeSet; -import android.util.SparseArray; import android.util.Xml; import android.view.InflateException; import android.view.ViewGroup; @@ -43,15 +41,7 @@ import java.util.ArrayList; */ public class TransitionInflater { - // We only need one inflater for any given context. Also, this allows us to associate - // ids with unique instances per-Context, used to avoid re-inflating - // already-inflated resources into new/different instances - private static final ArrayMap sInflaterMap = - new ArrayMap(); - private Context mContext; - // TODO: do we need id maps for transitions and transitionMgrs as well? - SparseArray mScenes = new SparseArray(); private TransitionInflater(Context context) { mContext = context; @@ -61,13 +51,7 @@ public class TransitionInflater { * Obtains the TransitionInflater from the given context. */ public static TransitionInflater from(Context context) { - TransitionInflater inflater = sInflaterMap.get(context); - if (inflater != null) { - return inflater; - } - inflater = new TransitionInflater(context); - sInflaterMap.put(context, inflater); - return inflater; + return new TransitionInflater(context); } /** diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index 404709c6a112a27fac8e7b73e457d7cbd6ee012e..3bf67902e98622ccb18c06e7a2d56039eb24c63c 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -19,6 +19,7 @@ package android.transition; import android.content.Context; import android.util.ArrayMap; import android.util.Log; +import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -81,6 +82,8 @@ public class TransitionManager { * an {@link AutoTransition} instance. * * @param transition The default transition to be used for scene changes. + * + * @hide pending later changes */ public void setDefaultTransition(Transition transition) { sDefaultTransition = transition; @@ -92,6 +95,8 @@ public class TransitionManager { * * @return The current default transition. * @see #setDefaultTransition(Transition) + * + * @hide pending later changes */ public static Transition getDefaultTransition() { return sDefaultTransition; @@ -104,7 +109,7 @@ public class TransitionManager { * transition to run. * @param transition The transition that will play when the given scene is * entered. A value of null will result in the default behavior of - * using the {@link #getDefaultTransition() default transition} instead. + * using the default transition instead. */ public void setTransition(Scene scene, Transition transition) { mSceneTransitions.put(scene, transition); @@ -120,7 +125,7 @@ public class TransitionManager { * be run * @param transition The transition that will play when the given scene is * entered. A value of null will result in the default behavior of - * using the {@link #getDefaultTransition() default transition} instead. + * using the default transition instead. */ public void setTransition(Scene fromScene, Scene toScene, Transition transition) { ArrayMap sceneTransitionMap = mScenePairTransitions.get(toScene); @@ -138,8 +143,8 @@ public class TransitionManager { * * @param scene The scene being entered * @return The Transition to be used for the given scene change. If no - * Transition was specified for this scene change, the {@link #getDefaultTransition() - * default transition} will be used instead. + * Transition was specified for this scene change, the default transition + * will be used instead. */ private Transition getTransition(Scene scene) { Transition transition = null; @@ -205,47 +210,90 @@ public class TransitionManager { private static void sceneChangeRunTransition(final ViewGroup sceneRoot, final Transition transition) { - if (transition != null) { - final ViewTreeObserver observer = sceneRoot.getViewTreeObserver(); - final ViewTreeObserver.OnPreDrawListener listener = - new ViewTreeObserver.OnPreDrawListener() { - public boolean onPreDraw() { - sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this); - sPendingTransitions.remove(sceneRoot); - // Add to running list, handle end to remove it - final ArrayMap> runningTransitions = - getRunningTransitions(); - ArrayList currentTransitions = runningTransitions.get(sceneRoot); - ArrayList previousRunningTransitions = null; - if (currentTransitions == null) { - currentTransitions = new ArrayList(); - runningTransitions.put(sceneRoot, currentTransitions); - } else if (currentTransitions.size() > 0) { - previousRunningTransitions = new ArrayList(currentTransitions); - } - currentTransitions.add(transition); - transition.addListener(new Transition.TransitionListenerAdapter() { - @Override - public void onTransitionEnd(Transition transition) { - ArrayList currentTransitions = - runningTransitions.get(sceneRoot); - currentTransitions.remove(transition); - } - }); - transition.captureValues(sceneRoot, false); - if (previousRunningTransitions != null) { - for (Transition runningTransition : previousRunningTransitions) { - runningTransition.resume(); - } - } - transition.playTransition(sceneRoot); + if (transition != null && sceneRoot != null) { + MultiListener listener = new MultiListener(transition, sceneRoot); + sceneRoot.addOnAttachStateChangeListener(listener); + sceneRoot.getViewTreeObserver().addOnPreDrawListener(listener); + } + } - return true; + /** + * This private utility class is used to listen for both OnPreDraw and + * OnAttachStateChange events. OnPreDraw events are the main ones we care + * about since that's what triggers the transition to take place. + * OnAttachStateChange events are also important in case the view is removed + * from the hierarchy before the OnPreDraw event takes place; it's used to + * clean up things since the OnPreDraw listener didn't get called in time. + */ + private static class MultiListener implements ViewTreeObserver.OnPreDrawListener, + View.OnAttachStateChangeListener { + + Transition mTransition; + ViewGroup mSceneRoot; + + MultiListener(Transition transition, ViewGroup sceneRoot) { + mTransition = transition; + mSceneRoot = sceneRoot; + } + + private void removeListeners() { + mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this); + mSceneRoot.removeOnAttachStateChangeListener(this); + } + + @Override + public void onViewAttachedToWindow(View v) { + } + + @Override + public void onViewDetachedFromWindow(View v) { + removeListeners(); + + sPendingTransitions.remove(mSceneRoot); + ArrayList runningTransitions = getRunningTransitions().get(mSceneRoot); + if (runningTransitions != null && runningTransitions.size() > 0) { + for (Transition runningTransition : runningTransitions) { + runningTransition.resume(); } - }; - observer.addOnPreDrawListener(listener); + } + mTransition.clearValues(true); } - } + + @Override + public boolean onPreDraw() { + removeListeners(); + sPendingTransitions.remove(mSceneRoot); + // Add to running list, handle end to remove it + final ArrayMap> runningTransitions = + getRunningTransitions(); + ArrayList currentTransitions = runningTransitions.get(mSceneRoot); + ArrayList previousRunningTransitions = null; + if (currentTransitions == null) { + currentTransitions = new ArrayList(); + runningTransitions.put(mSceneRoot, currentTransitions); + } else if (currentTransitions.size() > 0) { + previousRunningTransitions = new ArrayList(currentTransitions); + } + currentTransitions.add(mTransition); + mTransition.addListener(new Transition.TransitionListenerAdapter() { + @Override + public void onTransitionEnd(Transition transition) { + ArrayList currentTransitions = + runningTransitions.get(mSceneRoot); + currentTransitions.remove(transition); + } + }); + mTransition.captureValues(mSceneRoot, false); + if (previousRunningTransitions != null) { + for (Transition runningTransition : previousRunningTransitions) { + runningTransition.resume(); + } + } + mTransition.playTransition(mSceneRoot); + + return true; + } + }; private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) { diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index dae47b88c4e23f909793d254b3d4bc5e2f5e3901..6cda90536c58af77b6c1332ecffa4fc11ed4dad5 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -66,6 +66,14 @@ public class DisplayMetrics { */ public static final int DENSITY_XHIGH = 320; + /** + * Intermediate density for screens that sit somewhere between + * {@link #DENSITY_XHIGH} (320dpi) and {@link #DENSITY_XXHIGH} (480 dpi). + * This is not a density that applications should target, instead relying + * on the system to scale their {@link #DENSITY_XXHIGH} assets for them. + */ + public static final int DENSITY_400 = 400; + /** * Standard quantized DPI for extra-extra-high-density screens. Applications * should not generally worry about this density; relying on XHIGH graphics diff --git a/core/java/android/util/MapCollections.java b/core/java/android/util/MapCollections.java index f4a9b0bbb73e9a09e4f6daa6cbc2fb79e68925df..28b788b97aa7ceb601740a59df53a004d5c66a33 100644 --- a/core/java/android/util/MapCollections.java +++ b/core/java/android/util/MapCollections.java @@ -97,10 +97,10 @@ abstract class MapCollections { if (!mEntryValid) { throw new IllegalStateException(); } + colRemoveAt(mIndex); mIndex--; mEnd--; mEntryValid = false; - colRemoveAt(mIndex); } @Override diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index f28e4b58108f892a3e0315a43e9efb23eb83c9ae..f1523aefe04c6379ac567c08ab66220965d9279e 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -23,6 +23,9 @@ import android.os.Message; import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; +import android.util.TimeUtils; + +import java.io.PrintWriter; /** * Coordinates the timing of animations, input and drawing. @@ -256,6 +259,15 @@ public final class Choreographer { return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay; } + void dump(String prefix, PrintWriter writer) { + String innerPrefix = prefix + " "; + writer.print(prefix); writer.println("Choreographer:"); + writer.print(innerPrefix); writer.print("mFrameScheduled="); + writer.println(mFrameScheduled); + writer.print(innerPrefix); writer.print("mLastFrameTime="); + writer.println(TimeUtils.formatUptime(mLastFrameTimeNanos / 1000000)); + } + /** * Posts a callback to run on the next frame. *

    diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 354ea6613a397fd861c310ff1e3f0a415d7c43c5..7d310a254990888e684e133d0871eaea832de836 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -643,6 +643,15 @@ public final class Display { || uid == 0; } + /** + * Returns true if the display is a public presentation display. + * @hide + */ + public boolean isPublicPresentation() { + return (mFlags & (Display.FLAG_PRIVATE | Display.FLAG_PRESENTATION)) == + Display.FLAG_PRESENTATION; + } + private void updateDisplayInfoLocked() { // Note: The display manager caches display info objects on our behalf. DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId); diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java index 9a89fa58d4573318cb9cc68ec3f81666821b1f68..324a1ae3e6062765ab043c707c6e52b562242220 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -123,7 +123,7 @@ public class Gravity public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END; /** - * Apply a gravity constant to an object. This suppose that the layout direction is LTR. + * Apply a gravity constant to an object. This supposes that the layout direction is LTR. * * @param gravity The desired placement of the object, as defined by the * constants in this class. diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 9d4af006258e2ea448fe995eed27df7cd65f4458..c92a104b239d4dd553a21c5c1981e16ed730d90d 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -78,7 +78,8 @@ interface IWindowManager void addWindowToken(IBinder token, int type); void removeWindowToken(IBinder token); void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId, - int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId); + int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId, + int configChanges); void setAppGroupId(IBinder token, int groupId); void setAppOrientation(IApplicationToken token, int requestedOrientation); int getAppOrientation(IApplicationToken token); @@ -211,7 +212,7 @@ interface IWindowManager /** * Called by the status bar to notify Views of changes to System UI visiblity. */ - void statusBarVisibilityChanged(int visibility); + oneway void statusBarVisibilityChanged(int visibility); /** * Block until the given window has been drawn to the screen. diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index f5ee7edca50ae76e8b26059870a1c1d43893b73e..25972e7737a85fd35c6929764a19b23b7c58cf82 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -48,7 +48,7 @@ public abstract class InputEventReceiver { InputChannel inputChannel, MessageQueue messageQueue); private static native void nativeDispose(int receiverPtr); private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled); - private static native void nativeConsumeBatchedInputEvents(int receiverPtr, + private static native boolean nativeConsumeBatchedInputEvents(int receiverPtr, long frameTimeNanos); /** @@ -165,14 +165,17 @@ public abstract class InputEventReceiver { * * @param frameTimeNanos The time in the {@link System#nanoTime()} time base * when the current display frame started rendering, or -1 if unknown. + * + * @return Whether a batch was consumed */ - public final void consumeBatchedInputEvents(long frameTimeNanos) { + public final boolean consumeBatchedInputEvents(long frameTimeNanos) { if (mReceiverPtr == 0) { Log.w(TAG, "Attempted to consume batched input events but the input event " + "receiver has already been disposed."); } else { - nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos); + return nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos); } + return false; } // Called from native code. diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index f36c78fd480783f111a1051a8b4f3fbe9dd13b35..42a58a8efc22239cbea53a24c29090e503ea1a4b 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -323,6 +323,10 @@ public class ScaleGestureDetector { mInProgress = false; mInitialSpan = 0; mDoubleTapMode = DOUBLE_TAP_MODE_NONE; + } else if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS && streamComplete) { + mInProgress = false; + mInitialSpan = 0; + mDoubleTapMode = DOUBLE_TAP_MODE_NONE; } if (streamComplete) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e2d98edafc49afde5477b711c655165deee24324..b0bae463112dcd8daa2e96712136ae168aa9abfa 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -18,7 +18,6 @@ package android.view; import android.content.ClipData; import android.content.Context; -import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; @@ -3102,6 +3101,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static final int UNDEFINED_PADDING = Integer.MIN_VALUE; + /** + * Cache if a left padding has been defined + */ + private boolean mLeftPaddingDefined = false; + + /** + * Cache if a right padding has been defined + */ + private boolean mRightPaddingDefined = false; + /** * @hide */ @@ -3530,10 +3539,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int overScrollMode = mOverScrollMode; boolean initializeScrollbars = false; - boolean leftPaddingDefined = false; - boolean rightPaddingDefined = false; boolean startPaddingDefined = false; boolean endPaddingDefined = false; + boolean leftPaddingDefined = false; + boolean rightPaddingDefined = false; final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; @@ -3865,6 +3874,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setBackground(background); } + // setBackground above will record that padding is currently provided by the background. + // If we have padding specified via xml, record that here instead and use it. + mLeftPaddingDefined = leftPaddingDefined; + mRightPaddingDefined = rightPaddingDefined; + if (padding >= 0) { leftPadding = padding; topPadding = padding; @@ -3882,11 +3896,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Padding from the background drawable is stored at this point in mUserPaddingLeftInitial // and mUserPaddingRightInitial) so drawable padding will be used as ultimate default if // defined. - if (!leftPaddingDefined && startPaddingDefined) { + if (!mLeftPaddingDefined && startPaddingDefined) { leftPadding = startPadding; } mUserPaddingLeftInitial = (leftPadding >= 0) ? leftPadding : mUserPaddingLeftInitial; - if (!rightPaddingDefined && endPaddingDefined) { + if (!mRightPaddingDefined && endPaddingDefined) { rightPadding = endPadding; } mUserPaddingRightInitial = (rightPadding >= 0) ? rightPadding : mUserPaddingRightInitial; @@ -3898,10 +3912,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // defined. final boolean hasRelativePadding = startPaddingDefined || endPaddingDefined; - if (leftPaddingDefined && !hasRelativePadding) { + if (mLeftPaddingDefined && !hasRelativePadding) { mUserPaddingLeftInitial = leftPadding; } - if (rightPaddingDefined && !hasRelativePadding) { + if (mRightPaddingDefined && !hasRelativePadding) { mUserPaddingRightInitial = rightPadding; } } @@ -5900,6 +5914,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sThreadLocal.set(localInsets); } boolean res = computeFitSystemWindows(insets, localInsets); + mUserPaddingLeftInitial = localInsets.left; + mUserPaddingRightInitial = localInsets.right; internalSetPadding(localInsets.left, localInsets.top, localInsets.right, localInsets.bottom); return res; @@ -9044,9 +9060,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public interface OnLayoutChangeListener { /** - * Called when the focus state of a view has changed. + * Called when the layout bounds of a view changes due to layout processing. * - * @param v The view whose state has changed. + * @param v The view whose bounds have changed. * @param left The new value of the view's left property. * @param top The new value of the view's top property. * @param right The new value of the view's right property. @@ -10287,7 +10303,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * The horizontal location of this view relative to its {@link #getTop() top} position. + * The vertical location of this view relative to its {@link #getTop() top} position. * This position is post-layout, in addition to wherever the object's * layout placed it. * @@ -12133,12 +12149,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (!isTextAlignmentResolved()) { resolveTextAlignment(); } - if (!isPaddingResolved()) { - resolvePadding(); - } + // Should resolve Drawables before Padding because we need the layout direction of the + // Drawable to correctly resolve Padding. if (!isDrawablesResolved()) { resolveDrawables(); } + if (!isPaddingResolved()) { + resolvePadding(); + } onRtlPropertiesChanged(getLayoutDirection()); return true; } @@ -12341,6 +12359,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // If start / end padding are defined, they will be resolved (hence overriding) to // left / right or right / left depending on the resolved layout direction. // If start / end padding are not defined, use the left / right ones. + if (mBackground != null && (!mLeftPaddingDefined || !mRightPaddingDefined)) { + Rect padding = sThreadLocal.get(); + if (padding == null) { + padding = new Rect(); + sThreadLocal.set(padding); + } + mBackground.getPadding(padding); + if (!mLeftPaddingDefined) { + mUserPaddingLeftInitial = padding.left; + } + if (!mRightPaddingDefined) { + mUserPaddingRightInitial = padding.right; + } + } switch (resolvedLayoutDirection) { case LAYOUT_DIRECTION_RTL: if (mUserPaddingStart != UNDEFINED_PADDING) { @@ -15336,6 +15368,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mUserPaddingRightInitial = padding.right; internalSetPadding(padding.left, padding.top, padding.right, padding.bottom); } + mLeftPaddingDefined = false; + mRightPaddingDefined = false; } // Compare the minimum sizes of the old Drawable and the new. If there isn't an old or @@ -15432,6 +15466,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mUserPaddingLeftInitial = left; mUserPaddingRightInitial = right; + mLeftPaddingDefined = true; + mRightPaddingDefined = true; + internalSetPadding(left, top, right, bottom); } @@ -15517,6 +15554,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mUserPaddingStart = start; mUserPaddingEnd = end; + mLeftPaddingDefined = true; + mRightPaddingDefined = true; switch(getLayoutDirection()) { case LAYOUT_DIRECTION_RTL: diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 946106868d57cfa88ce80758c1021270bbf2e221..bc0d7e3c806be0a0f040e67017d0afc8b41419c6 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -77,8 +77,10 @@ import com.android.internal.policy.PolicyManager; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.RootViewSurfaceTaker; +import java.io.FileDescriptor; import java.io.IOException; import java.io.OutputStream; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashSet; @@ -3391,16 +3393,7 @@ public final class ViewRootImpl implements ViewParent, public final void deliver(QueuedInputEvent q) { if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { forward(q); - } else if (mView == null || !mAdded) { - Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent); - finish(q, false); - } else if (!mAttachInfo.mHasWindowFocus && - !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) && - !isTerminalInputEvent(q.mEvent)) { - // If this is a focused event and the window doesn't currently have input focus, - // then drop this event. This could be an event that came back from the previous - // stage but the window has lost focus in the meantime. - Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent); + } else if (shouldDropInputEvent(q)) { finish(q, false); } else { apply(q, onProcess(q)); @@ -3458,6 +3451,28 @@ public final class ViewRootImpl implements ViewParent, finishInputEvent(q); } } + + protected boolean shouldDropInputEvent(QueuedInputEvent q) { + if (mView == null || !mAdded) { + Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent); + return true; + } else if (!mAttachInfo.mHasWindowFocus && + !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) && + !isTerminalInputEvent(q.mEvent)) { + // If this is a focused event and the window doesn't currently have input focus, + // then drop this event. This could be an event that came back from the previous + // stage but the window has lost focus in the meantime. + Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent); + return true; + } + return false; + } + + void dump(String prefix, PrintWriter writer) { + if (mNext != null) { + mNext.dump(prefix, writer); + } + } } /** @@ -3595,6 +3610,16 @@ public final class ViewRootImpl implements ViewParent, mQueueLength -= 1; Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength); } + + @Override + void dump(String prefix, PrintWriter writer) { + writer.print(prefix); + writer.print(getClass().getName()); + writer.print(": mQueueLength="); + writer.println(mQueueLength); + + super.dump(prefix, writer); + } } /** @@ -3828,6 +3853,10 @@ public final class ViewRootImpl implements ViewParent, return FINISH_HANDLED; } + if (shouldDropInputEvent(q)) { + return FINISH_NOT_HANDLED; + } + // If the Control modifier is held, try to interpret the key as a shortcut. if (event.getAction() == KeyEvent.ACTION_DOWN && event.isCtrlPressed() @@ -3836,12 +3865,18 @@ public final class ViewRootImpl implements ViewParent, if (mView.dispatchKeyShortcutEvent(event)) { return FINISH_HANDLED; } + if (shouldDropInputEvent(q)) { + return FINISH_NOT_HANDLED; + } } // Apply the fallback event policy. if (mFallbackEventHandler.dispatchKeyEvent(event)) { return FINISH_HANDLED; } + if (shouldDropInputEvent(q)) { + return FINISH_NOT_HANDLED; + } // Handle automatic focus changes. if (event.getAction() == KeyEvent.ACTION_DOWN) { @@ -5201,6 +5236,53 @@ public final class ViewRootImpl implements ViewParent, mView.debug(); } + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + String innerPrefix = prefix + " "; + writer.print(prefix); writer.println("ViewRoot:"); + writer.print(innerPrefix); writer.print("mAdded="); writer.print(mAdded); + writer.print(" mRemoved="); writer.println(mRemoved); + writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled="); + writer.println(mConsumeBatchedInputScheduled); + writer.print(innerPrefix); writer.print("mPendingInputEventCount="); + writer.println(mPendingInputEventCount); + writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled="); + writer.println(mProcessInputEventsScheduled); + writer.print(innerPrefix); writer.print("mTraversalScheduled="); + writer.print(mTraversalScheduled); + if (mTraversalScheduled) { + writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")"); + } else { + writer.println(); + } + mFirstInputStage.dump(innerPrefix, writer); + + mChoreographer.dump(prefix, writer); + + writer.print(prefix); writer.println("View Hierarchy:"); + dumpViewHierarchy(innerPrefix, writer, mView); + } + + private void dumpViewHierarchy(String prefix, PrintWriter writer, View view) { + writer.print(prefix); + if (view == null) { + writer.println("null"); + return; + } + writer.println(view.toString()); + if (!(view instanceof ViewGroup)) { + return; + } + ViewGroup grp = (ViewGroup)view; + final int N = grp.getChildCount(); + if (N <= 0) { + return; + } + prefix = prefix + " "; + for (int i=0; inot be set up to be able - * to display text, so it should only be used in situations where this is - * not needed. + * of the application be shown behind, through transparent UI parts in the + * fullscreen IME. The part of the UI visible to the user may not be responsive + * to touch because the IME will receive touch events, which may confuse the + * user; use {@link #IME_FLAG_NO_FULLSCREEN} instead for a better experience. + * Using this flag is discouraged and it may become deprecated in the future. + * Its meaning is unclear in some situations and it may not work appropriately + * on older versions of the platform. */ public static final int IME_FLAG_NO_EXTRACT_UI = 0x10000000; /** - * Flag of {@link #imeOptions}: used in conjunction with - * {@link #IME_MASK_ACTION}, this indicates that the action should not - * be available as an accessory button when the input method is full-screen. - * Note that by setting this flag, there can be cases where the action - * is simply never available to the user. Setting this generally means - * that you think showing text being edited is more important than the - * action you have supplied. + * Flag of {@link #imeOptions}: used in conjunction with one of the actions + * masked by {@link #IME_MASK_ACTION}, this indicates that the action + * should not be available as an accessory button on the right of the extracted + * text when the input method is full-screen. Note that by setting this flag, + * there can be cases where the action is simply never available to the + * user. Setting this generally means that you think that in fullscreen mode, + * where there is little space to show the text, it's not worth taking some + * screen real estate to display the action and it should be used instead + * to show more text. */ public static final int IME_FLAG_NO_ACCESSORY_ACTION = 0x20000000; /** - * Flag of {@link #imeOptions}: used in conjunction with - * {@link #IME_MASK_ACTION}, this indicates that the action should not - * be available in-line as a replacement for "enter" key. Typically this is - * because the action has such a significant impact or is not recoverable - * enough that accidentally hitting it should be avoided, such as sending - * a message. Note that {@link android.widget.TextView} will automatically set this - * flag for you on multi-line text views. + * Flag of {@link #imeOptions}: used in conjunction with one of the actions + * masked by {@link #IME_MASK_ACTION}. If this flag is not set, IMEs will + * normally replace the "enter" key with the action supplied. This flag + * indicates that the action should not be available in-line as a replacement + * for the "enter" key. Typically this is because the action has such a + * significant impact or is not recoverable enough that accidentally hitting + * it should be avoided, such as sending a message. Note that + * {@link android.widget.TextView} will automatically set this flag for you + * on multi-line text views. */ public static final int IME_FLAG_NO_ENTER_ACTION = 0x40000000; /** - * Flag of {@link #imeOptions}: used to request that the IME is capable of + * Flag of {@link #imeOptions}: used to request an IME that is capable of * inputting ASCII characters. The intention of this flag is to ensure that - * the user can type Roman alphabet characters in a {@link android.widget.TextView} - * used for, typically, account ID or password input. It is expected that IMEs - * normally are able to input ASCII even without being told so (such IMEs - * already respect this flag in a sense), but there could be some cases they - * aren't when, for instance, only non-ASCII input languagaes like Arabic, - * Greek, Hebrew, Russian are enabled in the IME. Applications need to be - * aware that the flag is not a guarantee, and not all IMEs will respect it. + * the user can type Roman alphabet characters in a {@link android.widget.TextView}. + * It is typically used for an account ID or password input. A lot of the time, + * IMEs are already able to input ASCII even without being told so (such IMEs + * already respect this flag in a sense), but there are cases when this is not + * the default. For instance, users of languages using a different script like + * Arabic, Greek, Hebrew or Russian typically have a keyboard that can't + * input ASCII characters by default. Applications need to be + * aware that the flag is not a guarantee, and some IMEs may not respect it. * However, it is strongly recommended for IME authors to respect this flag - * especially when their IME could end up with a state that has only non-ASCII - * input languages enabled. + * especially when their IME could end up with a state where only languages + * using non-ASCII are enabled. */ public static final int IME_FLAG_FORCE_ASCII = 0x80000000; @@ -209,8 +224,13 @@ public class EditorInfo implements InputType, Parcelable { /** * In some cases an IME may be able to display an arbitrary label for - * a command the user can perform, which you can specify here. You can - * not count on this being used. + * a command the user can perform, which you can specify here. This is + * typically used as the label for the action to use in-line as a replacement + * for the "enter" key (see {@link #actionId}). Remember the key where + * this will be displayed is typically very small, and there are significant + * localization challenges to make this fit in all supported languages. Also + * you can not count absolutely on this being used, as some IMEs may + * ignore this. */ public CharSequence actionLabel = null; @@ -224,13 +244,17 @@ public class EditorInfo implements InputType, Parcelable { /** * The text offset of the start of the selection at the time editing - * began; -1 if not known. + * began; -1 if not known. Keep in mind some IMEs may not be able + * to give their full feature set without knowing the cursor position; + * avoid passing -1 here if you can. */ public int initialSelStart = -1; /** * The text offset of the end of the selection at the time editing - * began; -1 if not known. + * began; -1 if not known. Keep in mind some IMEs may not be able + * to give their full feature set without knowing the cursor position; + * avoid passing -1 here if you can. */ public int initialSelEnd = -1; @@ -280,7 +304,7 @@ public class EditorInfo implements InputType, Parcelable { * Any extra data to supply to the input method. This is for extended * communication with specific input methods; the name fields in the * bundle should be scoped (such as "com.mydomain.im.SOME_FIELD") so - * that they don't conflict with others. This field is can be + * that they don't conflict with others. This field can be * filled in from the {@link android.R.attr#editorExtras} * attribute of a TextView. */ diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index 59330caf0e4185e7b2fffa794b82b23e7f3dff75..3537aeccbb0b590798d3f480c268734815a2eb6c 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -142,7 +142,11 @@ public interface InputConnection { * conditions in implementing this call. An IME can make a change * to the text and use this method right away; you need to make * sure the returned value is consistent with the result of the - * latest edits. + * latest edits. Also, you may return less than n characters if performance + * dictates so, but keep in mind IMEs are relying on this for many + * functions: you should not, for example, limit the returned value to + * the current line, and specifically do not return 0 characters unless + * the cursor is really at the start of the text.

    * * @param n The expected length of the text. * @param flags Supplies additional options controlling how the text is @@ -176,7 +180,11 @@ public interface InputConnection { * conditions in implementing this call. An IME can make a change * to the text and use this method right away; you need to make * sure the returned value is consistent with the result of the - * latest edits.

    + * latest edits. Also, you may return less than n characters if performance + * dictates so, but keep in mind IMEs are relying on this for many + * functions: you should not, for example, limit the returned value to + * the current line, and specifically do not return 0 characters unless + * the cursor is really at the end of the text.

    * * @param n The expected length of the text. * @param flags Supplies additional options controlling how the text is diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index c440c7bd40c2e483ebab25a31739ac8d8ac06361..5df581194434725f13c3722e263f1bbd1a49e341 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -45,6 +45,17 @@ import java.util.Map; /** * This class is used to specify meta information of an input method. + * + *

    It should be defined in an XML resource file with an {@code <input-method>} element. + * For more information, see the guide to + * + * Creating an Input Method.

    + * + * @see InputMethodSubtype + * + * @attr ref android.R.styleable#InputMethod_settingsActivity + * @attr ref android.R.styleable#InputMethod_isDefault + * @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod */ public final class InputMethodInfo implements Parcelable { static final String TAG = "InputMethodInfo"; diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java index 88b29776fa0f6a89f04f9dac9fd5019901e26845..2ab30246662caf1acd903dc5912bc6b759aff5c1 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtype.java +++ b/core/java/android/view/inputmethod/InputMethodSubtype.java @@ -38,9 +38,22 @@ import java.util.Locale; * the specified subtype of the designated IME directly. * *

    It should be defined in an XML resource file of the input method with the - * <subtype> element. For more information, see the guide to - * + * <subtype> element, which resides within an {@code <input-method>} element. + * For more information, see the guide to + * * Creating an Input Method.

    + * + * @see InputMethodInfo + * + * @attr ref android.R.styleable#InputMethod_Subtype_label + * @attr ref android.R.styleable#InputMethod_Subtype_icon + * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeLocale + * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeMode + * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeExtraValue + * @attr ref android.R.styleable#InputMethod_Subtype_isAuxiliary + * @attr ref android.R.styleable#InputMethod_Subtype_overridesImplicitlyEnabledSubtype + * @attr ref android.R.styleable#InputMethod_Subtype_subtypeId + * @attr ref android.R.styleable#InputMethod_Subtype_isAsciiCapable */ public final class InputMethodSubtype implements Parcelable { private static final String TAG = InputMethodSubtype.class.getSimpleName(); @@ -521,6 +534,13 @@ public final class InputMethodSubtype implements Parcelable { private static int hashCodeInternal(String locale, String mode, String extraValue, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable) { + // CAVEAT: Must revisit how to compute needsToCalculateCompatibleHashCode when a new + // attribute is added in order to avoid enabled subtypes being unexpectedly disabled. + final boolean needsToCalculateCompatibleHashCode = !isAsciiCapable; + if (needsToCalculateCompatibleHashCode) { + return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary, + overridesImplicitlyEnabledSubtype}); + } return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary, overridesImplicitlyEnabledSubtype, isAsciiCapable}); } diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java new file mode 100644 index 0000000000000000000000000000000000000000..bbd3f2b0b15f812cd17614c0cc77fba35f08f9c3 --- /dev/null +++ b/core/java/android/webkit/CacheManager.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.content.Context; +import android.net.http.Headers; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; + + +/** + * Manages the HTTP cache used by an application's {@link WebView} instances. + * @deprecated Access to the HTTP cache will be removed in a future release. + * @hide Since {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ +// The class CacheManager provides the persistent cache of content that is +// received over the network. The component handles parsing of HTTP headers and +// utilizes the relevant cache headers to determine if the content should be +// stored and if so, how long it is valid for. Network requests are provided to +// this component and if they can not be resolved by the cache, the HTTP headers +// are attached, as appropriate, to the request for revalidation of content. The +// class also manages the cache size. +// +// CacheManager may only be used if your activity contains a WebView. +@Deprecated +public final class CacheManager { + /** + * Represents a resource stored in the HTTP cache. Instances of this class + * can be obtained by calling + * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map))}. + * + * @deprecated Access to the HTTP cache will be removed in a future release. + */ + @Deprecated + public static class CacheResult { + // these fields are saved to the database + int httpStatusCode; + long contentLength; + long expires; + String expiresString; + String localPath; + String lastModified; + String etag; + String mimeType; + String location; + String encoding; + String contentdisposition; + String crossDomain; + + // these fields are NOT saved to the database + InputStream inStream; + OutputStream outStream; + File outFile; + + /** + * Gets the status code of this cache entry. + * + * @return the status code of this cache entry + */ + public int getHttpStatusCode() { + return httpStatusCode; + } + + /** + * Gets the content length of this cache entry. + * + * @return the content length of this cache entry + */ + public long getContentLength() { + return contentLength; + } + + /** + * Gets the path of the file used to store the content of this cache + * entry, relative to the base directory of the cache. See + * {@link CacheManager#getCacheFileBaseDir CacheManager.getCacheFileBaseDir()}. + * + * @return the path of the file used to store this cache entry + */ + public String getLocalPath() { + return localPath; + } + + /** + * Gets the expiry date of this cache entry, expressed in milliseconds + * since midnight, January 1, 1970 UTC. + * + * @return the expiry date of this cache entry + */ + public long getExpires() { + return expires; + } + + /** + * Gets the expiry date of this cache entry, expressed as a string. + * + * @return the expiry date of this cache entry + * + */ + public String getExpiresString() { + return expiresString; + } + + /** + * Gets the date at which this cache entry was last modified, expressed + * as a string. + * + * @return the date at which this cache entry was last modified + */ + public String getLastModified() { + return lastModified; + } + + /** + * Gets the entity tag of this cache entry. + * + * @return the entity tag of this cache entry + */ + public String getETag() { + return etag; + } + + /** + * Gets the MIME type of this cache entry. + * + * @return the MIME type of this cache entry + */ + public String getMimeType() { + return mimeType; + } + + /** + * Gets the value of the HTTP 'Location' header with which this cache + * entry was received. + * + * @return the HTTP 'Location' header for this cache entry + */ + public String getLocation() { + return location; + } + + /** + * Gets the encoding of this cache entry. + * + * @return the encoding of this cache entry + */ + public String getEncoding() { + return encoding; + } + + /** + * Gets the value of the HTTP 'Content-Disposition' header with which + * this cache entry was received. + * + * @return the HTTP 'Content-Disposition' header for this cache entry + * + */ + public String getContentDisposition() { + return contentdisposition; + } + + /** + * Gets the input stream to the content of this cache entry, to allow + * content to be read. See + * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map)}. + * + * @return an input stream to the content of this cache entry + */ + public InputStream getInputStream() { + return inStream; + } + + /** + * Gets an output stream to the content of this cache entry, to allow + * content to be written. See + * {@link CacheManager#saveCacheFile CacheManager.saveCacheFile(String, CacheResult)}. + * + * @return an output stream to the content of this cache entry + */ + // Note that this is always null for objects returned by getCacheFile()! + public OutputStream getOutputStream() { + return outStream; + } + + + /** + * Sets an input stream to the content of this cache entry. + * + * @param stream an input stream to the content of this cache entry + */ + public void setInputStream(InputStream stream) { + this.inStream = stream; + } + + /** + * Sets the encoding of this cache entry. + * + * @param encoding the encoding of this cache entry + */ + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + /** + * @hide + */ + public void setContentLength(long contentLength) { + this.contentLength = contentLength; + } + } + + /** + * Gets the base directory in which the files used to store the contents of + * cache entries are placed. See + * {@link CacheManager.CacheResult#getLocalPath CacheManager.CacheResult.getLocalPath()}. + * + * @return the base directory of the cache + * @deprecated This method no longer has any effect and always returns null. + */ + @Deprecated + public static File getCacheFileBaseDir() { + return null; + } + + /** + * Gets whether the HTTP cache is disabled. + * + * @return true if the HTTP cache is disabled + * @deprecated This method no longer has any effect and always returns false. + */ + @Deprecated + public static boolean cacheDisabled() { + return false; + } + + /** + * Starts a cache transaction. Returns true if this is the only running + * transaction. Otherwise, this transaction is nested inside currently + * running transactions and false is returned. + * + * @return true if this is the only running transaction + * @deprecated This method no longer has any effect and always returns false. + */ + @Deprecated + public static boolean startCacheTransaction() { + return false; + } + + /** + * Ends the innermost cache transaction and returns whether this was the + * only running transaction. + * + * @return true if this was the only running transaction + * @deprecated This method no longer has any effect and always returns false. + */ + @Deprecated + public static boolean endCacheTransaction() { + return false; + } + + /** + * Gets the cache entry for the specified URL, or null if none is found. + * If a non-null value is provided for the HTTP headers map, and the cache + * entry needs validation, appropriate headers will be added to the map. + * The input stream of the CacheEntry object should be closed by the caller + * when access to the underlying file is no longer required. + * + * @param url the URL for which a cache entry is requested + * @param headers a map from HTTP header name to value, to be populated + * for the returned cache entry + * @return the cache entry for the specified URL + * @deprecated This method no longer has any effect and always returns null. + */ + @Deprecated + public static CacheResult getCacheFile(String url, + Map headers) { + return null; + } + + /** + * Adds a cache entry to the HTTP cache for the specicifed URL. Also closes + * the cache entry's output stream. + * + * @param url the URL for which the cache entry should be added + * @param cacheResult the cache entry to add + * @deprecated Access to the HTTP cache will be removed in a future release. + */ + @Deprecated + public static void saveCacheFile(String url, CacheResult cacheResult) { + saveCacheFile(url, 0, cacheResult); + } + + static void saveCacheFile(String url, long postIdentifier, + CacheResult cacheRet) { + try { + cacheRet.outStream.close(); + } catch (IOException e) { + return; + } + + // This method is exposed in the public API but the API provides no + // way to obtain a new CacheResult object with a non-null output + // stream ... + // - CacheResult objects returned by getCacheFile() have a null + // output stream. + // - new CacheResult objects have a null output stream and no + // setter is provided. + // Since this method throws a null pointer exception in this case, + // it is effectively useless from the point of view of the public + // API. + // + // With the Chromium HTTP stack we continue to throw the same + // exception for 'backwards compatibility' with the Android HTTP + // stack. + // + // This method is not used from within this package, and for public API + // use, we should already have thrown an exception above. + assert false; + } +} diff --git a/core/java/android/webkit/PluginData.java b/core/java/android/webkit/PluginData.java new file mode 100644 index 0000000000000000000000000000000000000000..88fc9b70cc7a8d0b57b902d3e8ce45113c91c172 --- /dev/null +++ b/core/java/android/webkit/PluginData.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import java.io.InputStream; +import java.util.Map; + +/** + * This class encapsulates the content generated by a plugin. The + * data itself is meant to be loaded into webkit via the + * PluginContentLoader class, which needs to be able to construct an + * HTTP response. For this, it needs a stream with the response body, + * the length of the body, the response headers, and the response + * status code. The PluginData class is the container for all these + * parts. + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ +@Deprecated +public final class PluginData { + /** + * The content stream. + */ + private InputStream mStream; + /** + * The content length. + */ + private long mContentLength; + /** + * The associated HTTP response headers stored as a map of + * lowercase header name to [ unmodified header name, header value]. + * TODO: This design was always a hack. Remove (involves updating + * the Gears C++ side). + */ + private Map mHeaders; + + /** + * The associated HTTP response code. + */ + private int mStatusCode; + + /** + * Creates a PluginData instance. + * + * @param stream The stream that supplies content for the plugin. + * @param length The length of the plugin content. + * @param headers The response headers. Map of + * lowercase header name to [ unmodified header name, header value] + * @param length The HTTP response status code. + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public PluginData( + InputStream stream, + long length, + Map headers, + int code) { + mStream = stream; + mContentLength = length; + mHeaders = headers; + mStatusCode = code; + } + + /** + * Returns the input stream that contains the plugin content. + * + * @return An InputStream instance with the plugin content. + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public InputStream getInputStream() { + return mStream; + } + + /** + * Returns the length of the plugin content. + * + * @return the length of the plugin content. + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public long getContentLength() { + return mContentLength; + } + + /** + * Returns the HTTP response headers associated with the plugin + * content. + * + * @return A Map containing all headers. The + * mapping is 'lowercase header name' to ['unmodified header + * name', header value]. + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public Map getHeaders() { + return mHeaders; + } + + /** + * Returns the HTTP status code for the response. + * + * @return The HTTP statue code, e.g 200. + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public int getStatusCode() { + return mStatusCode; + } +} diff --git a/core/java/android/webkit/UrlInterceptHandler.java b/core/java/android/webkit/UrlInterceptHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..59fc0cba1fd38e40de573b3e6c084e5f79729914 --- /dev/null +++ b/core/java/android/webkit/UrlInterceptHandler.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.webkit.CacheManager.CacheResult; +import android.webkit.PluginData; +import java.util.Map; + +/** + * @hide + * @deprecated This interface was inteded to be used by Gears. Since Gears was + * deprecated, so is this class. + */ +@Deprecated +public interface UrlInterceptHandler { + + /** + * Given an URL, returns the CacheResult which contains the + * surrogate response for the request, or null if the handler is + * not interested. + * + * @param url URL string. + * @param headers The headers associated with the request. May be null. + * @return The CacheResult containing the surrogate response. + * + * @hide + * @deprecated Do not use, this interface is deprecated. + */ + @Deprecated + public CacheResult service(String url, Map headers); + + /** + * Given an URL, returns the PluginData which contains the + * surrogate response for the request, or null if the handler is + * not interested. + * + * @param url URL string. + * @param headers The headers associated with the request. May be null. + * @return The PluginData containing the surrogate response. + * + * @hide + * @deprecated Do not use, this interface is deprecated. + */ + @Deprecated + public PluginData getPluginData(String url, Map headers); +} diff --git a/core/java/android/webkit/UrlInterceptRegistry.java b/core/java/android/webkit/UrlInterceptRegistry.java new file mode 100644 index 0000000000000000000000000000000000000000..bdf6747aa5a4d91ee6dce90962b0603531e3b9eb --- /dev/null +++ b/core/java/android/webkit/UrlInterceptRegistry.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.webkit.CacheManager.CacheResult; +import android.webkit.PluginData; +import android.webkit.UrlInterceptHandler; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; + +/** + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ +@Deprecated +public final class UrlInterceptRegistry { + + private final static String LOGTAG = "intercept"; + + private static boolean mDisabled = false; + + private static LinkedList mHandlerList; + + private static synchronized LinkedList getHandlers() { + if(mHandlerList == null) + mHandlerList = new LinkedList(); + return mHandlerList; + } + + /** + * set the flag to control whether url intercept is enabled or disabled + * + * @param disabled true to disable the cache + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public static synchronized void setUrlInterceptDisabled(boolean disabled) { + mDisabled = disabled; + } + + /** + * get the state of the url intercept, enabled or disabled + * + * @return return if it is disabled + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public static synchronized boolean urlInterceptDisabled() { + return mDisabled; + } + + /** + * Register a new UrlInterceptHandler. This handler will be called + * before any that were previously registered. + * + * @param handler The new UrlInterceptHandler object + * @return true if the handler was not previously registered. + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public static synchronized boolean registerHandler( + UrlInterceptHandler handler) { + if (!getHandlers().contains(handler)) { + getHandlers().addFirst(handler); + return true; + } else { + return false; + } + } + + /** + * Unregister a previously registered UrlInterceptHandler. + * + * @param handler A previously registered UrlInterceptHandler. + * @return true if the handler was found and removed from the list. + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public static synchronized boolean unregisterHandler( + UrlInterceptHandler handler) { + return getHandlers().remove(handler); + } + + /** + * Given an url, returns the CacheResult of the first + * UrlInterceptHandler interested, or null if none are. + * + * @return A CacheResult containing surrogate content. + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public static synchronized CacheResult getSurrogate( + String url, Map headers) { + if (urlInterceptDisabled()) { + return null; + } + Iterator iter = getHandlers().listIterator(); + while (iter.hasNext()) { + UrlInterceptHandler handler = (UrlInterceptHandler) iter.next(); + CacheResult result = handler.service(url, headers); + if (result != null) { + return result; + } + } + return null; + } + + /** + * Given an url, returns the PluginData of the first + * UrlInterceptHandler interested, or null if none are or if + * intercepts are disabled. + * + * @return A PluginData instance containing surrogate content. + * + * @hide + * @deprecated This class was intended to be used by Gears. Since Gears was + * deprecated, so is this class. + */ + @Deprecated + public static synchronized PluginData getPluginData( + String url, Map headers) { + if (urlInterceptDisabled()) { + return null; + } + Iterator iter = getHandlers().listIterator(); + while (iter.hasNext()) { + UrlInterceptHandler handler = (UrlInterceptHandler) iter.next(); + PluginData data = handler.getPluginData(url, headers); + if (data != null) { + return data; + } + } + return null; + } +} diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 3eb0052145569dac857336b3659cf2a2ea70ab32..092f4749e0404ab741392db73a8bd3ce3bc118f3 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -6686,6 +6686,13 @@ public abstract class AbsListView extends AdapterView implements Te scrap.dispatchStartTemporaryDetach(); + // The the accessibility state of the view may change while temporary + // detached and we do not allow detached views to fire accessibility + // events. So we are announcing that the subtree changed giving a chance + // to clients holding on to a view in this subtree to refresh it. + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); + // Don't scrap views that have transient state. final boolean scrapHasTransientState = scrap.hasTransientState(); if (scrapHasTransientState) { diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java index 736566e451128cdc218be2798b19b2ecd6c56da2..61df92265518d7f03a6cdbe56d76537405d97467 100644 --- a/core/java/android/widget/ActivityChooserModel.java +++ b/core/java/android/widget/ActivityChooserModel.java @@ -16,9 +16,12 @@ package android.widget; +import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.DataSetObservable; import android.os.AsyncTask; @@ -708,7 +711,12 @@ public class ActivityChooserModel extends DataSetObservable { final int resolveInfoCount = resolveInfos.size(); for (int i = 0; i < resolveInfoCount; i++) { ResolveInfo resolveInfo = resolveInfos.get(i); - mActivities.add(new ActivityResolveInfo(resolveInfo)); + ActivityInfo activityInfo = resolveInfo.activityInfo; + if (ActivityManager.checkComponentPermission(activityInfo.permission, + android.os.Process.myUid(), activityInfo.applicationInfo.uid, + activityInfo.exported) == PackageManager.PERMISSION_GRANTED) { + mActivities.add(new ActivityResolveInfo(resolveInfo)); + } } return true; } @@ -930,29 +938,31 @@ public class ActivityChooserModel extends DataSetObservable { private final class DefaultSorter implements ActivitySorter { private static final float WEIGHT_DECAY_COEFFICIENT = 0.95f; - private final Map mPackageNameToActivityMap = - new HashMap(); + private final Map mPackageNameToActivityMap = + new HashMap(); public void sort(Intent intent, List activities, List historicalRecords) { - Map packageNameToActivityMap = - mPackageNameToActivityMap; - packageNameToActivityMap.clear(); + Map componentNameToActivityMap = + mPackageNameToActivityMap; + componentNameToActivityMap.clear(); final int activityCount = activities.size(); for (int i = 0; i < activityCount; i++) { ActivityResolveInfo activity = activities.get(i); activity.weight = 0.0f; - String packageName = activity.resolveInfo.activityInfo.packageName; - packageNameToActivityMap.put(packageName, activity); + ComponentName componentName = new ComponentName( + activity.resolveInfo.activityInfo.packageName, + activity.resolveInfo.activityInfo.name); + componentNameToActivityMap.put(componentName, activity); } final int lastShareIndex = historicalRecords.size() - 1; float nextRecordWeight = 1; for (int i = lastShareIndex; i >= 0; i--) { HistoricalRecord historicalRecord = historicalRecords.get(i); - String packageName = historicalRecord.activity.getPackageName(); - ActivityResolveInfo activity = packageNameToActivityMap.get(packageName); + ComponentName componentName = historicalRecord.activity; + ActivityResolveInfo activity = componentNameToActivityMap.get(componentName); if (activity != null) { activity.weight += historicalRecord.weight * nextRecordWeight; nextRecordWeight = nextRecordWeight * WEIGHT_DECAY_COEFFICIENT; diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index dff1531fc8b7eacf2e10e45d662ea57f7f2673bb..86129643686680b283a53193a6f744b6ac6fb7e1 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -18,6 +18,7 @@ package android.widget; import com.android.internal.R; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -27,6 +28,7 @@ import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.util.Log; import android.view.ActionProvider; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -63,6 +65,8 @@ import android.widget.ListPopupWindow.ForwardingListener; */ public class ActivityChooserView extends ViewGroup implements ActivityChooserModelClient { + private static final String LOG_TAG = "ActivityChooserView"; + /** * An adapter for displaying the activities in an {@link AdapterView}. */ @@ -543,9 +547,9 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } // Activity chooser content. if (mDefaultActivityButton.getVisibility() == VISIBLE) { - mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground); + mActivityChooserContent.setBackground(mActivityChooserContentBackground); } else { - mActivityChooserContent.setBackgroundDrawable(null); + mActivityChooserContent.setBackground(null); } } @@ -577,7 +581,8 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod Intent launchIntent = mAdapter.getDataModel().chooseActivity(position); if (launchIntent != null) { launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - mContext.startActivity(launchIntent); + ResolveInfo resolveInfo = mAdapter.getDataModel().getActivity(position); + startActivity(launchIntent, resolveInfo); } } } break; @@ -595,7 +600,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod Intent launchIntent = mAdapter.getDataModel().chooseActivity(index); if (launchIntent != null) { launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - mContext.startActivity(launchIntent); + startActivity(launchIntent, defaultActivity); } } else if (view == mExpandActivityOverflowButton) { mIsSelectingDefaultActivity = false; @@ -632,6 +637,18 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod mOnDismissListener.onDismiss(); } } + + private void startActivity(Intent intent, ResolveInfo resolveInfo) { + try { + mContext.startActivity(intent); + } catch (RuntimeException re) { + CharSequence appLabel = resolveInfo.loadLabel(mContext.getPackageManager()); + String message = mContext.getString( + R.string.activitychooserview_choose_application_error, appLabel); + Log.e(LOG_TAG, message); + Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show(); + } + } } /** @@ -805,10 +822,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod return mDataModel.getHistorySize(); } - public int getMaxActivityCount() { - return mMaxActivityCount; - } - public ActivityChooserModel getDataModel() { return mDataModel; } diff --git a/core/java/android/widget/BaseAdapter.java b/core/java/android/widget/BaseAdapter.java index 401fcb83499d5cb85c5e4379079af3780471e776..c960342f7c652724fd97ea33cddec5ce91c0629c 100644 --- a/core/java/android/widget/BaseAdapter.java +++ b/core/java/android/widget/BaseAdapter.java @@ -24,8 +24,8 @@ import android.view.ViewGroup; /** * Common base class of common implementation for an {@link Adapter} that can be * used in both {@link ListView} (by implementing the specialized - * {@link ListAdapter} interface} and {@link Spinner} (by implementing the - * specialized {@link SpinnerAdapter} interface. + * {@link ListAdapter} interface) and {@link Spinner} (by implementing the + * specialized {@link SpinnerAdapter} interface). */ public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter { private final DataSetObservable mDataSetObservable = new DataSetObservable(); diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 4614c53eb9b2089f3ae06abdfa3e7b6a05035540..01ac8fdba6d0ee3a0cd64a58e3a0e424e26c3dd5 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -29,6 +29,7 @@ import android.content.res.TypedArray; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; +import android.text.TextUtils; import android.text.TextUtils.TruncateAt; import android.util.IntProperty; import android.util.MathUtils; @@ -176,6 +177,9 @@ class FastScroller { */ private int mState; + /** Whether the preview image is visible. */ + private boolean mShowingPreview; + private BaseAdapter mListAdapter; private SectionIndexer mSectionIndexer; @@ -769,6 +773,8 @@ class FastScroller { mDecorAnimation = new AnimatorSet(); mDecorAnimation.playTogether(fadeOut, slideOut); mDecorAnimation.start(); + + mShowingPreview = false; } /** @@ -790,6 +796,8 @@ class FastScroller { mDecorAnimation = new AnimatorSet(); mDecorAnimation.playTogether(fadeIn, fadeOut, slideIn); mDecorAnimation.start(); + + mShowingPreview = false; } /** @@ -809,6 +817,8 @@ class FastScroller { mDecorAnimation = new AnimatorSet(); mDecorAnimation.playTogether(fadeIn, slideIn); mDecorAnimation.start(); + + mShowingPreview = true; } private void postAutoHide() { @@ -982,9 +992,10 @@ class FastScroller { if (mCurrentSection != sectionIndex) { mCurrentSection = sectionIndex; - if (transitionPreviewLayout(sectionIndex)) { + final boolean hasPreview = transitionPreviewLayout(sectionIndex); + if (!mShowingPreview && hasPreview) { transitionToDragging(); - } else { + } else if (mShowingPreview && !hasPreview) { transitionToVisible(); } } @@ -1072,7 +1083,7 @@ class FastScroller { mPreviewAnimation.start(); - return (text != null && text.length() > 0); + return !TextUtils.isEmpty(text); } /** @@ -1184,7 +1195,19 @@ class FastScroller { / positionsInSection; } - return (section + posWithinSection) / sectionCount; + float result = (section + posWithinSection) / sectionCount; + + // Fake out the scroll bar for the last item. Since the section indexer + // won't ever actually move the list in this end space, make scrolling + // across the last item account for whatever space is remaining. + if (firstVisibleItem > 0 && firstVisibleItem + visibleItemCount == totalItemCount) { + final View lastChild = mList.getChildAt(visibleItemCount - 1); + final float lastItemVisible = (float) (mList.getHeight() - mList.getPaddingBottom() + - lastChild.getTop()) / lastChild.getHeight(); + result += (1 - result) * lastItemVisible; + } + + return result; } /** diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 9e35a236fdec90b5425e80a227f7a2f6a01ab1a1..7daf798194fb87bc28c4f9b2d42a8cefd90e1bee 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -710,6 +710,7 @@ public class ImageView extends View { } d.setLevel(mLevel); d.setLayoutDirection(getLayoutDirection()); + d.setVisible(getVisibility() == VISIBLE, true); mDrawableWidth = d.getIntrinsicWidth(); mDrawableHeight = d.getIntrinsicHeight(); applyColorMod(); diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 65a2d4d2d02d85429166c6a41983fa45ecfec5ad..5392a96715bf3590f986439b13663a592a3b52ab 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -959,9 +959,11 @@ public class ProgressBar extends View { if (!mInDrawing) { if (verifyDrawable(dr)) { final Rect dirty = dr.getBounds(); + final int scrollX = mScrollX + mPaddingLeft; + final int scrollY = mScrollY + mPaddingTop; - invalidate(dirty.left + mScrollX, dirty.top + mScrollY, - dirty.right + mScrollX, dirty.bottom + mScrollY); + invalidate(dirty.left + scrollX, dirty.top + scrollY, + dirty.right + scrollX, dirty.bottom + scrollY); } else { super.invalidateDrawable(dr); } diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java index 368f6adca4fdcee914ba4af5cadab1cacec7da64..fd2f7549e70a17708fff0fab6adf16037da534ac 100644 --- a/core/java/android/widget/QuickContactBadge.java +++ b/core/java/android/widget/QuickContactBadge.java @@ -92,7 +92,9 @@ public class QuickContactBadge extends ImageView implements OnClickListener { com.android.internal.R.styleable.Theme_quickContactBadgeOverlay); styledAttributes.recycle(); - mQueryHandler = new QueryHandler(mContext.getContentResolver()); + if (!isInEditMode()) { + mQueryHandler = new QueryHandler(mContext.getContentResolver()); + } setOnClickListener(this); } @@ -199,7 +201,7 @@ public class QuickContactBadge extends ImageView implements OnClickListener { public void assignContactFromEmail(String emailAddress, boolean lazyLookup, Bundle extras) { mContactEmail = emailAddress; mExtras = extras; - if (!lazyLookup) { + if (!lazyLookup && mQueryHandler != null) { mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null, Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)), EMAIL_LOOKUP_PROJECTION, null, null, null); @@ -239,7 +241,7 @@ public class QuickContactBadge extends ImageView implements OnClickListener { public void assignContactFromPhone(String phoneNumber, boolean lazyLookup, Bundle extras) { mContactPhone = phoneNumber; mExtras = extras; - if (!lazyLookup) { + if (!lazyLookup && mQueryHandler != null) { mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null, Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone), PHONE_LOOKUP_PROJECTION, null, null, null); @@ -262,12 +264,12 @@ public class QuickContactBadge extends ImageView implements OnClickListener { if (mContactUri != null) { QuickContact.showQuickContact(getContext(), QuickContactBadge.this, mContactUri, QuickContact.MODE_LARGE, mExcludeMimes); - } else if (mContactEmail != null) { + } else if (mContactEmail != null && mQueryHandler != null) { extras.putString(EXTRA_URI_CONTENT, mContactEmail); mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, extras, Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)), EMAIL_LOOKUP_PROJECTION, null, null, null); - } else if (mContactPhone != null) { + } else if (mContactPhone != null && mQueryHandler != null) { extras.putString(EXTRA_URI_CONTENT, mContactPhone); mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras, Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone), diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index cb930d6fbcb2555c8c51ef4f889d4ec7a5595905..7a9809fda6059cc17fbbb6c1b7daf1419aa9196a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -56,6 +56,7 @@ import android.text.Selection; import android.text.SpanWatcher; import android.text.Spannable; import android.text.SpannableString; +import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.SpannedString; import android.text.StaticLayout; @@ -3494,19 +3495,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ss.selEnd = end; if (mText instanceof Spanned) { - /* - * Calling setText() strips off any ChangeWatchers; - * strip them now to avoid leaking references. - * But do it to a copy so that if there are any - * further changes to the text of this view, it - * won't get into an inconsistent state. - */ - - Spannable sp = new SpannableString(mText); - - for (ChangeWatcher cw : sp.getSpans(0, sp.length(), ChangeWatcher.class)) { - sp.removeSpan(cw); - } + Spannable sp = new SpannableStringBuilder(mText); if (mEditor != null) { removeMisspelledSpans(sp); diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index fbdf318752af2f734cf8fd02ce7275259c06b0c3..d57b7391c4d083746507dc86493b5183d42d0b7d 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -56,7 +56,17 @@ import java.util.Vector; * can load images from various sources (such as resources or content * providers), takes care of computing its measurement from the video so that * it can be used in any layout manager, and provides various display options - * such as scaling and tinting. + * such as scaling and tinting.

    + * + * Note: VideoView does not retain its full state when going into the + * background. In particular, it does not restore the current play state, + * play position, selected tracks, or any subtitle tracks added via + * {@link #addSubtitleSource addSubtitleSource()}. Applications should + * save and restore these on their own in + * {@link android.app.Activity#onSaveInstanceState} and + * {@link android.app.Activity#onRestoreInstanceState}.

    + * Also note that the audio session id (from {@link #getAudioSessionId}) may + * change from its previously returned value when the VideoView is restored. */ public class VideoView extends SurfaceView implements MediaPlayerControl, SubtitleController.Anchor { diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java index 061bb00cfd530e3bde1bd53d9b00b2d1cf124575..b152297275a8128fd78f4b67cdf84c67daa00982 100644 --- a/core/java/android/widget/ViewFlipper.java +++ b/core/java/android/widget/ViewFlipper.java @@ -90,7 +90,7 @@ public class ViewFlipper extends ViewAnimator { final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_USER_PRESENT); - getContext().registerReceiver(mReceiver, filter); + getContext().registerReceiver(mReceiver, filter, null, mHandler); if (mAutoStart) { // Automatically start when requested diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 525517c00eb5068feb8ceffdc55969559b25d50b..43c4b49ce946a44c4d39be76d8db3ee78acac170 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -68,6 +68,8 @@ interface IBatteryStats { void noteFullWifiLockReleasedFromSource(in WorkSource ws); void noteWifiScanStartedFromSource(in WorkSource ws); void noteWifiScanStoppedFromSource(in WorkSource ws); + void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph); + void noteWifiBatchedScanStoppedFromSource(in WorkSource ws); void noteWifiMulticastEnabledFromSource(in WorkSource ws); void noteWifiMulticastDisabledFromSource(in WorkSource ws); void noteNetworkInterfaceType(String iface, int type); diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialog.java b/core/java/com/android/internal/app/MediaRouteChooserDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..47d2a9c3468d67a7e3172b7a24a304bc7fc338f9 --- /dev/null +++ b/core/java/com/android/internal/app/MediaRouteChooserDialog.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import com.android.internal.R; + +import android.app.Dialog; +import android.content.Context; +import android.media.MediaRouter; +import android.media.MediaRouter.RouteInfo; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.Comparator; + +/** + * This class implements the route chooser dialog for {@link MediaRouter}. + *

    + * This dialog allows the user to choose a route that matches a given selector. + *

    + * + * @see MediaRouteButton + * @see MediaRouteActionProvider + * + * TODO: Move this back into the API, as in the support library media router. + */ +public class MediaRouteChooserDialog extends Dialog { + private final MediaRouter mRouter; + private final MediaRouterCallback mCallback; + + private int mRouteTypes; + private View.OnClickListener mExtendedSettingsClickListener; + private RouteAdapter mAdapter; + private ListView mListView; + private Button mExtendedSettingsButton; + private boolean mAttachedToWindow; + + public MediaRouteChooserDialog(Context context, int theme) { + super(context, theme); + + mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); + mCallback = new MediaRouterCallback(); + } + + /** + * Gets the media route types for filtering the routes that the user can + * select using the media route chooser dialog. + * + * @return The route types. + */ + public int getRouteTypes() { + return mRouteTypes; + } + + /** + * Sets the types of routes that will be shown in the media route chooser dialog + * launched by this button. + * + * @param types The route types to match. + */ + public void setRouteTypes(int types) { + if (mRouteTypes != types) { + mRouteTypes = types; + + if (mAttachedToWindow) { + mRouter.removeCallback(mCallback); + mRouter.addCallback(types, mCallback, + MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); + } + + refreshRoutes(); + } + } + + public void setExtendedSettingsClickListener(View.OnClickListener listener) { + if (listener != mExtendedSettingsClickListener) { + mExtendedSettingsClickListener = listener; + updateExtendedSettingsButton(); + } + } + + /** + * Returns true if the route should be included in the list. + *

    + * The default implementation returns true for enabled non-default routes that + * match the route types. Subclasses can override this method to filter routes + * differently. + *

    + * + * @param route The route to consider, never null. + * @return True if the route should be included in the chooser dialog. + */ + public boolean onFilterRoute(MediaRouter.RouteInfo route) { + return !route.isDefault() && route.isEnabled() && route.matchesTypes(mRouteTypes); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getWindow().requestFeature(Window.FEATURE_LEFT_ICON); + + setContentView(R.layout.media_route_chooser_dialog); + setTitle(mRouteTypes == MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY + ? R.string.media_route_chooser_title_for_remote_display + : R.string.media_route_chooser_title); + + // Must be called after setContentView. + getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, + R.drawable.ic_media_route_off_holo_dark); + + mAdapter = new RouteAdapter(getContext()); + mListView = (ListView)findViewById(R.id.media_route_list); + mListView.setAdapter(mAdapter); + mListView.setOnItemClickListener(mAdapter); + mListView.setEmptyView(findViewById(android.R.id.empty)); + + mExtendedSettingsButton = (Button)findViewById(R.id.media_route_extended_settings_button); + updateExtendedSettingsButton(); + } + + private void updateExtendedSettingsButton() { + if (mExtendedSettingsButton != null) { + mExtendedSettingsButton.setOnClickListener(mExtendedSettingsClickListener); + mExtendedSettingsButton.setVisibility( + mExtendedSettingsClickListener != null ? View.VISIBLE : View.GONE); + } + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + mAttachedToWindow = true; + mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); + refreshRoutes(); + } + + @Override + public void onDetachedFromWindow() { + mAttachedToWindow = false; + mRouter.removeCallback(mCallback); + + super.onDetachedFromWindow(); + } + + /** + * Refreshes the list of routes that are shown in the chooser dialog. + */ + public void refreshRoutes() { + if (mAttachedToWindow) { + mAdapter.update(); + } + } + + private final class RouteAdapter extends ArrayAdapter + implements ListView.OnItemClickListener { + private final LayoutInflater mInflater; + + public RouteAdapter(Context context) { + super(context, 0); + mInflater = LayoutInflater.from(context); + } + + public void update() { + clear(); + final int count = mRouter.getRouteCount(); + for (int i = 0; i < count; i++) { + MediaRouter.RouteInfo route = mRouter.getRouteAt(i); + if (onFilterRoute(route)) { + add(route); + } + } + sort(RouteComparator.sInstance); + notifyDataSetChanged(); + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + return getItem(position).isEnabled(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = convertView; + if (view == null) { + view = mInflater.inflate(R.layout.media_route_list_item, parent, false); + } + MediaRouter.RouteInfo route = getItem(position); + TextView text1 = (TextView)view.findViewById(android.R.id.text1); + TextView text2 = (TextView)view.findViewById(android.R.id.text2); + text1.setText(route.getName()); + CharSequence description = route.getDescription(); + if (TextUtils.isEmpty(description)) { + text2.setVisibility(View.GONE); + text2.setText(""); + } else { + text2.setVisibility(View.VISIBLE); + text2.setText(description); + } + view.setEnabled(route.isEnabled()); + return view; + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + MediaRouter.RouteInfo route = getItem(position); + if (route.isEnabled()) { + route.select(); + dismiss(); + } + } + } + + private final class MediaRouterCallback extends MediaRouter.SimpleCallback { + @Override + public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) { + refreshRoutes(); + } + + @Override + public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) { + refreshRoutes(); + } + + @Override + public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) { + refreshRoutes(); + } + + @Override + public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { + dismiss(); + } + } + + private static final class RouteComparator implements Comparator { + public static final RouteComparator sInstance = new RouteComparator(); + + @Override + public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) { + return lhs.getName().toString().compareTo(rhs.getName().toString()); + } + } +} diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java index e30002185a688a15cd5997dbbce27e39b8e1fb9b..ae362af74837248deda2a1ada380e3589e8d9d11 100644 --- a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java +++ b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,675 +16,86 @@ package com.android.internal.app; -import com.android.internal.R; - -import android.app.Activity; import android.app.Dialog; import android.app.DialogFragment; -import android.app.MediaRouteActionProvider; -import android.app.MediaRouteButton; import android.content.Context; -import android.graphics.drawable.Drawable; -import android.hardware.display.DisplayManager; -import android.media.MediaRouter; -import android.media.MediaRouter.RouteCategory; -import android.media.MediaRouter.RouteGroup; -import android.media.MediaRouter.RouteInfo; import android.os.Bundle; -import android.text.TextUtils; -import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.CheckBox; -import android.widget.Checkable; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.SeekBar; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +import android.view.View.OnClickListener; /** - * This class implements the route chooser dialog for {@link MediaRouter}. + * Media route chooser dialog fragment. + *

    + * Creates a {@link MediaRouteChooserDialog}. The application may subclass + * this dialog fragment to customize the media route chooser dialog. + *

    * - * @see MediaRouteButton - * @see MediaRouteActionProvider + * TODO: Move this back into the API, as in the support library media router. */ public class MediaRouteChooserDialogFragment extends DialogFragment { - private static final String TAG = "MediaRouteChooserDialogFragment"; - public static final String FRAGMENT_TAG = "android:MediaRouteChooserDialogFragment"; - - private static final int[] ITEM_LAYOUTS = new int[] { - R.layout.media_route_list_item_top_header, - R.layout.media_route_list_item_section_header, - R.layout.media_route_list_item, - R.layout.media_route_list_item_checkable, - R.layout.media_route_list_item_collapse_group - }; + private final String ARGUMENT_ROUTE_TYPES = "routeTypes"; - MediaRouter mRouter; - private int mRouteTypes; - - private LayoutInflater mInflater; - private LauncherListener mLauncherListener; - private View.OnClickListener mExtendedSettingsListener; - private RouteAdapter mAdapter; - private ListView mListView; - private SeekBar mVolumeSlider; - private ImageView mVolumeIcon; - - final RouteComparator mComparator = new RouteComparator(); - final MediaRouterCallback mCallback = new MediaRouterCallback(); - private boolean mIgnoreSliderVolumeChanges; - private boolean mIgnoreCallbackVolumeChanges; + private View.OnClickListener mExtendedSettingsClickListener; + /** + * Creates a media route chooser dialog fragment. + *

    + * All subclasses of this class must also possess a default constructor. + *

    + */ public MediaRouteChooserDialogFragment() { - setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog); + setCancelable(true); + setStyle(STYLE_NORMAL, android.R.style.Theme_DeviceDefault_Dialog); } - public void setLauncherListener(LauncherListener listener) { - mLauncherListener = listener; - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE); - mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); - } - - @Override - public void onDetach() { - super.onDetach(); - if (mLauncherListener != null) { - mLauncherListener.onDetached(this); - } - if (mAdapter != null) { - mAdapter = null; - } - mInflater = null; - mRouter.removeCallback(mCallback); - mRouter = null; - } - - public void setExtendedSettingsClickListener(View.OnClickListener listener) { - mExtendedSettingsListener = listener; + public int getRouteTypes() { + Bundle args = getArguments(); + return args != null ? args.getInt(ARGUMENT_ROUTE_TYPES) : 0; } public void setRouteTypes(int types) { - mRouteTypes = types; - } - - void updateVolume() { - if (mRouter == null) return; - - final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes); - mVolumeIcon.setImageResource(selectedRoute == null || - selectedRoute.getPlaybackType() == RouteInfo.PLAYBACK_TYPE_LOCAL ? - R.drawable.ic_audio_vol : R.drawable.ic_media_route_on_holo_dark); - - mIgnoreSliderVolumeChanges = true; - - if (selectedRoute == null || - selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_FIXED) { - // Disable the slider and show it at max volume. - mVolumeSlider.setMax(1); - mVolumeSlider.setProgress(1); - mVolumeSlider.setEnabled(false); - } else { - mVolumeSlider.setEnabled(true); - mVolumeSlider.setMax(selectedRoute.getVolumeMax()); - mVolumeSlider.setProgress(selectedRoute.getVolume()); - } - - mIgnoreSliderVolumeChanges = false; - } - - void changeVolume(int newValue) { - if (mIgnoreSliderVolumeChanges) return; - - final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes); - if (selectedRoute != null && - selectedRoute.getVolumeHandling() == RouteInfo.PLAYBACK_VOLUME_VARIABLE) { - final int maxVolume = selectedRoute.getVolumeMax(); - newValue = Math.max(0, Math.min(newValue, maxVolume)); - selectedRoute.requestSetVolume(newValue); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - mInflater = inflater; - final View layout = inflater.inflate(R.layout.media_route_chooser_layout, container, false); - - mVolumeIcon = (ImageView) layout.findViewById(R.id.volume_icon); - mVolumeSlider = (SeekBar) layout.findViewById(R.id.volume_slider); - updateVolume(); - mVolumeSlider.setOnSeekBarChangeListener(new VolumeSliderChangeListener()); - - if (mExtendedSettingsListener != null) { - final View extendedSettingsButton = layout.findViewById(R.id.extended_settings); - extendedSettingsButton.setVisibility(View.VISIBLE); - extendedSettingsButton.setOnClickListener(mExtendedSettingsListener); - } - - final ListView list = (ListView) layout.findViewById(R.id.list); - list.setItemsCanFocus(true); - list.setAdapter(mAdapter = new RouteAdapter()); - list.setOnItemClickListener(mAdapter); - - mListView = list; - - mAdapter.scrollToSelectedItem(); - - return layout; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - return new RouteChooserDialog(getActivity(), getTheme()); - } - - private static class ViewHolder { - public TextView text1; - public TextView text2; - public ImageView icon; - public ImageButton expandGroupButton; - public RouteAdapter.ExpandGroupListener expandGroupListener; - public int position; - public CheckBox check; - } - - private class RouteAdapter extends BaseAdapter implements ListView.OnItemClickListener { - private static final int VIEW_TOP_HEADER = 0; - private static final int VIEW_SECTION_HEADER = 1; - private static final int VIEW_ROUTE = 2; - private static final int VIEW_GROUPING_ROUTE = 3; - private static final int VIEW_GROUPING_DONE = 4; - - private int mSelectedItemPosition = -1; - private final ArrayList mItems = new ArrayList(); - - private RouteCategory mCategoryEditingGroups; - private RouteGroup mEditingGroup; - - // Temporary lists for manipulation - private final ArrayList mCatRouteList = new ArrayList(); - private final ArrayList mSortRouteList = new ArrayList(); - - private boolean mIgnoreUpdates; - - RouteAdapter() { - update(); - } - - void update() { - /* - * This is kind of wacky, but our data sets are going to be - * fairly small on average. Ideally we should be able to do some of this stuff - * in-place instead. - * - * Basic idea: each entry in mItems represents an item in the list for quick access. - * Entries can be a RouteCategory (section header), a RouteInfo with a category of - * mCategoryEditingGroups (a flattened RouteInfo pulled out of its group, allowing - * the user to change the group), - */ - if (mIgnoreUpdates) return; - - mItems.clear(); - - final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes); - mSelectedItemPosition = -1; - - List routes; - final int catCount = mRouter.getCategoryCount(); - for (int i = 0; i < catCount; i++) { - final RouteCategory cat = mRouter.getCategoryAt(i); - routes = cat.getRoutes(mCatRouteList); - - if (!cat.isSystem()) { - mItems.add(cat); - } - - if (cat == mCategoryEditingGroups) { - addGroupEditingCategoryRoutes(routes); - } else { - addSelectableRoutes(selectedRoute, routes); - } - - routes.clear(); + if (types != getRouteTypes()) { + Bundle args = getArguments(); + if (args == null) { + args = new Bundle(); } + args.putInt(ARGUMENT_ROUTE_TYPES, types); + setArguments(args); - notifyDataSetChanged(); - if (mListView != null && mSelectedItemPosition >= 0) { - mListView.setItemChecked(mSelectedItemPosition, true); - } - } - - void scrollToEditingGroup() { - if (mCategoryEditingGroups == null || mListView == null) return; - - int pos = 0; - int bound = 0; - final int itemCount = mItems.size(); - for (int i = 0; i < itemCount; i++) { - final Object item = mItems.get(i); - if (item != null && item == mCategoryEditingGroups) { - bound = i; - } - if (item == null) { - pos = i; - break; // this is always below the category header; we can stop here. - } - } - - mListView.smoothScrollToPosition(pos, bound); - } - - void scrollToSelectedItem() { - if (mListView == null || mSelectedItemPosition < 0) return; - - mListView.smoothScrollToPosition(mSelectedItemPosition); - } - - void addSelectableRoutes(RouteInfo selectedRoute, List from) { - final int routeCount = from.size(); - for (int j = 0; j < routeCount; j++) { - final RouteInfo info = from.get(j); - if (info == selectedRoute) { - mSelectedItemPosition = mItems.size(); - } - mItems.add(info); - } - } - - void addGroupEditingCategoryRoutes(List from) { - // Unpack groups and flatten for presentation - // mSortRouteList will always be empty here. - final int topCount = from.size(); - for (int i = 0; i < topCount; i++) { - final RouteInfo route = from.get(i); - final RouteGroup group = route.getGroup(); - if (group == route) { - // This is a group, unpack it. - final int groupCount = group.getRouteCount(); - for (int j = 0; j < groupCount; j++) { - final RouteInfo innerRoute = group.getRouteAt(j); - mSortRouteList.add(innerRoute); - } - } else { - mSortRouteList.add(route); - } - } - // Sort by name. This will keep the route positions relatively stable even though they - // will be repeatedly added and removed. - Collections.sort(mSortRouteList, mComparator); - - mItems.addAll(mSortRouteList); - mSortRouteList.clear(); - - mItems.add(null); // Sentinel reserving space for the "done" button. - } - - @Override - public int getCount() { - return mItems.size(); - } - - @Override - public int getViewTypeCount() { - return 5; - } - - @Override - public int getItemViewType(int position) { - final Object item = getItem(position); - if (item instanceof RouteCategory) { - return position == 0 ? VIEW_TOP_HEADER : VIEW_SECTION_HEADER; - } else if (item == null) { - return VIEW_GROUPING_DONE; - } else { - final RouteInfo info = (RouteInfo) item; - if (info.getCategory() == mCategoryEditingGroups) { - return VIEW_GROUPING_ROUTE; - } - return VIEW_ROUTE; - } - } - - @Override - public boolean areAllItemsEnabled() { - return false; - } - - @Override - public boolean isEnabled(int position) { - switch (getItemViewType(position)) { - case VIEW_ROUTE: - return ((RouteInfo) mItems.get(position)).isEnabled(); - case VIEW_GROUPING_ROUTE: - case VIEW_GROUPING_DONE: - return true; - default: - return false; - } - } - - @Override - public Object getItem(int position) { - return mItems.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final int viewType = getItemViewType(position); - - ViewHolder holder; - if (convertView == null) { - convertView = mInflater.inflate(ITEM_LAYOUTS[viewType], parent, false); - holder = new ViewHolder(); - holder.position = position; - holder.text1 = (TextView) convertView.findViewById(R.id.text1); - holder.text2 = (TextView) convertView.findViewById(R.id.text2); - holder.icon = (ImageView) convertView.findViewById(R.id.icon); - holder.check = (CheckBox) convertView.findViewById(R.id.check); - holder.expandGroupButton = (ImageButton) convertView.findViewById( - R.id.expand_button); - if (holder.expandGroupButton != null) { - holder.expandGroupListener = new ExpandGroupListener(); - holder.expandGroupButton.setOnClickListener(holder.expandGroupListener); - } - - final View fview = convertView; - final ListView list = (ListView) parent; - final ViewHolder fholder = holder; - convertView.setOnClickListener(new View.OnClickListener() { - @Override public void onClick(View v) { - list.performItemClick(fview, fholder.position, 0); - } - }); - convertView.setTag(holder); - } else { - holder = (ViewHolder) convertView.getTag(); - holder.position = position; - } - - switch (viewType) { - case VIEW_ROUTE: - case VIEW_GROUPING_ROUTE: - bindItemView(position, holder); - break; - case VIEW_SECTION_HEADER: - case VIEW_TOP_HEADER: - bindHeaderView(position, holder); - break; - } - - convertView.setActivated(position == mSelectedItemPosition); - convertView.setEnabled(isEnabled(position)); - - return convertView; - } - - void bindItemView(int position, ViewHolder holder) { - RouteInfo info = (RouteInfo) mItems.get(position); - holder.text1.setText(info.getName(getActivity())); - final CharSequence status = info.getStatus(); - if (TextUtils.isEmpty(status)) { - holder.text2.setVisibility(View.GONE); - } else { - holder.text2.setVisibility(View.VISIBLE); - holder.text2.setText(status); - } - Drawable icon = info.getIconDrawable(); - if (icon != null) { - // Make sure we have a fresh drawable where it doesn't matter if we mutate it - icon = icon.getConstantState().newDrawable(getResources()); - } - holder.icon.setImageDrawable(icon); - holder.icon.setVisibility(icon != null ? View.VISIBLE : View.GONE); - - RouteCategory cat = info.getCategory(); - boolean canGroup = false; - if (cat == mCategoryEditingGroups) { - RouteGroup group = info.getGroup(); - holder.check.setEnabled(group.getRouteCount() > 1); - holder.check.setChecked(group == mEditingGroup); - } else { - if (cat.isGroupable()) { - final RouteGroup group = (RouteGroup) info; - canGroup = group.getRouteCount() > 1 || - getItemViewType(position - 1) == VIEW_ROUTE || - (position < getCount() - 1 && - getItemViewType(position + 1) == VIEW_ROUTE); - } - } - - if (holder.expandGroupButton != null) { - holder.expandGroupButton.setVisibility(canGroup ? View.VISIBLE : View.GONE); - holder.expandGroupListener.position = position; - } - } - - void bindHeaderView(int position, ViewHolder holder) { - RouteCategory cat = (RouteCategory) mItems.get(position); - holder.text1.setText(cat.getName(getActivity())); - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - final int type = getItemViewType(position); - if (type == VIEW_SECTION_HEADER || type == VIEW_TOP_HEADER) { - return; - } else if (type == VIEW_GROUPING_DONE) { - finishGrouping(); - return; - } else { - final Object item = getItem(position); - if (!(item instanceof RouteInfo)) { - // Oops. Stale event running around? Skip it. - return; - } - - final RouteInfo route = (RouteInfo) item; - if (type == VIEW_ROUTE) { - mRouter.selectRouteInt(mRouteTypes, route); - dismiss(); - } else if (type == VIEW_GROUPING_ROUTE) { - final Checkable c = (Checkable) view; - final boolean wasChecked = c.isChecked(); - - mIgnoreUpdates = true; - RouteGroup oldGroup = route.getGroup(); - if (!wasChecked && oldGroup != mEditingGroup) { - // Assumption: in a groupable category oldGroup will never be null. - if (mRouter.getSelectedRoute(mRouteTypes) == oldGroup) { - // Old group was selected but is now empty. Select the group - // we're manipulating since that's where the last route went. - mRouter.selectRouteInt(mRouteTypes, mEditingGroup); - } - oldGroup.removeRoute(route); - mEditingGroup.addRoute(route); - c.setChecked(true); - } else if (wasChecked && mEditingGroup.getRouteCount() > 1) { - mEditingGroup.removeRoute(route); - - // In a groupable category this will add - // the route into its own new group. - mRouter.addRouteInt(route); - } - mIgnoreUpdates = false; - update(); - } - } - } - - boolean isGrouping() { - return mCategoryEditingGroups != null; - } - - void finishGrouping() { - mCategoryEditingGroups = null; - mEditingGroup = null; - getDialog().setCanceledOnTouchOutside(true); - update(); - scrollToSelectedItem(); - } - - class ExpandGroupListener implements View.OnClickListener { - int position; - - @Override - public void onClick(View v) { - // Assumption: this is only available for the user to click if we're presenting - // a groupable category, where every top-level route in the category is a group. - final RouteGroup group = (RouteGroup) getItem(position); - mEditingGroup = group; - mCategoryEditingGroups = group.getCategory(); - getDialog().setCanceledOnTouchOutside(false); - mRouter.selectRouteInt(mRouteTypes, mEditingGroup); - update(); - scrollToEditingGroup(); - } - } - } - - class MediaRouterCallback extends MediaRouter.Callback { - @Override - public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { - mAdapter.update(); - updateVolume(); - } - - @Override - public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { - mAdapter.update(); - } - - @Override - public void onRouteAdded(MediaRouter router, RouteInfo info) { - mAdapter.update(); - } - - @Override - public void onRouteRemoved(MediaRouter router, RouteInfo info) { - if (info == mAdapter.mEditingGroup) { - mAdapter.finishGrouping(); - } - mAdapter.update(); - } - - @Override - public void onRouteChanged(MediaRouter router, RouteInfo info) { - mAdapter.notifyDataSetChanged(); - } - - @Override - public void onRouteGrouped(MediaRouter router, RouteInfo info, - RouteGroup group, int index) { - mAdapter.update(); - } - - @Override - public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) { - mAdapter.update(); - } - - @Override - public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) { - if (!mIgnoreCallbackVolumeChanges) { - updateVolume(); + MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog(); + if (dialog != null) { + dialog.setRouteTypes(types); } } } - class RouteComparator implements Comparator { - @Override - public int compare(RouteInfo lhs, RouteInfo rhs) { - return lhs.getName(getActivity()).toString() - .compareTo(rhs.getName(getActivity()).toString()); - } - } - - class RouteChooserDialog extends Dialog { - public RouteChooserDialog(Context context, int theme) { - super(context, theme); - } - - @Override - public void onBackPressed() { - if (mAdapter != null && mAdapter.isGrouping()) { - mAdapter.finishGrouping(); - } else { - super.onBackPressed(); - } - } - - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) { - final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes); - if (selectedRoute != null) { - selectedRoute.requestUpdateVolume(-1); - return true; - } - } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) { - final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes); - if (selectedRoute != null) { - mRouter.getSelectedRoute(mRouteTypes).requestUpdateVolume(1); - return true; - } - } - return super.onKeyDown(keyCode, event); - } + public void setExtendedSettingsClickListener(View.OnClickListener listener) { + if (listener != mExtendedSettingsClickListener) { + mExtendedSettingsClickListener = listener; - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) { - return true; - } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) { - return true; - } else { - return super.onKeyUp(keyCode, event); + MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog(); + if (dialog != null) { + dialog.setExtendedSettingsClickListener(listener); } } } /** - * Implemented by the MediaRouteButton that launched this dialog + * Called when the chooser dialog is being created. + *

    + * Subclasses may override this method to customize the dialog. + *

    */ - public interface LauncherListener { - public void onDetached(MediaRouteChooserDialogFragment detachedFragment); + public MediaRouteChooserDialog onCreateChooserDialog( + Context context, Bundle savedInstanceState) { + return new MediaRouteChooserDialog(context, getTheme()); } - class VolumeSliderChangeListener implements SeekBar.OnSeekBarChangeListener { - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - changeVolume(progress); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - mIgnoreCallbackVolumeChanges = true; - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - mIgnoreCallbackVolumeChanges = false; - updateVolume(); - } - + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + MediaRouteChooserDialog dialog = onCreateChooserDialog(getActivity(), savedInstanceState); + dialog.setRouteTypes(getRouteTypes()); + dialog.setExtendedSettingsClickListener(mExtendedSettingsClickListener); + return dialog; } } diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..8fc99c7305015a2d890c5ab479b5a85458ed2f22 --- /dev/null +++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import com.android.internal.R; + +import android.app.Dialog; +import android.app.MediaRouteActionProvider; +import android.app.MediaRouteButton; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.media.MediaRouter; +import android.media.MediaRouter.RouteGroup; +import android.media.MediaRouter.RouteInfo; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.SeekBar; + +/** + * This class implements the route controller dialog for {@link MediaRouter}. + *

    + * This dialog allows the user to control or disconnect from the currently selected route. + *

    + * + * @see MediaRouteButton + * @see MediaRouteActionProvider + * + * TODO: Move this back into the API, as in the support library media router. + */ +public class MediaRouteControllerDialog extends Dialog { + // Time to wait before updating the volume when the user lets go of the seek bar + // to allow the route provider time to propagate the change and publish a new + // route descriptor. + private static final int VOLUME_UPDATE_DELAY_MILLIS = 250; + + private final MediaRouter mRouter; + private final MediaRouterCallback mCallback; + private final MediaRouter.RouteInfo mRoute; + + private boolean mCreated; + private Drawable mMediaRouteConnectingDrawable; + private Drawable mMediaRouteOnDrawable; + private Drawable mCurrentIconDrawable; + + private boolean mVolumeControlEnabled = true; + private LinearLayout mVolumeLayout; + private SeekBar mVolumeSlider; + private boolean mVolumeSliderTouched; + + private View mControlView; + + private Button mDisconnectButton; + + public MediaRouteControllerDialog(Context context, int theme) { + super(context, theme); + + mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); + mCallback = new MediaRouterCallback(); + mRoute = mRouter.getSelectedRoute(); + } + + /** + * Gets the route that this dialog is controlling. + */ + public MediaRouter.RouteInfo getRoute() { + return mRoute; + } + + /** + * Provides the subclass an opportunity to create a view that will + * be included within the body of the dialog to offer additional media controls + * for the currently playing content. + * + * @param savedInstanceState The dialog's saved instance state. + * @return The media control view, or null if none. + */ + public View onCreateMediaControlView(Bundle savedInstanceState) { + return null; + } + + /** + * Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}. + * + * @return The media control view, or null if none. + */ + public View getMediaControlView() { + return mControlView; + } + + /** + * Sets whether to enable the volume slider and volume control using the volume keys + * when the route supports it. + *

    + * The default value is true. + *

    + */ + public void setVolumeControlEnabled(boolean enable) { + if (mVolumeControlEnabled != enable) { + mVolumeControlEnabled = enable; + if (mCreated) { + updateVolume(); + } + } + } + + /** + * Returns whether to enable the volume slider and volume control using the volume keys + * when the route supports it. + */ + public boolean isVolumeControlEnabled() { + return mVolumeControlEnabled; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getWindow().requestFeature(Window.FEATURE_LEFT_ICON); + + setContentView(R.layout.media_route_controller_dialog); + + mVolumeLayout = (LinearLayout)findViewById(R.id.media_route_volume_layout); + mVolumeSlider = (SeekBar)findViewById(R.id.media_route_volume_slider); + mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + private final Runnable mStopTrackingTouch = new Runnable() { + @Override + public void run() { + if (mVolumeSliderTouched) { + mVolumeSliderTouched = false; + updateVolume(); + } + } + }; + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + if (mVolumeSliderTouched) { + mVolumeSlider.removeCallbacks(mStopTrackingTouch); + } else { + mVolumeSliderTouched = true; + } + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // Defer resetting mVolumeSliderTouched to allow the media route provider + // a little time to settle into its new state and publish the final + // volume update. + mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser) { + mRoute.requestSetVolume(progress); + } + } + }); + + mDisconnectButton = (Button)findViewById(R.id.media_route_disconnect_button); + mDisconnectButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mRoute.isSelected()) { + mRouter.getDefaultRoute().select(); + } + dismiss(); + } + }); + + mCreated = true; + if (update()) { + mControlView = onCreateMediaControlView(savedInstanceState); + FrameLayout controlFrame = + (FrameLayout)findViewById(R.id.media_route_control_frame); + if (mControlView != null) { + controlFrame.addView(mControlView); + controlFrame.setVisibility(View.VISIBLE); + } else { + controlFrame.setVisibility(View.GONE); + } + } + } + + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + mRouter.addCallback(0, mCallback, MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS); + update(); + } + + @Override + public void onDetachedFromWindow() { + mRouter.removeCallback(mCallback); + + super.onDetachedFromWindow(); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1); + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + return true; + } + return super.onKeyUp(keyCode, event); + } + + private boolean update() { + if (!mRoute.isSelected() || mRoute.isDefault()) { + dismiss(); + return false; + } + + setTitle(mRoute.getName()); + updateVolume(); + + Drawable icon = getIconDrawable(); + if (icon != mCurrentIconDrawable) { + mCurrentIconDrawable = icon; + getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, icon); + } + return true; + } + + private Drawable getIconDrawable() { + if (mRoute.isConnecting()) { + if (mMediaRouteConnectingDrawable == null) { + mMediaRouteConnectingDrawable = getContext().getResources().getDrawable( + R.drawable.ic_media_route_connecting_holo_dark); + } + return mMediaRouteConnectingDrawable; + } else { + if (mMediaRouteOnDrawable == null) { + mMediaRouteOnDrawable = getContext().getResources().getDrawable( + R.drawable.ic_media_route_on_holo_dark); + } + return mMediaRouteOnDrawable; + } + } + + private void updateVolume() { + if (!mVolumeSliderTouched) { + if (isVolumeControlAvailable()) { + mVolumeLayout.setVisibility(View.VISIBLE); + mVolumeSlider.setMax(mRoute.getVolumeMax()); + mVolumeSlider.setProgress(mRoute.getVolume()); + } else { + mVolumeLayout.setVisibility(View.GONE); + } + } + } + + private boolean isVolumeControlAvailable() { + return mVolumeControlEnabled && mRoute.getVolumeHandling() == + MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE; + } + + private final class MediaRouterCallback extends MediaRouter.SimpleCallback { + @Override + public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { + update(); + } + + @Override + public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) { + update(); + } + + @Override + public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) { + if (route == mRoute) { + updateVolume(); + } + } + + @Override + public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, + int index) { + update(); + } + + @Override + public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) { + update(); + } + } +} diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java b/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..108e81fd5e020237755a671e16c381628c898f86 --- /dev/null +++ b/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.Context; +import android.os.Bundle; + +/** + * Media route controller dialog fragment. + *

    + * Creates a {@link MediaRouteControllerDialog}. The application may subclass + * this dialog fragment to customize the media route controller dialog. + *

    + * + * TODO: Move this back into the API, as in the support library media router. + */ +public class MediaRouteControllerDialogFragment extends DialogFragment { + /** + * Creates a media route controller dialog fragment. + *

    + * All subclasses of this class must also possess a default constructor. + *

    + */ + public MediaRouteControllerDialogFragment() { + setCancelable(true); + setStyle(STYLE_NORMAL, android.R.style.Theme_DeviceDefault_Dialog); + } + + /** + * Called when the controller dialog is being created. + *

    + * Subclasses may override this method to customize the dialog. + *

    + */ + public MediaRouteControllerDialog onCreateControllerDialog( + Context context, Bundle savedInstanceState) { + return new MediaRouteControllerDialog(context, getTheme()); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return onCreateControllerDialog(getActivity(), savedInstanceState); + } +} diff --git a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java new file mode 100644 index 0000000000000000000000000000000000000000..fad7fd493dc40127a82ba5571eeb7fe9c00d93ed --- /dev/null +++ b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + + +import android.app.Activity; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.FragmentManager; +import android.content.Context; +import android.media.MediaRouter; +import android.util.Log; +import android.view.View; + +/** + * Shows media route dialog as appropriate. + * @hide + */ +public abstract class MediaRouteDialogPresenter { + private static final String TAG = "MediaRouter"; + + private static final String CHOOSER_FRAGMENT_TAG = + "android.app.MediaRouteButton:MediaRouteChooserDialogFragment"; + private static final String CONTROLLER_FRAGMENT_TAG = + "android.app.MediaRouteButton:MediaRouteControllerDialogFragment"; + + public static DialogFragment showDialogFragment(Activity activity, + int routeTypes, View.OnClickListener extendedSettingsClickListener) { + final MediaRouter router = (MediaRouter)activity.getSystemService( + Context.MEDIA_ROUTER_SERVICE); + final FragmentManager fm = activity.getFragmentManager(); + + MediaRouter.RouteInfo route = router.getSelectedRoute(); + if (route.isDefault() || !route.matchesTypes(routeTypes)) { + if (fm.findFragmentByTag(CHOOSER_FRAGMENT_TAG) != null) { + Log.w(TAG, "showDialog(): Route chooser dialog already showing!"); + return null; + } + MediaRouteChooserDialogFragment f = new MediaRouteChooserDialogFragment(); + f.setRouteTypes(routeTypes); + f.setExtendedSettingsClickListener(extendedSettingsClickListener); + f.show(fm, CHOOSER_FRAGMENT_TAG); + return f; + } else { + if (fm.findFragmentByTag(CONTROLLER_FRAGMENT_TAG) != null) { + Log.w(TAG, "showDialog(): Route controller dialog already showing!"); + return null; + } + MediaRouteControllerDialogFragment f = new MediaRouteControllerDialogFragment(); + f.show(fm, CONTROLLER_FRAGMENT_TAG); + return f; + } + } + + public static Dialog createDialog(Context context, + int routeTypes, View.OnClickListener extendedSettingsClickListener) { + final MediaRouter router = (MediaRouter)context.getSystemService( + Context.MEDIA_ROUTER_SERVICE); + + MediaRouter.RouteInfo route = router.getSelectedRoute(); + if (route.isDefault() || !route.matchesTypes(routeTypes)) { + final MediaRouteChooserDialog d = new MediaRouteChooserDialog( + context, android.R.style.Theme_DeviceDefault_Dialog); + d.setRouteTypes(routeTypes); + d.setExtendedSettingsClickListener(extendedSettingsClickListener); + return d; + } else { + MediaRouteControllerDialog d = new MediaRouteControllerDialog( + context, android.R.style.Theme_DeviceDefault_Dialog); + return d; + } + } +} diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index 222e44673c4133b94322282b79b8c83d8f7472ce..0cad33cff6b8b212443adcb40058398b2cf2fedc 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -135,10 +135,10 @@ public final class ProcessStats implements Parcelable { }; static final String[] STATE_NAMES = new String[] { - "Persistent", "Top ", "Imp Fg ", "Imp Bg ", - "Backup ", "Heavy Wght", "Service ", "Service Rs", - "Receiver ", "Home ", - "Last Act ", "Cch Act ", "Cch CliAct", "Cch Empty " + "Persist", "Top ", "ImpFg ", "ImpBg ", + "Backup ", "HeavyWt", "Service", "ServRst", + "Receivr", "Home ", + "LastAct", "CchAct ", "CchCAct", "CchEmty" }; public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] { @@ -314,16 +314,16 @@ public final class ProcessStats implements Parcelable { private static void printScreenLabel(PrintWriter pw, int offset) { switch (offset) { case ADJ_NOTHING: - pw.print(" "); + pw.print(" "); break; case ADJ_SCREEN_OFF: - pw.print("Screen Off / "); + pw.print("SOff/"); break; case ADJ_SCREEN_ON: - pw.print("Screen On / "); + pw.print("SOn /"); break; default: - pw.print("?????????? / "); + pw.print("????/"); break; } } @@ -344,25 +344,31 @@ public final class ProcessStats implements Parcelable { } } - private static void printMemLabel(PrintWriter pw, int offset) { + private static void printMemLabel(PrintWriter pw, int offset, char sep) { switch (offset) { case ADJ_NOTHING: - pw.print(" "); + pw.print(" "); + if (sep != 0) pw.print(' '); break; case ADJ_MEM_FACTOR_NORMAL: - pw.print("Norm / "); + pw.print("Norm"); + if (sep != 0) pw.print(sep); break; case ADJ_MEM_FACTOR_MODERATE: - pw.print("Mod / "); + pw.print("Mod "); + if (sep != 0) pw.print(sep); break; case ADJ_MEM_FACTOR_LOW: - pw.print("Low / "); + pw.print("Low "); + if (sep != 0) pw.print(sep); break; case ADJ_MEM_FACTOR_CRITICAL: - pw.print("Crit / "); + pw.print("Crit"); + if (sep != 0) pw.print(sep); break; default: - pw.print("???? / "); + pw.print("????"); + if (sep != 0) pw.print(sep); break; } } @@ -399,8 +405,9 @@ public final class ProcessStats implements Parcelable { printScreenLabel(pw, printedScreen != iscreen ? iscreen : STATE_NOTHING); printedScreen = iscreen; - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, (char)0); printedMem = imem; + pw.print(": "); TimeUtils.formatDuration(time, pw); pw.println(running); } totalTime += time; @@ -409,8 +416,7 @@ public final class ProcessStats implements Parcelable { } if (totalTime != 0 && pw != null) { pw.print(prefix); - printScreenLabel(pw, STATE_NOTHING); - pw.print("TOTAL: "); + pw.print(" TOTAL: "); TimeUtils.formatDuration(totalTime, pw); pw.println(); } @@ -569,7 +575,7 @@ public final class ProcessStats implements Parcelable { printedScreen = iscreen; } if (memStates.length > 1) { - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '/'); printedMem = imem; } pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); @@ -585,9 +591,9 @@ public final class ProcessStats implements Parcelable { printScreenLabel(pw, STATE_NOTHING); } if (memStates.length > 1) { - printMemLabel(pw, STATE_NOTHING); + printMemLabel(pw, STATE_NOTHING, '/'); } - pw.print("TOTAL : "); + pw.print("TOTAL : "); TimeUtils.formatDuration(totalTime, pw); pw.println(); } @@ -621,7 +627,7 @@ public final class ProcessStats implements Parcelable { printedScreen = iscreen; } if (memStates.length > 1) { - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '/'); printedMem = imem; } pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); @@ -798,7 +804,7 @@ public final class ProcessStats implements Parcelable { new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true); dumpProcessSummaryDetails(pw, proc, prefix, " Receiver: ", screenStates, memStates, new int[] {STATE_RECEIVER}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Home: ", screenStates, memStates, + dumpProcessSummaryDetails(pw, proc, prefix, " (Home): ", screenStates, memStates, new int[] {STATE_HOME}, now, totalTime, true); dumpProcessSummaryDetails(pw, proc, prefix, " (Last Act): ", screenStates, memStates, new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true); @@ -1733,13 +1739,17 @@ public final class ProcessStats implements Parcelable { pw.print(" pkg="); pw.println(proc.mCommonProcess.mPackage); } } - pw.print(prefix); pw.print("mActive="); pw.println(proc.mActive); + if (proc.mActive) { + pw.print(prefix); pw.print("mActive="); pw.println(proc.mActive); + } if (proc.mDead) { pw.print(prefix); pw.print("mDead="); pw.println(proc.mDead); } - pw.print(prefix); pw.print("mNumActiveServices="); pw.print(proc.mNumActiveServices); - pw.print(" mNumStartedServices="); - pw.println(proc.mNumStartedServices); + if (proc.mNumActiveServices != 0 || proc.mNumStartedServices != 0) { + pw.print(prefix); pw.print("mNumActiveServices="); pw.print(proc.mNumActiveServices); + pw.print(" mNumStartedServices="); + pw.println(proc.mNumStartedServices); + } } public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary, @@ -1748,21 +1758,34 @@ public final class ProcessStats implements Parcelable { mStartTime, now); ArrayMap> pkgMap = mPackages.getMap(); boolean printedHeader = false; + boolean sepNeeded = false; for (int ip=0; ip uids = pkgMap.valueAt(ip); + final String pkgName = pkgMap.keyAt(ip); + final SparseArray uids = pkgMap.valueAt(ip); for (int iu=0; iu 0 || NSRVS > 0) { if (!printedHeader) { pw.println("Per-Package Stats:"); printedHeader = true; + sepNeeded = true; } pw.print(" * "); pw.print(pkgName); pw.print(" / "); UserHandle.formatUid(pw, uid); pw.println(":"); @@ -1770,6 +1793,9 @@ public final class ProcessStats implements Parcelable { if (!dumpSummary || dumpAll) { for (int iproc=0; iproc procs = new ArrayList(); for (int iproc=0; iproc> procMap = mProcesses.getMap(); + printedHeader = false; + int numShownProcs = 0, numTotalProcs = 0; + for (int ip=0; ip uids = procMap.valueAt(ip); + for (int iu=0; iu collectProcessesLocked(int[] screenStates, int[] memStates, int[] procStates, int sortProcStates[], long now, String reqPackage, boolean activeOnly) { - ArraySet foundProcs = new ArraySet(); - ArrayMap> pkgMap = mPackages.getMap(); + final ArraySet foundProcs = new ArraySet(); + final ArrayMap> pkgMap = mPackages.getMap(); for (int ip=0; ip procs = pkgMap.valueAt(ip); + final String pkgName = pkgMap.keyAt(ip); + final SparseArray procs = pkgMap.valueAt(ip); for (int iu=0; iu= 0) { - mListView.setItemChecked(initialHighlight, true); - onItemClick(null, null, initialHighlight, 0); // Other entries are not used + // Set the initial highlight if there was a preferred or last used choice + final int initialHighlight = mAdapter.getInitialHighlight(); + if (initialHighlight >= 0) { + mListView.setItemChecked(initialHighlight, true); + onItemClick(null, null, initialHighlight, 0); // Other entries are not used + } } } diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 5bfa1b236b3ddcae63b098ce21597bf2a552833b..1e37fd9935a0feb266b8039b0caa33117c2923b5 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -23,6 +23,12 @@ import android.os.ParcelFileDescriptor; /** {@hide} */ interface IBackupTransport { + /** + * Ask the transport for the name under which it should be registered. This will + * typically be its host service's component name, but need not be. + */ + String name(); + /** * Ask the transport for an Intent that can be used to launch any internal * configuration Activity that it wishes to present. For example, the transport diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index eb2d1fe3e79a6269e16ea045ae5a2e74720dba73..494bc7898e93d1ec06491f6f4e6e95ba9088b5ab 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -19,6 +19,7 @@ package com.android.internal.backup; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.RestoreSet; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; @@ -71,6 +72,10 @@ public class LocalTransport extends IBackupTransport.Stub { } } + public String name() { + return new ComponentName(mContext, this.getClass()).flattenToShortString(); + } + public Intent configurationIntent() { // The local transport is not user-configurable return null; diff --git a/core/java/com/android/internal/backup/LocalTransportService.java b/core/java/com/android/internal/backup/LocalTransportService.java new file mode 100644 index 0000000000000000000000000000000000000000..d05699a73ae6b918c6aa5f88b3069f45dcf9157f --- /dev/null +++ b/core/java/com/android/internal/backup/LocalTransportService.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.backup; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class LocalTransportService extends Service { + private static LocalTransport sTransport = null; + + @Override + public void onCreate() { + if (sTransport == null) { + sTransport = new LocalTransport(this); + } + } + + @Override + public IBinder onBind(Intent intent) { + return sTransport; + } +} diff --git a/core/java/com/android/internal/inputmethod/InputMethodRoot.java b/core/java/com/android/internal/inputmethod/InputMethodRoot.java deleted file mode 100644 index eddea99f928904dbf0e0ec56e5811bc7799fa23a..0000000000000000000000000000000000000000 --- a/core/java/com/android/internal/inputmethod/InputMethodRoot.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.inputmethod; - -import android.content.Context; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; - -public class InputMethodRoot extends LinearLayout { - - private View mNavigationGuard; - - public InputMethodRoot(Context context) { - this(context, null); - } - - public InputMethodRoot(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public InputMethodRoot(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - requestFitSystemWindows(); - } - - @Override - protected boolean fitSystemWindows(Rect insets) { - if (mNavigationGuard == null) { - mNavigationGuard = findViewById(com.android.internal.R.id.navigationGuard); - } - if (mNavigationGuard == null) { - return super.fitSystemWindows(insets); - } - ViewGroup.LayoutParams lp = mNavigationGuard.getLayoutParams(); - lp.height = insets.bottom; - mNavigationGuard.setLayoutParams(lp); - return true; - } -} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index f85b3532ece2dc5d1b406046b4e7736027191c48..2e5fcecbf26ccb9c02cc6f94921e9207f4a63545 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -84,7 +84,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 66 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 67 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -154,6 +154,8 @@ public final class BatteryStatsImpl extends BatteryStats { final ArrayList mFullWifiLockTimers = new ArrayList(); final ArrayList mWifiMulticastTimers = new ArrayList(); final ArrayList mWifiScanTimers = new ArrayList(); + final SparseArray> mWifiBatchedScanTimers = + new SparseArray>(); // Last partial timers we use for distributing CPU usage. final ArrayList mLastPartialTimers = new ArrayList(); @@ -2172,6 +2174,9 @@ public final class BatteryStatsImpl extends BatteryStats { case TelephonyManager.NETWORK_TYPE_EHRPD: bin = DATA_CONNECTION_EHRPD; break; + case TelephonyManager.NETWORK_TYPE_HSPAP: + bin = DATA_CONNECTION_HSPAP; + break; default: bin = DATA_CONNECTION_OTHER; break; @@ -2401,6 +2406,14 @@ public final class BatteryStatsImpl extends BatteryStats { getUidStatsLocked(uid).noteWifiScanStoppedLocked(); } + public void noteWifiBatchedScanStartedLocked(int uid, int csph) { + getUidStatsLocked(uid).noteWifiBatchedScanStartedLocked(csph); + } + + public void noteWifiBatchedScanStoppedLocked(int uid) { + getUidStatsLocked(uid).noteWifiBatchedScanStoppedLocked(); + } + int mWifiMulticastNesting = 0; public void noteWifiMulticastEnabledLocked(int uid) { @@ -2453,6 +2466,20 @@ public final class BatteryStatsImpl extends BatteryStats { } } + public void noteWifiBatchedScanStartedFromSourceLocked(WorkSource ws, int csph) { + int N = ws.size(); + for (int i=0; i 8 && bin < NUM_WIFI_BATCHED_SCAN_BINS) { + csph = csph >> 3; + bin++; + } + + if (mWifiBatchedScanBinStarted == bin) return; + + if (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED) { + mWifiBatchedScanTimer[mWifiBatchedScanBinStarted]. + stopRunningLocked(BatteryStatsImpl.this); + } + mWifiBatchedScanBinStarted = bin; + if (mWifiBatchedScanTimer[bin] == null) { + makeWifiBatchedScanBin(bin, null); + } + mWifiBatchedScanTimer[bin].startRunningLocked(BatteryStatsImpl.this); + } + + @Override + public void noteWifiBatchedScanStoppedLocked() { + if (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED) { + mWifiBatchedScanTimer[mWifiBatchedScanBinStarted]. + stopRunningLocked(BatteryStatsImpl.this); + mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED; + } + } + @Override public void noteWifiMulticastEnabledLocked() { if (!mWifiMulticastEnabled) { @@ -2850,6 +2912,15 @@ public final class BatteryStatsImpl extends BatteryStats { return mWifiScanTimer.getTotalTimeLocked(batteryRealtime, which); } + @Override + public long getWifiBatchedScanTime(int csphBin, long batteryRealtime, int which) { + if (csphBin < 0 || csphBin >= NUM_WIFI_BATCHED_SCAN_BINS) return 0; + if (mWifiBatchedScanTimer[csphBin] == null) { + return 0; + } + return mWifiBatchedScanTimer[csphBin].getTotalTimeLocked(batteryRealtime, which); + } + @Override public long getWifiMulticastTime(long batteryRealtime, int which) { if (mWifiMulticastTimer == null) { @@ -2911,6 +2982,24 @@ public final class BatteryStatsImpl extends BatteryStats { return mUserActivityCounters[type].getCountLocked(which); } + void makeWifiBatchedScanBin(int i, Parcel in) { + if (i < 0 || i >= NUM_WIFI_BATCHED_SCAN_BINS) return; + + ArrayList collected = mWifiBatchedScanTimers.get(i); + if (collected == null) { + collected = new ArrayList(); + mWifiBatchedScanTimers.put(i, collected); + } + if (in == null) { + mWifiBatchedScanTimer[i] = new StopwatchTimer(this, WIFI_BATCHED_SCAN, collected, + mUnpluggables); + } else { + mWifiBatchedScanTimer[i] = new StopwatchTimer(this, WIFI_BATCHED_SCAN, collected, + mUnpluggables, in); + } + } + + void initUserActivityLocked() { mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES]; for (int i=0; igetPaintOptionsAndroid().getLanguage().getTag(); + hb_buffer_set_language(mBuffer, hb_language_from_string(langString.c_str(), -1)); hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length); // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index a7a0bb222cdf8923312adaec318037c4c0a3b649..ccd75d565ae5867f0bf42ea048268d956f1cf008 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -34,6 +34,13 @@ static SkTypeface* Typeface_create(JNIEnv* env, jobject, jstring name, if (NULL != name) { AutoJavaStringToUTF8 str(env, name); face = SkTypeface::CreateFromName(str.c_str(), style); + // Try to find the closest matching font, using the standard heuristic + if (NULL == face) { + face = SkTypeface::CreateFromName(str.c_str(), (SkTypeface::Style)(style ^ SkTypeface::kItalic)); + } + for (int i = 0; NULL == face && i < 4; i++) { + face = SkTypeface::CreateFromName(str.c_str(), (SkTypeface::Style)i); + } } // return the default font at the best style if no exact match exists @@ -45,8 +52,13 @@ static SkTypeface* Typeface_create(JNIEnv* env, jobject, jstring name, static SkTypeface* Typeface_createFromTypeface(JNIEnv* env, jobject, SkTypeface* family, int style) { SkTypeface* face = SkTypeface::CreateFromTypeface(family, (SkTypeface::Style)style); - // return the default font at the best style if the requested style does not - // exist in the provided family + // Try to find the closest matching font, using the standard heuristic + if (NULL == face) { + face = SkTypeface::CreateFromTypeface(family, (SkTypeface::Style)(style ^ SkTypeface::kItalic)); + } + for (int i = 0; NULL == face && i < 4; i++) { + face = SkTypeface::CreateFromTypeface(family, (SkTypeface::Style)i); + } if (NULL == face) { face = SkTypeface::CreateFromName(NULL, (SkTypeface::Style)style); } diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp index b57a0fe8aa10a05200b242ddaa45d9cd95cea7aa..6175a8f8136947aa131dc07a02c3133ad4f4ef3d 100644 --- a/core/jni/android/graphics/pdf/PdfDocument.cpp +++ b/core/jni/android/graphics/pdf/PdfDocument.cpp @@ -17,62 +17,138 @@ #include "jni.h" #include "GraphicsJNI.h" #include +#include + +#include "CreateJavaOutputStreamAdaptor.h" #include "SkCanvas.h" -#include "SkPDFDevice.h" -#include "SkPDFDocument.h" +#include "SkDocument.h" +#include "SkPicture.h" +#include "SkStream.h" #include "SkRect.h" -#include "SkSize.h" -#include "CreateJavaOutputStreamAdaptor.h" -#include "JNIHelp.h" namespace android { -#define LOGD(x...) do { Log::Instance()->printf(Log::ELogD, x); } while(0) +struct PageRecord { -static jint nativeCreateDocument(JNIEnv* env, jobject clazz) { - return reinterpret_cast(new SkPDFDocument()); -} + PageRecord(int width, int height, const SkRect& contentRect) + : mPicture(new SkPicture()), mWidth(width), mHeight(height) { + mContentRect = contentRect; + } -static void nativeFinalize(JNIEnv* env, jobject thiz, jint documentPtr) { - delete reinterpret_cast(documentPtr); -} + ~PageRecord() { + mPicture->unref(); + } -static jint nativeCreatePage(JNIEnv* env, jobject thiz, jint pageWidth, jint pageHeight, - jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) { + SkPicture* const mPicture; + const int mWidth; + const int mHeight; + SkRect mContentRect; +}; + +class PdfDocument { +public: + PdfDocument() { + mCurrentPage = NULL; + } + + SkCanvas* startPage(int width, int height, + int contentLeft, int contentTop, int contentRight, int contentBottom) { + assert(mCurrentPage == NULL); + + SkRect contentRect = SkRect::MakeLTRB( + contentLeft, contentTop, contentRight, contentBottom); + PageRecord* page = new PageRecord(width, height, contentRect); + mPages.push_back(page); + mCurrentPage = page; + + SkCanvas* canvas = page->mPicture->beginRecording( + contentRect.width(), contentRect.height(), 0); + + // We pass this canvas to Java where it is used to construct + // a Java Canvas object which dereferences the pointer when it + // is destroyed, so we have to bump up the reference count. + canvas->ref(); + + return canvas; + } - SkMatrix transformation; - transformation.setTranslate(contentLeft, contentTop); + void finishPage() { + assert(mCurrentPage != NULL); + mCurrentPage->mPicture->endRecording(); + mCurrentPage = NULL; + } - SkISize skPageSize = SkISize::Make(pageWidth, pageHeight); - SkISize skContentSize = SkISize::Make(contentRight - contentLeft, contentBottom - contentTop); + void write(SkWStream* stream) { + SkDocument* document = SkDocument::CreatePDF(stream); + for (unsigned i = 0; i < mPages.size(); i++) { + PageRecord* page = mPages[i]; - SkPDFDevice* skPdfDevice = new SkPDFDevice(skPageSize, skContentSize, transformation); - return reinterpret_cast(new SkCanvas(skPdfDevice)); + SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight, + &(page->mContentRect)); + + canvas->clipRect(page->mContentRect); + canvas->translate(page->mContentRect.left(), page->mContentRect.top()); + canvas->drawPicture(*page->mPicture); + + document->endPage(); + } + document->close(); + } + + void close() { + for (unsigned i = 0; i < mPages.size(); i++) { + delete mPages[i]; + } + delete mCurrentPage; + mCurrentPage = NULL; + } + +private: + ~PdfDocument() { + close(); + } + + std::vector mPages; + PageRecord* mCurrentPage; +}; + +static jint nativeCreateDocument(JNIEnv* env, jobject thiz) { + return reinterpret_cast(new PdfDocument()); +} + +static jint nativeStartPage(JNIEnv* env, jobject thiz, jint documentPtr, + jint pageWidth, jint pageHeight, + jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) { + PdfDocument* document = reinterpret_cast(documentPtr); + return reinterpret_cast(document->startPage(pageWidth, pageHeight, + contentLeft, contentTop, contentRight, contentBottom)); } -static void nativeAppendPage(JNIEnv* env, jobject thiz, jint documentPtr, jint pagePtr) { - SkCanvas* page = reinterpret_cast(pagePtr); - SkPDFDocument* document = reinterpret_cast(documentPtr); - SkPDFDevice* device = static_cast(page->getDevice()); - document->appendPage(device); +static void nativeFinishPage(JNIEnv* env, jobject thiz, jint documentPtr) { + PdfDocument* document = reinterpret_cast(documentPtr); + document->finishPage(); } -static void nativeWriteTo(JNIEnv* env, jobject clazz, jint documentPtr, - jobject out, jbyteArray chunk) { +static void nativeWriteTo(JNIEnv* env, jobject thiz, jint documentPtr, jobject out, + jbyteArray chunk) { + PdfDocument* document = reinterpret_cast(documentPtr); SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk); - SkPDFDocument* document = reinterpret_cast(documentPtr); - document->emitPDF(skWStream); + document->write(skWStream); delete skWStream; } +static void nativeClose(JNIEnv* env, jobject thiz, jint documentPtr) { + PdfDocument* document = reinterpret_cast(documentPtr); + document->close(); +} + static JNINativeMethod gPdfDocument_Methods[] = { {"nativeCreateDocument", "()I", (void*) nativeCreateDocument}, - {"nativeFinalize", "(I)V", (void*) nativeFinalize}, - {"nativeCreatePage", "(IIIIII)I", - (void*) nativeCreatePage}, - {"nativeAppendPage", "(II)V", (void*) nativeAppendPage}, - {"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo} + {"nativeStartPage", "(IIIIIII)I", (void*) nativeStartPage}, + {"nativeFinishPage", "(I)V", (void*) nativeFinishPage}, + {"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo}, + {"nativeClose", "(I)V", (void*) nativeClose} }; int register_android_graphics_pdf_PdfDocument(JNIEnv* env) { diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp deleted file mode 100644 index d1245da247770663d0717af98fcdeea42db6def3..0000000000000000000000000000000000000000 --- a/core/jni/android_os_FileUtils.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* //device/libs/android_runtime/android_util_Process.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "FileUtils" - -#include - -#include - -#include "JNIHelp.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path) -{ - if (path == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return -1; - } - const char *pathStr = env->GetStringUTFChars(path, NULL); - int result = -1; - // only if our system supports this ioctl - #ifdef VFAT_IOCTL_GET_VOLUME_ID - int fd = open(pathStr, O_RDONLY); - if (fd >= 0) { - result = ioctl(fd, VFAT_IOCTL_GET_VOLUME_ID); - close(fd); - } - #endif - - env->ReleaseStringUTFChars(path, pathStr); - return result; -} - -static const JNINativeMethod methods[] = { - {"getFatVolumeId", "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId}, -}; - -static const char* const kFileUtilsPathName = "android/os/FileUtils"; - -int register_android_os_FileUtils(JNIEnv* env) -{ - return AndroidRuntime::registerNativeMethods( - env, kFileUtilsPathName, - methods, NELEM(methods)); -} - -} diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp index 01d02c58c96d4590f38372c502797871f4164303..b11c5bbdac29fd6605ae3d6753d7afe694f2d889 100644 --- a/core/jni/android_os_Trace.cpp +++ b/core/jni/android_os_Trace.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "Trace" +// #define LOG_NDEBUG 0 #include #include @@ -46,6 +47,8 @@ static jlong android_os_Trace_nativeGetEnabledTags(JNIEnv* env, jclass clazz) { static void android_os_Trace_nativeTraceCounter(JNIEnv* env, jclass clazz, jlong tag, jstring nameStr, jint value) { ScopedUtfChars name(env, nameStr); + + ALOGV("%s: %lld %s %d", __FUNCTION__, tag, name.c_str(), value); atrace_int(tag, name.c_str(), value); } @@ -55,11 +58,15 @@ static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass clazz, ScopedStringChars jchars(env, nameStr); String8 utf8Chars(reinterpret_cast(jchars.get()), jchars.size()); sanitizeString(utf8Chars); + + ALOGV("%s: %lld %s", __FUNCTION__, tag, utf8Chars.string()); atrace_begin(tag, utf8Chars.string()); } static void android_os_Trace_nativeTraceEnd(JNIEnv* env, jclass clazz, jlong tag) { + + ALOGV("%s: %lld", __FUNCTION__, tag); atrace_end(tag); } @@ -69,6 +76,8 @@ static void android_os_Trace_nativeAsyncTraceBegin(JNIEnv* env, jclass clazz, ScopedStringChars jchars(env, nameStr); String8 utf8Chars(reinterpret_cast(jchars.get()), jchars.size()); sanitizeString(utf8Chars); + + ALOGV("%s: %lld %s %d", __FUNCTION__, tag, utf8Chars.string(), cookie); atrace_async_begin(tag, utf8Chars.string(), cookie); } @@ -78,16 +87,22 @@ static void android_os_Trace_nativeAsyncTraceEnd(JNIEnv* env, jclass clazz, ScopedStringChars jchars(env, nameStr); String8 utf8Chars(reinterpret_cast(jchars.get()), jchars.size()); sanitizeString(utf8Chars); + + ALOGV("%s: %lld %s %d", __FUNCTION__, tag, utf8Chars.string(), cookie); atrace_async_end(tag, utf8Chars.string(), cookie); } static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv* env, jclass clazz, jboolean allowed) { + ALOGV("%s: %d", __FUNCTION__, allowed); + atrace_set_debuggable(allowed); } static void android_os_Trace_nativeSetTracingEnabled(JNIEnv* env, jclass clazz, jboolean enabled) { + ALOGV("%s: %d", __FUNCTION__, enabled); + atrace_set_tracing_enabled(enabled); } diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index b254de715bdc50991a75a7da2191ee60fa584219..92a3e62a8d5ff9ffe68261b5eab6b3afa0cfa174 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -56,7 +56,8 @@ public: status_t initialize(); void dispose(); status_t finishInputEvent(uint32_t seq, bool handled); - status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime); + status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime, + bool* outConsumedBatch); protected: virtual ~NativeInputEventReceiver(); @@ -167,7 +168,7 @@ int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) if (events & ALOOPER_EVENT_INPUT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); - status_t status = consumeEvents(env, false /*consumeBatches*/, -1); + status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL); mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); return status == OK || status == NO_MEMORY ? 1 : 0; } @@ -214,7 +215,7 @@ int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) } status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, - bool consumeBatches, nsecs_t frameTime) { + bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.", getInputChannelName(), consumeBatches ? "true" : "false", frameTime); @@ -223,6 +224,9 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, if (consumeBatches) { mBatchedInputEventPending = false; } + if (outConsumedBatch) { + *outConsumedBatch = false; + } ScopedLocalRef receiverObj(env, NULL); bool skipCallbacks = false; @@ -285,13 +289,17 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, static_cast(inputEvent)); break; - case AINPUT_EVENT_TYPE_MOTION: + case AINPUT_EVENT_TYPE_MOTION: { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Received motion event.", getInputChannelName()); #endif - inputEventObj = android_view_MotionEvent_obtainAsCopy(env, - static_cast(inputEvent)); + MotionEvent* motionEvent = static_cast(inputEvent); + if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) { + *outConsumedBatch = true; + } + inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent); break; + } default: assert(false); // InputConsumer should prevent this from ever happening @@ -370,16 +378,20 @@ static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, } } -static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr, +static bool nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr, jlong frameTimeNanos) { sp receiver = reinterpret_cast(receiverPtr); - status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos); + bool consumedBatch; + status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos, + &consumedBatch); if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) { String8 message; message.appendFormat("Failed to consume batched input event. status=%d", status); jniThrowRuntimeException(env, message.string()); + return false; } + return consumedBatch; } @@ -392,7 +404,7 @@ static JNINativeMethod gMethods[] = { (void*)nativeDispose }, { "nativeFinishInputEvent", "(IIZ)V", (void*)nativeFinishInputEvent }, - { "nativeConsumeBatchedInputEvents", "(IJ)V", + { "nativeConsumeBatchedInputEvents", "(IJ)Z", (void*)nativeConsumeBatchedInputEvents }, }; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index eba6231a0b7ca9a58188d10672c6cb17ca17b6a0..b198937df18d6d443ff32a41f39ab4d2aabc031d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1153,7 +1153,7 @@ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion} is 4 or higher.--> @@ -1958,8 +1958,7 @@ + the system can bind to it. --> + + + + + + + + + + diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png index 3b64f4781e70f274dee31f10e17d3ffb6a2d2e52..28a1cba41614aa05af5bdaa5359729a4ec88d3dc 100644 Binary files a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png and b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png differ diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png index 3b64f4781e70f274dee31f10e17d3ffb6a2d2e52..28a1cba41614aa05af5bdaa5359729a4ec88d3dc 100644 Binary files a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png and b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png differ diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png index 6a2a92cceda58a1cba91389f6fefaf1d9999e7bc..72b0d42438d1d1a8185739c69be8b31c17fba86f 100644 Binary files a/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png and b/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png differ diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png index 6a2a92cceda58a1cba91389f6fefaf1d9999e7bc..72b0d42438d1d1a8185739c69be8b31c17fba86f 100644 Binary files a/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png and b/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png differ diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png index b9266a65c9d018c6be030ebe568e766d640f6217..eff3cc4f72b9505541b293ab871aaf6a0c6e7e39 100644 Binary files a/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png and b/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png differ diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png index b9266a65c9d018c6be030ebe568e766d640f6217..eff3cc4f72b9505541b293ab871aaf6a0c6e7e39 100644 Binary files a/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png and b/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png differ diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png index 42fc83cdee2d9ddb11528afe8e1761d6ba750ade..986f797ca69bb783d0e7199d70bd9bb219537da2 100644 Binary files a/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png and b/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_group_collapse.png b/core/res/res/drawable-hdpi/ic_media_group_collapse.png deleted file mode 100644 index 89abf2c21ac572a9e3e18e17d6deb9b8fdc32aa6..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-hdpi/ic_media_group_collapse.png and /dev/null differ diff --git a/core/res/res/drawable-hdpi/ic_media_group_expand.png b/core/res/res/drawable-hdpi/ic_media_group_expand.png deleted file mode 100644 index d9470b2ef098e7eadcba417b4be0de6d7dd56a74..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-hdpi/ic_media_group_expand.png and /dev/null differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png index b47d666cf95de07353137cf4ef1dec736c98d204..458a2a66ce788990b018c0e204aa08d457dbdf55 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_disabled_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png index 13d803cb98a5213b497aaf8c8dd98f18b2cb2909..c91faa929c5532cc4d9a4de7040ca9233e3b326e 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_off_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png index 3ae436b656d50807b1f3b0bbfca8ef870c5c6e96..14c9183fb5b2110b7c2d00d8846130fa17f658fd 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_off_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png index 24824fc9d997aa9a05f1270902d61d8b77671e19..b388d8686ac9c04c1489150c4372f8bf6099901f 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png index af3819bbf044cf4ad039b6e436ba51102efae6c8..76c132380dde16d1cd0d99c5e2f4d258d1438562 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_on_0_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png index 83dc251908ef39cac4f0ceae40451c7caa0561d5..fd39f9d6aa17adf32801b97267a19ccf1a8a0031 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png index 8d9d5923cfd06a82b7c768b940700dd6a0b17d8b..c74727aabe219db3db2e2153f548370b5aa47b0a 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_on_1_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png index 1310ec9eb3e404e9e85efd235fb7eee7d776d45b..826c9aefed7a0e9e159a133077e71681860a3b9b 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png index 1705074567c88ca689d4b44ef78c6a36f7996017..d0baec3fbaa9ddd74bb9d93a641aad811cfd7c31 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_on_2_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png index 7027b88ec1574d795c2e35ce50d8d0a57704cc87..c60ff599decaf8fee27d3e752568ce323f6a6383 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png and b/core/res/res/drawable-hdpi/ic_media_route_on_holo_dark.png differ diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png index 7027b88ec1574d795c2e35ce50d8d0a57704cc87..75552ccf0ab86eec457884b9395d00a53adad952 100644 Binary files a/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png and b/core/res/res/drawable-hdpi/ic_media_route_on_holo_light.png differ diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_0.png b/core/res/res/drawable-hdpi/ic_notification_cast_0.png new file mode 100644 index 0000000000000000000000000000000000000000..a35f28177f0a5afd3be9ee342ef3a36a2efa279b Binary files /dev/null and b/core/res/res/drawable-hdpi/ic_notification_cast_0.png differ diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_1.png b/core/res/res/drawable-hdpi/ic_notification_cast_1.png new file mode 100644 index 0000000000000000000000000000000000000000..9f6e2adef029e019e248c91c3652ba58e2189545 Binary files /dev/null and b/core/res/res/drawable-hdpi/ic_notification_cast_1.png differ diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_2.png b/core/res/res/drawable-hdpi/ic_notification_cast_2.png new file mode 100644 index 0000000000000000000000000000000000000000..737137a81340f11a16eae55abd00388a75d45df4 Binary files /dev/null and b/core/res/res/drawable-hdpi/ic_notification_cast_2.png differ diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_on.png b/core/res/res/drawable-hdpi/ic_notification_cast_on.png new file mode 100644 index 0000000000000000000000000000000000000000..ff2753ab68f5c2868d54f7df0204f023bcdb383a Binary files /dev/null and b/core/res/res/drawable-hdpi/ic_notification_cast_on.png differ diff --git a/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png deleted file mode 100644 index 35f27df17260ced48bcfa57c102153a7af95894a..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png and /dev/null differ diff --git a/core/res/res/drawable-hdpi/toast_frame.9.png b/core/res/res/drawable-hdpi/toast_frame.9.png index ca65994ad1feb97fd09e8b52e5864c58d3b19585..a804a8a9456438adfa3fc1ebb234b2c19be684b5 100644 Binary files a/core/res/res/drawable-hdpi/toast_frame.9.png and b/core/res/res/drawable-hdpi/toast_frame.9.png differ diff --git a/core/res/res/drawable-hdpi/toast_frame_holo.9.png b/core/res/res/drawable-hdpi/toast_frame_holo.9.png deleted file mode 100644 index a804a8a9456438adfa3fc1ebb234b2c19be684b5..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-hdpi/toast_frame_holo.9.png and /dev/null differ diff --git a/core/res/res/drawable-ldpi/toast_frame.9.png b/core/res/res/drawable-ldpi/toast_frame.9.png index 3b344ff372eb8ea55ec94cfbc597b140bc0efa49..e64dc75750516400d385c7bbc7d5a1858ec2c25a 100644 Binary files a/core/res/res/drawable-ldpi/toast_frame.9.png and b/core/res/res/drawable-ldpi/toast_frame.9.png differ diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png index 87933fa4ddc9015bce2efe5d2a88aa2686cd13ea..3ce61b3df2613ed43eec5ed849b99b896f26bb1e 100644 Binary files a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png and b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png differ diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png index 87933fa4ddc9015bce2efe5d2a88aa2686cd13ea..3ce61b3df2613ed43eec5ed849b99b896f26bb1e 100644 Binary files a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png and b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png differ diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png index d424a0e77e9b92c3cb42d140502be078ae2bae96..82e54fdae006fe132e54249cf976aab843f827ab 100644 Binary files a/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png and b/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png differ diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png index d424a0e77e9b92c3cb42d140502be078ae2bae96..82e54fdae006fe132e54249cf976aab843f827ab 100644 Binary files a/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png and b/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png differ diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png index 076386849f2ff03aed924dc833857fa56a3e75fe..c3898719dc1236c8d130574b03b36c932da59207 100644 Binary files a/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png and b/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png differ diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png index 076386849f2ff03aed924dc833857fa56a3e75fe..c3898719dc1236c8d130574b03b36c932da59207 100644 Binary files a/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png and b/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png differ diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png index accc761582d5aa86b4ce217db72fb26170a76447..211be67c688d31592592d123af83984576c8d017 100644 Binary files a/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png and b/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_group_collapse.png b/core/res/res/drawable-mdpi/ic_media_group_collapse.png deleted file mode 100644 index 34454ac3e7e8cc5bf9505f2bb9577e8ae7ad96d5..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-mdpi/ic_media_group_collapse.png and /dev/null differ diff --git a/core/res/res/drawable-mdpi/ic_media_group_expand.png b/core/res/res/drawable-mdpi/ic_media_group_expand.png deleted file mode 100644 index 8ce5a448d813e05e3f9d2b559fef9431ca3bc8de..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-mdpi/ic_media_group_expand.png and /dev/null differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png index 6764598fee303e4ddc45291a6e14f538a921be1f..9d92648511ae15ae49d8ac65dc5470181b5f56f1 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_off_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png index 94e0bb641e914f6d93cad52dc991d1d04e10cc49..3e27fc8f2eb8281e1c70886a956f88319b8eb9d9 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_off_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png index 5ce2f205234dfcd9112af6045e8cb2f9014e2921..72b9e78956ec41cc22ade0fd54b2b6b9df6b8ca0 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png index 5105e9081aeb802c782e116061d888cde0e23d24..bd462a2b2ccc59601cd9fa451252fa0f6dc961dc 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_on_0_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png index 68c06ed77aae390672d99b4d4bdf9954148b40f9..0a2cc89d535ad154e2335b2fab353d015ce63473 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png index 6e9b14422993060b702719632c88d15a60fd17b2..d162503202acce3c9b7a25f2fd1360be4851df59 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_on_1_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png index 45dc56f3d47100d45e0d30ce3b5e061383a69741..997e32b680b901d58453191e50c9ac7b68d3c93e 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png index 46e743ad7ed6cd9fc9c7937bbec60cada91395f7..d314967d38a1d6ce8f981a5411ee0f560daad962 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_on_2_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png index e384691fe8b8803151abe6c26f324a775de778fa..f15d7a9d77607beccd5a4fbef63bd9bde67ce75f 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png and b/core/res/res/drawable-mdpi/ic_media_route_on_holo_dark.png differ diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png index e384691fe8b8803151abe6c26f324a775de778fa..26d46f8b7b98074753717bbdeed4159d13cdbc35 100644 Binary files a/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png and b/core/res/res/drawable-mdpi/ic_media_route_on_holo_light.png differ diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_0.png b/core/res/res/drawable-mdpi/ic_notification_cast_0.png new file mode 100644 index 0000000000000000000000000000000000000000..d9cedbdeb3e5b1b434548b40cdbd5548cb4d956f Binary files /dev/null and b/core/res/res/drawable-mdpi/ic_notification_cast_0.png differ diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_1.png b/core/res/res/drawable-mdpi/ic_notification_cast_1.png new file mode 100644 index 0000000000000000000000000000000000000000..414c67f17002b29e85815fab49921e4406994af5 Binary files /dev/null and b/core/res/res/drawable-mdpi/ic_notification_cast_1.png differ diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_2.png b/core/res/res/drawable-mdpi/ic_notification_cast_2.png new file mode 100644 index 0000000000000000000000000000000000000000..280a8880e5223411e91d83c9ebc8dfa7bca14f1b Binary files /dev/null and b/core/res/res/drawable-mdpi/ic_notification_cast_2.png differ diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_on.png b/core/res/res/drawable-mdpi/ic_notification_cast_on.png new file mode 100644 index 0000000000000000000000000000000000000000..ab5f1d78608f3b4dc5e57867008539beae0fdbd9 Binary files /dev/null and b/core/res/res/drawable-mdpi/ic_notification_cast_on.png differ diff --git a/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png deleted file mode 100644 index f9c8678f7f644922472396a865a8359d866d543f..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png and /dev/null differ diff --git a/core/res/res/drawable-mdpi/toast_frame.9.png b/core/res/res/drawable-mdpi/toast_frame.9.png index 9e93fe71619f55a208406f25573d7fd820a03887..778e4e67653d706207f006e9b931a6999b6be0bb 100644 Binary files a/core/res/res/drawable-mdpi/toast_frame.9.png and b/core/res/res/drawable-mdpi/toast_frame.9.png differ diff --git a/core/res/res/drawable-mdpi/toast_frame_holo.9.png b/core/res/res/drawable-mdpi/toast_frame_holo.9.png deleted file mode 100644 index 778e4e67653d706207f006e9b931a6999b6be0bb..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-mdpi/toast_frame_holo.9.png and /dev/null differ diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png index d591bf8c503a6ebfa68739251558b43275aa0aa5..41230fe9ea7def430298fe5accde04a70b6ff5cc 100644 Binary files a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png and b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png differ diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png index d591bf8c503a6ebfa68739251558b43275aa0aa5..41230fe9ea7def430298fe5accde04a70b6ff5cc 100644 Binary files a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png and b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png differ diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png index b410d238e50cc06501a312336744089ef9f9baa8..9fa8682273db6a65b8f76eebca6500e8c40bcd97 100644 Binary files a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png and b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png differ diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png index b410d238e50cc06501a312336744089ef9f9baa8..9fa8682273db6a65b8f76eebca6500e8c40bcd97 100644 Binary files a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png and b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png differ diff --git a/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png index aed57c60364374232abc05ec00a2317291ac7975..73488f35ce217af556736a04c5f080c525e93c4d 100644 Binary files a/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png and b/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png differ diff --git a/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png index aed57c60364374232abc05ec00a2317291ac7975..73488f35ce217af556736a04c5f080c525e93c4d 100644 Binary files a/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png and b/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png differ diff --git a/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png index 38f8c013f45aef4e9bab44201e9ed5014ff58112..28edccd5dd29a5d608fa91ab9946636c90a22ac7 100644 Binary files a/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png and b/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_group_collapse.png b/core/res/res/drawable-xhdpi/ic_media_group_collapse.png deleted file mode 100644 index 2fb7428f266fc2e9f8089ed40ce9ffb9fa39c087..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-xhdpi/ic_media_group_collapse.png and /dev/null differ diff --git a/core/res/res/drawable-xhdpi/ic_media_group_expand.png b/core/res/res/drawable-xhdpi/ic_media_group_expand.png deleted file mode 100644 index 5755b9da2f03a9e75c00d251170e1b78f6a68aa8..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-xhdpi/ic_media_group_expand.png and /dev/null differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png index 1d48e12a3c68e92de8256de580dbbe7ad9ac5562..045eee0549d877202d31f9bafe958f6710613853 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png index 2c8d1ec19b254c697975b93207f3c20e9e545f18..6e14e296336ab6d6e2fa46a66e159dc8891ec702 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_disabled_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png index 00b20433ab790b50b26731beca9d3179d4b725bc..121bbf6a46a0b5d7212a40a6e53460f59389c290 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png index ce1d9395473404bb4d01bff5a47847c15f556b8b..468a0c36c07f1d92fc3a4209efca53754fe737a1 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_off_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png index 3064b46e5299285fb2a46bff50cef90fcd03a0e5..414a322e8b4904147d6bb304ec18dff599ca1645 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png index 431668642ce154320e2c839646343fe069bfee1e..6088a48e4113244500576d43b38462460053b440 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_0_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png index 25c4e3118cf02711aa3ad9c09fd6eb84668fe2e0..363d7d4681ce4b2d16bf862172e5ba6b7d22be6f 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png index 8e32bd2902ac23d79546cfa186607f65cc539fd2..edf731e8fd4220581a6d12d16ed8ce8a5f485b0e 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_1_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png index aeaa78ffaec350678962f044ed27af1bf1b1d332..85cba7b80fc90e29e01c8551b20318e3ec7a8712 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png index 85277fa12ebd0188bd9daf8942dfe1c3c2f9a746..e65ac319044bdbfd6d7f4aeee0a38985b9528660 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_2_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png index b01dbe8ceae44966a051da62e43a954fb23f7cdf..d8e3e3aeae38888f6f8a8ae599e46c5fbb722e2f 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_dark.png differ diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png index c19a2ad0163b1896d057bfc003f820a811b530aa..562dc9a4f2cf40fa7e3b0c3628d0f66a2eedce7e 100644 Binary files a/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png and b/core/res/res/drawable-xhdpi/ic_media_route_on_holo_light.png differ diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_0.png b/core/res/res/drawable-xhdpi/ic_notification_cast_0.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb23a06df3512f4c1e1eead5d7d242632027d42 Binary files /dev/null and b/core/res/res/drawable-xhdpi/ic_notification_cast_0.png differ diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_1.png b/core/res/res/drawable-xhdpi/ic_notification_cast_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f01d17db0821ff88c1e50d3c5cffccd23c3be2ee Binary files /dev/null and b/core/res/res/drawable-xhdpi/ic_notification_cast_1.png differ diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_2.png b/core/res/res/drawable-xhdpi/ic_notification_cast_2.png new file mode 100644 index 0000000000000000000000000000000000000000..4f4ba7f98bcd5aa1a92e3a1ad15f8f4a846fe94d Binary files /dev/null and b/core/res/res/drawable-xhdpi/ic_notification_cast_2.png differ diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_on.png b/core/res/res/drawable-xhdpi/ic_notification_cast_on.png new file mode 100644 index 0000000000000000000000000000000000000000..38f15ddc416d02474901fa908de4f105eaa387a8 Binary files /dev/null and b/core/res/res/drawable-xhdpi/ic_notification_cast_on.png differ diff --git a/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png deleted file mode 100644 index 4cc0ee83faf896e950cd5673826a7e0c24e9946b..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png and /dev/null differ diff --git a/core/res/res/drawable-xhdpi/toast_frame.9.png b/core/res/res/drawable-xhdpi/toast_frame.9.png index 1f63420602c211130f68e75affe5dc2b39ca9cf7..77e69c72ab9afccc0afa1772c9087ad3333189d4 100644 Binary files a/core/res/res/drawable-xhdpi/toast_frame.9.png and b/core/res/res/drawable-xhdpi/toast_frame.9.png differ diff --git a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png deleted file mode 100644 index 77e69c72ab9afccc0afa1772c9087ad3333189d4..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png and /dev/null differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png index 7b0c383a5093d6a0cec9889c5ab6df226b7a63d2..178774ca179a17547bab9a1bada938da3d460dd1 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png index efb624e182a791451a495b3c8267cf48e663aa2b..2dc2092f0418914b2f3d149e800257532c6cce15 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png index 5ee57e46a01057bb4b920e74c42dfe6a83806cd6..592ee8cdf3261d3e27f25e796658514f380b0bf0 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png index 6bc2e4aa22657ff41a6b86918848557c160d8528..f0549e24188de13a686d28235670143edc960838 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_off_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png index c13af9c80afd56d465f02584f1dd15bd283723dc..91268f5bdc81c5b99b6604255b50ea8f1f5a1f5c 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png index 744fb42a66334ee73d6f5352e93af3360d2a30aa..9d5436f8669914f8cc789daf8dc26da9b69d0051 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png index ca4d59c5b0c24baeb43569edbaf5eed78363dd34..8e77483bce59a4943576d49e150f2ff2a1c219a8 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png index fde5688ad09216154412b77cb8160f8f49d597ca..f396d22170f58d148e0148f940c74b87d467f51a 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png index b8715c39d35b22b84dbee6a4375bdc6b8e20d26e..260bab4f3959b48f8cc09322bdddf11e50ba056a 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png index 668bb2510a559384ef243cafc2dd8c451597abb3..2c9fb1da498a8e78caa99c3b77a9eb1303f94b9b 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png index 7f54a623dd7ea902e248d68f46c7f124665fe12f..bdbd59c1264492a7bc401579797ecc7be5ac87c0 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_dark.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png index 2df924de1b82c2e891561cfbceeb002c8fe3a43d..f5c33dd210e5535ae5b31cc7edc6ea7cadc9c612 100644 Binary files a/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png and b/core/res/res/drawable-xxhdpi/ic_media_route_on_holo_light.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png new file mode 100644 index 0000000000000000000000000000000000000000..f5b16ed10e0e3e917e30fc04f031768613f4dd18 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png new file mode 100644 index 0000000000000000000000000000000000000000..22efeec677f7c3502e494ee612eb962c2bb4493f Binary files /dev/null and b/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png new file mode 100644 index 0000000000000000000000000000000000000000..e24cd970da16916deac9cdb4ac98250ff0902a7d Binary files /dev/null and b/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png new file mode 100644 index 0000000000000000000000000000000000000000..da1a627be5bf158ac193c3295292434a6c4a6a89 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-xxhdpi/ic_notify_wifidisplay.png deleted file mode 100644 index fea4774c8d727c7ad16daff267585a74e9c36aa7..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-xxhdpi/ic_notify_wifidisplay.png and /dev/null differ diff --git a/core/res/res/drawable-xxhdpi/toast_frame.9.png b/core/res/res/drawable-xxhdpi/toast_frame.9.png index 882b9c61e020537f39bdcadb6047341dc9603296..edecb6320de52da7e822c84484454fce5ed41a59 100644 Binary files a/core/res/res/drawable-xxhdpi/toast_frame.9.png and b/core/res/res/drawable-xxhdpi/toast_frame.9.png differ diff --git a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png deleted file mode 100644 index edecb6320de52da7e822c84484454fce5ed41a59..0000000000000000000000000000000000000000 Binary files a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png and /dev/null differ diff --git a/core/res/res/drawable/ic_notification_cast_connecting.xml b/core/res/res/drawable/ic_notification_cast_connecting.xml new file mode 100644 index 0000000000000000000000000000000000000000..a390bce58943ef884700a8575ad22bf0be2d3b22 --- /dev/null +++ b/core/res/res/drawable/ic_notification_cast_connecting.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml index f97225eaeb9e29ee928d87d6ae80f32f9f5cc9eb..c0cd93db456b6fd54299a2c71330a0a5a9ddb8cb 100644 --- a/core/res/res/layout/immersive_mode_cling.xml +++ b/core/res/res/layout/immersive_mode_cling.xml @@ -37,8 +37,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/cling_bg" - android:paddingLeft="14dp" - android:paddingRight="14dp" + android:paddingStart="14dp" + android:paddingEnd="14dp" android:paddingTop="24dp" android:paddingBottom="24dp"> - - - + diff --git a/core/res/res/layout/media_route_chooser_dialog.xml b/core/res/res/layout/media_route_chooser_dialog.xml new file mode 100644 index 0000000000000000000000000000000000000000..d1c6267ea4e75595e1f39f78a7137fe430bbca6c --- /dev/null +++ b/core/res/res/layout/media_route_chooser_dialog.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + +