diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2013-12-05 13:10:46 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2013-12-05 13:10:46 -0800 |
commit | ebcb32f58a6220802ca129ea33f47b4b69931a10 (patch) | |
tree | 32b57c1d6ba9180ae63979e06d7421e107a9aa6c /core | |
parent | 6e2d0c1d91f644ab50e0c0b7cae4306262a4ca41 (diff) | |
parent | bac61807d3bcfff957b358cb9ad77850bd373689 (diff) | |
download | frameworks_base-ebcb32f58a6220802ca129ea33f47b4b69931a10.zip frameworks_base-ebcb32f58a6220802ca129ea33f47b4b69931a10.tar.gz frameworks_base-ebcb32f58a6220802ca129ea33f47b4b69931a10.tar.bz2 |
Merge commit 'bac61807d3bcfff957b358cb9ad77850bd373689' into HEAD
Change-Id: I29374270c8e0c2f2859efaf1d55af9f73da0f8d7
Diffstat (limited to 'core')
251 files changed, 5251 insertions, 2657 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index e29f8ea..d6db8c2 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<N; i++) { - dumpViewHierarchy(prefix, writer, grp.getChildAt(i)); + if (getWindow() != null && + getWindow().peekDecorView() != null && + getWindow().peekDecorView().getViewRootImpl() != null) { + getWindow().peekDecorView().getViewRootImpl().dump(prefix, fd, writer, args); } + + mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix); } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index bb04063..7ca3459 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2272,9 +2272,12 @@ public class ActivityManager { public static void dumpPackageStateStatic(FileDescriptor fd, String packageName) { FileOutputStream fout = new FileOutputStream(fd); PrintWriter pw = new FastPrintWriter(fout); - dumpService(pw, fd, Context.ACTIVITY_SERVICE, new String[] { "package", packageName }); + dumpService(pw, fd, Context.ACTIVITY_SERVICE, new String[] { + "-a", "package", packageName }); pw.println(); - dumpService(pw, fd, ProcessStats.SERVICE_NAME, new String[] { packageName }); + dumpService(pw, fd, "meminfo", new String[] { "--local", packageName }); + pw.println(); + dumpService(pw, fd, ProcessStats.SERVICE_NAME, new String[] { "-a", packageName }); pw.println(); dumpService(pw, fd, "usagestats", new String[] { "--packages", packageName }); pw.println(); @@ -2296,7 +2299,7 @@ public class ActivityManager { pw.flush(); tp = new TransferPipe(); tp.setBufferPrefix(" "); - service.dump(tp.getWriteFd().getFileDescriptor(), args); + service.dumpAsync(tp.getWriteFd().getFileDescriptor(), args); tp.go(fd); } catch (Throwable e) { if (tp != null) { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index df63ab3..5e3dc02 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -534,17 +534,10 @@ public final class ActivityThread { private native void dumpGraphicsInfo(FileDescriptor fd); private class ApplicationThread extends ApplicationThreadNative { - private static final String HEAP_FULL_COLUMN - = "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s"; - private static final String HEAP_COLUMN - = "%13s %8s %8s %8s %8s %8s %8s %8s"; private static final String ONE_COUNT_COLUMN = "%21s %8d"; private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s"; - // Formatting for checkin service - update version if row format changes - private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3; - private int mLastProcessState = -1; private void updatePendingConfiguration(Configuration config) { @@ -558,7 +551,7 @@ public final class ActivityThread { public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges) { - queueOrSendMessage( + sendMessage( finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY, token, (userLeaving ? 1 : 0), @@ -567,32 +560,32 @@ public final class ActivityThread { public final void scheduleStopActivity(IBinder token, boolean showWindow, int configChanges) { - queueOrSendMessage( + sendMessage( showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE, token, 0, configChanges); } public final void scheduleWindowVisibility(IBinder token, boolean showWindow) { - queueOrSendMessage( + sendMessage( showWindow ? H.SHOW_WINDOW : H.HIDE_WINDOW, token); } public final void scheduleSleeping(IBinder token, boolean sleeping) { - queueOrSendMessage(H.SLEEPING, token, sleeping ? 1 : 0); + sendMessage(H.SLEEPING, token, sleeping ? 1 : 0); } public final void scheduleResumeActivity(IBinder token, int processState, boolean isForward) { updateProcessState(processState, false); - queueOrSendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0); + sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0); } public final void scheduleSendResult(IBinder token, List<ResultInfo> 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<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { - pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(','); - pw.print(memInfo.getOtherPss(i)); pw.print(','); - pw.print(memInfo.getOtherSwappablePss(i)); pw.print(','); - pw.print(memInfo.getOtherSharedDirty(i)); pw.print(','); - pw.print(memInfo.getOtherSharedClean(i)); pw.print(','); - pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(','); - pw.print(memInfo.getOtherPrivateClean(i)); pw.print(','); - } // Object counts pw.print(viewInstanceCount); pw.print(','); @@ -1039,128 +964,6 @@ public final class ActivityThread { return; } - // otherwise, show human-readable format - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private", - "Shared", "Private", "Swapped", "Heap", "Heap", "Heap"); - printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", - "Clean", "Clean", "Dirty", "Size", "Alloc", "Free"); - printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------", - "------", "------", "------", "------", "------", "------"); - printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss, - memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, - memInfo.nativePrivateDirty, memInfo.nativeSharedClean, - memInfo.nativePrivateClean, memInfo.nativeSwappedOut, - nativeMax, nativeAllocated, nativeFree); - printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss, - memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, - memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, - memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, - dalvikMax, dalvikAllocated, dalvikFree); - } else { - printRow(pw, HEAP_COLUMN, "", "Pss", "Private", - "Private", "Swapped", "Heap", "Heap", "Heap"); - printRow(pw, HEAP_COLUMN, "", "Total", "Dirty", - "Clean", "Dirty", "Size", "Alloc", "Free"); - printRow(pw, HEAP_COLUMN, "", "------", "------", "------", - "------", "------", "------", "------", "------"); - printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, - memInfo.nativePrivateDirty, - memInfo.nativePrivateClean, memInfo.nativeSwappedOut, - nativeMax, nativeAllocated, nativeFree); - printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, - memInfo.dalvikPrivateDirty, - memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, - dalvikMax, dalvikAllocated, dalvikFree); - } - - int otherPss = memInfo.otherPss; - int otherSwappablePss = memInfo.otherSwappablePss; - int otherSharedDirty = memInfo.otherSharedDirty; - int otherPrivateDirty = memInfo.otherPrivateDirty; - int otherSharedClean = memInfo.otherSharedClean; - int otherPrivateClean = memInfo.otherPrivateClean; - int otherSwappedOut = memInfo.otherSwappedOut; - - for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { - final int myPss = memInfo.getOtherPss(i); - final int mySwappablePss = memInfo.getOtherSwappablePss(i); - final int mySharedDirty = memInfo.getOtherSharedDirty(i); - final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); - final int mySharedClean = memInfo.getOtherSharedClean(i); - final int myPrivateClean = memInfo.getOtherPrivateClean(i); - final int mySwappedOut = memInfo.getOtherSwappedOut(i); - if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 - || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) { - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, mySwappablePss, mySharedDirty, myPrivateDirty, - mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); - } else { - printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, myPrivateDirty, - myPrivateClean, mySwappedOut, "", "", ""); - } - otherPss -= myPss; - otherSwappablePss -= mySwappablePss; - otherSharedDirty -= mySharedDirty; - otherPrivateDirty -= myPrivateDirty; - otherSharedClean -= mySharedClean; - otherPrivateClean -= myPrivateClean; - otherSwappedOut -= mySwappedOut; - } - } - - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss, - otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, - otherSwappedOut, "", "", ""); - printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(), - memInfo.getTotalSwappablePss(), - memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), - memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), - memInfo.getTotalSwappedOut(), nativeMax+dalvikMax, - nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); - } else { - printRow(pw, HEAP_COLUMN, "Unknown", otherPss, - otherPrivateDirty, otherPrivateClean, otherSwappedOut, - "", "", ""); - printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), - memInfo.getTotalPrivateDirty(), - memInfo.getTotalPrivateClean(), - memInfo.getTotalSwappedOut(), - nativeMax+dalvikMax, - nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); - } - - if (dumpDalvik) { - pw.println(" "); - pw.println(" Dalvik Details"); - - for (int i=Debug.MemoryInfo.NUM_OTHER_STATS; - i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; i++) { - final int myPss = memInfo.getOtherPss(i); - final int mySwappablePss = memInfo.getOtherSwappablePss(i); - final int mySharedDirty = memInfo.getOtherSharedDirty(i); - final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); - final int mySharedClean = memInfo.getOtherSharedClean(i); - final int myPrivateClean = memInfo.getOtherPrivateClean(i); - final int mySwappedOut = memInfo.getOtherSwappedOut(i); - if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 - || mySharedClean != 0 || myPrivateClean != 0) { - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, mySwappablePss, mySharedDirty, myPrivateDirty, - mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); - } else { - printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, myPrivateDirty, - myPrivateClean, mySwappedOut, "", "", ""); - } - } - } - } - pw.println(" "); pw.println(" Objects"); printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRootImpl:", @@ -1225,7 +1028,7 @@ public final class ActivityThread { @Override public void unstableProviderDied(IBinder provider) { - queueOrSendMessage(H.UNSTABLE_PROVIDER_DIED, provider); + sendMessage(H.UNSTABLE_PROVIDER_DIED, provider); } @Override @@ -1235,30 +1038,26 @@ public final class ActivityThread { cmd.activityToken = activityToken; cmd.requestToken = requestToken; cmd.requestType = requestType; - queueOrSendMessage(H.REQUEST_ASSIST_CONTEXT_EXTRAS, cmd); - } - - private void printRow(PrintWriter pw, String format, Object...objs) { - pw.println(String.format(format, objs)); + sendMessage(H.REQUEST_ASSIST_CONTEXT_EXTRAS, cmd); } public void setCoreSettings(Bundle coreSettings) { - queueOrSendMessage(H.SET_CORE_SETTINGS, coreSettings); + sendMessage(H.SET_CORE_SETTINGS, coreSettings); } public void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) { UpdateCompatibilityData ucd = new UpdateCompatibilityData(); ucd.pkg = pkg; ucd.info = info; - queueOrSendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd); + sendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd); } public void scheduleTrimMemory(int level) { - queueOrSendMessage(H.TRIM_MEMORY, null, level); + sendMessage(H.TRIM_MEMORY, null, level); } public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) { - queueOrSendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0); + sendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0); } public void setProcessState(int state) { @@ -1281,7 +1080,7 @@ public final class ActivityThread { @Override public void scheduleInstallProvider(ProviderInfo provider) { - queueOrSendMessage(H.INSTALL_PROVIDER, provider); + sendMessage(H.INSTALL_PROVIDER, provider); } } @@ -1959,6 +1758,223 @@ public final class ActivityThread { } } + private static final String HEAP_FULL_COLUMN + = "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s"; + private static final String HEAP_COLUMN + = "%13s %8s %8s %8s %8s %8s %8s %8s"; + + // Formatting for checkin service - update version if row format changes + private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3; + + static void printRow(PrintWriter pw, String format, Object...objs) { + pw.println(String.format(format, objs)); + } + + public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, + boolean dumpFullInfo, boolean dumpDalvik, int pid, String processName, + long nativeMax, long nativeAllocated, long nativeFree, + long dalvikMax, long dalvikAllocated, long dalvikFree) { + + // For checkin, we print one long comma-separated list of values + if (checkin) { + // NOTE: if you change anything significant below, also consider changing + // ACTIVITY_THREAD_CHECKIN_VERSION. + + // Header + pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(','); + pw.print(pid); 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<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { + pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(','); + pw.print(memInfo.getOtherPss(i)); pw.print(','); + pw.print(memInfo.getOtherSwappablePss(i)); pw.print(','); + pw.print(memInfo.getOtherSharedDirty(i)); pw.print(','); + pw.print(memInfo.getOtherSharedClean(i)); pw.print(','); + pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(','); + pw.print(memInfo.getOtherPrivateClean(i)); pw.print(','); + } + return; + } + + // otherwise, show human-readable format + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private", + "Shared", "Private", "Swapped", "Heap", "Heap", "Heap"); + printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", + "Clean", "Clean", "Dirty", "Size", "Alloc", "Free"); + printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------", + "------", "------", "------", "------", "------", "------"); + printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss, + memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, + memInfo.nativePrivateDirty, memInfo.nativeSharedClean, + memInfo.nativePrivateClean, memInfo.nativeSwappedOut, + nativeMax, nativeAllocated, nativeFree); + printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss, + memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, + memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, + memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, + dalvikMax, dalvikAllocated, dalvikFree); + } else { + printRow(pw, HEAP_COLUMN, "", "Pss", "Private", + "Private", "Swapped", "Heap", "Heap", "Heap"); + printRow(pw, HEAP_COLUMN, "", "Total", "Dirty", + "Clean", "Dirty", "Size", "Alloc", "Free"); + printRow(pw, HEAP_COLUMN, "", "------", "------", "------", + "------", "------", "------", "------", "------"); + printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, + memInfo.nativePrivateDirty, + memInfo.nativePrivateClean, memInfo.nativeSwappedOut, + nativeMax, nativeAllocated, nativeFree); + printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, + memInfo.dalvikPrivateDirty, + memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, + dalvikMax, dalvikAllocated, dalvikFree); + } + + int otherPss = memInfo.otherPss; + int otherSwappablePss = memInfo.otherSwappablePss; + int otherSharedDirty = memInfo.otherSharedDirty; + int otherPrivateDirty = memInfo.otherPrivateDirty; + int otherSharedClean = memInfo.otherSharedClean; + int otherPrivateClean = memInfo.otherPrivateClean; + int otherSwappedOut = memInfo.otherSwappedOut; + + for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { + final int myPss = memInfo.getOtherPss(i); + final int mySwappablePss = memInfo.getOtherSwappablePss(i); + final int mySharedDirty = memInfo.getOtherSharedDirty(i); + final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); + final int mySharedClean = memInfo.getOtherSharedClean(i); + final int myPrivateClean = memInfo.getOtherPrivateClean(i); + final int mySwappedOut = memInfo.getOtherSwappedOut(i); + if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 + || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) { + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, mySwappablePss, mySharedDirty, myPrivateDirty, + mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); + } else { + printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, myPrivateDirty, + myPrivateClean, mySwappedOut, "", "", ""); + } + otherPss -= myPss; + otherSwappablePss -= mySwappablePss; + otherSharedDirty -= mySharedDirty; + otherPrivateDirty -= myPrivateDirty; + otherSharedClean -= mySharedClean; + otherPrivateClean -= myPrivateClean; + otherSwappedOut -= mySwappedOut; + } + } + + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss, + otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, + otherSwappedOut, "", "", ""); + printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(), + memInfo.getTotalSwappablePss(), + memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), + memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), + memInfo.getTotalSwappedOut(), nativeMax+dalvikMax, + nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); + } else { + printRow(pw, HEAP_COLUMN, "Unknown", otherPss, + otherPrivateDirty, otherPrivateClean, otherSwappedOut, + "", "", ""); + printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), + memInfo.getTotalPrivateDirty(), + memInfo.getTotalPrivateClean(), + memInfo.getTotalSwappedOut(), + nativeMax+dalvikMax, + nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); + } + + if (dumpDalvik) { + pw.println(" "); + pw.println(" Dalvik Details"); + + for (int i=Debug.MemoryInfo.NUM_OTHER_STATS; + i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; i++) { + final int myPss = memInfo.getOtherPss(i); + final int mySwappablePss = memInfo.getOtherSwappablePss(i); + final int mySharedDirty = memInfo.getOtherSharedDirty(i); + final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); + final int mySharedClean = memInfo.getOtherSharedClean(i); + final int myPrivateClean = memInfo.getOtherPrivateClean(i); + final int mySwappedOut = memInfo.getOtherSwappedOut(i); + if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 + || mySharedClean != 0 || myPrivateClean != 0) { + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, mySwappablePss, mySharedDirty, myPrivateDirty, + mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); + } else { + printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, myPrivateDirty, + myPrivateClean, mySwappedOut, "", "", ""); + } + } + } + } + } + public void registerOnActivityPausedListener(Activity activity, OnActivityPausedListener listener) { synchronized (mOnPauseListeners) { @@ -2033,28 +2049,31 @@ public final class ActivityThread { mAppThread.scheduleSendResult(token, list); } - // if the thread hasn't started yet, we don't have the handler, so just - // save the messages until we're ready. - private void queueOrSendMessage(int what, Object obj) { - queueOrSendMessage(what, obj, 0, 0); + private void sendMessage(int what, Object obj) { + sendMessage(what, obj, 0, 0, false); } - private void queueOrSendMessage(int what, Object obj, int arg1) { - queueOrSendMessage(what, obj, arg1, 0); + private void sendMessage(int what, Object obj, int arg1) { + sendMessage(what, obj, arg1, 0, false); } - private void queueOrSendMessage(int what, Object obj, int arg1, int arg2) { - synchronized (this) { - if (DEBUG_MESSAGES) Slog.v( - TAG, "SCHEDULE " + what + " " + mH.codeToString(what) - + ": " + arg1 + " / " + obj); - Message msg = Message.obtain(); - msg.what = what; - msg.obj = obj; - msg.arg1 = arg1; - msg.arg2 = arg2; - mH.sendMessage(msg); + private void sendMessage(int what, Object obj, int arg1, int arg2) { + sendMessage(what, obj, arg1, arg2, false); + } + + private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { + if (DEBUG_MESSAGES) Slog.v( + TAG, "SCHEDULE " + what + " " + mH.codeToString(what) + + ": " + arg1 + " / " + obj); + Message msg = Message.obtain(); + msg.what = what; + msg.obj = obj; + msg.arg1 = arg1; + msg.arg2 = arg2; + if (async) { + msg.setAsynchronous(true); } + mH.sendMessage(msg); } final void scheduleContextCleanup(ContextImpl context, String who, @@ -2063,7 +2082,7 @@ public final class ActivityThread { cci.context = context; cci.who = who; cci.what = what; - queueOrSendMessage(H.CLEAN_UP_CONTEXT, cci); + sendMessage(H.CLEAN_UP_CONTEXT, cci); } private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { @@ -3592,7 +3611,7 @@ public final class ActivityThread { target.onlyLocalRequest = true; } mRelaunchingActivities.add(target); - queueOrSendMessage(H.RELAUNCH_ACTIVITY, target); + sendMessage(H.RELAUNCH_ACTIVITY, target); } if (fromServer) { @@ -4900,7 +4919,7 @@ public final class ActivityThread { mPendingConfiguration.isOtherSeqNewer(newConfig)) { mPendingConfiguration = newConfig; - queueOrSendMessage(H.CONFIGURATION_CHANGED, newConfig); + sendMessage(H.CONFIGURATION_CHANGED, newConfig); } } } diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 5c3a3e5..0cf7ad0 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -48,6 +48,15 @@ import android.os.WorkSource; * etc) it is easier and much more efficient to use * {@link android.os.Handler}.</b> * + * <p class="caution"><strong>Note:</strong> 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. + * * <p>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+. - * * <p>Schedule an alarm. <b>Note: for timing operations (ticks, timeouts, - * etc) it is easier and much more efficient to use - * {@link android.os.Handler}.</b> 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}.</b> + * If there is already an alarm scheduled for the same IntentSender, that previous + * alarm will first be canceled. * - * <p>If the time occurs in the past, the alarm will be triggered + * <p>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. * * <p> - * 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. + * + * <div class="note"> + * <p> + * <b>Note:</b> 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. + * + * <p> + * 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' <em>actual</em> delivery ordering may not match + * the order of their <em>requested</em> 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)}. + * + * <p> + * 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. + * </div> + * + * @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}.</b> If there is already an alarm scheduled * for the same IntentSender, it will first be canceled. * - * <p>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 + * <p>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. + * <p class="note"> + * <b>Note:</b> 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 + * <p> + * 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. + * + * <p> + * 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 <i>duration,</i> 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. + * + * <p> + * 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. + * + * <p> + * <b>Note:</b> 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. - * + * * <p>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. + * <p class="note"> + * 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 b741cc5..2ed8b0f 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. *<p> * 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. - *<p> - * 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 a307a73..028fa68 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 22a21cd..aab6ed8 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 63b641c..dffa969 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. + * <p> + * 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. + * </p><p> + * 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. + * </p> + */ 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<MediaRouteActionProvider> mAp; + private static class MediaRouterCallback extends MediaRouter.SimpleCallback { + private final WeakReference<MediaRouteActionProvider> mProviderWeak; - RouterCallback(MediaRouteActionProvider ap) { - mAp = new WeakReference<MediaRouteActionProvider>(ap); + public MediaRouterCallback(MediaRouteActionProvider provider) { + mProviderWeak = new WeakReference<MediaRouteActionProvider>(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 7e0a27a..a7982f4 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,54 +97,100 @@ 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. + * <p> + * 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. + * </p><p> + * This will attach a {@link DialogFragment} to the containing Activity. + * </p> + */ + 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()) { return true; @@ -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 7bcf43e..2045ed8 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 607930c..91b0d7c 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 5822e46..d789a94 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 - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} to + * <p>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. * <p>Android system services will handle the necessary user interactions * to confirm and complete the bonding process. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * <p>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} - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * <p>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. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * <p>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 a9d0559..ddde3fb 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 - // <path-permission> 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 <path-permission> means we lose - // default <provider> access. - allowDefaultRead = false; - missingPerm = pathPerm; - } + // track if unprotected read is allowed; any denied + // <path-permission> 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 <path-permission> means we lose + // default <provider> access. + allowDefaultRead = false; + missingPerm = pathPerm; } } } - - // if we passed <path-permission> checks above, and no default - // <provider> 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 <path-permission> checks above, and no default + // <provider> 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 - // <path-permission> 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 <path-permission> means we lose - // default <provider> access. - allowDefaultWrite = false; - missingPerm = pathPerm; - } + // track if unprotected write is allowed; any denied + // <path-permission> 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 <path-permission> means we lose + // default <provider> access. + allowDefaultWrite = false; + missingPerm = pathPerm; } } } - - // if we passed <path-permission> checks above, and no default - // <provider> 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 <path-permission> checks above, and no default + // <provider> 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 0284882..cffc653 100644 --- a/core/java/android/content/SyncInfo.java +++ b/core/java/android/content/SyncInfo.java @@ -55,6 +55,14 @@ public class SyncInfo implements Parcelable { } /** @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 267fb2a..20002ad 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 17d13e5..e6da288 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<String> grantedPermissions, PackageUserState state, int userId) { diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 14f67c5..50fdb41 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 4fe2c4d..a38beec 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -334,6 +334,27 @@ public final class CameraCharacteristics extends CameraMetadata { /** * <p> + * 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. + * </p> + * <p> + * 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. + * </p> + * + * <b>Optional</b> - This value may be null on some devices. + * + * @hide + */ + public static final Key<Byte> QUIRKS_USE_PARTIAL_RESULT = + new Key<Byte>("android.quirks.usePartialResult", byte.class); + + /** + * <p> * How many output streams can be allocated at * the same time for each type of stream * </p> diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 7095e4d..9e8d7d1 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -631,6 +631,36 @@ public interface CameraDevice extends AutoCloseable { } /** + * This method is called when some results from an image capture are + * available. + * + * <p>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.</p> + * + * <p>The default implementation of this method does nothing.</p> + * + * @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 dbd0457..535b963 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -591,6 +591,32 @@ public final class CaptureResult extends CameraMetadata { /** * <p> + * 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. + * </p> + * <p> + * 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. + * </p> + * + * <b>Optional</b> - This value may be null on some devices. + * + * @hide + */ + public static final Key<Boolean> QUIRKS_PARTIAL_RESULT = + new Key<Boolean>("android.quirks.partialResult", boolean.class); + + /** + * <p> * A frame counter set by the framework. This value monotonically * increases with every new result (that is, each new result has a unique * frameCount value). diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java index c5d0999..40586f0 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<CaptureListenerHolder> mCaptureListenerMap = new SparseArray<CaptureListenerHolder>(); - private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>(); + private int mRepeatingRequestId = REQUEST_ID_NONE; + private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>(); // Map stream IDs to Surfaces private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>(); @@ -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<Integer> 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 f12be5f..d5208d9 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. + * <p> + * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. + * </p> + * * @hide */ public void scanWifiDisplays() { @@ -312,8 +316,7 @@ public final class DisplayManager { * Automatically remembers the display after a successful connection, if not * already remembered. * </p><p> - * 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}. * </p> * * @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 b674324..8a2c2b6 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 44cfa94..010e527 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 486e75a..6743c6c 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -49,6 +49,8 @@ import android.util.Log; * <h3>Developer Guides</h3> * <p>For more information about using NFC, read the * <a href="{@docRoot}guide/topics/nfc/index.html">Near Field Communication</a> developer guide.</p> + * <p>To perform basic file sharing between devices, read + * <a href="{@docRoot}training/beam-files/index.html">Sharing Files with NFC</a>. * </div> */ 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. + * <p>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 bc98a0b..b1a9ea3 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -106,6 +106,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. */ public static final int STATS_SINCE_CHARGED = 0; @@ -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 114a1ea..bc51a60 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -457,6 +457,13 @@ public class Build { * margins correctly.</li> * <li> {@link android.app.ActionBar}'s window content overlay is allowed to be * drawn.</li> + * <li>The {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} + * permission is now always enforced.</li> + * <li>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.</li> * </ul> */ public static final int KITKAT = 19; diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 5de365f..af57507 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<String, Object>(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 4d48fd4..ff3e277 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 4c7bbb4..56176a4 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 78c859e..21e9f6b 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 0abc149..51203a4 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 d1b8213..799de5c 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 02b1998..94b9617 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<N; i++) { - if (DEBUG_ARRAY_MAP) Log.d(TAG, " Write #" + i + ": key=0x" - + (val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0) + " " + val.keyAt(i)); + if (DEBUG_ARRAY_MAP) startPos = dataPosition(); writeValue(val.keyAt(i)); writeValue(val.valueAt(i)); + if (DEBUG_ARRAY_MAP) Log.d(TAG, " Write #" + i + " " + + (dataPosition()-startPos) + " bytes: key=0x" + + Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0) + + " " + val.keyAt(i)); } } @@ -2303,11 +2307,14 @@ public final class Parcel { here.fillInStackTrace(); Log.d(TAG, "Reading " + N + " ArrayMap entries", here); } + int startPos; while (N > 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 1456387..5273c20 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 bb3d296..3249bcb 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 + * <p>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. + * <p>For information about using the Systrace tool, read <a + * href="{@docRoot}tools/debugging/systrace.html">Analyzing Display and Performance + * with Systrace</a>. */ public final class Trace { /* diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index 177a955..0285cb9 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<StorageVolume> CREATOR = new Creator<StorageVolume>() { @@ -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 37a8102..f7d1eb7 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -1069,11 +1069,11 @@ public class Preference implements Comparable<Preference> { * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>; * greater than 0 if this Preference sorts after <var>another</var>. */ + @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 2ab5a91..7ddfa87 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<Header> headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG); diff --git a/core/java/android/print/IPrintDocumentAdapter.aidl b/core/java/android/print/IPrintDocumentAdapter.aidl index b12c922..2b95c12 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 0000000..4443df0 --- /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 cdcd0c7..d6320f0 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 e1a9cb7..c6254e0 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. * <p> * <strong>Note: </strong>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. * </p> */ public static final MediaSize UNKNOWN_PORTRAIT = @@ -288,7 +292,7 @@ public final class PrintAttributes implements Parcelable { * Unknown media size in landscape mode. * <p> * <strong>Note: </strong>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. * </p> */ 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 <strong>internationalized</strong> human readable label. + * @param label The <strong>localized</strong> 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 <strong>internationalized</strong> human readable label. + * @param label The <strong>localized</strong> 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 4113ac7..1f59bef 100644 --- a/core/java/android/print/PrintDocumentAdapter.java +++ b/core/java/android/print/PrintDocumentAdapter.java @@ -38,15 +38,46 @@ import android.os.ParcelFileDescriptor; * </li> * <li> * 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 <strong>may</strong> get + * a call to {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, + * WriteResultCallback)} asking you to write a PDF file with the content for + * specific pages. * </li> * <li> * Finally, you will receive a call to {@link #onFinish()}. You can use this * callback to release resources allocated in {@link #onStart()}. * </li> * </ul> + * <p> + * 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. + * </p> + * <p> + * 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. + * </p> + * <p> + * 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)}. + * </p> + * <p> + * 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. + * </p> * </p> * <h3>Implementation</h3> * <p> @@ -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. * </p> * <p> * 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 <code>true</code> if * the current layout is for a print preview, <code>false</code> 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 <strong>must</strong> invoke: {@link * LayoutResultCallback#onLayoutFinished(PrintDocumentInfo, boolean)} with * the last argument <code>true</code> or <code>false</code> 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 <strong>must</strong> 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. + * </p> + * <p> + * 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 <strong>will not</strong> be made on the + * main thread and can be registered as follows: * </p> + * <pre> + * cancellationSignal.setOnCancelListener(new OnCancelListener() { + * @Override + * public void onCancel() { + * // Cancel layout + * } + * }); + * </pre> * <p> * <strong>Note:</strong> 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. * </p> * * @param oldAttributes The old print attributes. @@ -128,17 +193,41 @@ public abstract class PrintDocumentAdapter { * on the main thread. *<p> * 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 <strong>must</strong> 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. + * </p> + * <p> + * 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 + * <strong>will not</strong> be made on the main thread and can be registered + * as follows: * </p> + * <pre> + * cancellationSignal.setOnCancelListener(new OnCancelListener() { + * @Override + * public void onCancel() { + * // Cancel write + * } + * }); + * </pre> * <p> * <strong>Note:</strong> 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. * </p> * * @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 <code>null</code> + * 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 <strong>localized</strong> error message. + * shown to the user. May be <code>null</code> 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 <code>null</code>. * @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 <strong>localized</strong> error message. + * shown to the user. May be <code>null</code> 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 b721ef4..928be6c 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. + * <p> + * 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)}. + * </p> + * <p> + * An example usage looks like this: + * <pre> + * + * . . . + * + * 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()); + * } + * + * . . . + * + * </pre> + * </p> */ 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. + * <p> + * 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. + * </p> */ public static final int CONTENT_TYPE_DOCUMENT = 0; /** * Content type: photo. + * <p> + * 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. + * </p> */ 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. + * * <p> - * 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. * </p> * - * @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 535ae43..0abe219 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 c6f0a68..63f94fe 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. * <p> * 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. * <p> - * Next valid states: None + * Next valid states: {@link #STATE_CANCELED}, {@link #STATE_STARTED} * </p> */ 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. * <p> * 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 <code>null</code>. */ 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 dbd8278..d1bb8fd 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); * </pre> + * + * <h3>Print mechanics</h3> + * <p> + * 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. + * </p> + * <h3>Print jobs</h3> + * <p> + * 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. + * </p> + * <p> + * 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. + * </p> + * <p> + * An applications can query the print spooler for current print jobs it created + * but not print jobs created by other applications. + * </p> + * + * @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. + * <p> + * 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. + * <p> + * 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. + * </p> + * <p> + * 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. + * </p> + * <p> + * 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. + * </p> + * + * @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 <code>null</code>. * @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 df51ec1..b615600 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<MediaSize> getMediaSizes() { - return mMediaSizes; + return Collections.unmodifiableList(mMediaSizes); } /** @@ -121,7 +128,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable { * @return The resolutions. */ public List<Resolution> 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. * <p> * 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 <code>null</code>. * - * @throws IllegalArgumentException If the printer id is null. + * @throws IllegalArgumentException If the printer id is <code>null</code>. */ 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 ad79a38..7fcc81f 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 <strong>localized</strong> 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 <strong>localized</strong> 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 0000000..579567d --- /dev/null +++ b/core/java/android/print/package.html @@ -0,0 +1,46 @@ +<HTML> +<BODY> +<h3>Overview</h3> +<p> +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. +</p> +<p> +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. +<p/> +<h3>Print contract</h3> +<p> +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. +</p> +<h3>Print jobs</h3> +<p> +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. +</p> +<h3>Printers</h3> +<p> +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. +<p> +</BODY> +</HTML> diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java index fdeb373..6fa0bdd 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 0fc5f7f..eb0ac2e 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)}. * </p> + * <p> + * 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. + * </p> */ 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 8e9636c..a2c6c09 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); } /** @@ -195,6 +205,19 @@ public final class PrintServiceInfo implements Parcelable { } /** + * The advanced print options activity name. + * <p> + * <strong>Statically set from + * {@link PrintService#SERVICE_META_DATA meta-data}.</strong> + * </p> + * + * @return The advanced print options activity name. + */ + public String getAdvancedOptionsActivityName() { + return mAdvancedPrintOptionsActivityName; + } + + /** * {@inheritDoc} */ public int describeContents() { @@ -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 7f8dca2..86d3cf8 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 c9efb53..49816f8 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. * </p> * <h3>Documents</h3> * <p> @@ -91,8 +91,8 @@ import java.io.FileNotFoundException; * <p> * 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. * </p> @@ -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. * </p> * <h3>Roots</h3> * <p> @@ -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. * <p> * 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. * <p> * 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. + * <p> + * 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()}. + * <p> + * 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 { * <p> * Only documents may be returned; directories are not supported in search * results. + * <p> + * 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. + * <p> + * 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. * <p> - * 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. * <p> - * 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. * <p> - * 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 7f24539..04f3f0a 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. + * <p class="note"><strong>Note:</strong> When a device has <a + * href="{@docRoot}about/versions/android-4.2.html#MultipleUsers">multiple users</a> + * (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.</p> */ 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 b808363..2752085 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<Set<String>>() { @Override public Set<String> 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<String> featureSet = new HashSet<String>(); Collections.addAll(featureSet, features); diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java index 5fbd22e..4f996cd 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 160c630..f839d52 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 6d066d6..c596388 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 + * <p>IME authors: If the class is not one you * understand, assume {@link #TYPE_CLASS_TEXT} with NO variation - * or flags. + * or flags.<p> */ 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}. + * <p>IME authors: If you do not recognize + * the variation, normal should be assumed.</p> */ 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 d798abe..e1f1896 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<SparseArray<Scene>> sScenes = new ThreadLocal<SparseArray<Scene>>(); /** * Returns a Scene described by the resource file associated with the given - * <code>layoutId</code> 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. + * <code>layoutId</code> parameter. If such a Scene has already been created for + * the given <code>sceneRoot</code>, 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<Scene> scenes = sScenes.get(); + SparseArray<Scene> scenes = (SparseArray<Scene>) sceneRoot.getTag( + com.android.internal.R.id.scene_layoutid_cache); if (scenes == null) { scenes = new SparseArray<Scene>(); - 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 dcf668b..da9ba5a 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) { @@ -1055,6 +1047,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 * method to capture values for all possible views. @@ -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 4af0f51..9f77d5e 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<Context, TransitionInflater> sInflaterMap = - new ArrayMap<Context, TransitionInflater>(); - private Context mContext; - // TODO: do we need id maps for transitions and transitionMgrs as well? - SparseArray<Scene> mScenes = new SparseArray<Scene>(); 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 404709c..3bf6790 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<Scene, Transition> 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<ViewGroup, ArrayList<Transition>> runningTransitions = - getRunningTransitions(); - ArrayList<Transition> currentTransitions = runningTransitions.get(sceneRoot); - ArrayList<Transition> previousRunningTransitions = null; - if (currentTransitions == null) { - currentTransitions = new ArrayList<Transition>(); - runningTransitions.put(sceneRoot, currentTransitions); - } else if (currentTransitions.size() > 0) { - previousRunningTransitions = new ArrayList<Transition>(currentTransitions); - } - currentTransitions.add(transition); - transition.addListener(new Transition.TransitionListenerAdapter() { - @Override - public void onTransitionEnd(Transition transition) { - ArrayList<Transition> 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<Transition> 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<ViewGroup, ArrayList<Transition>> runningTransitions = + getRunningTransitions(); + ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot); + ArrayList<Transition> previousRunningTransitions = null; + if (currentTransitions == null) { + currentTransitions = new ArrayList<Transition>(); + runningTransitions.put(mSceneRoot, currentTransitions); + } else if (currentTransitions.size() > 0) { + previousRunningTransitions = new ArrayList<Transition>(currentTransitions); + } + currentTransitions.add(mTransition); + mTransition.addListener(new Transition.TransitionListenerAdapter() { + @Override + public void onTransitionEnd(Transition transition) { + ArrayList<Transition> 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 dae47b8..6cda905 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -67,6 +67,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 * being scaled up to it should be sufficient for almost all cases. diff --git a/core/java/android/util/MapCollections.java b/core/java/android/util/MapCollections.java index f4a9b0b..28b788b 100644 --- a/core/java/android/util/MapCollections.java +++ b/core/java/android/util/MapCollections.java @@ -97,10 +97,10 @@ abstract class MapCollections<K, V> { 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 f28e4b5..f1523ae 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. * <p> diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 354ea66..7d310a2 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 9a89fa5..324a1ae 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 9d4af00..c92a104 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 f5ee7ed..25972e7 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 f36c78f..42a58a8 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 e2d98ed..b0bae46 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; @@ -3103,6 +3102,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 */ int mOldWidthMeasureSpec = Integer.MIN_VALUE; @@ -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 9461068..bc0d7e3 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; i<N; i++) { + dumpViewHierarchy(prefix, writer, grp.getChildAt(i)); + } + } + public void dumpGfxInfo(int[] info) { info[0] = info[1] = 0; if (mView != null) { @@ -5570,7 +5652,13 @@ public final class ViewRootImpl implements ViewParent, if (mConsumeBatchedInputScheduled) { mConsumeBatchedInputScheduled = false; if (mInputEventReceiver != null) { - mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos); + if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)) { + // If we consumed a batch here, we want to go ahead and schedule the + // consumption of batched input events on the next frame. Otherwise, we would + // wait until we have more input events pending and might get starved by other + // things occurring in the process. + scheduleConsumeBatchedInput(); + } } doProcessInputEvents(); } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 9fc37cf..4f53c1e 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -2540,7 +2540,7 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Class with information if a node is a range. Use - * {@link RangeInfo#obtain(int, float, float, float) to get an instance. + * {@link RangeInfo#obtain(int, float, float, float)} to get an instance. */ public static final class RangeInfo { private static final int MAX_POOL_SIZE = 10; diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 5146567..d4e005b 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -70,14 +70,14 @@ public class EditorInfo implements InputType, Parcelable { /** * Bits of {@link #IME_MASK_ACTION}: the action key performs a "search" * operation, taking the user to the results of searching for the text - * the have typed (in whatever context is appropriate). + * they have typed (in whatever context is appropriate). */ public static final int IME_ACTION_SEARCH = 0x00000003; /** * Bits of {@link #IME_MASK_ACTION}: the action key performs a "send" * operation, delivering the text to its target. This is typically used - * when composing a message. + * when composing a message in IM or SMS where sending is immediate. */ public static final int IME_ACTION_SEND = 0x00000004; @@ -89,22 +89,31 @@ public class EditorInfo implements InputType, Parcelable { /** * Bits of {@link #IME_MASK_ACTION}: the action key performs a "done" - * operation, typically meaning the IME will be closed. + * operation, typically meaning there is nothing more to input and the + * IME will be closed. */ public static final int IME_ACTION_DONE = 0x00000006; /** * Bits of {@link #IME_MASK_ACTION}: Like {@link #IME_ACTION_NEXT}, but * for moving to the previous field. This will normally not be used to - * specify an action (since it precludes {@link #IME_ACTION_NEXT}, but + * specify an action (since it precludes {@link #IME_ACTION_NEXT}), but * can be returned to the app if it sets {@link #IME_FLAG_NAVIGATE_PREVIOUS}. */ public static final int IME_ACTION_PREVIOUS = 0x00000007; /** * Flag of {@link #imeOptions}: used to request that the IME never go - * into fullscreen mode. Applications need to be aware that the flag is not - * a guarantee, and not all IMEs will respect it. + * into fullscreen mode. + * By default, IMEs may go into full screen mode when they think + * it's appropriate, for example on small screens in landscape + * orientation where displaying a software keyboard may occlude + * such a large portion of the screen that the remaining part is + * too small to meaningfully display the application UI. + * If this flag is set, compliant IMEs will never go into full screen mode, + * and always leave some space to display the application UI. + * Applications need to be aware that the flag is not a guarantee, and + * some IMEs may ignore it. */ public static final int IME_FLAG_NO_FULLSCREEN = 0x2000000; @@ -136,50 +145,56 @@ public class EditorInfo implements InputType, Parcelable { * Flag of {@link #imeOptions}: used to specify that the IME does not need * to show its extracted text UI. For input methods that may be fullscreen, * often when in landscape mode, this allows them to be smaller and let part - * of the application be shown behind. Though there will likely be limited - * access to the application available from the user, it can make the - * experience of a (mostly) fullscreen IME less jarring. Note that when - * this flag is specified the IME may <em>not</em> 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 59330ca..3537aec 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.</p> * * @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.</p> + * 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.</p> * * @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 c440c7b..5df5811 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. + * + * <p>It should be defined in an XML resource file with an {@code <input-method>} element. + * For more information, see the guide to + * <a href="{@docRoot}guide/topics/text/creating-input-method.html"> + * Creating an Input Method</a>.</p> + * + * @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 88b2977..2ab3024 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. * * <p>It should be defined in an XML resource file of the input method with the - * <code><subtype></code> element. For more information, see the guide to - * <a href="{@docRoot}resources/articles/creating-input-method.html"> + * <code><subtype></code> element, which resides within an {@code <input-method>} element. + * For more information, see the guide to + * <a href="{@docRoot}guide/topics/text/creating-input-method.html"> * Creating an Input Method</a>.</p> + * + * @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 0000000..bbd3f2b --- /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<String, String>))}. + * + * @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<String, String>)}. + * + * @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<String, String> 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 0000000..88fc9b7 --- /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<String, String[]> 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<String, String[]> 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<String, String[]> 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<String, String[]> 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 0000000..59fc0cb --- /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<String, String> 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<String, String> headers); +} diff --git a/core/java/android/webkit/UrlInterceptRegistry.java b/core/java/android/webkit/UrlInterceptRegistry.java new file mode 100644 index 0000000..bdf6747 --- /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<UrlInterceptHandler>(); + 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<String, String> 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<String, String> 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 3eb0052..092f474 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<ListAdapter> 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 736566e..61df922 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<String, ActivityResolveInfo> mPackageNameToActivityMap = - new HashMap<String, ActivityResolveInfo>(); + private final Map<ComponentName, ActivityResolveInfo> mPackageNameToActivityMap = + new HashMap<ComponentName, ActivityResolveInfo>(); public void sort(Intent intent, List<ActivityResolveInfo> activities, List<HistoricalRecord> historicalRecords) { - Map<String, ActivityResolveInfo> packageNameToActivityMap = - mPackageNameToActivityMap; - packageNameToActivityMap.clear(); + Map<ComponentName, ActivityResolveInfo> 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 dff1531..8612964 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 401fcb8..c960342 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 4614c53..01ac8fd 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 9e35a23..7daf798 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 65a2d4d..5392a96 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 368f6ad..fd2f754 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 cb930d6..7a9809f 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 fbdf318..d57b739 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.<p> + * + * <em>Note: VideoView does not retain its full state when going into the + * background.</em> 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}.<p> + * 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 061bb00..b152297 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 525517c..43c4b49 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 0000000..47d2a9c --- /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}. + * <p> + * This dialog allows the user to choose a route that matches a given selector. + * </p> + * + * @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. + * <p> + * The default implementation returns true for enabled non-default routes that + * match the route types. Subclasses can override this method to filter routes + * differently. + * </p> + * + * @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<MediaRouter.RouteInfo> + 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<MediaRouter.RouteInfo> { + 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 e300021..ae362af 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. + * <p> + * Creates a {@link MediaRouteChooserDialog}. The application may subclass + * this dialog fragment to customize the media route chooser dialog. + * </p> * - * @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. + * <p> + * All subclasses of this class must also possess a default constructor. + * </p> + */ 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<Object> mItems = new ArrayList<Object>(); - - private RouteCategory mCategoryEditingGroups; - private RouteGroup mEditingGroup; - - // Temporary lists for manipulation - private final ArrayList<RouteInfo> mCatRouteList = new ArrayList<RouteInfo>(); - private final ArrayList<RouteInfo> mSortRouteList = new ArrayList<RouteInfo>(); - - 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<RouteInfo> 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<RouteInfo> 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<RouteInfo> 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<RouteInfo> { - @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. + * <p> + * Subclasses may override this method to customize the dialog. + * </p> */ - 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 0000000..8fc99c7 --- /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}. + * <p> + * This dialog allows the user to control or disconnect from the currently selected route. + * </p> + * + * @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. + * <p> + * The default value is true. + * </p> + */ + 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 0000000..108e81f --- /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. + * <p> + * Creates a {@link MediaRouteControllerDialog}. The application may subclass + * this dialog fragment to customize the media route controller dialog. + * </p> + * + * 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. + * <p> + * All subclasses of this class must also possess a default constructor. + * </p> + */ + public MediaRouteControllerDialogFragment() { + setCancelable(true); + setStyle(STYLE_NORMAL, android.R.style.Theme_DeviceDefault_Dialog); + } + + /** + * Called when the controller dialog is being created. + * <p> + * Subclasses may override this method to customize the dialog. + * </p> + */ + 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 0000000..fad7fd4 --- /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 222e446..0cad33c 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<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); boolean printedHeader = false; + boolean sepNeeded = false; for (int ip=0; ip<pkgMap.size(); ip++) { - String pkgName = pkgMap.keyAt(ip); - if (reqPackage != null && !reqPackage.equals(pkgName)) { - continue; - } - SparseArray<PackageState> uids = pkgMap.valueAt(ip); + final String pkgName = pkgMap.keyAt(ip); + final SparseArray<PackageState> uids = pkgMap.valueAt(ip); for (int iu=0; iu<uids.size(); iu++) { - int uid = uids.keyAt(iu); - PackageState pkgState = uids.valueAt(iu); + final int uid = uids.keyAt(iu); + final PackageState pkgState = uids.valueAt(iu); final int NPROCS = pkgState.mProcesses.size(); final int NSRVS = pkgState.mServices.size(); + final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName); + if (!pkgMatch) { + boolean procMatch = false; + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (reqPackage.equals(proc.mName)) { + procMatch = true; + break; + } + } + if (!procMatch) { + continue; + } + } if (NPROCS > 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<NPROCS; iproc++) { ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (!pkgMatch && !reqPackage.equals(proc.mName)) { + continue; + } if (activeOnly && !proc.isInUse()) { pw.print(" (Not active: "); pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")"); @@ -1777,7 +1803,11 @@ public final class ProcessStats implements Parcelable { } pw.print(" Process "); pw.print(pkgState.mProcesses.keyAt(iproc)); - pw.print(" ("); + if (proc.mCommonProcess.mMultiPackage) { + pw.print(" (multi, "); + } else { + pw.print(" (unique, "); + } pw.print(proc.mDurationsTableSize); pw.print(" entries)"); pw.println(":"); @@ -1791,6 +1821,9 @@ public final class ProcessStats implements Parcelable { ArrayList<ProcessState> procs = new ArrayList<ProcessState>(); for (int iproc=0; iproc<NPROCS; iproc++) { ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (!pkgMatch && !reqPackage.equals(proc.mName)) { + continue; + } if (activeOnly && !proc.isInUse()) { continue; } @@ -1801,6 +1834,9 @@ public final class ProcessStats implements Parcelable { } for (int isvc=0; isvc<NSRVS; isvc++) { ServiceState svc = pkgState.mServices.valueAt(isvc); + if (!pkgMatch && !reqPackage.equals(svc.mProcessName)) { + continue; + } if (activeOnly && !svc.isInUse()) { pw.print(" (Not active: "); pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")"); @@ -1830,64 +1866,73 @@ public final class ProcessStats implements Parcelable { if (svc.mOwner != null) { pw.print(" mOwner="); pw.println(svc.mOwner); } + if (svc.mStarted || svc.mRestarting) { + pw.print(" mStarted="); pw.print(svc.mStarted); + pw.print(" mRestarting="); pw.println(svc.mRestarting); + } } } } } - if (reqPackage == null) { - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - printedHeader = false; - int numShownProcs = 0, numTotalProcs = 0; - for (int ip=0; ip<procMap.size(); ip++) { - String procName = procMap.keyAt(ip); - SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - int uid = uids.keyAt(iu); - numTotalProcs++; - ProcessState proc = uids.valueAt(iu); - if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING - && proc.mPssTableSize == 0) { - continue; - } - numShownProcs++; - if (!printedHeader) { - pw.println(); - pw.println("Per-Process Stats:"); - printedHeader = true; - } - if (activeOnly && !proc.isInUse()) { - pw.print(" (Not active: "); pw.print(procName); pw.println(")"); - continue; - } - pw.print(" * "); pw.print(procName); pw.print(" / "); - UserHandle.formatUid(pw, uid); - pw.print(" ("); pw.print(proc.mDurationsTableSize); - pw.print(" entries)"); pw.println(":"); - dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES, now); - dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES); - if (dumpAll) { - dumpProcessInternalLocked(pw, " ", proc, dumpAll); - } + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + printedHeader = false; + int numShownProcs = 0, numTotalProcs = 0; + for (int ip=0; ip<procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + numTotalProcs++; + ProcessState proc = uids.valueAt(iu); + if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING + && proc.mPssTableSize == 0) { + continue; } + if (!proc.mMultiPackage) { + continue; + } + if (reqPackage != null && !reqPackage.equals(procName) + && !reqPackage.equals(proc.mPackage)) { + continue; + } + numShownProcs++; + if (sepNeeded) { + pw.println(); + } + sepNeeded = true; + if (!printedHeader) { + pw.println("Multi-Package Common Processes:"); + printedHeader = true; + } + if (activeOnly && !proc.isInUse()) { + pw.print(" (Not active: "); pw.print(procName); pw.println(")"); + continue; + } + pw.print(" * "); pw.print(procName); pw.print(" / "); + UserHandle.formatUid(pw, uid); + pw.print(" ("); pw.print(proc.mDurationsTableSize); + pw.print(" entries)"); pw.println(":"); + dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES); + dumpProcessInternalLocked(pw, " ", proc, dumpAll); } - if (dumpAll) { - pw.println(); - pw.print(" Total procs: "); pw.print(numShownProcs); - pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total"); - } + } + if (dumpAll) { + pw.println(); + pw.print(" Total procs: "); pw.print(numShownProcs); + pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total"); + } + if (sepNeeded) { pw.println(); - if (dumpSummary) { - pw.println("Summary:"); - dumpSummaryLocked(pw, reqPackage, now, activeOnly); - } else { - dumpTotalsLocked(pw, now); - } + } + if (dumpSummary) { + pw.println("Summary:"); + dumpSummaryLocked(pw, reqPackage, now, activeOnly); } else { - pw.println(); dumpTotalsLocked(pw, now); } @@ -1920,8 +1965,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; @@ -1930,8 +1976,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(); } @@ -2021,17 +2066,20 @@ public final class ProcessStats implements Parcelable { public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates, int[] procStates, int sortProcStates[], long now, String reqPackage, boolean activeOnly) { - ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>(); - ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + final ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>(); + final ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); for (int ip=0; ip<pkgMap.size(); ip++) { - if (reqPackage != null && !reqPackage.equals(pkgMap.keyAt(ip))) { - continue; - } - SparseArray<PackageState> procs = pkgMap.valueAt(ip); + final String pkgName = pkgMap.keyAt(ip); + final SparseArray<PackageState> procs = pkgMap.valueAt(ip); for (int iu=0; iu<procs.size(); iu++) { - PackageState state = procs.valueAt(iu); - for (int iproc=0; iproc<state.mProcesses.size(); iproc++) { - ProcessState proc = state.mProcesses.valueAt(iproc); + final PackageState state = procs.valueAt(iu); + final int NPROCS = state.mProcesses.size(); + final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName); + for (int iproc=0; iproc<NPROCS; iproc++) { + final ProcessState proc = state.mProcesses.valueAt(iproc); + if (!pkgMatch && !reqPackage.equals(proc.mName)) { + continue; + } if (activeOnly && !proc.isInUse()) { continue; } @@ -2591,23 +2639,35 @@ public final class ProcessStats implements Parcelable { } } - void incStartedServices(int memFactor, long now) { + void incStartedServices(int memFactor, long now, String serviceName) { + if (false) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "incStartedServices: " + this + " service=" + serviceName + + " to " + (mNumStartedServices+1), here); + } if (mCommonProcess != this) { - mCommonProcess.incStartedServices(memFactor, now); + mCommonProcess.incStartedServices(memFactor, now, serviceName); } mNumStartedServices++; if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) { - setState(STATE_NOTHING, memFactor, now, null); + setState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now); } } - void decStartedServices(int memFactor, long now) { + void decStartedServices(int memFactor, long now, String serviceName) { + if (false) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName + + " to " + (mNumStartedServices-1), here); + } if (mCommonProcess != this) { - mCommonProcess.decStartedServices(memFactor, now); + mCommonProcess.decStartedServices(memFactor, now, serviceName); } mNumStartedServices--; - if (mNumStartedServices == 0 && mCurState == STATE_SERVICE_RESTARTING) { - setState(STATE_NOTHING, memFactor, now, null); + if (mNumStartedServices == 0 && (mCurState%STATE_COUNT) == STATE_SERVICE_RESTARTING) { + setState(STATE_NOTHING, now); } else if (mNumStartedServices < 0) { Slog.wtfStack(TAG, "Proc started services underrun: pkg=" + mPackage + " uid=" + mUid + " name=" + mName); @@ -2863,6 +2923,8 @@ public final class ProcessStats implements Parcelable { public int mRunState = STATE_NOTHING; long mRunStartTime; + boolean mStarted; + boolean mRestarting; int mStartedCount; public int mStartedState = STATE_NOTHING; long mStartedStartTime; @@ -2892,10 +2954,9 @@ public final class ProcessStats implements Parcelable { // There was already an old owner, reset this object for its // new owner. mOwner = newOwner; - if (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING - || mExecState != STATE_NOTHING) { + if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) { long now = SystemClock.uptimeMillis(); - if (mStartedState != STATE_NOTHING) { + if (mStarted) { if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner + " from " + mOwner + " while started: pkg=" + mPackage + " service=" + mName + " proc=" + mProc); @@ -2921,10 +2982,9 @@ public final class ProcessStats implements Parcelable { public void clearCurrentOwner(Object owner, boolean silently) { if (mOwner == owner) { mProc.decActiveServices(mName); - if (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING - || mExecState != STATE_NOTHING) { + if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) { long now = SystemClock.uptimeMillis(); - if (mStartedState != STATE_NOTHING) { + if (mStarted) { if (!silently) { Slog.wtfStack(TAG, "Service owner " + owner + " cleared while started: pkg=" + mPackage + " service=" @@ -3032,7 +3092,18 @@ public final class ProcessStats implements Parcelable { if (mOwner == null) { Slog.wtf(TAG, "Starting service " + this + " without owner"); } + mStarted = started; + updateStartedState(memFactor, now); + } + + public void setRestarting(boolean restarting, int memFactor, long now) { + mRestarting = restarting; + updateStartedState(memFactor, now); + } + + void updateStartedState(int memFactor, long now) { final boolean wasStarted = mStartedState != STATE_NOTHING; + final boolean started = mStarted || mRestarting; final int state = started ? memFactor : STATE_NOTHING; if (mStartedState != state) { if (mStartedState != STATE_NOTHING) { @@ -3046,9 +3117,9 @@ public final class ProcessStats implements Parcelable { mProc = mProc.pullFixedProc(mPackage); if (wasStarted != started) { if (started) { - mProc.incStartedServices(memFactor, now); + mProc.incStartedServices(memFactor, now, mName); } else { - mProc.decStartedServices(memFactor, now); + mProc.decStartedServices(memFactor, now, mName); } } updateRunning(memFactor, now); diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index cd853b6..591267e 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -181,11 +181,12 @@ public class ResolverActivity extends AlertActivity implements AdapterView.OnIte } else { mAlwaysUseOption = false; } - } - final int initialHighlight = mAdapter.getInitialHighlight(); - if (initialHighlight >= 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 5bfa1b2..1e37fd9 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 eb2d1fe..494bc78 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 0000000..d05699a --- /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 eddea99..0000000 --- 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 f85b353..2e5fcec 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<StopwatchTimer> mFullWifiLockTimers = new ArrayList<StopwatchTimer>(); final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<StopwatchTimer>(); final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<StopwatchTimer>(); + final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers = + new SparseArray<ArrayList<StopwatchTimer>>(); // Last partial timers we use for distributing CPU usage. final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>(); @@ -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<N; i++) { + noteWifiBatchedScanStartedLocked(ws.get(i), csph); + } + } + + public void noteWifiBatchedScanStoppedFromSourceLocked(WorkSource ws) { + int N = ws.size(); + for (int i=0; i<N; i++) { + noteWifiBatchedScanStoppedLocked(ws.get(i)); + } + } + public void noteWifiMulticastEnabledFromSourceLocked(WorkSource ws) { int N = ws.size(); for (int i=0; i<N; i++) { @@ -2576,6 +2603,10 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mWifiScanStarted; StopwatchTimer mWifiScanTimer; + private static final int NO_BATCHED_SCAN_STARTED = -1; + int mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED; + StopwatchTimer[] mWifiBatchedScanTimer; + boolean mWifiMulticastEnabled; StopwatchTimer mWifiMulticastTimer; @@ -2626,6 +2657,7 @@ public final class BatteryStatsImpl extends BatteryStats { mFullWifiLockTimers, mUnpluggables); mWifiScanTimer = new StopwatchTimer(Uid.this, WIFI_SCAN, mWifiScanTimers, mUnpluggables); + mWifiBatchedScanTimer = new StopwatchTimer[NUM_WIFI_BATCHED_SCAN_BINS]; mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED, mWifiMulticastTimers, mUnpluggables); } @@ -2716,6 +2748,36 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override + public void noteWifiBatchedScanStartedLocked(int csph) { + int bin = 0; + while (csph > 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) { mWifiMulticastEnabled = true; @@ -2851,6 +2913,15 @@ public final class BatteryStatsImpl extends BatteryStats { } @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) { return 0; @@ -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<StopwatchTimer> collected = mWifiBatchedScanTimers.get(i); + if (collected == null) { + collected = new ArrayList<StopwatchTimer>(); + 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; i<NUM_USER_ACTIVITY_TYPES; i++) { @@ -2971,6 +3060,14 @@ public final class BatteryStatsImpl extends BatteryStats { active |= !mWifiScanTimer.reset(BatteryStatsImpl.this, false); active |= mWifiScanStarted; } + if (mWifiBatchedScanTimer != null) { + for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) { + if (mWifiBatchedScanTimer[i] != null) { + active |= !mWifiBatchedScanTimer[i].reset(BatteryStatsImpl.this, false); + } + } + active |= (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED); + } if (mWifiMulticastTimer != null) { active |= !mWifiMulticastTimer.reset(BatteryStatsImpl.this, false); active |= mWifiMulticastEnabled; @@ -3077,6 +3174,11 @@ public final class BatteryStatsImpl extends BatteryStats { if (mWifiScanTimer != null) { mWifiScanTimer.detach(); } + for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) { + if (mWifiBatchedScanTimer[i] != null) { + mWifiBatchedScanTimer[i].detach(); + } + } if (mWifiMulticastTimer != null) { mWifiMulticastTimer.detach(); } @@ -3154,6 +3256,14 @@ public final class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) { + if (mWifiBatchedScanTimer[i] != null) { + out.writeInt(1); + mWifiBatchedScanTimer[i].writeToParcel(out, batteryRealtime); + } else { + out.writeInt(0); + } + } if (mWifiMulticastTimer != null) { out.writeInt(1); mWifiMulticastTimer.writeToParcel(out, batteryRealtime); @@ -3263,6 +3373,14 @@ public final class BatteryStatsImpl extends BatteryStats { } else { mWifiScanTimer = null; } + mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED; + for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) { + if (in.readInt() != 0) { + makeWifiBatchedScanBin(i, in); + } else { + mWifiBatchedScanTimer[i] = null; + } + } mWifiMulticastEnabled = false; if (in.readInt() != 0) { mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED, @@ -5460,6 +5578,13 @@ public final class BatteryStatsImpl extends BatteryStats { if (in.readInt() != 0) { u.mWifiScanTimer.readSummaryFromParcelLocked(in); } + u.mWifiBatchedScanBinStarted = Uid.NO_BATCHED_SCAN_STARTED; + for (int i = 0; i < Uid.NUM_WIFI_BATCHED_SCAN_BINS; i++) { + if (in.readInt() != 0) { + u.makeWifiBatchedScanBin(i, null); + u.mWifiBatchedScanTimer[i].readSummaryFromParcelLocked(in); + } + } u.mWifiMulticastEnabled = false; if (in.readInt() != 0) { u.mWifiMulticastTimer.readSummaryFromParcelLocked(in); @@ -5671,6 +5796,14 @@ public final class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + for (int i = 0; i < Uid.NUM_WIFI_BATCHED_SCAN_BINS; i++) { + if (u.mWifiBatchedScanTimer[i] != null) { + out.writeInt(1); + u.mWifiBatchedScanTimer[i].writeSummaryFromParcelLocked(out, NOWREAL); + } else { + out.writeInt(0); + } + } if (u.mWifiMulticastTimer != null) { out.writeInt(1); u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL); @@ -5906,6 +6039,7 @@ public final class BatteryStatsImpl extends BatteryStats { mWifiRunningTimers.clear(); mFullWifiLockTimers.clear(); mWifiScanTimers.clear(); + mWifiBatchedScanTimers.clear(); mWifiMulticastTimers.clear(); sNumSpeedSteps = in.readInt(); diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 99a6843..94750d3 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -136,6 +136,13 @@ public class PowerProfile { public static final String POWER_CPU_SPEEDS = "cpu.speeds"; /** + * Power consumed by wif batched scaning. Broken down into bins by + * Channels Scanned per Hour. May do 1-720 scans per hour of 1-100 channels + * for a range of 1-72,000. Going logrithmic (1-8, 9-64, 65-512, 513-4096, 4097-)! + */ + public static final String POWER_WIFI_BATCHED_SCAN = "wifi.batchedscan"; + + /** * Battery capacity in milliAmpHour (mAh). */ public static final String POWER_BATTERY_CAPACITY = "battery.capacity"; @@ -171,7 +178,7 @@ public class PowerProfile { String element = parser.getName(); if (element == null) break; - + if (parsingArray && !element.equals(TAG_ARRAYITEM)) { // Finish array sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl index 45a38be..63ff5a0 100644 --- a/core/java/com/android/internal/policy/IKeyguardService.aidl +++ b/core/java/com/android/internal/policy/IKeyguardService.aidl @@ -43,4 +43,5 @@ interface IKeyguardService { oneway void showAssistant(); oneway void dispatch(in MotionEvent event); oneway void launchCamera(); + oneway void onBootCompleted(); } diff --git a/core/java/com/android/internal/view/CheckableLinearLayout.java b/core/java/com/android/internal/view/CheckableLinearLayout.java deleted file mode 100644 index 3fb7cec..0000000 --- a/core/java/com/android/internal/view/CheckableLinearLayout.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.view; - -import com.android.internal.R; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.Checkable; -import android.widget.CheckBox; -import android.widget.LinearLayout; - -public class CheckableLinearLayout extends LinearLayout implements Checkable { - private CheckBox mCheckBox; - - public CheckableLinearLayout(Context context) { - super(context); - // TODO Auto-generated constructor stub - } - - public CheckableLinearLayout(Context context, AttributeSet attrs) { - super(context, attrs); - // TODO Auto-generated constructor stub - } - - public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - // TODO Auto-generated constructor stub - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mCheckBox = (CheckBox) findViewById(R.id.check); - } - - @Override - public void setChecked(boolean checked) { - mCheckBox.setChecked(checked); - } - - @Override - public boolean isChecked() { - return mCheckBox.isChecked(); - } - - @Override - public void toggle() { - mCheckBox.toggle(); - } -} diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 44e7ec1..4654178 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -721,7 +721,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter if (subMenu == null) return false; mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId(); - return false; + final MenuPresenter.Callback cb = getCallback(); + return cb != null ? cb.onOpenSubMenu(subMenu) : false; } @Override @@ -729,6 +730,10 @@ public class ActionMenuPresenter extends BaseMenuPresenter if (menu instanceof SubMenuBuilder) { ((SubMenuBuilder) menu).getRootMenu().close(false); } + final MenuPresenter.Callback cb = getCallback(); + if (cb != null) { + cb.onCloseMenu(menu, allMenusAreClosing); + } } } diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java index db0d6dd..92e9ea6 100644 --- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java @@ -144,6 +144,10 @@ public abstract class BaseMenuPresenter implements MenuPresenter { mCallback = cb; } + public Callback getCallback() { + return mCallback; + } + /** * Create a new item view that can be re-bound to other item data later. * diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index b5d74e8..786f5cf 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -526,7 +526,7 @@ public class ActionBarView extends AbsActionBarView { if (mLogoNavItem != null) { mLogoNavItem.setTitle(title); } - mUpGoerFive.setContentDescription(buildHomeContentDescription()); + updateHomeAccessibility(mUpGoerFive.isEnabled()); } public CharSequence getSubtitle() { @@ -544,7 +544,7 @@ public class ActionBarView extends AbsActionBarView { (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle)); mTitleLayout.setVisibility(visible ? VISIBLE : GONE); } - mUpGoerFive.setContentDescription(buildHomeContentDescription()); + updateHomeAccessibility(mUpGoerFive.isEnabled()); } public void setHomeButtonEnabled(boolean enable) { @@ -566,7 +566,11 @@ public class ActionBarView extends AbsActionBarView { mUpGoerFive.setEnabled(enable); mUpGoerFive.setFocusable(enable); // Make sure the home button has an accurate content description for accessibility. - if (!enable) { + updateHomeAccessibility(enable); + } + + private void updateHomeAccessibility(boolean homeEnabled) { + if (!homeEnabled) { mUpGoerFive.setContentDescription(null); mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); } else { @@ -677,19 +681,7 @@ public class ActionBarView extends AbsActionBarView { } // Make sure the home button has an accurate content description for accessibility. - if (!mHomeLayout.isEnabled()) { - mHomeLayout.setContentDescription(null); - mHomeLayout.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); - } else { - mHomeLayout.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO); - if ((options & ActionBar.DISPLAY_HOME_AS_UP) != 0) { - mHomeLayout.setContentDescription(mContext.getResources().getText( - R.string.action_bar_up_description)); - } else { - mHomeLayout.setContentDescription(mContext.getResources().getText( - R.string.action_bar_home_description)); - } - } + updateHomeAccessibility(mUpGoerFive.isEnabled()); } public void setIcon(Drawable icon) { @@ -1340,11 +1332,13 @@ public class ActionBarView extends AbsActionBarView { public void setHomeActionContentDescription(CharSequence description) { mHomeDescription = description; + updateHomeAccessibility(mUpGoerFive.isEnabled()); } public void setHomeActionContentDescription(int resId) { mHomeDescriptionRes = resId; mHomeDescription = resId != 0 ? getResources().getText(resId) : null; + updateHomeAccessibility(mUpGoerFive.isEnabled()); } static class SavedState extends BaseSavedState { diff --git a/core/jni/Android.mk b/core/jni/Android.mk index fb1bb76..1958584 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -60,7 +60,6 @@ LOCAL_SRC_FILES:= \ android_text_AndroidCharacter.cpp \ android_text_AndroidBidi.cpp \ android_os_Debug.cpp \ - android_os_FileUtils.cpp \ android_os_MemoryFile.cpp \ android_os_MessageQueue.cpp \ android_os_Parcel.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 2de308a..97ea5e6 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -142,7 +142,6 @@ extern int register_android_os_SystemProperties(JNIEnv *env); extern int register_android_os_SystemClock(JNIEnv* env); extern int register_android_os_Trace(JNIEnv* env); extern int register_android_os_FileObserver(JNIEnv *env); -extern int register_android_os_FileUtils(JNIEnv *env); extern int register_android_os_UEventObserver(JNIEnv* env); extern int register_android_os_MemoryFile(JNIEnv* env); extern int register_android_net_LocalSocketImpl(JNIEnv* env); @@ -1177,7 +1176,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_database_SQLiteDebug), REG_JNI(register_android_os_Debug), REG_JNI(register_android_os_FileObserver), - REG_JNI(register_android_os_FileUtils), REG_JNI(register_android_os_MessageQueue), REG_JNI(register_android_os_SELinux), REG_JNI(register_android_os_Trace), diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index a0c50fa..92d253f 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -721,7 +721,8 @@ void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* conte hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); hb_buffer_set_script(mBuffer, run.script); - // Should set language here (for bug 7004056) + SkString langString = paint->getPaintOptionsAndroid().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 a7a0bb2..ccd75d5 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 b57a0fe..6175a8f 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 <android_runtime/AndroidRuntime.h> +#include <vector> + +#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<jint>(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<SkPDFDocument*>(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<jint>(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<PageRecord*> mPages; + PageRecord* mCurrentPage; +}; + +static jint nativeCreateDocument(JNIEnv* env, jobject thiz) { + return reinterpret_cast<jint>(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<PdfDocument*>(documentPtr); + return reinterpret_cast<jint>(document->startPage(pageWidth, pageHeight, + contentLeft, contentTop, contentRight, contentBottom)); } -static void nativeAppendPage(JNIEnv* env, jobject thiz, jint documentPtr, jint pagePtr) { - SkCanvas* page = reinterpret_cast<SkCanvas*>(pagePtr); - SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr); - SkPDFDevice* device = static_cast<SkPDFDevice*>(page->getDevice()); - document->appendPage(device); +static void nativeFinishPage(JNIEnv* env, jobject thiz, jint documentPtr) { + PdfDocument* document = reinterpret_cast<PdfDocument*>(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<PdfDocument*>(documentPtr); SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk); - SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr); - document->emitPDF(skWStream); + document->write(skWStream); delete skWStream; } +static void nativeClose(JNIEnv* env, jobject thiz, jint documentPtr) { + PdfDocument* document = reinterpret_cast<PdfDocument*>(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 d1245da..0000000 --- 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 <utils/Log.h> - -#include <android_runtime/AndroidRuntime.h> - -#include "JNIHelp.h" - -#include <sys/errno.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <fcntl.h> -#include <signal.h> -#include <sys/ioctl.h> -#include <linux/msdos_fs.h> - -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 01d02c5..b11c5bb 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 <JNIHelp.h> #include <ScopedUtfChars.h> @@ -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<const char16_t*>(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<const char16_t*>(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<const char16_t*>(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 b254de7..92a3e62 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<jobject> receiverObj(env, NULL); bool skipCallbacks = false; @@ -285,13 +289,17 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, static_cast<KeyEvent*>(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<MotionEvent*>(inputEvent)); + MotionEvent* motionEvent = static_cast<MotionEvent*>(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<NativeInputEventReceiver> receiver = reinterpret_cast<NativeInputEventReceiver*>(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 eba6231..b198937 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}</a> is 4 or higher.--> <permission android:name="android.permission.READ_EXTERNAL_STORAGE" - android:permissionGroup="android.permission-group.SYSTEM_TOOLS" + android:permissionGroup="android.permission-group.STORAGE" android:label="@string/permlab_sdcardRead" android:description="@string/permdesc_sdcardRead" android:protectionLevel="normal" /> @@ -1958,8 +1958,7 @@ <!-- Must be required by a {@link android.nfc.cardemulation.HostApduService} or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only - the system can bind to it. - @hide --> + the system can bind to it. --> <permission android:name="android.permission.BIND_NFC_SERVICE" android:label="@string/permlab_bindNfcService" android:description="@string/permdesc_bindNfcService" @@ -1993,6 +1992,14 @@ android:description="@string/permdesc_bindWallpaper" android:protectionLevel="signature|system" /> + <!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider}, + to ensure that only the system can bind to it. + @hide --> + <permission android:name="android.permission.BIND_REMOTE_DISPLAY" + android:label="@string/permlab_bindRemoteDisplay" + android:description="@string/permdesc_bindRemoteDisplay" + android:protectionLevel="signature" /> + <!-- Must be required by device administration receiver, to ensure that only the system can interact with it. --> <permission android:name="android.permission.BIND_DEVICE_ADMIN" @@ -2682,6 +2689,15 @@ <service android:name="android.hardware.location.GeofenceHardwareService" android:permission="android.permission.LOCATION_HARDWARE" android:exported="false" /> + + <service android:name="com.android.internal.backup.LocalTransportService" + android:permission="android.permission.CONFIRM_FULL_BACKUP" + android:exported="false"> + <intent-filter> + <action android:name="android.backup.TRANSPORT_HOST" /> + </intent-filter> + </service> + </application> </manifest> 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 Binary files differindex 3b64f47..28a1cba 100644 --- 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 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 Binary files differindex 3b64f47..28a1cba 100644 --- 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 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 Binary files differindex 6a2a92c..72b0d42 100644 --- 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 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 Binary files differindex 6a2a92c..72b0d42 100644 --- 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 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 Binary files differindex b9266a6..eff3cc4 100644 --- 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 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 Binary files differindex b9266a6..eff3cc4 100644 --- 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 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 Binary files differindex 42fc83c..986f797 100644 --- 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 diff --git a/core/res/res/drawable-hdpi/ic_media_group_collapse.png b/core/res/res/drawable-hdpi/ic_media_group_collapse.png Binary files differdeleted file mode 100644 index 89abf2c..0000000 --- a/core/res/res/drawable-hdpi/ic_media_group_collapse.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ic_media_group_expand.png b/core/res/res/drawable-hdpi/ic_media_group_expand.png Binary files differdeleted file mode 100644 index d9470b2..0000000 --- a/core/res/res/drawable-hdpi/ic_media_group_expand.png +++ /dev/null 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 Binary files differindex b47d666..458a2a6 100644 --- 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 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 Binary files differindex 13d803c..c91faa9 100644 --- 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 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 Binary files differindex 3ae436b..14c9183 100644 --- 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 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 Binary files differindex 24824fc..b388d86 100644 --- 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 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 Binary files differindex af3819b..76c1323 100644 --- 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 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 Binary files differindex 83dc251..fd39f9d 100644 --- 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 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 Binary files differindex 8d9d592..c74727a 100644 --- 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 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 Binary files differindex 1310ec9..826c9ae 100644 --- 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 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 Binary files differindex 1705074..d0baec3 100644 --- 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 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 Binary files differindex 7027b88..c60ff59 100644 --- 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 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 Binary files differindex 7027b88..75552cc 100644 --- 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 diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_0.png b/core/res/res/drawable-hdpi/ic_notification_cast_0.png Binary files differnew file mode 100644 index 0000000..a35f281 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_notification_cast_0.png diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_1.png b/core/res/res/drawable-hdpi/ic_notification_cast_1.png Binary files differnew file mode 100644 index 0000000..9f6e2ad --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_notification_cast_1.png diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_2.png b/core/res/res/drawable-hdpi/ic_notification_cast_2.png Binary files differnew file mode 100644 index 0000000..737137a --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_notification_cast_2.png diff --git a/core/res/res/drawable-hdpi/ic_notification_cast_on.png b/core/res/res/drawable-hdpi/ic_notification_cast_on.png Binary files differnew file mode 100644 index 0000000..ff2753a --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_notification_cast_on.png diff --git a/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png Binary files differdeleted file mode 100644 index 35f27df..0000000 --- a/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/toast_frame.9.png b/core/res/res/drawable-hdpi/toast_frame.9.png Binary files differindex ca65994..a804a8a 100644 --- a/core/res/res/drawable-hdpi/toast_frame.9.png +++ b/core/res/res/drawable-hdpi/toast_frame.9.png diff --git a/core/res/res/drawable-hdpi/toast_frame_holo.9.png b/core/res/res/drawable-hdpi/toast_frame_holo.9.png Binary files differdeleted file mode 100644 index a804a8a..0000000 --- a/core/res/res/drawable-hdpi/toast_frame_holo.9.png +++ /dev/null diff --git a/core/res/res/drawable-ldpi/toast_frame.9.png b/core/res/res/drawable-ldpi/toast_frame.9.png Binary files differindex 3b344ff..e64dc75 100644 --- a/core/res/res/drawable-ldpi/toast_frame.9.png +++ b/core/res/res/drawable-ldpi/toast_frame.9.png 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 Binary files differindex 87933fa..3ce61b3 100644 --- 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 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 Binary files differindex 87933fa..3ce61b3 100644 --- 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 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 Binary files differindex d424a0e..82e54fd 100644 --- 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 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 Binary files differindex d424a0e..82e54fd 100644 --- 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 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 Binary files differindex 0763868..c389871 100644 --- 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 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 Binary files differindex 0763868..c389871 100644 --- 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 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 Binary files differindex accc761..211be67 100644 --- 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 diff --git a/core/res/res/drawable-mdpi/ic_media_group_collapse.png b/core/res/res/drawable-mdpi/ic_media_group_collapse.png Binary files differdeleted file mode 100644 index 34454ac..0000000 --- a/core/res/res/drawable-mdpi/ic_media_group_collapse.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_media_group_expand.png b/core/res/res/drawable-mdpi/ic_media_group_expand.png Binary files differdeleted file mode 100644 index 8ce5a44..0000000 --- a/core/res/res/drawable-mdpi/ic_media_group_expand.png +++ /dev/null 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 Binary files differindex 6764598..9d92648 100644 --- 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 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 Binary files differindex 94e0bb6..3e27fc8 100644 --- 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 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 Binary files differindex 5ce2f20..72b9e78 100644 --- 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 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 Binary files differindex 5105e90..bd462a2 100644 --- 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 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 Binary files differindex 68c06ed..0a2cc89 100644 --- 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 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 Binary files differindex 6e9b144..d162503 100644 --- 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 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 Binary files differindex 45dc56f3d..997e32b 100644 --- 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 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 Binary files differindex 46e743a..d314967 100644 --- 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 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 Binary files differindex e384691..f15d7a9 100644 --- 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 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 Binary files differindex e384691..26d46f8 100644 --- 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 diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_0.png b/core/res/res/drawable-mdpi/ic_notification_cast_0.png Binary files differnew file mode 100644 index 0000000..d9cedbd --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_notification_cast_0.png diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_1.png b/core/res/res/drawable-mdpi/ic_notification_cast_1.png Binary files differnew file mode 100644 index 0000000..414c67f --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_notification_cast_1.png diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_2.png b/core/res/res/drawable-mdpi/ic_notification_cast_2.png Binary files differnew file mode 100644 index 0000000..280a888 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_notification_cast_2.png diff --git a/core/res/res/drawable-mdpi/ic_notification_cast_on.png b/core/res/res/drawable-mdpi/ic_notification_cast_on.png Binary files differnew file mode 100644 index 0000000..ab5f1d7 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_notification_cast_on.png diff --git a/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png Binary files differdeleted file mode 100644 index f9c8678..0000000 --- a/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/toast_frame.9.png b/core/res/res/drawable-mdpi/toast_frame.9.png Binary files differindex 9e93fe7..778e4e6 100644 --- a/core/res/res/drawable-mdpi/toast_frame.9.png +++ b/core/res/res/drawable-mdpi/toast_frame.9.png diff --git a/core/res/res/drawable-mdpi/toast_frame_holo.9.png b/core/res/res/drawable-mdpi/toast_frame_holo.9.png Binary files differdeleted file mode 100644 index 778e4e6..0000000 --- a/core/res/res/drawable-mdpi/toast_frame_holo.9.png +++ /dev/null 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 Binary files differindex d591bf8..41230fe 100644 --- 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 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 Binary files differindex d591bf8..41230fe 100644 --- 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 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 Binary files differindex b410d23..9fa8682 100644 --- 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 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 Binary files differindex b410d23..9fa8682 100644 --- 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 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 Binary files differindex aed57c6..73488f3 100644 --- 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 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 Binary files differindex aed57c6..73488f3 100644 --- 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 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 Binary files differindex 38f8c01..28edccd 100644 --- 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 diff --git a/core/res/res/drawable-xhdpi/ic_media_group_collapse.png b/core/res/res/drawable-xhdpi/ic_media_group_collapse.png Binary files differdeleted file mode 100644 index 2fb7428..0000000 --- a/core/res/res/drawable-xhdpi/ic_media_group_collapse.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ic_media_group_expand.png b/core/res/res/drawable-xhdpi/ic_media_group_expand.png Binary files differdeleted file mode 100644 index 5755b9d..0000000 --- a/core/res/res/drawable-xhdpi/ic_media_group_expand.png +++ /dev/null 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 Binary files differindex 1d48e12..045eee0 100644 --- 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 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 Binary files differindex 2c8d1ec..6e14e29 100644 --- 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 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 Binary files differindex 00b2043..121bbf6 100644 --- 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 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 Binary files differindex ce1d939..468a0c3 100644 --- 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 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 Binary files differindex 3064b46..414a322 100644 --- 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 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 Binary files differindex 4316686..6088a48 100644 --- 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 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 Binary files differindex 25c4e31..363d7d4 100644 --- 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 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 Binary files differindex 8e32bd2..edf731e 100644 --- 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 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 Binary files differindex aeaa78f..85cba7b 100644 --- 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 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 Binary files differindex 85277fa..e65ac31 100644 --- 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 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 Binary files differindex b01dbe8..d8e3e3a 100644 --- 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 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 Binary files differindex c19a2ad..562dc9a 100644 --- 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 diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_0.png b/core/res/res/drawable-xhdpi/ic_notification_cast_0.png Binary files differnew file mode 100644 index 0000000..5fb23a0 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_notification_cast_0.png diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_1.png b/core/res/res/drawable-xhdpi/ic_notification_cast_1.png Binary files differnew file mode 100644 index 0000000..f01d17d --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_notification_cast_1.png diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_2.png b/core/res/res/drawable-xhdpi/ic_notification_cast_2.png Binary files differnew file mode 100644 index 0000000..4f4ba7f --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_notification_cast_2.png diff --git a/core/res/res/drawable-xhdpi/ic_notification_cast_on.png b/core/res/res/drawable-xhdpi/ic_notification_cast_on.png Binary files differnew file mode 100644 index 0000000..38f15dd --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_notification_cast_on.png diff --git a/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png Binary files differdeleted file mode 100644 index 4cc0ee8..0000000 --- a/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/toast_frame.9.png b/core/res/res/drawable-xhdpi/toast_frame.9.png Binary files differindex 1f63420..77e69c7 100644 --- a/core/res/res/drawable-xhdpi/toast_frame.9.png +++ b/core/res/res/drawable-xhdpi/toast_frame.9.png diff --git a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png Binary files differdeleted file mode 100644 index 77e69c7..0000000 --- a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png +++ /dev/null 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 Binary files differindex 7b0c383..178774c 100644 --- 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 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 Binary files differindex efb624e..2dc2092 100644 --- 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 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 Binary files differindex 5ee57e4..592ee8c 100644 --- 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 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 Binary files differindex 6bc2e4a..f0549e2 100644 --- 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 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 Binary files differindex c13af9c..91268f5 100644 --- 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 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 Binary files differindex 744fb42..9d5436f 100644 --- 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 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 Binary files differindex ca4d59c..8e77483 100644 --- 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 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 Binary files differindex fde5688..f396d22 100644 --- 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 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 Binary files differindex b8715c3..260bab4 100644 --- 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 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 Binary files differindex 668bb25..2c9fb1d 100644 --- 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 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 Binary files differindex 7f54a62..bdbd59c 100644 --- 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 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 Binary files differindex 2df924d..f5c33dd 100644 --- 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 diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png Binary files differnew file mode 100644 index 0000000..f5b16ed --- /dev/null +++ b/core/res/res/drawable-xxhdpi/ic_notification_cast_0.png diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png Binary files differnew file mode 100644 index 0000000..22efeec --- /dev/null +++ b/core/res/res/drawable-xxhdpi/ic_notification_cast_1.png diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png Binary files differnew file mode 100644 index 0000000..e24cd97 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/ic_notification_cast_2.png diff --git a/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png b/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png Binary files differnew file mode 100644 index 0000000..da1a627 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/ic_notification_cast_on.png diff --git a/core/res/res/drawable-xxhdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-xxhdpi/ic_notify_wifidisplay.png Binary files differdeleted file mode 100644 index fea4774..0000000 --- a/core/res/res/drawable-xxhdpi/ic_notify_wifidisplay.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/toast_frame.9.png b/core/res/res/drawable-xxhdpi/toast_frame.9.png Binary files differindex 882b9c6..edecb63 100644 --- a/core/res/res/drawable-xxhdpi/toast_frame.9.png +++ b/core/res/res/drawable-xxhdpi/toast_frame.9.png diff --git a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png Binary files differdeleted file mode 100644 index edecb63..0000000 --- a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png +++ /dev/null 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 0000000..a390bce --- /dev/null +++ b/core/res/res/drawable/ic_notification_cast_connecting.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright 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. + */ +--> +<animation-list + xmlns:android="http://schemas.android.com/apk/res/android" + android:oneshot="false"> + <item android:drawable="@drawable/ic_notification_cast_0" android:duration="500" /> + <item android:drawable="@drawable/ic_notification_cast_1" android:duration="500" /> + <item android:drawable="@drawable/ic_notification_cast_2" android:duration="500" /> + <item android:drawable="@drawable/ic_notification_cast_1" android:duration="500" /> +</animation-list> diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml index f97225e..c0cd93d 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"> <TextView diff --git a/core/res/res/layout/input_method.xml b/core/res/res/layout/input_method.xml index 00a3990..79f1ce8 100644 --- a/core/res/res/layout/input_method.xml +++ b/core/res/res/layout/input_method.xml @@ -18,7 +18,7 @@ */ --> -<com.android.internal.inputmethod.InputMethodRoot xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/parentPanel" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -53,8 +53,4 @@ android:visibility="gone"> </FrameLayout> - <View android:id="@+id/navigationGuard" - android:layout_width="match_parent" - android:layout_height="0dp" - android:background="@+color/input_method_navigation_guard"/> -</com.android.internal.inputmethod.InputMethodRoot> +</LinearLayout> 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 0000000..d1c6267 --- /dev/null +++ b/core/res/res/layout/media_route_chooser_dialog.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:divider="?android:attr/dividerHorizontal" + android:showDividers="middle"> + <!-- List of routes. --> + <ListView android:id="@+id/media_route_list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" /> + + <!-- Content to show when list is empty. --> + <LinearLayout android:id="@android:id/empty" + android:layout_width="match_parent" + android:layout_height="64dp" + android:orientation="horizontal" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:visibility="gone"> + <ProgressBar android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" /> + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:paddingLeft="16dp" + android:text="@string/media_route_chooser_searching" /> + </LinearLayout> + + <!-- Settings button. --> + <LinearLayout android:layout_width="match_parent" + android:layout_height="wrap_content" + style="?attr/buttonBarStyle"> + <Button android:id="@+id/media_route_extended_settings_button" + android:layout_width="match_parent" + android:layout_height="match_parent" + style="?attr/buttonBarButtonStyle" + android:gravity="center" + android:text="@string/media_route_chooser_extended_settings" + android:visibility="gone" /> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/layout/media_route_chooser_layout.xml b/core/res/res/layout/media_route_chooser_layout.xml deleted file mode 100644 index 5fcb8c8..0000000 --- a/core/res/res/layout/media_route_chooser_layout.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:showDividers="middle" - android:divider="?android:attr/dividerHorizontal"> - <LinearLayout android:layout_width="match_parent" - android:layout_height="?android:attr/listPreferredItemHeight" - android:gravity="center_vertical" - android:padding="8dp"> - <ImageView android:id="@+id/volume_icon" - android:layout_width="48dp" - android:layout_height="48dp" - android:src="@android:drawable/ic_audio_vol" - android:gravity="center" - android:scaleType="center" /> - <SeekBar android:id="@+id/volume_slider" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" /> - <ImageButton android:id="@+id/extended_settings" - android:layout_width="48dp" - android:layout_height="48dp" - android:background="?android:attr/selectableItemBackground" - android:src="@android:drawable/ic_sysbar_quicksettings" - android:visibility="gone" /> - </LinearLayout> - <ListView android:id="@id/list" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> -</LinearLayout> diff --git a/core/res/res/layout/media_route_controller_dialog.xml b/core/res/res/layout/media_route_controller_dialog.xml new file mode 100644 index 0000000..78287e0 --- /dev/null +++ b/core/res/res/layout/media_route_controller_dialog.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:divider="?android:attr/dividerHorizontal" + android:showDividers="middle"> + <!-- Optional volume slider section. --> + <LinearLayout android:id="@+id/media_route_volume_layout" + android:layout_width="match_parent" + android:layout_height="64dp" + android:gravity="center_vertical" + android:padding="8dp" + android:visibility="gone"> + <ImageView android:layout_width="48dp" + android:layout_height="48dp" + android:src="@drawable/ic_audio_vol" + android:gravity="center" + android:scaleType="center" /> + <SeekBar android:id="@+id/media_route_volume_slider" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" /> + </LinearLayout> + + <!-- Optional content view section. --> + <FrameLayout android:id="@+id/media_route_control_frame" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" /> + + <!-- Disconnect button. --> + <LinearLayout android:layout_width="match_parent" + android:layout_height="wrap_content" + style="?attr/buttonBarStyle"> + <Button android:id="@+id/media_route_disconnect_button" + android:layout_width="match_parent" + android:layout_height="match_parent" + style="?attr/buttonBarButtonStyle" + android:gravity="center" + android:text="@string/media_route_controller_disconnect" /> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/layout/media_route_list_item.xml b/core/res/res/layout/media_route_list_item.xml index 423d544..bdca433 100644 --- a/core/res/res/layout/media_route_list_item.xml +++ b/core/res/res/layout/media_route_list_item.xml @@ -20,13 +20,6 @@ android:background="@drawable/item_background_activated_holo_dark" android:gravity="center_vertical"> - <ImageView android:layout_width="56dp" - android:layout_height="56dp" - android:scaleType="center" - android:id="@+id/icon" - android:visibility="gone" - android:duplicateParentState="true" /> - <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" @@ -53,14 +46,4 @@ android:duplicateParentState="true" /> </LinearLayout> - <ImageButton - android:layout_width="56dp" - android:layout_height="56dp" - android:id="@+id/expand_button" - android:background="?android:attr/selectableItemBackground" - android:src="@drawable/ic_media_group_expand" - android:scaleType="center" - android:visibility="gone" - android:duplicateParentState="true" /> - </LinearLayout> diff --git a/core/res/res/layout/media_route_list_item_checkable.xml b/core/res/res/layout/media_route_list_item_checkable.xml deleted file mode 100644 index 5fb82f7..0000000 --- a/core/res/res/layout/media_route_list_item_checkable.xml +++ /dev/null @@ -1,60 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<com.android.internal.view.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="?android:attr/listPreferredItemHeight" - android:background="?android:attr/selectableItemBackground" - android:gravity="center_vertical"> - - <ImageView android:layout_width="56dp" - android:layout_height="56dp" - android:scaleType="center" - android:id="@+id/icon" - android:visibility="gone" /> - - <LinearLayout android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:orientation="vertical" - android:gravity="start|center_vertical" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> - - <TextView android:id="@android:id/text1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" /> - - <TextView android:id="@android:id/text2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </LinearLayout> - - <CheckBox - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="16dp" - android:id="@+id/check" - android:focusable="false" - android:clickable="false" /> - -</com.android.internal.view.CheckableLinearLayout> diff --git a/core/res/res/layout/media_route_list_item_collapse_group.xml b/core/res/res/layout/media_route_list_item_collapse_group.xml deleted file mode 100644 index 323e24d..0000000 --- a/core/res/res/layout/media_route_list_item_collapse_group.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?android:attr/selectableItemBackground"> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="?android:attr/listPreferredItemHeightSmall" - android:background="#19ffffff" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:gravity="center_vertical"> - - <TextView android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:singleLine="true" - android:ellipsize="marquee" - android:text="@string/media_route_chooser_grouping_done" - android:textAppearance="?android:attr/textAppearanceMedium" /> - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_media_group_collapse" - android:scaleType="center" /> - - </LinearLayout> -</FrameLayout>
\ No newline at end of file diff --git a/core/res/res/layout/media_route_list_item_section_header.xml b/core/res/res/layout/media_route_list_item_section_header.xml deleted file mode 100644 index 949635f..0000000 --- a/core/res/res/layout/media_route_list_item_section_header.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="16dp"> - <TextView - android:id="@android:id/text1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall" - android:background="#19ffffff" - android:textStyle="bold" - android:textAllCaps="true" - android:gravity="center_vertical" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:minHeight="24dp" - /> -</FrameLayout> diff --git a/core/res/res/values-mcc302-mnc500/config.xml b/core/res/res/values-mcc302-mnc500/config.xml new file mode 100644 index 0000000..706570c --- /dev/null +++ b/core/res/res/values-mcc302-mnc500/config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 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. +*/ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Don't use roaming icon for considered operators --> + <string-array translatable="false" name="config_operatorConsideredNonRoaming"> + <item>302</item> + </string-array> +</resources> diff --git a/core/res/res/values-mcc302-mnc510/config.xml b/core/res/res/values-mcc302-mnc510/config.xml new file mode 100644 index 0000000..706570c --- /dev/null +++ b/core/res/res/values-mcc302-mnc510/config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 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. +*/ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Don't use roaming icon for considered operators --> + <string-array translatable="false" name="config_operatorConsideredNonRoaming"> + <item>302</item> + </string-array> +</resources> diff --git a/core/res/res/values-mcc310-mnc260/config.xml b/core/res/res/values-mcc310-mnc260/config.xml index 886ecbe..d602c9f 100644 --- a/core/res/res/values-mcc310-mnc260/config.xml +++ b/core/res/res/values-mcc310-mnc260/config.xml @@ -21,22 +21,6 @@ for different hardware and product builds. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering --> - <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or - <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH --> - <integer-array translatable="false" name="config_tether_upstream_types"> - <item>1</item> - <item>4</item> - <item>7</item> - <item>9</item> - </integer-array> - - <!-- String containing the apn value for tethering. May be overriden by secure settings - TETHER_DUN_APN. Value is a comma separated series of strings: - "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type" - note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" --> - <string translatable="false" name="config_tether_apndata">T-Mobile Tethering,pcweb.tmobile.com,,,,,,,,,310,260,,DUN</string> - <!-- Configure mobile network MTU. Carrier specific value is set here. --> <integer name="config_mobile_mtu">1440</integer> diff --git a/core/res/res/values-mcc311-mnc190/config.xml b/core/res/res/values-mcc311-mnc190/config.xml new file mode 100644 index 0000000..a6c4d1b --- /dev/null +++ b/core/res/res/values-mcc311-mnc190/config.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 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 my 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. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering --> + <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or + <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH --> + <integer-array translatable="false" name="config_tether_upstream_types"> + <item>1</item> + <item>4</item> + <item>7</item> + <item>9</item> + </integer-array> + + <!-- String containing the apn value for tethering. May be overriden by secure settings + TETHER_DUN_APN. Value is a comma separated series of strings: + "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type" + note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" --> + <string translatable="false" name="config_tether_apndata">Tether,broadband.cellular1.net,,,,,,,,,311,190,,DUN</string> + +</resources> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index ef30b98..91af50a 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -22,7 +22,7 @@ <!-- Do not translate. These are all of the drawable resources that should be preloaded by the zygote process before it starts forking application processes. --> <array name="preloaded_drawables"> - <item>@drawable/toast_frame_holo</item> + <item>@drawable/toast_frame</item> <item>@drawable/btn_check_on_pressed_holo_light</item> <item>@drawable/btn_check_on_pressed_holo_dark</item> <item>@drawable/btn_check_on_holo_light</item> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 42ea384..b34c792 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -585,8 +585,8 @@ <!-- Disable lockscreen rotation by default --> <bool name="config_enableLockScreenRotation">false</bool> - <!-- Disable lockscreen translucent decor by default --> - <bool name="config_enableLockScreenTranslucentDecor">false</bool> + <!-- Enable lockscreen translucent decor by default --> + <bool name="config_enableLockScreenTranslucentDecor">true</bool> <!-- Enable translucent decor by default --> <bool name="config_enableTranslucentDecor">true</bool> @@ -1176,6 +1176,22 @@ where if the preferred is used we don't try the others. --> <bool name="config_dontPreferApn">false</bool> + <!-- The list of ril radio technologies (see ServiceState.java) which only support + a single data connection at one time. This may change by carrier via + overlays (some don't support multiple pdp on UMTS). All unlisted radio + tech types support unlimited types (practically only 2-4 used). --> + <integer-array name="config_onlySingleDcAllowed"> + <item>4</item> <!-- IS95A --> + <item>5</item> <!-- IS95B --> + <item>6</item> <!-- 1xRTT --> + <item>7</item> <!-- EVDO_0 --> + <item>8</item> <!-- EVDO_A --> + <item>12</item> <!-- EVDO_B --> + </integer-array> + + <!-- Set to true if after a provisioning apn the radio should be restarted --> + <bool name="config_restartRadioAfterProvisioning">false</bool> + <!-- Vibrator pattern to be used as the default for notifications that specify DEFAULT_VIBRATE. --> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index aad6252..f96195c 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -53,7 +53,7 @@ <!-- Minimum size of the fastscroll overlay --> <dimen name="fastscroll_overlay_size">104dp</dimen> <!-- Text size of the fastscroll overlay --> - <dimen name="fastscroll_overlay_text_size">24sp</dimen> + <dimen name="fastscroll_overlay_text_size">52sp</dimen> <!-- Padding of the fastscroll overlay --> <dimen name="fastscroll_overlay_padding">16dp</dimen> <!-- Width of the fastscroll thumb --> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 15df295..5c0baaa 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -81,4 +81,5 @@ <item type="id" name="popup_submenu_presenter" /> <item type="id" name="action_bar_spinner" /> <item type="id" name="current_scene" /> + <item type="id" name="scene_layoutid_cache" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index e82ad1e..9025400 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -97,6 +97,13 @@ the SIM card. --> <string name="needPuk">Your SIM card is PUK-locked. Type the PUK code to unlock it.</string> <string name="needPuk2">Type PUK2 to unblock SIM card.</string> + <!-- Displayed when user attempts to change SIM PIN1 without enabling PIN1. --> + <string name="enablePin">Unsuccessful, enable SIM/RUIM Lock.</string> + <!-- Displayed when a SIM PIN/PUK is entered incorrectly. --> + <plurals name="pinpuk_attempts"> + <item quantity="one">You have <xliff:g id="number">%d</xliff:g> remaining attempt before SIM is locked.</item> + <item quantity="other">You have <xliff:g id="number">%d</xliff:g> remaining attempts before SIM is locked.</item> + </plurals> <!-- Title for the dialog used to display the user's IMEI number [CHAR LIMIT=10] --> <string name="imei">IMEI</string> @@ -1045,6 +1052,12 @@ interface of a wallpaper. Should never be needed for normal apps.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_bindRemoteDisplay">bind to a remote display</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_bindRemoteDisplay">Allows the holder to bind to the top-level + interface of a remote display. Should never be needed for normal apps.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_bindRemoteViews">bind to a widget service</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_bindRemoteViews">Allows the holder to bind to the top-level @@ -3894,6 +3907,9 @@ <!-- Description of the shwoing of a popup window with activities to choose from. [CHAR LIMIT=NONE] --> <string name="activitychooserview_choose_application">Choose an app</string> + <!-- Error message if the share target app cannto be launched. [CHAR LIMIT=NONE] --> + <string name="activitychooserview_choose_application_error">Couldn\'t launch <xliff:g id="application_name" example="Acme">%s</xliff:g></string> + <!-- ShareActionProvider - accessibility support --> <!-- Description of the choose target button in a ShareActionProvider (share UI). [CHAR LIMIT=NONE] --> <string name="shareactionprovider_share_with">Share with</string> @@ -4080,12 +4096,24 @@ <!-- Description of a wireless display route. [CHAR LIMIT=50] --> <string name="wireless_display_route_description">Wireless display</string> - <!-- "Done" button for MediaRouter chooser dialog when grouping routes. [CHAR LIMIT=NONE] --> - <string name="media_route_chooser_grouping_done">Done</string> - <!-- Content description of a MediaRouteButton for accessibility support --> <string name="media_route_button_content_description">Media output</string> + <!-- Title of the media route chooser dialog. [CHAR LIMIT=40] --> + <string name="media_route_chooser_title">Connect to device</string> + + <!-- Title of the media route chooser dialog for selecting remote display routes. [CHAR LIMIT=40] --> + <string name="media_route_chooser_title_for_remote_display">Cast screen to device</string> + + <!-- Placeholder text to show when no devices have been found. [CHAR LIMIT=50] --> + <string name="media_route_chooser_searching">Searching for devices\u2026</string> + + <!-- Button to access extended settings. [CHAR LIMIT=30] --> + <string name="media_route_chooser_extended_settings">Settings</string> + + <!-- Button to disconnect from a media route. [CHAR LIMIT=30] --> + <string name="media_route_controller_disconnect">Disconnect</string> + <!-- Status message for remote routes attempting to scan/determine availability --> <string name="media_route_status_scanning">Scanning...</string> @@ -4118,10 +4146,14 @@ <!-- Title text to append when the display is secure. [CHAR LIMIT=30] --> <string name="display_manager_overlay_display_secure_suffix">, secure</string> + <!-- Title of the notification to indicate the process of connecting to a wifi display. [CHAR LIMIT=50] --> + <string name="wifi_display_notification_connecting_title">Casting screen</string> + <!-- Message of the notification to indicate the process of connecting to a wifi display. [CHAR LIMIT=80] --> + <string name="wifi_display_notification_connecting_message">Connecting to <xliff:g id="name">%1$s</xliff:g></string> <!-- Title of the notification to indicate an active wifi display connection. [CHAR LIMIT=50] --> - <string name="wifi_display_notification_title">Wireless display is connected</string> + <string name="wifi_display_notification_connected_title">Casting screen</string> <!-- Message of the notification to indicate an active wifi display connection. [CHAR LIMIT=80] --> - <string name="wifi_display_notification_message">This screen is showing on another device</string> + <string name="wifi_display_notification_connected_message">Connected to <xliff:g id="name">%1$s</xliff:g></string> <!-- Label of a button to disconnect an active wifi display connection. [CHAR LIMIT=25] --> <string name="wifi_display_notification_disconnect">Disconnect</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f578694..6d90973 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -55,6 +55,7 @@ <java-symbol type="id" name="clearDefaultHint" /> <java-symbol type="id" name="contentPanel" /> <java-symbol type="id" name="current_scene" /> + <java-symbol type="id" name="scene_layoutid_cache" /> <java-symbol type="id" name="customPanel" /> <java-symbol type="id" name="datePicker" /> <java-symbol type="id" name="day" /> @@ -105,7 +106,6 @@ <java-symbol type="id" name="month" /> <java-symbol type="id" name="month_name" /> <java-symbol type="id" name="name" /> - <java-symbol type="id" name="navigationGuard" /> <java-symbol type="id" name="next" /> <java-symbol type="id" name="next_button" /> <java-symbol type="id" name="new_app_action" /> @@ -283,6 +283,7 @@ <java-symbol type="bool" name="config_safe_media_volume_enabled" /> <java-symbol type="bool" name="config_camera_sound_forced" /> <java-symbol type="bool" name="config_dontPreferApn" /> + <java-symbol type="bool" name="config_restartRadioAfterProvisioning" /> <java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" /> <java-symbol type="bool" name="config_useFixedVolume" /> <java-symbol type="bool" name="config_forceDefaultOrientation" /> @@ -392,6 +393,7 @@ <java-symbol type="string" name="accessibility_enabled" /> <java-symbol type="string" name="activity_chooser_view_see_all" /> <java-symbol type="string" name="activitychooserview_choose_application" /> + <java-symbol type="string" name="activitychooserview_choose_application_error" /> <java-symbol type="string" name="alternate_eri_file" /> <java-symbol type="string" name="alwaysUse" /> <java-symbol type="string" name="autofill_address_line_1_label_re" /> @@ -612,6 +614,7 @@ <java-symbol type="string" name="more_item_label" /> <java-symbol type="string" name="needPuk" /> <java-symbol type="string" name="needPuk2" /> + <java-symbol type="string" name="enablePin" /> <java-symbol type="string" name="new_app_action" /> <java-symbol type="string" name="new_app_description" /> <java-symbol type="string" name="noApplications" /> @@ -965,6 +968,7 @@ <java-symbol type="plurals" name="num_minutes_ago" /> <java-symbol type="plurals" name="num_seconds_ago" /> <java-symbol type="plurals" name="restr_pin_countdown" /> + <java-symbol type="plurals" name="pinpuk_attempts" /> <java-symbol type="array" name="carrier_properties" /> <java-symbol type="array" name="config_data_usage_network_types" /> @@ -1093,7 +1097,11 @@ <java-symbol type="drawable" name="notification_template_icon_bg" /> <java-symbol type="drawable" name="notification_template_icon_low_bg" /> <java-symbol type="drawable" name="ic_media_route_on_holo_dark" /> + <java-symbol type="drawable" name="ic_media_route_off_holo_dark" /> + <java-symbol type="drawable" name="ic_media_route_connecting_holo_dark" /> <java-symbol type="drawable" name="ic_media_route_disabled_holo_dark" /> + <java-symbol type="drawable" name="ic_notification_cast_connecting" /> + <java-symbol type="drawable" name="ic_notification_cast_on" /> <java-symbol type="drawable" name="cling_button" /> <java-symbol type="drawable" name="cling_arrow_up" /> <java-symbol type="drawable" name="cling_bg" /> @@ -1250,17 +1258,17 @@ <java-symbol type="attr" name="mediaRouteButtonStyle" /> <java-symbol type="attr" name="externalRouteEnabledDrawable" /> - <java-symbol type="id" name="extended_settings" /> - <java-symbol type="id" name="check" /> - <java-symbol type="id" name="volume_slider" /> - <java-symbol type="id" name="volume_icon" /> - <java-symbol type="drawable" name="ic_media_route_on_holo_dark" /> - <java-symbol type="layout" name="media_route_chooser_layout" /> - <java-symbol type="layout" name="media_route_list_item_top_header" /> - <java-symbol type="layout" name="media_route_list_item_section_header" /> + <java-symbol type="layout" name="media_route_chooser_dialog" /> + <java-symbol type="layout" name="media_route_controller_dialog" /> <java-symbol type="layout" name="media_route_list_item" /> - <java-symbol type="layout" name="media_route_list_item_checkable" /> - <java-symbol type="layout" name="media_route_list_item_collapse_group" /> + <java-symbol type="id" name="media_route_list" /> + <java-symbol type="id" name="media_route_volume_layout" /> + <java-symbol type="id" name="media_route_volume_slider" /> + <java-symbol type="id" name="media_route_control_frame" /> + <java-symbol type="id" name="media_route_disconnect_button" /> + <java-symbol type="id" name="media_route_extended_settings_button" /> + <java-symbol type="string" name="media_route_chooser_title" /> + <java-symbol type="string" name="media_route_chooser_title_for_remote_display" /> <java-symbol type="string" name="bluetooth_a2dp_audio_route_name" /> <java-symbol type="dimen" name="config_minScalingSpan" /> @@ -1432,6 +1440,7 @@ <java-symbol type="array" name="config_locationProviderPackageNames" /> <java-symbol type="array" name="config_defaultNotificationVibePattern" /> <java-symbol type="array" name="config_notificationFallbackVibePattern" /> + <java-symbol type="array" name="config_onlySingleDcAllowed" /> <java-symbol type="bool" name="config_animateScreenLights" /> <java-symbol type="bool" name="config_automatic_brightness_available" /> <java-symbol type="bool" name="config_enableFusedLocationOverlay" /> @@ -1447,7 +1456,6 @@ <java-symbol type="color" name="config_defaultNotificationColor" /> <java-symbol type="color" name="input_method_navigation_guard" /> <java-symbol type="drawable" name="ic_notification_ime_default" /> - <java-symbol type="drawable" name="ic_notify_wifidisplay" /> <java-symbol type="drawable" name="ic_menu_refresh" /> <java-symbol type="drawable" name="stat_notify_car_mode" /> <java-symbol type="drawable" name="stat_notify_disabled" /> @@ -1590,8 +1598,10 @@ <java-symbol type="string" name="vpn_lockdown_error" /> <java-symbol type="string" name="vpn_lockdown_config" /> <java-symbol type="string" name="wallpaper_binding_label" /> - <java-symbol type="string" name="wifi_display_notification_title" /> - <java-symbol type="string" name="wifi_display_notification_message" /> + <java-symbol type="string" name="wifi_display_notification_connecting_title" /> + <java-symbol type="string" name="wifi_display_notification_connecting_message" /> + <java-symbol type="string" name="wifi_display_notification_connected_title" /> + <java-symbol type="string" name="wifi_display_notification_connected_message" /> <java-symbol type="string" name="wifi_display_notification_disconnect" /> <java-symbol type="style" name="Theme.Dialog.AppError" /> <java-symbol type="style" name="Theme.Toast" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 4c80e7d..c8d9fc6 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -1048,7 +1048,7 @@ please see themes_device_defaults.xml. <item name="presentationTheme">@android:style/Theme.Holo.Dialog.Presentation</item> <!-- Toast attributes --> - <item name="toastFrameBackground">@android:drawable/toast_frame_holo</item> + <item name="toastFrameBackground">@android:drawable/toast_frame</item> <!-- Panel attributes --> <item name="panelBackground">@android:drawable/menu_hardkey_panel_holo_dark</item> @@ -1363,7 +1363,7 @@ please see themes_device_defaults.xml. <item name="presentationTheme">@android:style/Theme.Holo.Light.Dialog.Presentation</item> <!-- Toast attributes --> - <item name="toastFrameBackground">@android:drawable/toast_frame_holo</item> + <item name="toastFrameBackground">@android:drawable/toast_frame</item> <!-- Panel attributes --> <item name="panelBackground">@android:drawable/menu_hardkey_panel_holo_light</item> diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml index 7af3b9c..3215e17 100644 --- a/core/res/res/xml/power_profile.xml +++ b/core/res/res/xml/power_profile.xml @@ -58,4 +58,12 @@ </array> <!-- This is the battery capacity in mAh (measured at nominal voltage) --> <item name="battery.capacity">1000</item> + + <array name="wifi.batchedscan"> <!-- mA --> + <value>.0002</value> <!-- 1-8/hr --> + <value>.002</value> <!-- 9-64/hr --> + <value>.02</value> <!-- 65-512/hr --> + <value>.2</value> <!-- 513-4,096/hr --> + <value>2</value> <!-- 4097-/hr --> + </array> </device> diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java index 76b702e..4a58f88 100644 --- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java +++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java @@ -36,8 +36,6 @@ import com.android.bandwidthtest.util.BandwidthTestUtil; import com.android.bandwidthtest.util.ConnectionUtil; import java.io.File; -import java.util.HashMap; -import java.util.Map; /** * Test that downloads files from a test server and reports the bandwidth metrics collected. @@ -131,8 +129,8 @@ public class BandwidthTest extends InstrumentationTestCase { results.putString("device_id", mDeviceId); results.putString("timestamp", ts); results.putInt("size", FILE_SIZE); - AddStatsToResults(PROF_LABEL, prof_stats, results); - AddStatsToResults(PROC_LABEL, proc_stats, results); + addStatsToResults(PROF_LABEL, prof_stats, results, mUid); + addStatsToResults(PROC_LABEL, proc_stats, results, mUid); getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results); // Clean up. @@ -185,8 +183,8 @@ public class BandwidthTest extends InstrumentationTestCase { results.putString("device_id", mDeviceId); results.putString("timestamp", ts); results.putInt("size", FILE_SIZE); - AddStatsToResults(PROF_LABEL, prof_stats, results); - AddStatsToResults(PROC_LABEL, proc_stats, results); + addStatsToResults(PROF_LABEL, prof_stats, results, mUid); + addStatsToResults(PROC_LABEL, proc_stats, results, mUid); getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results); // Clean up. @@ -242,8 +240,9 @@ public class BandwidthTest extends InstrumentationTestCase { results.putString("device_id", mDeviceId); results.putString("timestamp", ts); results.putInt("size", FILE_SIZE); - AddStatsToResults(PROF_LABEL, prof_stats, results); - AddStatsToResults(PROC_LABEL, proc_stats, results); + addStatsToResults(PROF_LABEL, prof_stats, results, mUid); + // remember to use download manager uid for proc stats + addStatsToResults(PROC_LABEL, proc_stats, results, downloadManagerUid); getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results); // Clean up. @@ -302,46 +301,35 @@ public class BandwidthTest extends InstrumentationTestCase { * @param label to attach to this given stats. * @param stats {@link NetworkStats} to add. * @param results {@link Bundle} to be added to. + * @param uid for which to report the results. */ - public void AddStatsToResults(String label, NetworkStats stats, Bundle results){ + public void addStatsToResults(String label, NetworkStats stats, Bundle results, int uid){ if (results == null || results.isEmpty()) { Log.e(LOG_TAG, "Empty bundle provided."); return; } - // Merge stats across all sets. - Map<Integer, Entry> totalStats = new HashMap<Integer, Entry>(); + Entry totalStats = null; for (int i = 0; i < stats.size(); ++i) { Entry statsEntry = stats.getValues(i, null); // We are only interested in the all inclusive stats. if (statsEntry.tag != 0) { continue; } - Entry mapEntry = null; - if (totalStats.containsKey(statsEntry.uid)) { - mapEntry = totalStats.get(statsEntry.uid); - switch (statsEntry.set) { - case NetworkStats.SET_ALL: - mapEntry.rxBytes = statsEntry.rxBytes; - mapEntry.txBytes = statsEntry.txBytes; - break; - case NetworkStats.SET_DEFAULT: - case NetworkStats.SET_FOREGROUND: - mapEntry.rxBytes += statsEntry.rxBytes; - mapEntry.txBytes += statsEntry.txBytes; - break; - default: - Log.w(LOG_TAG, "Invalid state found in NetworkStats."); - } + // skip stats for other uids + if (statsEntry.uid != uid) { + continue; + } + if (totalStats == null || statsEntry.set == NetworkStats.SET_ALL) { + totalStats = statsEntry; } else { - totalStats.put(statsEntry.uid, statsEntry); + totalStats.rxBytes += statsEntry.rxBytes; + totalStats.txBytes += statsEntry.txBytes; } } - // Ouput merged stats to bundle. - for (Entry entry : totalStats.values()) { - results.putInt(label + "uid", entry.uid); - results.putLong(label + "tx", entry.txBytes); - results.putLong(label + "rx", entry.rxBytes); - } + // Output merged stats to bundle. + results.putInt(label + "uid", totalStats.uid); + results.putLong(label + "tx", totalStats.txBytes); + results.putLong(label + "rx", totalStats.rxBytes); } /** diff --git a/core/tests/coretests/apks/version_nosys/Android.mk b/core/tests/coretests/apks/version_nosys/Android.mk new file mode 100644 index 0000000..bbc8e12 --- /dev/null +++ b/core/tests/coretests/apks/version_nosys/Android.mk @@ -0,0 +1,9 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_PACKAGE_NAME := version_1_nosys +LOCAL_AAPT_FLAGS := --version-code 1 --version-name 1.0 +LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test +include $(FrameworkCoreTests_BUILD_PACKAGE) + diff --git a/core/res/res/layout/media_route_list_item_top_header.xml b/core/tests/coretests/apks/version_nosys/AndroidManifest.xml index 0c49b24..46aac38 100644 --- a/core/res/res/layout/media_route_list_item_top_header.xml +++ b/core/tests/coretests/apks/version_nosys/AndroidManifest.xml @@ -13,17 +13,15 @@ See the License for the specific language governing permissions and limitations under the License. --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.coretests.version_test"> -<TextView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@android:id/text1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall" - android:background="#19ffffff" - android:textStyle="bold" - android:textAllCaps="true" - android:gravity="center_vertical" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:minHeight="24dp" -/> + <!-- Do not ask for this system permission --> +<!-- <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" /> +--> + <!-- Which permission it uses is not important as long as it's a normal + permission --> + <uses-permission android:name="android.permission.VIBRATE" /> + + <application android:hasCode="false"/> +</manifest> diff --git a/core/tests/coretests/apks/version_nosys/res/values/strings.xml b/core/tests/coretests/apks/version_nosys/res/values/strings.xml new file mode 100644 index 0000000..3b8b3b1 --- /dev/null +++ b/core/tests/coretests/apks/version_nosys/res/values/strings.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Just need this dummy file to have something to build. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="dummy">dummy</string> +</resources> diff --git a/core/tests/coretests/apks/version_nosys/src/com/android/frameworks/coretests/version_test/NullProvider.java b/core/tests/coretests/apks/version_nosys/src/com/android/frameworks/coretests/version_test/NullProvider.java new file mode 100644 index 0000000..f5742f0 --- /dev/null +++ b/core/tests/coretests/apks/version_nosys/src/com/android/frameworks/coretests/version_test/NullProvider.java @@ -0,0 +1,39 @@ +package com.android.frameworks.coretests.version_test; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class NullProvider extends ContentProvider { + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public String getType(Uri uri) { + return "text/plain"; + } +} |