diff options
125 files changed, 2042 insertions, 1284 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 909fdb6..20e48f9 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -206,6 +206,9 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/servic $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/ims-common_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework2_intermediates) # ****************************************************************** # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER diff --git a/api/current.txt b/api/current.txt index c7a62ac..fb598fd 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5324,7 +5324,6 @@ package android.app.admin { method public void wipeData(int); field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.ACTION_PROVISION_MANAGED_PROFILE"; - field public static final java.lang.String ACTION_SEND_PROVISIONING_VALUES = "android.app.action.ACTION_SEND_PROVISIONING_VALUES"; field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"; field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION"; field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2 @@ -5341,7 +5340,6 @@ package android.app.admin { field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.locale"; field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.localTime"; field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.timeZone"; - field public static final java.lang.String EXTRA_PROVISIONING_TOKEN = "android.app.extra.token"; field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.wifiHidden"; field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PAC_URL = "android.app.extra.wifiPacUrl"; field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PASSWORD = "android.app.extra.wifiPassword"; @@ -27472,8 +27470,7 @@ package android.telecomm { public final class CallCapabilities { method public static java.lang.String toString(int); field public static final int ADD_CALL = 16; // 0x10 - field public static final int ALL = 511; // 0x1ff - field public static final int CONNECTION_HANDOFF = 256; // 0x100 + field public static final int ALL = 255; // 0xff field public static final int GENERIC_CONFERENCE = 128; // 0x80 field public static final int HOLD = 1; // 0x1 field public static final int MERGE_CALLS = 4; // 0x4 @@ -27515,43 +27512,8 @@ package android.telecomm { public abstract class CallService extends android.app.Service { ctor public CallService(); - method public abstract void abort(java.lang.String); - method public abstract void answer(java.lang.String); - method public abstract void call(android.telecomm.CallInfo); - method public abstract void disconnect(java.lang.String); - method protected final android.telecomm.CallServiceAdapter getAdapter(); method public final android.os.IBinder getBinder(); - method public abstract void hold(java.lang.String); - method protected void onAdapterAttached(android.telecomm.CallServiceAdapter); - method public abstract void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract void onFeaturesChanged(java.lang.String, int); - method public void onPostDialContinue(java.lang.String, boolean); - method public void onPostDialWait(android.telecomm.Connection, java.lang.String); - method public abstract void playDtmfTone(java.lang.String, char); - method public abstract void reject(java.lang.String); - method public abstract void setIncomingCallId(java.lang.String, android.os.Bundle); - method public abstract void stopDtmfTone(java.lang.String); - method public abstract void unhold(java.lang.String); - } - - public final class CallServiceAdapter implements android.os.IBinder.DeathRecipient { - method public void addConferenceCall(java.lang.String); - method public void binderDied(); - method public void cancelOutgoingCall(java.lang.String); - method public void handleFailedOutgoingCall(android.telecomm.ConnectionRequest, int, java.lang.String); - method public void handleSuccessfulOutgoingCall(java.lang.String); - method public void handoffCall(java.lang.String); - method public void notifyIncomingCall(android.telecomm.CallInfo); - method public void onPostDialWait(java.lang.String, java.lang.String); - method public void setActive(java.lang.String); - method public void setCallVideoProvider(java.lang.String, android.telecomm.CallVideoProvider); - method public void setDialing(java.lang.String); - method public void setDisconnected(java.lang.String, int, java.lang.String); - method public void setFeatures(java.lang.String, int); - method public void setOnHold(java.lang.String); - method public void setRequestingRingback(java.lang.String, boolean); - method public void setRinging(java.lang.String); } public final class CallServiceDescriptor implements android.os.Parcelable { @@ -27645,6 +27607,7 @@ package android.telecomm { method protected void onConference(); method protected void onDisconnect(); method protected void onHold(); + method protected void onPhoneAccountClicked(); method protected void onPlayDtmfTone(char); method protected void onPostDialContinue(boolean); method protected void onReject(); @@ -27694,27 +27657,15 @@ package android.telecomm { public abstract class ConnectionService extends android.telecomm.CallService { ctor public ConnectionService(); - method public final void abort(java.lang.String); - method public final void answer(java.lang.String); - method public final void call(android.telecomm.CallInfo); method public final void createRemoteOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.RemoteConnection>); - method public final void disconnect(java.lang.String); method public final java.util.Collection<android.telecomm.Connection> getAllConnections(); - method public final void hold(java.lang.String); method public final void lookupRemoteAccounts(android.net.Uri, android.telecomm.SimpleResponse<android.net.Uri, java.util.List<android.telecomm.PhoneAccount>>); method public final void maybeRespondToAccountLookup(); - method public final void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState); method protected void onConnectionAdded(android.telecomm.Connection); method protected void onConnectionRemoved(android.telecomm.Connection); method protected void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>); method protected void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.Connection>); method protected void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>); - method public final void onFeaturesChanged(java.lang.String, int); - method public final void playDtmfTone(java.lang.String, char); - method public final void reject(java.lang.String); - method public final void setIncomingCallId(java.lang.String, android.os.Bundle); - method public final void stopDtmfTone(java.lang.String); - method public final void unhold(java.lang.String); } public static abstract interface ConnectionService.OutgoingCallResponse { @@ -27736,9 +27687,9 @@ package android.telecomm { public final class InCallAdapter { method public void answerCall(java.lang.String); method public void disconnectCall(java.lang.String); - method public void handoffCall(java.lang.String); method public void holdCall(java.lang.String); method public void mute(boolean); + method public void phoneAccountClicked(java.lang.String); method public void playDtmfTone(java.lang.String, char); method public void postDialContinue(java.lang.String, boolean); method public void rejectCall(java.lang.String, boolean, java.lang.String); @@ -27760,7 +27711,6 @@ package android.telecomm { method public int getFeatures(); method public android.telecomm.GatewayInfo getGatewayInfo(); method public android.net.Uri getHandle(); - method public android.telecomm.CallServiceDescriptor getHandoffCallServiceDescriptor(); method public java.lang.String getId(); method public android.telecomm.CallState getState(); method public void writeToParcel(android.os.Parcel, int); @@ -27869,6 +27819,7 @@ package android.telecomm { field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR"; field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS"; field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE"; + field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO = "android.intent.extra.START_CALL_WITH_VIDEO"; } public class VideoCallProfile implements android.os.Parcelable { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index df51ff5..fe51d82 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -86,20 +86,6 @@ public class DevicePolicyManager { } /** - * Activity action: Used to indicate that the receiving activity is being started as part of the - * managed profile provisioning flow. This intent is typically sent to a mobile device - * management application (mdm) after the first part of the provisioning process is complete in - * the expectation that this app will (after optionally showing it's own UI) ultimately call - * {@link #ACTION_PROVISION_MANAGED_PROFILE} to complete the creation of the managed profile. - * - * <p> The intent may contain the extras {@link #EXTRA_PROVISIONING_TOKEN} and - * {@link #EXTRA_PROVISIONING_EMAIL_ADDRESS}. - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_SEND_PROVISIONING_VALUES - = "android.app.action.ACTION_SEND_PROVISIONING_VALUES"; - - /** * Activity action: Starts the provisioning flow which sets up a managed profile. * * <p>A managed profile allows data separation for example for the usage of a @@ -128,17 +114,6 @@ public class DevicePolicyManager { = "android.app.action.ACTION_PROVISION_MANAGED_PROFILE"; /** - * A broadcast intent with this action can be sent to ManagedProvisionning to specify that the - * user has already consented to the creation of the managed profile. - * The intent must contain the extras - * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} and - * {@link #EXTRA_PROVISIONING_TOKEN} - * @hide - */ - public static final String ACTION_PROVISIONING_USER_HAS_CONSENTED - = "android.app.action.ACTION_PROVISIONING_USER_HAS_CONSENTED"; - - /** * A String extra holding the package name of the mobile device management application that * will be set as the profile owner or device owner. * @@ -153,18 +128,6 @@ public class DevicePolicyManager { = "android.app.extra.deviceAdminPackageName"; /** - * An int extra used to identify that during the current setup process the user has already - * consented to setting up a managed profile. This is typically received by - * a mobile device management application when it is started with - * {@link #ACTION_SEND_PROVISIONING_VALUES} and passed on in an intent - * {@link #ACTION_PROVISION_MANAGED_PROFILE} which starts the setup of the managed profile. The - * token indicates that steps asking for user consent can be skipped as the user has previously - * consented. - */ - public static final String EXTRA_PROVISIONING_TOKEN - = "android.app.extra.token"; - - /** * A String extra holding the default name of the profile that is created during managed profile * provisioning. * @@ -174,12 +137,15 @@ public class DevicePolicyManager { = "android.app.extra.defaultManagedProfileName"; /** - * A String extra holding the email address of the profile that is created during managed - * profile provisioning. This is typically received by a mobile management application when it - * is started with {@link #ACTION_SEND_PROVISIONING_VALUES} and passed on in an intent - * {@link #ACTION_PROVISION_MANAGED_PROFILE} which starts the setup of the managed profile. It - * is eventually passed on in an intent + * A String extra that, holds the email address of the account which a managed profile is + * created for. Used with {@link #ACTION_PROVISION_MANAGED_PROFILE} and * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE}. + * + * <p> If the {@link #ACTION_PROVISION_MANAGED_PROFILE} intent that starts managed provisioning + * contains this extra, it is forwarded in the + * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} intent to the mobile + * device management application that was set as the profile owner during provisioning. + * It is usually used to avoid that the user has to enter their email address twice. */ public static final String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.ManagedProfileEmailAddress"; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index be4e864..bb90fd7 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -413,6 +413,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * Full path to the base APK for this application. */ + // TODO: verify that nobody is doing codePath comparisons against this public String sourceDir; /** diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index f176dfb..319559a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -279,11 +279,11 @@ public class PackageParser { mMetrics = metrics; } - private static final boolean isPackageFilename(File file) { + public static final boolean isPackageFilename(File file) { return isPackageFilename(file.getName()); } - private static final boolean isPackageFilename(String name) { + public static final boolean isPackageFilename(String name) { return name.endsWith(".apk"); } @@ -552,7 +552,7 @@ public class PackageParser { * Note that this <em>does not</em> perform signature verification; that * must be done separately in {@link #collectCertificates(Package, int)}. */ - public Package parseSplitPackage(File apkDir, int flags) throws PackageParserException { + public Package parseClusterPackage(File apkDir, int flags) throws PackageParserException { final File[] files = apkDir.listFiles(); if (ArrayUtils.isEmpty(files)) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, @@ -600,27 +600,23 @@ public class PackageParser { "Missing base APK in " + apkDir); } - // Always apply deterministic ordering based on splitName - final int size = apks.size(); - - final String[] splitNames = apks.keySet().toArray(new String[size]); - Arrays.sort(splitNames, sSplitNameComparator); - - final File[] splitFiles = new File[size]; - for (int i = 0; i < size; i++) { - splitFiles[i] = apks.get(splitNames[i]); - } - final Package pkg = parseBaseApk(baseFile, flags); if (pkg == null) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse base APK: " + baseFile); } - for (File splitFile : splitFiles) { - parseSplitApk(pkg, splitFile, flags); + // Always apply deterministic ordering based on splitName + final int size = apks.size(); + final String[] splitNames = apks.keySet().toArray(new String[size]); + Arrays.sort(splitNames, sSplitNameComparator); + + for (String splitName : splitNames) { + final File splitFile = apks.get(splitName); + parseSplitApk(pkg, splitFile, splitName, flags); } + pkg.codePath = apkDir.getAbsolutePath(); return pkg; } @@ -632,11 +628,12 @@ public class PackageParser { */ public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { final Package pkg = parseBaseApk(apkFile, flags); - if (pkg != null) { - return pkg; - } else { + if (pkg == null) { throw new PackageParserException(mParseError, "Failed to parse " + apkFile); } + + pkg.codePath = apkFile.getAbsolutePath(); + return pkg; } private Package parseBaseApk(File apkFile, int flags) { @@ -723,19 +720,28 @@ public class PackageParser { parser.close(); assmgr.close(); - pkg.codePath = apkPath; + pkg.baseCodePath = apkPath; pkg.mSignatures = null; + // TODO: Remove this when the WebView can load resources dynamically. b/11505352 + if (pkg.usesOptionalLibraries == null) { + pkg.usesOptionalLibraries = new ArrayList<String>(); + } + pkg.usesOptionalLibraries.add("com.android.webview"); + return pkg; } - private void parseSplitApk(Package pkg, File apkFile, int flags) throws PackageParserException { - final String apkPath = apkFile.getAbsolutePath(); + private void parseSplitApk(Package pkg, File apkFile, String splitName, int flags) + throws PackageParserException { + final String splitCodePath = apkFile.getAbsolutePath(); mArchiveSourcePath = apkFile.getAbsolutePath(); // TODO: expand split APK parsing + // TODO: extract splitName during parse + pkg.splitNames = ArrayUtils.appendElement(String.class, pkg.splitNames, splitName); pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths, - apkFile.getAbsolutePath()); + splitCodePath); } /** @@ -748,7 +754,7 @@ public class PackageParser { // TODO: extend to gather digest for split APKs try { - final StrictJarFile jarFile = new StrictJarFile(pkg.codePath); + final StrictJarFile jarFile = new StrictJarFile(pkg.baseCodePath); try { final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); if (je != null) { @@ -773,7 +779,7 @@ public class PackageParser { pkg.mSignatures = null; pkg.mSigningKeys = null; - collectCertificates(pkg, new File(pkg.codePath), flags); + collectCertificates(pkg, new File(pkg.baseCodePath), flags); if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { for (String splitCodePath : pkg.splitCodePaths) { @@ -3692,12 +3698,21 @@ public class PackageParser { public final static class Package { public String packageName; + /** Names of any split APKs, ordered by parsed splitName */ + public String[] splitNames; // TODO: work towards making these paths invariant - /** Base APK */ + /** + * Path where this package was found on disk. For monolithic packages + * this is path to single base APK file; for cluster packages this is + * path to the cluster directory. + */ public String codePath; - /** Split APKs, ordered by parsed splitName */ + + /** Path of base APK */ + public String baseCodePath; + /** Paths of any split APKs, ordered by parsed splitName */ public String[] splitCodePaths; // For now we only support one application per package. @@ -3816,9 +3831,9 @@ public class PackageParser { applicationInfo.uid = -1; } - public Collection<String> getAllCodePaths() { + public List<String> getAllCodePaths() { ArrayList<String> paths = new ArrayList<>(); - paths.add(codePath); + paths.add(baseCodePath); if (!ArrayUtils.isEmpty(splitCodePaths)) { Collections.addAll(paths, splitCodePaths); } diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 1dba77d..d5d5eb8 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -384,16 +384,18 @@ public class FileUtils { return filePath.startsWith(dirPath); } - public static void deleteContents(File dir) { + public static boolean deleteContents(File dir) { File[] files = dir.listFiles(); + boolean success = true; if (files != null) { for (File file : files) { if (file.isDirectory()) { - deleteContents(file); + success &= deleteContents(file); } - file.delete(); + success &= file.delete(); } } + return success; } /** @@ -411,4 +413,23 @@ public class FileUtils { } return true; } + + public static String rewriteAfterRename(File beforeDir, File afterDir, String path) { + final File result = rewriteAfterRename(beforeDir, afterDir, new File(path)); + return (result != null) ? result.getAbsolutePath() : null; + } + + /** + * Given a path under the "before" directory, rewrite it to live under the + * "after" directory. For example, {@code /before/foo/bar.txt} would become + * {@code /after/foo/bar.txt}. + */ + public static File rewriteAfterRename(File beforeDir, File afterDir, File file) { + if (contains(beforeDir, file)) { + final String splice = file.getAbsolutePath().substring( + beforeDir.getAbsolutePath().length()); + return new File(afterDir, splice); + } + return null; + } } diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java index c9dd5d7..71d12c6 100644 --- a/core/java/android/os/SELinux.java +++ b/core/java/android/os/SELinux.java @@ -171,4 +171,23 @@ public class SELinux { return false; } } + + /** + * Recursively restores all files under the given path to their default + * SELinux security context. If the system is not compiled with SELinux, + * then {@code true} is automatically returned. If SELinux is compiled in, + * but disabled, then {@code true} is returned. + * + * @return a boolean indicating whether the relabeling succeeded. + */ + public static boolean restoreconTree(File dir) { + final File[] files = dir.listFiles(); + boolean success = true; + if (files != null) { + for (File file : files) { + success &= restorecon(file); + } + } + return success; + } } diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index d6fa05a..755bb48 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -62,7 +62,7 @@ import java.util.List; import java.util.Locale; /** - * A widget that enables the user to select a number form a predefined range. + * A widget that enables the user to select a number from a predefined range. * There are two flavors of this widget and which one is presented to the user * depends on the current theme. * <ul> diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl index 77f0dec..3cd0417 100644 --- a/core/java/com/android/internal/app/IMediaContainerService.aidl +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -16,27 +16,25 @@ package com.android.internal.app; -import android.net.Uri; import android.os.ParcelFileDescriptor; import android.content.pm.ContainerEncryptionParams; import android.content.pm.PackageInfoLite; import android.content.res.ObbInfo; interface IMediaContainerService { - String copyResourceToContainer(in Uri packageURI, String containerId, String key, + String copyResourceToContainer(String packagePath, String containerId, String key, String resFileName, String publicResFileName, boolean isExternal, - boolean isForwardLocked, in String abiOverride); - int copyResource(in Uri packageURI, in ContainerEncryptionParams encryptionParams, + boolean isForwardLocked, String abiOverride); + int copyResource(String packagePath, in ContainerEncryptionParams encryptionParams, in ParcelFileDescriptor outStream); - PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold, - in String abiOverride); - boolean checkInternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in long threshold); - boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in String abiOverride); - ObbInfo getObbInfo(in String filename); - long calculateDirectorySize(in String directory); + PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, long threshold, + String abiOverride); + boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked, long threshold); + boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked, String abiOverride); + ObbInfo getObbInfo(String filename); + long calculateDirectorySize(String directory); /** Return file system stats: [0] is total bytes, [1] is available bytes */ - long[] getFileSystemStats(in String path); - void clearDirectory(in String directory); - long calculateInstalledSize(in String packagePath, boolean isForwardLocked, - in String abiOverride); + long[] getFileSystemStats(String path); + void clearDirectory(String directory); + long calculateInstalledSize(String packagePath, boolean isForwardLocked, String abiOverride); } diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index d66ef83..7f6159d 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -138,6 +138,7 @@ public class ArrayUtils * not found. */ public static <T> int indexOf(T[] array, T value) { + if (array == null) return -1; for (int i = 0; i < array.length; i++) { if (array[i] == null) { if (value == null) return i; @@ -161,6 +162,7 @@ public class ArrayUtils } public static boolean contains(int[] array, int value) { + if (array == null) return false; for (int element : array) { if (element == value) { return true; @@ -170,6 +172,7 @@ public class ArrayUtils } public static boolean contains(long[] array, long value) { + if (array == null) return false; for (long element : array) { if (element == value) { return true; @@ -325,4 +328,8 @@ public class ArrayUtils } return cur; } + + public static long[] cloneOrNull(long[] array) { + return (array != null) ? array.clone() : null; + } } diff --git a/core/res/res/drawable-hdpi/notification_bg_low_normal.9.png b/core/res/res/drawable-hdpi/notification_bg_low_normal.9.png Binary files differdeleted file mode 100644 index af91f5e..0000000 --- a/core/res/res/drawable-hdpi/notification_bg_low_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/notification_bg_low_pressed.9.png b/core/res/res/drawable-hdpi/notification_bg_low_pressed.9.png Binary files differdeleted file mode 100644 index 9832ace..0000000 --- a/core/res/res/drawable-hdpi/notification_bg_low_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/notification_bg_normal.9.png b/core/res/res/drawable-hdpi/notification_bg_normal.9.png Binary files differdeleted file mode 100644 index 6ebed8b..0000000 --- a/core/res/res/drawable-hdpi/notification_bg_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/notification_bg_normal_pressed.9.png b/core/res/res/drawable-hdpi/notification_bg_normal_pressed.9.png Binary files differdeleted file mode 100644 index c271b11..0000000 --- a/core/res/res/drawable-hdpi/notification_bg_normal_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/progress_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/progress_mtrl_alpha.9.png Binary files differindex b11de9e..fbb2e0c 100644 --- a/core/res/res/drawable-hdpi/progress_mtrl_alpha.9.png +++ b/core/res/res/drawable-hdpi/progress_mtrl_alpha.9.png diff --git a/core/res/res/drawable-hdpi/progress_primary_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/progress_primary_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index a278ed7..0000000 --- a/core/res/res/drawable-hdpi/progress_primary_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-ldpi/progress_mtrl_alpha.9.png b/core/res/res/drawable-ldpi/progress_mtrl_alpha.9.png Binary files differindex a58128f..cd39bb4 100644 --- a/core/res/res/drawable-ldpi/progress_mtrl_alpha.9.png +++ b/core/res/res/drawable-ldpi/progress_mtrl_alpha.9.png diff --git a/core/res/res/drawable-ldpi/progress_primary_mtrl_alpha.9.png b/core/res/res/drawable-ldpi/progress_primary_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index eb0933b..0000000 --- a/core/res/res/drawable-ldpi/progress_primary_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/notification_bg_low_normal.9.png b/core/res/res/drawable-mdpi/notification_bg_low_normal.9.png Binary files differdeleted file mode 100644 index 62de9d7..0000000 --- a/core/res/res/drawable-mdpi/notification_bg_low_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/notification_bg_low_pressed.9.png b/core/res/res/drawable-mdpi/notification_bg_low_pressed.9.png Binary files differdeleted file mode 100644 index 8a6011e..0000000 --- a/core/res/res/drawable-mdpi/notification_bg_low_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/notification_bg_normal.9.png b/core/res/res/drawable-mdpi/notification_bg_normal.9.png Binary files differdeleted file mode 100644 index aa239b3..0000000 --- a/core/res/res/drawable-mdpi/notification_bg_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/notification_bg_normal_pressed.9.png b/core/res/res/drawable-mdpi/notification_bg_normal_pressed.9.png Binary files differdeleted file mode 100644 index 525120d..0000000 --- a/core/res/res/drawable-mdpi/notification_bg_normal_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/progress_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/progress_mtrl_alpha.9.png Binary files differindex 8991421..92d4b05 100644 --- a/core/res/res/drawable-mdpi/progress_mtrl_alpha.9.png +++ b/core/res/res/drawable-mdpi/progress_mtrl_alpha.9.png diff --git a/core/res/res/drawable-mdpi/progress_primary_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/progress_primary_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index 07cbed8..0000000 --- a/core/res/res/drawable-mdpi/progress_primary_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/notification_bg_low_normal.9.png b/core/res/res/drawable-xhdpi/notification_bg_low_normal.9.png Binary files differdeleted file mode 100644 index 8c884de..0000000 --- a/core/res/res/drawable-xhdpi/notification_bg_low_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/notification_bg_low_pressed.9.png b/core/res/res/drawable-xhdpi/notification_bg_low_pressed.9.png Binary files differdeleted file mode 100644 index 2159cf5..0000000 --- a/core/res/res/drawable-xhdpi/notification_bg_low_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/notification_bg_normal.9.png b/core/res/res/drawable-xhdpi/notification_bg_normal.9.png Binary files differdeleted file mode 100644 index bdf477b..0000000 --- a/core/res/res/drawable-xhdpi/notification_bg_normal.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/notification_bg_normal_pressed.9.png b/core/res/res/drawable-xhdpi/notification_bg_normal_pressed.9.png Binary files differdeleted file mode 100644 index 3f054fb..0000000 --- a/core/res/res/drawable-xhdpi/notification_bg_normal_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/progress_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/progress_mtrl_alpha.9.png Binary files differindex 4970f56..e3c4eeb 100644 --- a/core/res/res/drawable-xhdpi/progress_mtrl_alpha.9.png +++ b/core/res/res/drawable-xhdpi/progress_mtrl_alpha.9.png diff --git a/core/res/res/drawable-xhdpi/progress_primary_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/progress_primary_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index 5c7e5cd..0000000 --- a/core/res/res/drawable-xhdpi/progress_primary_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/notification_bg_low_pressed.9.png b/core/res/res/drawable-xxhdpi/notification_bg_low_pressed.9.png Binary files differdeleted file mode 100644 index b4e7559..0000000 --- a/core/res/res/drawable-xxhdpi/notification_bg_low_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/notification_bg_normal_pressed.9.png b/core/res/res/drawable-xxhdpi/notification_bg_normal_pressed.9.png Binary files differdeleted file mode 100644 index 936fbe5..0000000 --- a/core/res/res/drawable-xxhdpi/notification_bg_normal_pressed.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/progress_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/progress_mtrl_alpha.9.png Binary files differindex 74a259b..452f45c 100644 --- a/core/res/res/drawable-xxhdpi/progress_mtrl_alpha.9.png +++ b/core/res/res/drawable-xxhdpi/progress_mtrl_alpha.9.png diff --git a/core/res/res/drawable-xxhdpi/progress_primary_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/progress_primary_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index 2d4eb3f..0000000 --- a/core/res/res/drawable-xxhdpi/progress_primary_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable/notification_bg.xml b/core/res/res/drawable/notification_bg.xml deleted file mode 100644 index 362a524..0000000 --- a/core/res/res/drawable/notification_bg.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android" - android:exitFadeDuration="@android:integer/config_mediumAnimTime"> - - <item android:state_pressed="true" android:drawable="@drawable/notification_bg_normal_pressed" /> - <item android:state_pressed="false" android:drawable="@drawable/notification_bg_normal" /> -</selector> diff --git a/core/res/res/drawable/notification_bg_low.xml b/core/res/res/drawable/notification_bg_low.xml deleted file mode 100644 index 466a885..0000000 --- a/core/res/res/drawable/notification_bg_low.xml +++ /dev/null @@ -1,22 +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. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android" - android:exitFadeDuration="@android:integer/config_mediumAnimTime"> - - <item android:state_pressed="true" android:drawable="@drawable/notification_bg_low_pressed" /> - <item android:state_pressed="false" android:drawable="@drawable/notification_bg_low_normal" /> -</selector> diff --git a/core/res/res/drawable/notification_material_bg.xml b/core/res/res/drawable/notification_material_bg.xml deleted file mode 100644 index 44c67be..0000000 --- a/core/res/res/drawable/notification_material_bg.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2014 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 - --> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true"> - <shape> - <solid android:color="#ffd0d0d0" /> - <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> - </shape> - </item> - <item> - <shape> - <solid android:color="#fffafafa" /> - <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> - </shape> - </item> -</selector>
\ No newline at end of file diff --git a/core/res/res/drawable/progress_horizontal_material.xml b/core/res/res/drawable/progress_horizontal_material.xml index eca1a24..d7440a9 100644 --- a/core/res/res/drawable/progress_horizontal_material.xml +++ b/core/res/res/drawable/progress_horizontal_material.xml @@ -22,12 +22,13 @@ <item android:id="@id/secondaryProgress"> <scale android:scaleWidth="100%"> <nine-patch android:src="@drawable/progress_mtrl_alpha" - android:tint="?attr/colorControlActivated" /> + android:tint="?attr/colorControlActivated" + android:alpha="0.5" /> </scale> </item> <item android:id="@id/progress"> <scale android:scaleWidth="100%"> - <nine-patch android:src="@drawable/progress_primary_mtrl_alpha" + <nine-patch android:src="@drawable/progress_mtrl_alpha" android:tint="?attr/colorControlActivated" /> </scale> </item> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 11d6dca..590abd4 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -432,7 +432,6 @@ <item>@drawable/list_divider_mtrl_alpha</item> <item>@drawable/list_section_divider_mtrl_alpha</item> <item>@drawable/popup_background_mtrl_mult</item> - <item>@drawable/progress_primary_mtrl_alpha</item> <item>@drawable/progress_mtrl_alpha</item> <item>@drawable/scrollbar_handle_mtrl_alpha</item> <item>@drawable/scrubber_control_from_pressed_mtrl_000</item> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a252a8b..3d1f5ee 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2394,7 +2394,7 @@ accessibility purposes. For example, a TextView before an EditText in the UI usually specifies what infomation is contained in the EditText. Hence, the TextView is a label for the EditText. --> - <attr name="labelFor" format="integer" /> + <attr name="labelFor" format="reference" /> <!-- Specifies a theme override for a view. When a theme override is set, the view will be inflated using a {@link android.content.Context} themed with diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index e94b9dd..d2e023d 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -365,7 +365,4 @@ <!-- width of ImmersiveModeConfirmation (-1 for match_parent) --> <dimen name="immersive_mode_cling_width">-1px</dimen> - <!-- radius of the corners of the material rounded rect background --> - <dimen name="notification_material_rounded_rect_radius">2dp</dimen> - </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c68e6e6..5fea729 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -347,7 +347,6 @@ <java-symbol type="dimen" name="notification_title_text_size" /> <java-symbol type="dimen" name="notification_subtext_size" /> <java-symbol type="dimen" name="immersive_mode_cling_width" /> - <java-symbol type="dimen" name="notification_material_rounded_rect_radius" /> <java-symbol type="string" name="add_account_button_label" /> <java-symbol type="string" name="addToDictionary" /> @@ -1116,9 +1115,6 @@ <java-symbol type="drawable" name="unlock_halo" /> <java-symbol type="drawable" name="unlock_ring" /> <java-symbol type="drawable" name="unlock_wave" /> - <java-symbol type="drawable" name="notification_bg" /> - <java-symbol type="drawable" name="notification_bg_dim" /> - <java-symbol type="drawable" name="notification_bg_low" /> <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" /> @@ -1695,8 +1691,6 @@ <java-symbol type="color" name="notification_icon_bg_color" /> <java-symbol type="drawable" name="notification_icon_legacy_bg" /> <java-symbol type="drawable" name="notification_icon_legacy_bg_inset" /> - <java-symbol type="drawable" name="notification_material_bg_dim" /> - <java-symbol type="drawable" name="notification_material_bg" /> <java-symbol type="drawable" name="notification_material_media_progress" /> <java-symbol type="color" name="notification_media_action_bg" /> <java-symbol type="color" name="notification_media_progress" /> diff --git a/media/java/android/media/AudioDevice.java b/media/java/android/media/AudioDevice.java new file mode 100644 index 0000000..1fd27fe --- /dev/null +++ b/media/java/android/media/AudioDevice.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2014 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.media; + +import android.util.SparseIntArray; + +/** + * @hide + * CANDIDATE FOR PUBLIC API + */ +public class AudioDevice { + + public static final int DEVICE_TYPE_UNKNOWN = 0; + public static final int DEVICE_TYPE_BUILTIN_EARPIECE = 1; + public static final int DEVICE_TYPE_BUILTIN_SPEAKER = 2; + public static final int DEVICE_TYPE_WIRED_HEADSET = 3; + public static final int DEVICE_TYPE_WIRED_HEADPHONES = 4; + public static final int DEVICE_TYPE_LINE_ANALOG = 5; + public static final int DEVICE_TYPE_LINE_DIGITAL = 6; + public static final int DEVICE_TYPE_BLUETOOTH_SCO = 7; + public static final int DEVICE_TYPE_BLUETOOTH_A2DP = 8; + public static final int DEVICE_TYPE_HDMI = 9; + public static final int DEVICE_TYPE_HDMI_ARC = 10; + public static final int DEVICE_TYPE_USB_DEVICE = 11; + public static final int DEVICE_TYPE_USB_ACCESSORY = 12; + public static final int DEVICE_TYPE_DOCK = 13; + public static final int DEVICE_TYPE_FM = 14; + public static final int DEVICE_TYPE_BUILTIN_MIC = 15; + public static final int DEVICE_TYPE_FM_TUNER = 16; + public static final int DEVICE_TYPE_TV_TUNER = 17; + public static final int DEVICE_TYPE_TELEPHONY = 18; + + AudioDevicePortConfig mConfig; + + AudioDevice(AudioDevicePortConfig config) { + mConfig = new AudioDevicePortConfig(config); + } + + public boolean isInputDevice() { + return (mConfig.port().role() == AudioPort.ROLE_SOURCE); + } + + public boolean isOutputDevice() { + return (mConfig.port().role() == AudioPort.ROLE_SINK); + } + + public int getDeviceType() { + return INT_TO_EXT_DEVICE_MAPPING.get(mConfig.port().type(), DEVICE_TYPE_UNKNOWN); + } + + public String getAddress() { + return mConfig.port().address(); + } + + private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING; + + static { + INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray(); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, DEVICE_TYPE_BUILTIN_EARPIECE); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_SPEAKER, DEVICE_TYPE_BUILTIN_SPEAKER); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_WIRED_HEADSET, DEVICE_TYPE_WIRED_HEADSET); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, DEVICE_TYPE_WIRED_HEADPHONES); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, DEVICE_TYPE_BLUETOOTH_SCO); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET, DEVICE_TYPE_BLUETOOTH_SCO); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT, DEVICE_TYPE_BLUETOOTH_SCO); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, DEVICE_TYPE_BLUETOOTH_A2DP); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, DEVICE_TYPE_BLUETOOTH_A2DP); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, DEVICE_TYPE_BLUETOOTH_A2DP); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_HDMI, DEVICE_TYPE_HDMI); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, DEVICE_TYPE_DOCK); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, DEVICE_TYPE_DOCK); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_USB_ACCESSORY, DEVICE_TYPE_USB_ACCESSORY); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_USB_DEVICE, DEVICE_TYPE_USB_DEVICE); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_TELEPHONY_TX, DEVICE_TYPE_TELEPHONY); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_LINE, DEVICE_TYPE_LINE_ANALOG); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_HDMI_ARC, DEVICE_TYPE_HDMI_ARC); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_SPDIF, DEVICE_TYPE_LINE_DIGITAL); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_FM, DEVICE_TYPE_FM); + + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUILTIN_MIC, DEVICE_TYPE_BUILTIN_MIC); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET, DEVICE_TYPE_BLUETOOTH_SCO); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_WIRED_HEADSET, DEVICE_TYPE_WIRED_HEADSET); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_HDMI, DEVICE_TYPE_HDMI); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_TELEPHONY_RX, DEVICE_TYPE_TELEPHONY); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BACK_MIC, DEVICE_TYPE_BUILTIN_MIC); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET, DEVICE_TYPE_DOCK); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_DGTL_DOCK_HEADSET, DEVICE_TYPE_DOCK); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_USB_ACCESSORY, DEVICE_TYPE_USB_ACCESSORY); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_USB_DEVICE, DEVICE_TYPE_USB_DEVICE); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_FM_TUNER, DEVICE_TYPE_FM_TUNER); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_TV_TUNER, DEVICE_TYPE_TV_TUNER); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_LINE, DEVICE_TYPE_LINE_ANALOG); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_SPDIF, DEVICE_TYPE_LINE_DIGITAL); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, DEVICE_TYPE_BLUETOOTH_A2DP); + + // not covered here, legacy + //AudioSystem.DEVICE_OUT_REMOTE_SUBMIX + //AudioSystem.DEVICE_IN_REMOTE_SUBMIX + } +} + diff --git a/media/java/android/media/AudioDevicePortConfig.java b/media/java/android/media/AudioDevicePortConfig.java index a381e10..e468a53 100644 --- a/media/java/android/media/AudioDevicePortConfig.java +++ b/media/java/android/media/AudioDevicePortConfig.java @@ -31,6 +31,11 @@ public class AudioDevicePortConfig extends AudioPortConfig { super((AudioPort)devicePort, samplingRate, channelMask, format, gain); } + AudioDevicePortConfig(AudioDevicePortConfig config) { + this(config.port(), config.samplingRate(), config.channelMask(), config.format(), + config.gain()); + } + /** * Returns the audio device port this AudioDevicePortConfig is issued from. */ diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 168fb41..74ff3b1 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -30,7 +30,6 @@ import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.content.res.ObbInfo; import android.content.res.ObbScanner; -import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Environment.UserEnvironment; @@ -45,7 +44,6 @@ import android.provider.Settings; import android.system.ErrnoException; import android.system.Os; import android.system.StructStatVfs; -import android.util.DisplayMetrics; import android.util.Slog; import com.android.internal.app.IMediaContainerService; @@ -53,6 +51,9 @@ import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.NativeLibraryHelper.ApkHandle; import com.android.internal.content.PackageHelper; +import libcore.io.IoUtils; +import libcore.io.Streams; + import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -71,9 +72,6 @@ import javax.crypto.CipherInputStream; import javax.crypto.Mac; import javax.crypto.NoSuchPaddingException; -import libcore.io.IoUtils; -import libcore.io.Streams; - /* * This service copies a downloaded apk to a file passed in as * a ParcelFileDescriptor or to a newly created container specified @@ -101,14 +99,14 @@ public class DefaultContainerService extends IntentService { * @return Returns the new cache path where the resource has been copied into * */ - public String copyResourceToContainer(final Uri packageURI, final String cid, + @Override + public String copyResourceToContainer(final String packagePath, final String cid, final String key, final String resFileName, final String publicResFileName, boolean isExternal, boolean isForwardLocked, String abiOverride) { - if (packageURI == null || cid == null) { + if (packagePath == null || cid == null) { return null; } - if (isExternal) { // Make sure the sdcard is mounted. String status = Environment.getExternalStorageState(); @@ -120,11 +118,11 @@ public class DefaultContainerService extends IntentService { ApkHandle handle = null; try { - handle = ApkHandle.create(packageURI.getPath()); - return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName, + handle = ApkHandle.create(packagePath); + return copyResourceInner(packagePath, cid, key, resFileName, publicResFileName, isExternal, isForwardLocked, handle, abiOverride); } catch (IOException ioe) { - Slog.w(TAG, "Problem opening APK: " + packageURI.getPath()); + Slog.w(TAG, "Problem opening APK: " + packagePath); return null; } finally { IoUtils.closeQuietly(handle); @@ -142,9 +140,10 @@ public class DefaultContainerService extends IntentService { * @return returns status code according to those in * {@link PackageManager} */ - public int copyResource(final Uri packageURI, ContainerEncryptionParams encryptionParams, - ParcelFileDescriptor outStream) { - if (packageURI == null || outStream == null) { + @Override + public int copyResource(final String packagePath, + ContainerEncryptionParams encryptionParams, ParcelFileDescriptor outStream) { + if (packagePath == null || outStream == null) { return PackageManager.INSTALL_FAILED_INVALID_URI; } @@ -152,18 +151,18 @@ public class DefaultContainerService extends IntentService { = new ParcelFileDescriptor.AutoCloseOutputStream(outStream); try { - copyFile(packageURI, autoOut, encryptionParams); + copyFile(packagePath, autoOut, encryptionParams); return PackageManager.INSTALL_SUCCEEDED; } catch (FileNotFoundException e) { - Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " FNF: " + Slog.e(TAG, "Could not copy URI " + packagePath.toString() + " FNF: " + e.getMessage()); return PackageManager.INSTALL_FAILED_INVALID_URI; } catch (IOException e) { - Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " IO: " + Slog.e(TAG, "Could not copy URI " + packagePath.toString() + " IO: " + e.getMessage()); return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } catch (DigestException e) { - Slog.e(TAG, "Could not copy URI " + packageURI.toString() + " Security: " + Slog.e(TAG, "Could not copy URI " + packagePath.toString() + " Security: " + e.getMessage()); return PackageManager.INSTALL_FAILED_INVALID_APK; } finally { @@ -217,9 +216,9 @@ public class DefaultContainerService extends IntentService { } @Override - public boolean checkInternalFreeStorage(Uri packageUri, boolean isForwardLocked, + public boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked, long threshold) throws RemoteException { - final File apkFile = new File(packageUri.getPath()); + final File apkFile = new File(packagePath); try { return isUnderInternalThreshold(apkFile, isForwardLocked, threshold); } catch (IOException e) { @@ -228,9 +227,9 @@ public class DefaultContainerService extends IntentService { } @Override - public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked, + public boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked, String abiOverride) throws RemoteException { - final File apkFile = new File(packageUri.getPath()); + final File apkFile = new File(packagePath); try { return isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride); } catch (IOException e) { @@ -238,6 +237,7 @@ public class DefaultContainerService extends IntentService { } } + @Override public ObbInfo getObbInfo(String filename) { try { return ObbScanner.getObbInfo(filename); @@ -347,11 +347,11 @@ public class DefaultContainerService extends IntentService { return mBinder; } - private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName, + private String copyResourceInner(String packagePath, String newCid, String key, String resFileName, String publicResFileName, boolean isExternal, boolean isForwardLocked, ApkHandle handle, String abiOverride) { // The .apk file - String codePath = packageURI.getPath(); + String codePath = packagePath; File codeFile = new File(codePath); String[] abiList = Build.SUPPORTED_ABIS; @@ -492,39 +492,13 @@ public class DefaultContainerService extends IntentService { } } - private void copyFile(Uri pPackageURI, OutputStream outStream, + private void copyFile(String packagePath, OutputStream outStream, ContainerEncryptionParams encryptionParams) throws FileNotFoundException, IOException, DigestException { - String scheme = pPackageURI.getScheme(); InputStream inStream = null; try { - if (scheme == null || scheme.equals("file")) { - final InputStream is = new FileInputStream(new File(pPackageURI.getPath())); - inStream = new BufferedInputStream(is); - } else if (scheme.equals("content")) { - final ParcelFileDescriptor fd; - try { - fd = getContentResolver().openFileDescriptor(pPackageURI, "r"); - } catch (FileNotFoundException e) { - Slog.e(TAG, "Couldn't open file descriptor from download service. " - + "Failed with exception " + e); - throw e; - } - - if (fd == null) { - Slog.e(TAG, "Provider returned no file descriptor for " + - pPackageURI.toString()); - throw new FileNotFoundException("provider returned no file descriptor"); - } else { - if (localLOGV) { - Slog.i(TAG, "Opened file descriptor from download service."); - } - inStream = new ParcelFileDescriptor.AutoCloseInputStream(fd); - } - } else { - Slog.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); - throw new FileNotFoundException("Package URI is not 'file:' or 'content:'"); - } + final InputStream is = new FileInputStream(new File(packagePath)); + inStream = new BufferedInputStream(is); /* * If this resource is encrypted, get the decrypted stream version diff --git a/core/res/res/drawable/notification_material_bg_dim.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml index 9b691e6..6a0277f 100644 --- a/core/res/res/drawable/notification_material_bg_dim.xml +++ b/packages/SystemUI/res/drawable/notification_material_bg.xml @@ -16,17 +16,11 @@ --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="@color/white"> + android:color="@color/notification_ripple_untinted_color"> <item> <shape> - <solid android:color="#d4ffffff" /> + <solid android:color="@color/notification_material_background_color" /> <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> </shape> </item> - <item android:id="@id/mask"> - <shape> - <solid android:color="@color/white" /> - <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> - </shape> - </item> -</ripple> +</ripple>
\ No newline at end of file diff --git a/core/res/res/drawable/notification_bg_dim.xml b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml index 5c245f8..b04394d 100644 --- a/core/res/res/drawable/notification_bg_dim.xml +++ b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml @@ -14,8 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> - -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="#ff444444"> - <item android:drawable="@drawable/notification_bg_normal" /> -</ripple>
\ No newline at end of file +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@color/notification_material_background_dimmed_color" /> + <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> +</shape> diff --git a/packages/SystemUI/res/drawable/notification_scrim.xml b/packages/SystemUI/res/drawable/notification_scrim.xml index ff7e31f..53ba213 100644 --- a/packages/SystemUI/res/drawable/notification_scrim.xml +++ b/packages/SystemUI/res/drawable/notification_scrim.xml @@ -17,6 +17,6 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="#34000000" /> - <corners android:radius="@*android:dimen/notification_material_rounded_rect_radius" /> + <solid android:color="#08000000" /> + <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_panel_background.xml b/packages/SystemUI/res/drawable/qs_panel_background.xml index e579d87..b4e311f 100644 --- a/packages/SystemUI/res/drawable/qs_panel_background.xml +++ b/packages/SystemUI/res/drawable/qs_panel_background.xml @@ -18,6 +18,6 @@ <corners android:topLeftRadius="0dp" android:topRightRadius="0dp" - android:bottomLeftRadius="@*android:dimen/notification_material_rounded_rect_radius" - android:bottomRightRadius="@*android:dimen/notification_material_rounded_rect_radius"/> + android:bottomLeftRadius="@dimen/notification_material_rounded_rect_radius" + android:bottomRightRadius="@dimen/notification_material_rounded_rect_radius"/> </shape> diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml index e1c460c..ca46437 100644 --- a/packages/SystemUI/res/layout/qs_detail.xml +++ b/packages/SystemUI/res/layout/qs_detail.xml @@ -17,41 +17,36 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/system_primary_color" > + android:background="@color/system_primary_color" + android:padding="16dp" > - <ImageView + <TextView android:id="@android:id/button1" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_alignParentStart="true" - android:contentDescription="@string/accessibility_quick_settings_close" - android:padding="@dimen/qs_panel_padding" - android:src="@drawable/ic_qs_back" /> + style="@style/QSBorderlessButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minWidth="88dp" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:text="@string/quick_settings_done" + android:textAppearance="@style/TextAppearance.QS.DetailButton" /> <TextView - android:id="@android:id/title" - android:layout_width="match_parent" - android:layout_height="64dp" - android:layout_alignParentTop="true" - android:layout_toEndOf="@android:id/button1" - android:layout_toStartOf="@android:id/checkbox" - android:gravity="center_vertical" - android:textAppearance="@style/TextAppearance.QS.DetailHeader" /> - - <ImageView - android:id="@android:id/custom" - android:layout_width="match_parent" + android:id="@android:id/button2" + style="@style/QSBorderlessButton" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_below="@android:id/title" - android:layout_marginLeft="16dip" - android:layout_marginRight="16dip" - android:scaleType="fitXY" - android:src="?android:attr/dividerHorizontal" /> + android:layout_alignParentBottom="true" + android:layout_marginEnd="8dp" + android:minWidth="132dp" + android:layout_toStartOf="@android:id/button1" + android:text="@string/quick_settings_more_settings" + android:textAppearance="@style/TextAppearance.QS.DetailButton" /> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_below="@android:id/custom" /> + android:layout_above="@android:id/button1" /> </RelativeLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml new file mode 100644 index 0000000..fcbb32c --- /dev/null +++ b/packages/SystemUI/res/layout/qs_detail_header.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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" + style="@style/BrightnessDialogContainer" + android:background="@drawable/btn_borderless_rect" > + + <TextView + android:id="@android:id/title" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textAppearance="@style/TextAppearance.QS.DetailHeader" /> + + <Switch + android:id="@android:id/toggle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="false" + android:textAppearance="@style/TextAppearance.QS.DetailHeader" /> + +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml new file mode 100644 index 0000000..c5eaed9 --- /dev/null +++ b/packages/SystemUI/res/layout/qs_detail_item.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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="@dimen/qs_detail_item_height" + android:gravity="center_vertical" + android:background="@drawable/btn_borderless_rect" + android:clickable="true" + android:orientation="horizontal" > + + <ImageView + android:id="@android:id/icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginEnd="12dp" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="20dp" + android:orientation="vertical" > + + <TextView + android:id="@android:id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" /> + + <TextView + android:id="@android:id/summary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="2dp" + android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" /> + </LinearLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml index 97ed9a0..58547b9 100644 --- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml @@ -30,7 +30,7 @@ <com.android.systemui.settings.ToggleSlider android:id="@+id/brightness_slider" android:layout_width="0dp" - android:layout_height="44dp" + android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="1" systemui:text="@string/status_bar_settings_auto_brightness_label" /> diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 2188a8e..1f68cd8 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -56,12 +56,6 @@ android:clipToPadding="false" android:clipChildren="false"> - <com.android.systemui.statusbar.stack.NotificationStackScrollLayout - android:id="@+id/notification_stack_scroller" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_marginBottom="@dimen/close_handle_underlap"/> - <ViewStub android:id="@+id/keyguard_user_switcher" android:layout_height="wrap_content" @@ -90,12 +84,25 @@ android:layout_marginRight="@dimen/notification_side_padding"/> <!-- A view to reserve space for the collapsed stack --> + <!-- Layout height: notification_min_height + bottom_stack_peek_amount --> <View - android:layout_height="@dimen/collapsed_stack_height" - android:layout_width="match_parent"/> + android:id="@+id/reserve_notification_space" + android:layout_height="@dimen/min_stack_height" + android:layout_width="match_parent" + android:layout_marginTop="@dimen/notifications_top_padding" /> + + <View + android:layout_height="@dimen/notification_side_padding" + android:layout_width="match_parent" /> </LinearLayout> </com.android.systemui.statusbar.phone.ObservableScrollView> + <com.android.systemui.statusbar.stack.NotificationStackScrollLayout + android:id="@+id/notification_stack_scroller" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginBottom="@dimen/close_handle_underlap"/> + </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer> <include layout="@layout/status_bar_expanded_header" /> diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml index d239208..70589b7 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml @@ -135,13 +135,22 @@ android:textColor="#ffffff" android:singleLine="true" /> - <include - layout="@layout/quick_settings_brightness_dialog" - android:id="@+id/brightness_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_alignParentBottom="true" - /> + + <include + android:id="@+id/qs_detail_header" + layout="@layout/qs_detail_header" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + /> + <include + android:id="@+id/brightness_container" + layout="@layout/quick_settings_brightness_dialog" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + /> + <TextView android:id="@+id/header_debug_info" diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml index 6d63bb0..6ab8cf3 100644 --- a/packages/SystemUI/res/layout/zen_mode_condition.xml +++ b/packages/SystemUI/res/layout/zen_mode_condition.xml @@ -41,7 +41,7 @@ <ImageView android:id="@android:id/button1" - style="@style/BorderlessButton" + style="@style/QSBorderlessButton" android:layout_width="@dimen/zen_mode_condition_height" android:layout_height="@dimen/zen_mode_condition_height" android:layout_alignParentEnd="true" @@ -53,7 +53,7 @@ <ImageView android:id="@android:id/button2" - style="@style/BorderlessButton" + style="@style/QSBorderlessButton" android:layout_width="@dimen/zen_mode_condition_height" android:layout_height="@dimen/zen_mode_condition_height" android:layout_alignParentEnd="true" diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml index 0a8f852..0420cbc 100644 --- a/packages/SystemUI/res/layout/zen_mode_panel.xml +++ b/packages/SystemUI/res/layout/zen_mode_panel.xml @@ -42,7 +42,7 @@ <TextView android:id="@android:id/button2" - style="@style/BorderlessButton" + style="@style/QSBorderlessButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 19d72c4..a93768b 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -60,6 +60,24 @@ <color name="keyguard_affordance">#ffffffff</color> + <!-- The color of the legacy notification background --> + <color name="notification_legacy_background_color">#ff1a1a1a</color> + + <!-- The color of the material notification background --> + <color name="notification_material_background_color">#fffafafa</color> + + <!-- The color of the material notification background when dimmed --> + <color name="notification_material_background_dimmed_color">#d4ffffff</color> + + <!-- The color of the material notification background when low priority --> + <color name="notification_material_background_low_priority_color">#ffdcdcdc</color> + + <!-- The color of the ripples on the untinted notifications --> + <color name="notification_ripple_untinted_color">#20000000</color> + + <!-- The color of the ripples on the tinted notifications --> + <color name="notification_ripple_tinted_color">#30ffffff</color> + <!-- The color of the circle around the primary user in the user switcher --> <color name="current_user_border_color">@color/primary_color</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 8ee9155..adec883 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -168,6 +168,9 @@ <dimen name="qs_tile_padding_bottom">16dp</dimen> <dimen name="qs_tile_spacing">4dp</dimen> <dimen name="qs_panel_padding_bottom">8dp</dimen> + <dimen name="qs_detail_item_height">48dp</dimen> + <dimen name="qs_detail_item_height_connected">72dp</dimen> + <!-- How far the expanded QS panel peeks from the header in collapsed state. --> <dimen name="qs_peek_height">8dp</dimen> @@ -227,6 +230,9 @@ <!-- Space reserved for the cards behind the top card in the bottom stack --> <dimen name="bottom_stack_peek_amount">12dp</dimen> + <!-- bottom_stack_peek_amount + notification_min_height --> + <dimen name="min_stack_height">76dp</dimen> + <!-- The height of the area before the bottom stack in which the notifications slow down --> <dimen name="bottom_stack_slow_down_length">12dp</dimen> @@ -248,9 +254,6 @@ <!-- The height of the speed bump view. --> <dimen name="speed_bump_height">16dp</dimen> - <!-- The total height of the stack in its collapsed size (i.e. when quick settings is open) --> - <dimen name="collapsed_stack_height">94dp</dimen> - <!-- Width of the zen mode interstitial dialog. --> <dimen name="zen_mode_dialog_width">320dp</dimen> @@ -306,6 +309,9 @@ phone hints. --> <dimen name="edge_tap_area_width">48dp</dimen> + <!-- radius of the corners of the material rounded rect background --> + <dimen name="notification_material_rounded_rect_radius">2dp</dimen> + <!-- end margin for multi user switch in expanded quick settings --> <dimen name="multi_user_switch_expanded_margin">8dp</dimen> diff --git a/packages/SystemUI/res/values/internal.xml b/packages/SystemUI/res/values/internal.xml index 7b93d31..3b593d2 100644 --- a/packages/SystemUI/res/values/internal.xml +++ b/packages/SystemUI/res/values/internal.xml @@ -17,6 +17,5 @@ <resources> <dimen name="status_bar_height">@*android:dimen/status_bar_height</dimen> <dimen name="navigation_bar_height">@*android:dimen/navigation_bar_height</dimen> - <drawable name="notification_material_bg">@*android:drawable/notification_material_bg</drawable> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index a8799f7..a5cfdbc 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -534,6 +534,10 @@ <string name="quick_settings_color_space_label">Color correction mode</string> <!-- QuickSettings: Control panel: Label for button that navigates to settings. [CHAR LIMIT=NONE] --> <string name="quick_settings_more_settings">More settings</string> + <!-- QuickSettings: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] --> + <string name="quick_settings_done">Done</string> + <!-- QuickSettings: Control panel: Label for connected device. [CHAR LIMIT=NONE] --> + <string name="quick_settings_connected">Connected</string> <!-- QuickSettings: Tethering. [CHAR LIMIT=NONE] --> <string name="quick_settings_tethering_label">Tethering</string> <!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index e5d5b03..dd1b749 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -176,6 +176,7 @@ <item name="android:textSize">14sp</item> <item name="android:textAllCaps">true</item> <item name="android:fontFamily">sans-serif-medium</item> + <item name="android:gravity">center</item> </style> <style name="BaseBrightnessDialogContainer"> @@ -233,6 +234,11 @@ <item name="android:colorControlActivated">#ffffffff</item> </style> + <style name="QSBorderlessButton"> + <item name="android:padding">12dp</item> + <item name="android:background">@drawable/btn_borderless_rect</item> + <item name="android:gravity">center</item> + </style> <style name="BorderlessButton" parent="@android:style/Widget.Material.Button.Borderless" /> <style name="BorderlessButton.Tiny"> diff --git a/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java b/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java index ff904b3..327ed6a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/CircularClipper.java @@ -21,23 +21,19 @@ import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.view.View; +import android.view.ViewAnimationUtils; /** Helper for view-level circular clip animations. **/ public class CircularClipper { private final View mTarget; - private Utils mUtils; private ValueAnimator mAnimator; public CircularClipper(View target) { mTarget = target; } - public void setUtils(Utils utils) { - mUtils = utils; - } - public void animateCircularClip(int x, int y, boolean in, AnimatorListener listener) { if (mAnimator != null) { mAnimator.cancel(); @@ -49,14 +45,7 @@ public class CircularClipper { r = (int) Math.max(r, Math.ceil(Math.sqrt(w * w + h * h))); r = (int) Math.max(r, Math.ceil(Math.sqrt(x * x + h * h))); - if (mUtils == null) { - mTarget.setVisibility(in ? View.VISIBLE : View.GONE); - if (listener != null) { - listener.onAnimationEnd(null); - } - return; - } - mAnimator = mUtils.createRevealAnimator(mTarget, x, y, 0, r); + mAnimator = ViewAnimationUtils.createCircularReveal(mTarget, x, y, 0, r); mAnimator.removeAllListeners(); if (listener != null) { mAnimator.addListener(listener); @@ -83,9 +72,4 @@ public class CircularClipper { mTarget.setVisibility(View.GONE); }; }; - - public interface Utils { - ValueAnimator createRevealAnimator(View v, int centerX, int centerY, - float startRadius, float endRadius); - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index d152887..51befd6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -20,15 +20,17 @@ import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.FrameLayout; import com.android.systemui.R; +import com.android.systemui.qs.QSTile.DetailAdapter; import java.util.ArrayList; @@ -38,7 +40,10 @@ public class QSPanel extends ViewGroup { private final Context mContext; private final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>(); - private final FrameLayout mDetail; + private final View mDetail; + private final ViewGroup mDetailContent; + private final View mDetailSettingsButton; + private final View mDetailDoneButton; private final CircularClipper mClipper; private final H mHandler = new H(); @@ -63,8 +68,10 @@ public class QSPanel extends ViewGroup { super(context, attrs); mContext = context; - mDetail = new FrameLayout(mContext); - mDetail.setBackgroundColor(mContext.getResources().getColor(R.color.system_primary_color)); + mDetail = LayoutInflater.from(context).inflate(R.layout.qs_detail, this, false); + mDetailContent = (ViewGroup) mDetail.findViewById(android.R.id.content); + mDetailSettingsButton = mDetail.findViewById(android.R.id.button2); + mDetailDoneButton = mDetail.findViewById(android.R.id.button1); mDetail.setVisibility(GONE); mDetail.setClickable(true); addView(mDetail); @@ -91,10 +98,6 @@ public class QSPanel extends ViewGroup { } } - public void setUtils(CircularClipper.Utils utils) { - mClipper.setUtils(utils); - } - public void setExpanded(boolean expanded) { if (mExpanded == expanded) return; mExpanded = expanded; @@ -141,6 +144,12 @@ public class QSPanel extends ViewGroup { public void onShowDetail(boolean show) { QSPanel.this.showDetail(show, r); } + @Override + public void onToggleStateChanged(boolean state) { + if (mDetailRecord == r) { + fireToggleStateChanged(state); + } + } }); final View.OnClickListener click = new View.OnClickListener() { @Override @@ -165,20 +174,34 @@ public class QSPanel extends ViewGroup { if (r == null) return; AnimatorListener listener = null; if (show) { - if (mDetailRecord != null) return; - if (r.detailView == null) { - r.detailView = r.tile.createDetailView(mContext, mDetail); - } - if (r.detailView == null) return; + if (mDetailRecord != null) return; // already showing something in detail + r.detailAdapter = r.tile.getDetailAdapter(); + if (r.detailAdapter == null) return; + r.detailView = r.detailAdapter.createDetailView(mContext, r.detailView, mDetailContent); + if (r.detailView == null) throw new IllegalStateException("Must return detail view"); + mDetailDoneButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + showDetail(false, mDetailRecord); + } + }); + final Intent settingsIntent = r.detailAdapter.getSettingsIntent(); + mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE); + mDetailSettingsButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mDetailRecord.tile.mHost.startSettingsActivity(settingsIntent); + } + }); mDetailRecord = r; - mDetail.removeAllViews(); + mDetailContent.removeAllViews(); mDetail.bringToFront(); - mDetail.addView(r.detailView); + mDetailContent.addView(r.detailView); } else { if (mDetailRecord == null) return; listener = mTeardownDetailWhenDone; } - fireShowingDetail(show); + fireShowingDetail(show ? r.detailAdapter : null); int x = r.tileView.getLeft() + r.tileView.getWidth() / 2; int y = r.tileView.getTop() + r.tileView.getHeight() / 2; mClipper.animateCircularClip(x, y, show, listener); @@ -215,11 +238,7 @@ public class QSPanel extends ViewGroup { record.tileView.measure(exactly(cw), exactly(ch)); } int h = rows == 0 ? 0 : (getRowTop(rows) + mPanelPaddingBottom); - mDetail.measure(exactly(width), unspecified()); - if (mDetail.getVisibility() == VISIBLE && mDetail.getChildCount() > 0) { - final int dmh = mDetail.getMeasuredHeight(); - if (dmh > 0) h = Math.max(h, dmh); - } + mDetail.measure(exactly(width), exactly(h)); setMeasuredDimension(width, h); } @@ -227,10 +246,6 @@ public class QSPanel extends ViewGroup { return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); } - private static int unspecified() { - return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - } - @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int w = getWidth(); @@ -263,9 +278,15 @@ public class QSPanel extends ViewGroup { return cols; } - private void fireShowingDetail(boolean showingDetail) { + private void fireShowingDetail(QSTile.DetailAdapter detail) { + if (mCallback != null) { + mCallback.onShowingDetail(detail); + } + } + + private void fireToggleStateChanged(boolean state) { if (mCallback != null) { - mCallback.onShowingDetail(showingDetail); + mCallback.onToggleStateChanged(state); } } @@ -286,18 +307,20 @@ public class QSPanel extends ViewGroup { QSTile<?> tile; QSTileView tileView; View detailView; + DetailAdapter detailAdapter; int row; int col; } private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { - mDetail.removeAllViews(); + mDetailContent.removeAllViews(); mDetailRecord = null; }; }; public interface Callback { - void onShowingDetail(boolean showingDetail); + void onShowingDetail(QSTile.DetailAdapter detail); + void onToggleStateChanged(boolean state); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index ba350e5..62c9d9f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -26,7 +26,6 @@ import android.util.Log; import android.view.View; import android.view.ViewGroup; -import com.android.systemui.R; import com.android.systemui.qs.QSTile.State; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; @@ -57,7 +56,6 @@ public abstract class QSTile<TState extends State> implements Listenable { protected final Context mContext; protected final H mHandler; protected final Handler mUiHandler = new Handler(Looper.getMainLooper()); - private final int mFeedbackStartDelay; private Callback mCallback; protected final TState mState = newTileState(); @@ -71,7 +69,6 @@ public abstract class QSTile<TState extends State> implements Listenable { mHost = host; mContext = host.getContext(); mHandler = new H(host.getLooper()); - mFeedbackStartDelay = mContext.getResources().getInteger(R.integer.feedback_start_delay); } public boolean supportsDualTargets() { @@ -86,10 +83,18 @@ public abstract class QSTile<TState extends State> implements Listenable { return new QSTileView(context); } - public View createDetailView(Context context, ViewGroup root) { + public DetailAdapter getDetailAdapter() { return null; // optional } + public interface DetailAdapter { + int getTitle(); + Boolean getToggleState(); + View createDetailView(Context context, View convertView, ViewGroup parent); + Intent getSettingsIntent(); + void setToggleState(boolean state); + } + // safe to call from any thread public void setCallback(Callback callback) { @@ -120,8 +125,8 @@ public abstract class QSTile<TState extends State> implements Listenable { mHandler.obtainMessage(H.USER_SWITCH, newUserId).sendToTarget(); } - protected void postAfterFeedback(Runnable runnable) { - mHandler.postDelayed(runnable, mFeedbackStartDelay); + public void fireToggleStateChanged(boolean state) { + mHandler.obtainMessage(H.TOGGLE_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget(); } // call only on tile worker looper @@ -155,6 +160,12 @@ public abstract class QSTile<TState extends State> implements Listenable { } } + private void handleToggleStateChanged(boolean state) { + if (mCallback != null) { + mCallback.onToggleStateChanged(state); + } + } + protected void handleUserSwitch(int newUserId) { handleRefreshState(null); } @@ -166,6 +177,7 @@ public abstract class QSTile<TState extends State> implements Listenable { private static final int REFRESH_STATE = 4; private static final int SHOW_DETAIL = 5; private static final int USER_SWITCH = 6; + private static final int TOGGLE_STATE_CHANGED = 7; private H(Looper looper) { super(looper); @@ -193,6 +205,9 @@ public abstract class QSTile<TState extends State> implements Listenable { } else if (msg.what == USER_SWITCH) { name = "handleUserSwitch"; handleUserSwitch(msg.arg1); + } else if (msg.what == TOGGLE_STATE_CHANGED) { + name = "handleToggleStateChanged"; + handleToggleStateChanged(msg.arg1 != 0); } } catch (Throwable t) { final String error = "Error in " + name; @@ -205,6 +220,7 @@ public abstract class QSTile<TState extends State> implements Listenable { public interface Callback { void onStateChanged(State state); void onShowDetail(boolean show); + void onToggleStateChanged(boolean state); } public interface Host { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java index 07ea825..a308e84 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java @@ -58,7 +58,7 @@ public class BugreportTile extends QSTile<QSTile.State> { @Override protected void handleClick() { - postAfterFeedback(new Runnable() { + mHandler.post(new Runnable() { @Override public void run() { mHost.collapsePanels(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 6793051..502713f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -65,7 +65,7 @@ public class CastTile extends QSTile<QSTile.BooleanState> { @Override protected void handleClick() { - postAfterFeedback(new Runnable() { + mHandler.post(new Runnable() { public void run() { mHost.collapsePanels(); mUiHandler.post(mShowDialog); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java index c4bdb19..3bdea79 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java @@ -22,11 +22,9 @@ import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.util.Log; -import android.view.LayoutInflater; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.ViewGroup; -import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.qs.QSTile; @@ -47,45 +45,8 @@ public class NotificationsTile extends QSTile<NotificationsTile.NotificationsSta } @Override - public View createDetailView(Context context, ViewGroup root) { - final View v = LayoutInflater.from(context).inflate(R.layout.qs_detail, root, false); - final TextView title = (TextView) v.findViewById(android.R.id.title); - title.setText(R.string.quick_settings_notifications_label); - final View close = v.findViewById(android.R.id.button1); - close.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showDetail(false); - } - }); - final ViewGroup content = (ViewGroup) v.findViewById(android.R.id.content); - final VolumeComponent volumeComponent = mHost.getVolumeComponent(); - final VolumePanel vp = new VolumePanel(mContext, content, mZenController); - v.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { - @Override - public void onViewDetachedFromWindow(View v) { - volumeComponent.setVolumePanel(null); - } - - @Override - public void onViewAttachedToWindow(View v) { - vp.updateStates(); - volumeComponent.setVolumePanel(vp); - } - }); - vp.setZenModePanelCallback(new ZenModePanel.Callback() { - @Override - public void onMoreSettings() { - mHost.startSettingsActivity(ZenModePanel.ZEN_SETTINGS); - } - - @Override - public void onInteraction() { - // noop - } - }); - vp.postVolumeChanged(AudioManager.STREAM_RING, AudioManager.FLAG_SHOW_UI); - return v; + public DetailAdapter getDetailAdapter() { + return mDetailAdapter; } @Override @@ -164,4 +125,58 @@ public class NotificationsTile extends QSTile<NotificationsTile.NotificationsSta } } }; + + private final DetailAdapter mDetailAdapter = new DetailAdapter() { + + @Override + public int getTitle() { + return R.string.quick_settings_notifications_label; + } + + @Override + public Boolean getToggleState() { + return null; + } + + public void setToggleState(boolean state) { + // noop + } + + public Intent getSettingsIntent() { + return ZenModePanel.ZEN_SETTINGS; + } + + @Override + public View createDetailView(Context context, View convertView, ViewGroup parent) { + if (convertView != null) return convertView; + final VolumeComponent volumeComponent = mHost.getVolumeComponent(); + final VolumePanel vp = new VolumePanel(mContext, parent, mZenController); + final View v = vp.getContentView(); + v.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { + @Override + public void onViewDetachedFromWindow(View v) { + volumeComponent.setVolumePanel(null); + } + + @Override + public void onViewAttachedToWindow(View v) { + vp.updateStates(); + volumeComponent.setVolumePanel(vp); + } + }); + vp.setZenModePanelCallback(new ZenModePanel.Callback() { + @Override + public void onMoreSettings() { + mHost.startSettingsActivity(ZenModePanel.ZEN_SETTINGS); + } + + @Override + public void onInteraction() { + // noop + } + }); + vp.postVolumeChanged(AudioManager.STREAM_RING, AudioManager.FLAG_SHOW_UI); + return v; + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index a236497..84eee24 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -21,23 +21,34 @@ import android.content.Intent; import android.content.res.Resources; import android.provider.Settings; import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.qs.QSTileView; import com.android.systemui.qs.SignalTileView; import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.AccessPoint; import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; /** Quick settings tile: Wifi **/ public class WifiTile extends QSTile<QSTile.SignalState> { private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS); + private static final int MAX_ITEMS = 4; // TODO temporary visual restriction private final NetworkController mController; + private final WifiDetailAdapter mDetailAdapter; public WifiTile(Host host) { super(host); mController = host.getNetworkController(); + mDetailAdapter = new WifiDetailAdapter(); } @Override @@ -54,12 +65,20 @@ public class WifiTile extends QSTile<QSTile.SignalState> { public void setListening(boolean listening) { if (listening) { mController.addNetworkSignalChangedCallback(mCallback); + mController.addAccessPointCallback(mDetailAdapter); + mController.scanForAccessPoints(); } else { mController.removeNetworkSignalChangedCallback(mCallback); + mController.removeAccessPointCallback(mDetailAdapter); } } @Override + public DetailAdapter getDetailAdapter() { + return mDetailAdapter; + } + + @Override public QSTileView createTileView(Context context) { return new SignalTileView(context); } @@ -71,7 +90,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { @Override protected void handleSecondaryClick() { - mHost.startSettingsActivity(WIFI_SETTINGS); + showDetail(true); } @Override @@ -83,6 +102,11 @@ public class WifiTile extends QSTile<QSTile.SignalState> { boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null); boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null); + boolean enabledChanging = state.enabled != cb.enabled; + if (enabledChanging) { + mDetailAdapter.postUpdateItems(); + fireToggleStateChanged(cb.enabled); + } state.enabled = cb.enabled; state.connected = wifiConnected; state.activityIn = cb.enabled && cb.activityIn; @@ -169,4 +193,96 @@ public class WifiTile extends QSTile<QSTile.SignalState> { // noop } }; + + private final class WifiDetailAdapter implements DetailAdapter, + NetworkController.AccessPointCallback { + + private LinearLayout mItems; + private AccessPoint[] mAccessPoints; + + @Override + public int getTitle() { + return R.string.quick_settings_wifi_label; + } + + public Intent getSettingsIntent() { + return WIFI_SETTINGS; + } + + @Override + public Boolean getToggleState() { + return mState.enabled; + } + + @Override + public void setToggleState(boolean state) { + if (DEBUG) Log.d(TAG, "setToggleState " + state); + mController.setWifiEnabled(state); + showDetail(false); + } + + @Override + public View createDetailView(Context context, View convertView, ViewGroup parent) { + if (convertView != null) return convertView; + mItems = new LinearLayout(context); + mItems.setOrientation(LinearLayout.VERTICAL); + updateItems(); + return mItems; + } + + @Override + public void onAccessPointsChanged(final AccessPoint[] accessPoints) { + mUiHandler.post(new Runnable() { + @Override + public void run() { + mAccessPoints = accessPoints; + updateItems(); + } + }); + } + + public void postUpdateItems() { + mUiHandler.post(new Runnable() { + @Override + public void run() { + updateItems(); + } + }); + } + + private void updateItems() { + if (mItems == null) return; + mItems.removeAllViews(); + if (mAccessPoints == null || mAccessPoints.length == 0 || !mState.enabled) return; + for (int i = 0; i < mAccessPoints.length; i++) { + final AccessPoint ap = mAccessPoints[i]; + if (ap == null) continue; + final View item = LayoutInflater.from(mContext).inflate(R.layout.qs_detail_item, + mItems, false); + final ImageView iv = (ImageView) item.findViewById(android.R.id.icon); + iv.setImageResource(ap.iconId); + final TextView title = (TextView) item.findViewById(android.R.id.title); + title.setText(ap.ssid); + final TextView summary = (TextView) item.findViewById(android.R.id.summary); + if (ap.isConnected) { + item.setMinimumHeight(mContext.getResources() + .getDimensionPixelSize(R.dimen.qs_detail_item_height_connected)); + summary.setText(R.string.quick_settings_connected); + } else { + summary.setVisibility(View.GONE); + } + item.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (!ap.isConnected) { + mController.connect(ap); + } + showDetail(false); + } + }); + mItems.addView(item); + if (mItems.getChildCount() == MAX_ITEMS) break; + } + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/Console.java b/packages/SystemUI/src/com/android/systemui/recents/Console.java index 33e05dd..0cb74b9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Console.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Console.java @@ -43,7 +43,7 @@ public class Console { public static final String AnsiWhite = "\u001B[37m"; // Console enabled state - public static final boolean Enabled = false; + public static boolean Enabled = false; /** Logs a key */ public static void log(String key) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index 4fb1918..7a49a04 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -55,9 +55,9 @@ public class Constants { public static final String TimeRecentsStartupKey = "startup"; public static final String TimeRecentsLaunchKey = "launchTask"; public static final String TimeRecentsScreenshotTransitionKey = "screenshot"; - public static final boolean TimeRecentsStartup = true; - public static final boolean TimeRecentsLaunchTask = true; - public static final boolean TimeRecentsScreenshotTransition = true; + public static final boolean TimeRecentsStartup = false; + public static final boolean TimeRecentsLaunchTask = false; + public static final boolean TimeRecentsScreenshotTransition = false; public static final boolean RecentsComponent = false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 4c52b24..19a38c7 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -65,7 +65,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView AppWidgetHostView mSearchAppWidgetHostView; boolean mVisible; - boolean mTaskLaunched; // Runnables to finish the Recents activity FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable(true); @@ -198,18 +197,24 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false); mConfig.launchedWithNoRecentTasks = !root.hasTasks(); - // Show the scrim if we animate into Recents without window transitions - mScrimViews.prepareEnterRecentsAnimation(); - // Add the default no-recents layout if (mEmptyView == null) { mEmptyView = mEmptyViewStub.inflate(); } if (mConfig.launchedWithNoRecentTasks) { mEmptyView.setVisibility(View.VISIBLE); + mRecentsView.setSearchBarVisibility(View.GONE); } else { mEmptyView.setVisibility(View.GONE); + if (mRecentsView.hasSearchBar()) { + mRecentsView.setSearchBarVisibility(View.VISIBLE); + } else { + addSearchBarAppWidgetView(); + } } + + // Show the scrim if we animate into Recents without window transitions + mScrimViews.prepareEnterRecentsAnimation(); } /** Attempts to allocate and bind the search bar app widget */ @@ -352,20 +357,10 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mFullscreenOverlayStub = (ViewStub) findViewById(R.id.fullscreen_overlay_stub); mScrimViews = new SystemBarScrimViews(this, mConfig); - // Update the recent tasks - updateRecentsTasks(getIntent()); - - // Prepare the screenshot transition if necessary - if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { - mFullScreenOverlayView = (FullscreenTransitionOverlayView) mFullscreenOverlayStub.inflate(); - mFullScreenOverlayView.setCallbacks(this); - mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot()); - } - // Bind the search app widget when we first start up bindSearchBarAppWidget(); - // Add the search bar layout - addSearchBarAppWidgetView(); + // Update the recent tasks + updateRecentsTasks(getIntent()); // Update if we are getting a configuration change if (savedInstanceState != null) { @@ -390,6 +385,13 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } catch (InvocationTargetException e) { e.printStackTrace(); } + + // Prepare the screenshot transition if necessary + if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { + mFullScreenOverlayView = (FullscreenTransitionOverlayView) mFullscreenOverlayStub.inflate(); + mFullScreenOverlayView.setCallbacks(this); + mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot()); + } } void onConfigurationChange() { @@ -404,8 +406,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - // Reset the task launched flag if we encounter an onNewIntent() before onStop() - mTaskLaunched = false; if (Console.Enabled) { Console.logDivider(Constants.Log.App.SystemUIHandshake); @@ -426,9 +426,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot()); } - - // Don't attempt to rebind the search bar widget, but just add the search bar layout - addSearchBarAppWidgetView(); } @Override @@ -509,7 +506,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } mVisible = false; - mTaskLaunched = false; } @Override @@ -632,15 +628,13 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } @Override - public void onTaskLaunching() { - mTaskLaunched = true; - + public void onTaskViewClicked() { // Mark recents as no longer visible AlternateRecentsComponent.notifyVisibilityChanged(false); } @Override - public void onLastTaskRemoved() { + public void onAllTaskViewsDismissed() { mFinishLaunchHomeRunnable.run(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index d57f779..3041a3c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -149,6 +149,9 @@ public class RecentsConfiguration { // Debug mode debugModeEnabled = settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false); + if (debugModeEnabled) { + Console.Enabled = true; + } // Animations animationPxMovementPerSecond = diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java index b6895d1..49149a6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java @@ -69,7 +69,7 @@ class SystemUIMessageHandler extends Handler { // since that is done when we compute the animation itself in the Recents component // Create a dummy task stack & compute the rect for the thumbnail to animate to - TaskStack stack = new TaskStack(context); + TaskStack stack = new TaskStack(); TaskStackView tsv = new TaskStackView(context, stack); TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm(); Bundle replyData = new Bundle(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index 15f4a76..9d8d746 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -239,13 +239,14 @@ class TaskResourceLoader implements Runnable { } thumbnail.setHasAlpha(false); loadThumbnail = thumbnail; - mThumbnailCache.put(t.key, thumbnail); } else { loadThumbnail = mDefaultThumbnail; Console.logError(mContext, "Failed to load task top thumbnail for: " + t.key.baseIntent.getComponent().getPackageName()); } + // We put the default thumbnail in the cache anyways + mThumbnailCache.put(t.key, loadThumbnail); } } if (!mCancelled) { @@ -321,6 +322,17 @@ public class RecentsTaskLoader { " iconCache: " + iconCacheSize); } + // Create the default assets + Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + icon.eraseColor(0x00000000); + mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + mDefaultThumbnail.setHasAlpha(false); + mDefaultThumbnail.eraseColor(0xFFffffff); + mLoadingThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + mLoadingThumbnail.setHasAlpha(false); + mLoadingThumbnail.eraseColor(0xFFffffff); + mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon); + // Initialize the proxy, cache and loaders mSystemServicesProxy = new SystemServicesProxy(context); mPackageMonitor = new RecentsPackageMonitor(); @@ -330,14 +342,6 @@ public class RecentsTaskLoader { mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache, mDefaultThumbnail); - // Create the default assets - Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - icon.eraseColor(0x00000000); - mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - mDefaultThumbnail.eraseColor(0xFFffffff); - mLoadingThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - mLoadingThumbnail.eraseColor(0x00000000); - mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon); if (Console.Enabled) { Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|defaultBitmaps]", @@ -392,7 +396,7 @@ public class RecentsTaskLoader { RecentsConfiguration config = RecentsConfiguration.getInstance(); Resources res = context.getResources(); ArrayList<Task> tasksToForceLoad = new ArrayList<Task>(); - TaskStack stack = new TaskStack(context); + TaskStack stack = new TaskStack(); SpaceNode root = new SpaceNode(context); root.setStack(stack); @@ -416,7 +420,9 @@ public class RecentsTaskLoader { activityLabel = (av.getLabel() != null ? av.getLabel() : ssp.getActivityLabel(info)); activityIcon = (av.getIcon() != null) ? ssp.getBadgedIcon(new BitmapDrawable(res, av.getIcon()), t.userId) : null; - activityColor = av.getPrimaryColor(); + if (av.getPrimaryColor() != 0) { + activityColor = av.getPrimaryColor(); + } } else { activityLabel = ssp.getActivityLabel(info); } @@ -464,10 +470,10 @@ public class RecentsTaskLoader { task.thumbnail = ssp.getTaskThumbnail(task.key.id); if (task.thumbnail != null) { task.thumbnail.setHasAlpha(false); - mThumbnailCache.put(task.key, task.thumbnail); } else { task.thumbnail = mDefaultThumbnail; } + mThumbnailCache.put(task.key, task.thumbnail); } else { // Either the task has updated, or we haven't cached any information for the // task, so reload it diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index d2de185..24e01bd 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -139,13 +139,10 @@ public class TaskStack { public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks); } - Context mContext; - FilteredTaskList mTaskList = new FilteredTaskList(); TaskStackCallbacks mCb; - public TaskStack(Context context) { - mContext = context; + public TaskStack() { } /** Sets the callbacks for this task stack */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index b5b9cb5..f203d3e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -55,8 +55,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV /** The RecentsView callbacks */ public interface RecentsViewCallbacks { - public void onTaskLaunching(); - public void onLastTaskRemoved(); + public void onTaskViewClicked(); + public void onAllTaskViewsDismissed(); public void onExitToHomeAnimationTriggered(); } @@ -66,8 +66,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // The space partitioning root of this container SpaceNode mBSP; - // Whether there are any tasks - boolean mHasTasks; // Search bar view View mSearchBar; // Recents view callbacks @@ -100,15 +98,21 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV public void setBSP(SpaceNode n) { mBSP = n; + // Remove all TaskStackViews (but leave the search bar) + int childCount = getChildCount(); + for (int i = childCount - 1; i >= 0; i--) { + View v = getChildAt(i); + if (v != mSearchBar) { + removeViewAt(i); + } + } + // Create and add all the stacks for this partition of space. - mHasTasks = false; - removeAllViews(); ArrayList<TaskStack> stacks = mBSP.getStacks(); for (TaskStack stack : stacks) { TaskStackView stackView = new TaskStackView(getContext(), stack); stackView.setCallbacks(this); addView(stackView); - mHasTasks |= (stack.getTaskCount() > 0); } // Enable debug mode drawing @@ -127,7 +131,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child instanceof TaskStackView) { + if (child != mSearchBar) { TaskStackView stackView = (TaskStackView) child; TaskStack stack = stackView.mStack; // Iterate the stack views and try and find the focused task @@ -140,7 +144,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]", "Found focused Task"); } - onTaskLaunched(stackView, tv, stack, task); + onTaskViewClicked(stackView, tv, stack, task); return true; } } @@ -159,7 +163,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child instanceof TaskStackView) { + if (child != mSearchBar) { TaskStackView stackView = (TaskStackView) child; TaskStack stack = stackView.mStack; ArrayList<Task> tasks = stack.getTasks(); @@ -176,7 +180,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV tv = stv; } } - onTaskLaunched(stackView, tv, stack, task); + onTaskViewClicked(stackView, tv, stack, task); return true; } } @@ -193,7 +197,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child instanceof TaskStackView) { + if (child != mSearchBar) { TaskStackView stackView = (TaskStackView) child; stackView.startEnterRecentsAnimation(ctx); } @@ -213,7 +217,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child instanceof TaskStackView) { + if (child != mSearchBar) { TaskStackView stackView = (TaskStackView) child; stackView.startExitToHomeAnimation(ctx); } @@ -238,7 +242,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // Add the new search bar if (searchBar != null) { mSearchBar = searchBar; - mSearchBar.setVisibility(mHasTasks ? View.VISIBLE : View.GONE); addView(mSearchBar); if (Console.Enabled) { @@ -250,6 +253,18 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } } + /** Returns whether there is currently a search bar */ + public boolean hasSearchBar() { + return mSearchBar != null; + } + + /** Sets the visibility of the search bar */ + public void setSearchBarVisibility(int visibility) { + if (mSearchBar != null) { + mSearchBar.setVisibility(visibility); + } + } + /** * This is called with the full size of the window since we are handling our own insets. */ @@ -289,7 +304,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child instanceof TaskStackView && child.getVisibility() != GONE) { + if (child != mSearchBar && child.getVisibility() != GONE) { child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode), MeasureSpec.makeMeasureSpec(childHeight, heightMode)); } @@ -333,7 +348,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child instanceof TaskStackView && child.getVisibility() != GONE) { + if (child != mSearchBar && child.getVisibility() != GONE) { TaskStackView tsv = (TaskStackView) child; child.layout(left, top, left + tsv.getMeasuredWidth(), top + tsv.getMeasuredHeight()); } @@ -370,7 +385,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child instanceof TaskStackView) { + if (child != mSearchBar) { stackView = (TaskStackView) child; stackView.onUserInteraction(); } @@ -384,7 +399,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child instanceof TaskStackView) { + if (child != mSearchBar) { stackView = (TaskStackView) child; break; } @@ -415,11 +430,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV /**** TaskStackView.TaskStackCallbacks Implementation ****/ @Override - public void onTaskLaunched(final TaskStackView stackView, final TaskView tv, - final TaskStack stack, final Task task) { + public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv, + final TaskStack stack, final Task task) { // Notify any callbacks of the launching of a new task if (mCb != null) { - mCb.onTaskLaunching(); + mCb.onTaskViewClicked(); } // Upfront the processing of the thumbnail @@ -491,7 +506,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } // And clean up the old task - onTaskRemoved(task); + onTaskViewDismissed(task); } if (Console.Enabled) { @@ -515,7 +530,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } @Override - public void onTaskAppInfoLaunched(Task t) { + public void onTaskViewAppInfoClicked(Task t) { // Create a new task stack with the application info details activity Intent baseIntent = t.key.baseIntent; Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, @@ -526,9 +541,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } @Override - public void onTaskRemoved(Task t) { + public void onTaskViewDismissed(Task t) { // Remove any stored data from the loader. We currently don't bother notifying the views - // that the data has been unloaded because at the point we call onTaskRemoved(), the views + // that the data has been unloaded because at the point we call onTaskViewDismissed(), the views // either don't need to be updated, or have already been removed. RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); loader.deleteTaskData(t, false); @@ -542,8 +557,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } @Override - public void onLastTaskRemoved() { - mCb.onLastTaskRemoved(); + public void onAllTaskViewsDismissed() { + mCb.onAllTaskViewsDismissed(); } @Override @@ -582,7 +597,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child instanceof TaskStackView) { + if (child != mSearchBar) { TaskStackView stackView = (TaskStackView) child; stackView.onComponentRemoved(cns); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java index 2c0dea3..9c60603 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java @@ -92,7 +92,6 @@ class TaskBarView extends FrameLayout { public boolean onTouchEvent(MotionEvent event) { // We ignore taps on the task bar except on the filter and dismiss buttons if (!Constants.DebugFlags.App.EnableTaskBarTouchEvents) return true; - if (mConfig.debugModeEnabled) return true; return super.onTouchEvent(event); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 2c05daa..dd47fdd 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -53,10 +53,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** The TaskView callbacks */ interface TaskStackViewCallbacks { - public void onTaskLaunched(TaskStackView stackView, TaskView tv, TaskStack stack, Task t); - public void onTaskAppInfoLaunched(Task t); - public void onTaskRemoved(Task t); - public void onLastTaskRemoved(); + public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t); + public void onTaskViewAppInfoClicked(Task t); + public void onTaskViewDismissed(Task t); + public void onAllTaskViewsDismissed(); public void onTaskStackFilterTriggered(); public void onTaskStackUnfilterTriggered(); } @@ -69,7 +69,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskStackViewTouchHandler mTouchHandler; TaskStackViewCallbacks mCb; ViewPool<TaskView, Task> mViewPool; - ArrayList<TaskViewTransform> mTaskTransforms = new ArrayList<TaskViewTransform>(); + ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>(); DozeTrigger mUIDozeTrigger; // The virtual stack scroll that we use for the card layout @@ -205,12 +205,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int stackScroll, int[] visibleRangeOut, boolean boundTranslationsToRect) { - // XXX: Optimization: Use binary search to find the visible range + // XXX: We should be intelligent about where to look for the visible stack range using the + // current stack scroll. int taskTransformCount = taskTransforms.size(); int taskCount = tasks.size(); - int firstVisibleIndex = -1; - int lastVisibleIndex = -1; + int frontMostVisibleIndex = -1; + int backMostVisibleIndex = -1; // We can reuse the task transforms where possible to reduce object allocation if (taskTransformCount < taskCount) { @@ -224,14 +225,24 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // Update the stack transforms - for (int i = 0; i < taskCount; i++) { + for (int i = taskCount - 1; i >= 0; i--) { TaskViewTransform transform = mStackAlgorithm.getStackTransform(i, stackScroll, taskTransforms.get(i)); if (transform.visible) { - if (firstVisibleIndex < 0) { - firstVisibleIndex = i; + if (frontMostVisibleIndex < 0) { + frontMostVisibleIndex = i; + } + backMostVisibleIndex = i; + } else { + if (backMostVisibleIndex != -1) { + // We've reached the end of the visible range, so going down the rest of the + // stack, we can just reset the transforms accordingly + while (i >= 0) { + taskTransforms.get(i).reset(); + i--; + } + break; } - lastVisibleIndex = i; } if (boundTranslationsToRect) { @@ -240,8 +251,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } if (visibleRangeOut != null) { - visibleRangeOut[0] = firstVisibleIndex; - visibleRangeOut[1] = lastVisibleIndex; + visibleRangeOut[0] = frontMostVisibleIndex; + visibleRangeOut[1] = backMostVisibleIndex; } } @@ -275,13 +286,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int[] visibleRange = mTmpVisibleRange; int stackScroll = getStackScroll(); ArrayList<Task> tasks = mStack.getTasks(); - updateStackTransforms(mTaskTransforms, tasks, stackScroll, visibleRange, false); + updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false); // Update the visible state of all the tasks int taskCount = tasks.size(); for (int i = 0; i < taskCount; i++) { Task task = tasks.get(i); - TaskViewTransform transform = mTaskTransforms.get(i); + TaskViewTransform transform = mCurrentTaskTransforms.get(i); TaskView tv = getChildViewForTask(task); if (transform.visible) { @@ -290,8 +301,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // When we are picking up a new view from the view pool, prepare it for any // following animation by putting it in a reasonable place if (mStackViewsAnimationDuration > 0 && i != 0) { - int fromIndex = (transform.t < 0) ? (visibleRange[0] - 1) : - (visibleRange[1] + 1); + int fromIndex = (transform.t < 0) ? + Math.max(0, (visibleRange[1] - 1)) : + Math.min(taskCount - 1, (visibleRange[0] + 1)); tv.updateViewPropertiesToTaskTransform( mStackAlgorithm.getStackTransform(fromIndex, stackScroll), 0); } @@ -310,10 +322,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskView tv = (TaskView) getChildAt(i); Task task = tv.getTask(); int taskIndex = mStack.indexOfTask(task); - if (taskIndex < 0 || !mTaskTransforms.get(taskIndex).visible) { + if (taskIndex < 0 || !mCurrentTaskTransforms.get(taskIndex).visible) { mViewPool.returnViewToPool(tv); } else { - tv.updateViewPropertiesToTaskTransform(mTaskTransforms.get(taskIndex), + tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex), mStackViewsAnimationDuration); } } @@ -769,9 +781,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal if (mConfig.launchedWithAltTab) { // When alt-tabbing, we focus the next previous task focusTask(Math.max(0, mStack.getTaskCount() - 2), false); - } else { - // Normally we just focus the front task - focusTask(Math.max(0, mStack.getTaskCount() - 1), false); } } } @@ -864,7 +873,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // Notify the callback that we've removed the task and it can clean up after it - mCb.onTaskRemoved(t); + mCb.onTaskViewDismissed(t); // Update the min/max scroll and animate other task views into their new positions updateMinMaxScroll(true); @@ -880,7 +889,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal shouldFinishActivity = (mStack.getTaskCount() == 0); } if (shouldFinishActivity) { - mCb.onLastTaskRemoved(); + mCb.onAllTaskViewsDismissed(); } } } @@ -1033,7 +1042,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /**** TaskViewCallbacks Implementation ****/ @Override - public void onTaskIconClicked(TaskView tv) { + public void onTaskViewAppIconClicked(TaskView tv) { if (Console.Enabled) { Console.log(Constants.Log.UI.ClickEvents, "[TaskStack|Clicked|Icon]", tv.getTask() + " is currently filtered: " + mStack.hasFilteredTasks(), @@ -1049,19 +1058,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } @Override - public void onTaskAppInfoClicked(TaskView tv) { + public void onTaskViewAppInfoClicked(TaskView tv) { if (mCb != null) { - mCb.onTaskAppInfoLaunched(tv.getTask()); + mCb.onTaskViewAppInfoClicked(tv.getTask()); } } @Override - public void onTaskFocused(TaskView tv) { - // Do nothing - } - - @Override - public void onTaskDismissed(TaskView tv) { + public void onTaskViewDismissed(TaskView tv) { Task task = tv.getTask(); // Remove the task from the view mStack.removeTask(task); @@ -1082,7 +1086,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mUIDozeTrigger.stopDozing(); if (mCb != null) { - mCb.onTaskLaunched(this, tv, mStack, task); + mCb.onTaskViewClicked(this, tv, mStack, task); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 51f994e..304d45c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -400,16 +400,11 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { // Re-enable clipping with the stack (we will reuse this view) tv.setClipViewInStack(true); // Remove the task view from the stack - mSv.onTaskDismissed(tv); + mSv.onTaskViewDismissed(tv); } @Override public void onSnapBackCompleted(View v) { - onDragCancelled(v); - } - - @Override - public void onDragCancelled(View v) { TaskView tv = (TaskView) v; // Disable HW layers on that task if (mSv.mHwLayersTrigger.getCount() == 0) { @@ -420,4 +415,9 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { // Re-enable touch events from this task view mSv.setTouchOnTaskView(tv, true); } + + @Override + public void onDragCancelled(View v) { + // Do nothing + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index c6bacbd..6b06945 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -47,10 +47,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On View.OnLongClickListener { /** The TaskView callbacks */ interface TaskViewCallbacks { - public void onTaskIconClicked(TaskView tv); - public void onTaskAppInfoClicked(TaskView tv); - public void onTaskFocused(TaskView tv); - public void onTaskDismissed(TaskView tv); + public void onTaskViewAppIconClicked(TaskView tv); + public void onTaskViewAppInfoClicked(TaskView tv); + public void onTaskViewDismissed(TaskView tv); } RecentsConfiguration mConfig; @@ -63,8 +62,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On boolean mTaskDataLoaded; boolean mIsFocused; boolean mClipViewInStack; - Point mLastTouchDown = new Point(); - Path mRoundedRectClipPath = new Path(); Rect mTmpRect = new Rect(); Paint mLayerPaint = new Paint(); @@ -110,6 +107,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On super(context, attrs, defStyleAttr, defStyleRes); mConfig = RecentsConfiguration.getInstance(); setWillNotDraw(false); + setClipToOutline(true); setDim(getDim()); } @@ -133,30 +131,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - // Update the rounded rect clip path - float radius = mConfig.taskViewRoundedCornerRadiusPx; - mRoundedRectClipPath.reset(); - mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()), - radius, radius, Path.Direction.CW); - // Update the outline Outline o = new Outline(); o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() - - mConfig.taskViewShadowOutlineBottomInsetPx, radius); + mConfig.taskViewShadowOutlineBottomInsetPx, mConfig.taskViewRoundedCornerRadiusPx); setOutline(o); } - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - mLastTouchDown.set((int) ev.getX(), (int) ev.getY()); - break; - } - return super.onInterceptTouchEvent(ev); - } - /** Set callback */ void setCallbacks(TaskViewCallbacks cb) { mCb = cb; @@ -291,7 +272,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On } else if (mConfig.launchedFromHome) { // Move the task view off screen (below) so we can animate it in setTranslationY(offscreenY); - setTranslationZ(0); + if (Constants.DebugFlags.App.EnableShadows) { + setTranslationZ(0); + } setScaleX(1f); setScaleY(1f); } @@ -365,11 +348,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1); int delay = mConfig.taskBarEnterAnimDelay + frontIndex * mConfig.taskViewEnterFromHomeDelay; + if (Constants.DebugFlags.App.EnableShadows) { + animate().translationZ(transform.translationZ); + } animate() .scaleX(transform.scale) .scaleY(transform.scale) .translationY(transform.translationY) - .translationZ(transform.translationZ) .setStartDelay(delay) .setUpdateListener(null) .setInterpolator(mConfig.quintOutInterpolator) @@ -385,6 +370,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On }) .start(); ctx.postAnimationTrigger.increment(); + } else { + // Otherwise, just enable the thumbnail clip + mEnableThumbnailClip.run(); } } @@ -527,11 +515,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On @Override public void draw(Canvas canvas) { - int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); - // Apply the rounded rect clip path on the whole view - canvas.clipPath(mRoundedRectClipPath); super.draw(canvas); - canvas.restoreToCount(restoreCount); // Apply the dim if necessary if (mDim > 0) { @@ -553,7 +537,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On requestFocus(); setFocusableInTouchMode(false); invalidate(); - mCb.onTaskFocused(this); } /** @@ -626,13 +609,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On @Override public void run() { if (v == mBarView.mApplicationIcon) { - mCb.onTaskIconClicked(tv); + mCb.onTaskViewAppIconClicked(tv); } else if (v == mBarView.mDismissButton) { // Animate out the view and call the callback startDeleteTaskAnimation(new Runnable() { @Override public void run() { - mCb.onTaskDismissed(tv); + mCb.onTaskViewDismissed(tv); } }); } @@ -643,7 +626,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On @Override public boolean onLongClick(View v) { if (v == mBarView.mApplicationIcon) { - mCb.onTaskAppInfoClicked(this); + mCb.onTaskViewAppInfoClicked(this); return true; } return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 1550217..c02a598 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -94,11 +94,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private boolean mDark; private final Paint mDarkPaint = createDarkPaint(); - private int mBgResId = com.android.internal.R.drawable.notification_material_bg; - private int mDimmedBgResId = com.android.internal.R.drawable.notification_material_bg_dim; - private int mBgTint = 0; - private int mDimmedBgTint = 0; private final int mRoundedRectCornerRadius; /** @@ -133,6 +129,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private ValueAnimator mAppearAnimator; private float mAppearAnimationFraction = -1.0f; private float mAppearAnimationTranslation; + private boolean mShowingLegacyBackground; + private final int mLegacyColor; + private final int mNormalColor; + private final int mLowPriorityColor; + private boolean mIsBelowSpeedBump; public ActivatableNotificationView(Context context, AttributeSet attrs) { super(context, attrs); @@ -148,7 +149,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView setClipToPadding(false); mAppearAnimationFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP); mRoundedRectCornerRadius = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.notification_material_rounded_rect_radius); + R.dimen.notification_material_rounded_rect_radius); + mLegacyColor = getResources().getColor(R.color.notification_legacy_background_color); + mNormalColor = getResources().getColor(R.color.notification_material_background_color); + mLowPriorityColor = getResources().getColor( + R.color.notification_material_background_low_priority_color); } @Override @@ -156,9 +161,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView super.onFinishInflate(); mBackgroundNormal = (NotificationBackgroundView) findViewById(R.id.backgroundNormal); mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed); + mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg); + mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim); updateBackground(); - updateBackgroundResources(); + updateBackgroundTint(); mScrimView = (NotificationScrimView) findViewById(R.id.scrim_view); + setScrimAmount(0); } private final Runnable mTapTimeoutRunnable = new Runnable() { @@ -177,6 +185,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } } + @Override + public void drawableHotspotChanged(float x, float y) { + if (!mDimmed){ + mBackgroundNormal.drawableHotspotChanged(x, y); + } + } + private boolean handleTouchEventDimmed(MotionEvent event) { int action = event.getActionMasked(); switch (action) { @@ -327,22 +342,36 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView return p; } + public void setShowingLegacyBackground(boolean showing) { + mShowingLegacyBackground = showing; + updateBackgroundTint(); + } + + @Override + public void setBelowSpeedBump(boolean below) { + super.setBelowSpeedBump(below); + if (below != mIsBelowSpeedBump) { + mIsBelowSpeedBump = below; + updateBackgroundTint(); + } + } + /** - * Sets the resource id for the background of this notification. - * - * @param bgResId The background resource to use in normal state. - * @param dimmedBgResId The background resource to use in dimmed state. + * Sets the tint color of the background */ - public void setBackgroundResourceIds(int bgResId, int bgTint, int dimmedBgResId, int dimmedTint) { - mBgResId = bgResId; - mBgTint = bgTint; - mDimmedBgResId = dimmedBgResId; - mDimmedBgTint = dimmedTint; - updateBackgroundResources(); + public void setTintColor(int color) { + mBgTint = color; + updateBackgroundTint(); } - public void setBackgroundResourceIds(int bgResId, int dimmedBgResId) { - setBackgroundResourceIds(bgResId, 0, dimmedBgResId, 0); + private void updateBackgroundTint() { + int color = getBackgroundColor(); + if (color == mNormalColor) { + // We don't need to tint a normal notification + color = 0; + } + mBackgroundDimmed.setTint(color); + mBackgroundNormal.setTint(color); } private void fadeBackground() { @@ -396,11 +425,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } } - private void updateBackgroundResources() { - mBackgroundDimmed.setCustomBackground(mDimmedBgResId, mDimmedBgTint); - mBackgroundNormal.setCustomBackground(mBgResId, mBgTint); - } - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); @@ -575,8 +599,15 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } private int getBackgroundColor() { - // TODO: get real color - return 0xfffafafa; + if (mBgTint != 0) { + return mBgTint; + } else if (mShowingLegacyBackground) { + return mLegacyColor; + } else if (mIsBelowSpeedBump) { + return mLowPriorityColor; + } else { + return mNormalColor; + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 3690701..df3e25d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -562,20 +562,14 @@ public abstract class BaseStatusBar extends SystemUI implements if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) { // Using custom RemoteViews if (version >= Build.VERSION_CODES.GINGERBREAD && version < Build.VERSION_CODES.L) { - entry.row.setBackgroundResourceIds( - com.android.internal.R.drawable.notification_bg, - com.android.internal.R.drawable.notification_bg_dim); + entry.row.setShowingLegacyBackground(true); entry.legacy = true; } } else { // Using platform templates final int color = sbn.getNotification().color; if (isMediaNotification(entry)) { - entry.row.setBackgroundResourceIds( - com.android.internal.R.drawable.notification_material_bg, - color, - com.android.internal.R.drawable.notification_material_bg_dim, - color); + entry.row.setTintColor(color); } } } @@ -903,15 +897,15 @@ public abstract class BaseStatusBar extends SystemUI implements protected void onShowSearchPanel() { } - public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) { + private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) { return inflateViews(entry, parent, false); } - public boolean inflateViewsForHeadsUp(NotificationData.Entry entry, ViewGroup parent) { + protected boolean inflateViewsForHeadsUp(NotificationData.Entry entry, ViewGroup parent) { return inflateViews(entry, parent, true); } - public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) { + private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) { int maxHeight = mRowMaxHeight; StatusBarNotification sbn = entry.notification; RemoteViews contentView = sbn.getNotification().contentView; @@ -933,11 +927,30 @@ public abstract class BaseStatusBar extends SystemUI implements Notification publicNotification = sbn.getNotification().publicVersion; - // create the row view - LayoutInflater inflater = (LayoutInflater)mContext.getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - ExpandableNotificationRow row = (ExpandableNotificationRow) inflater.inflate( - R.layout.status_bar_notification_row, parent, false); + ExpandableNotificationRow row; + + // Stash away previous user expansion state so we can restore it at + // the end. + boolean hasUserChangedExpansion = false; + boolean userExpanded = false; + boolean userLocked = false; + + if (entry.row != null) { + row = entry.row; + hasUserChangedExpansion = row.hasUserChangedExpansion(); + userExpanded = row.isUserExpanded(); + userLocked = row.isUserLocked(); + row.reset(); + if (hasUserChangedExpansion) { + row.setUserExpanded(userExpanded); + } + } else { + // create the row view + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row, + parent, false); + } // for blaming (see SwipeHelper.setLongPressListener) row.setTag(sbn.getPackageName()); @@ -1077,13 +1090,20 @@ public abstract class BaseStatusBar extends SystemUI implements entry.row = row; entry.row.setHeightRange(mRowMinHeight, maxHeight); entry.row.setOnActivatedListener(this); - entry.row.setIsBelowSpeedBump(isBelowSpeedBump(entry.notification)); entry.expanded = contentViewLocal; entry.expandedPublic = publicViewLocal; entry.setBigContentView(bigContentViewLocal); applyLegacyRowBackground(sbn, entry); + // Restore previous flags. + if (hasUserChangedExpansion) { + // Note: setUserExpanded() conveniently ignores calls with + // userExpanded=true if !isExpandable(). + row.setUserExpanded(userExpanded); + } + row.setUserLocked(userLocked); + return true; } @@ -1324,12 +1344,13 @@ public abstract class BaseStatusBar extends SystemUI implements RankingMap ranking); protected abstract void updateNotificationRanking(RankingMap ranking); public abstract void removeNotification(String key, RankingMap ranking); + public void updateNotification(StatusBarNotification notification, RankingMap ranking) { if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); final String key = notification.getKey(); boolean wasHeadsUp = isHeadsUp(key); - NotificationData.Entry oldEntry; + Entry oldEntry; if (wasHeadsUp) { oldEntry = mHeadsUpNotificationView.getEntry(); } else { @@ -1370,8 +1391,7 @@ public abstract class BaseStatusBar extends SystemUI implements + " publicView=" + publicContentView); } - // Can we just reapply the RemoteViews in place? If when didn't change, the order - // didn't change. + // Can we just reapply the RemoteViews in place? // 1U is never null boolean contentsUnchanged = oldEntry.expanded != null @@ -1484,15 +1504,9 @@ public abstract class BaseStatusBar extends SystemUI implements addNotification(notification, ranking); //this will pop the headsup } else { if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key); - removeNotificationViews(key, ranking); - addNotificationViews(notification, ranking); - final NotificationData.Entry newEntry = mNotificationData.findByKey(key); - final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion(); - if (userChangedExpansion) { - boolean userExpanded = oldEntry.row.isUserExpanded(); - newEntry.row.setUserExpanded(userExpanded); - newEntry.row.notifyHeightChanged(); - } + oldEntry.notification = notification; + inflateViews(oldEntry, mStackScroller, wasHeadsUp); + updateNotifications(); } } } @@ -1554,19 +1568,9 @@ public abstract class BaseStatusBar extends SystemUI implements } else { entry.row.setOnClickListener(null); } - boolean wasBelow = entry.row.isBelowSpeedBump(); - boolean nowBelow = isBelowSpeedBump(notification); - if (wasBelow != nowBelow) { - entry.row.setIsBelowSpeedBump(nowBelow); - } entry.row.notifyContentUpdated(); } - private boolean isBelowSpeedBump(StatusBarNotification notification) { - return notification.getNotification().priority == - Notification.PRIORITY_MIN; - } - protected void notifyHeadsUpScreenOn(boolean screenOn) { if (!screenOn) { scheduleHeadsUpEscalation(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 5981898..280bade 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -52,13 +52,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private NotificationContentView mPublicLayout; private NotificationContentView mPrivateLayout; private int mMaxExpandHeight; - private boolean mIsBelowSpeedBump; private View mVetoButton; public ExpandableNotificationRow(Context context, AttributeSet attrs) { super(context, attrs); } + /** + * Resets this view so it can be re-used for an updated notification. + */ + public void reset() { + mRowMinHeight = 0; + mRowMaxHeight = 0; + mExpandable = false; + mHasUserChangedExpansion = false; + mUserLocked = false; + mShowingPublic = false; + mIsSystemExpanded = false; + mExpansionDisabled = false; + mPublicLayout.reset(); + mPrivateLayout.reset(); + mMaxExpandHeight = 0; + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -111,6 +127,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { * @param userExpanded whether the user wants this notification to be expanded */ public void setUserExpanded(boolean userExpanded) { + if (userExpanded && !mExpandable) return; mHasUserChangedExpansion = true; mUserExpanded = userExpanded; } @@ -245,14 +262,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mPublicLayout.setClipTopAmount(clipTopAmount); } - public boolean isBelowSpeedBump() { - return mIsBelowSpeedBump; - } - - public void setIsBelowSpeedBump(boolean isBelow) { - this.mIsBelowSpeedBump = isBelow; - } - public void notifyContentUpdated() { mPublicLayout.notifyContentUpdated(); mPrivateLayout.notifyContentUpdated(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 0a5d138..b71cd77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -234,10 +234,18 @@ public abstract class ExpandableView extends FrameLayout { public abstract void setScrimAmount(float scrimAmount); + public void setBelowSpeedBump(boolean below) { + } + /** * A listener notifying when {@link #getActualHeight} changes. */ public interface OnHeightChangedListener { + + /** + * @param view the view for which the height changed, or {@code null} if just the top + * padding or the padding between the elements changed + */ void onHeightChanged(ExpandableView view); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java index 1c2ca91..ad274b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java @@ -17,11 +17,15 @@ package com.android.systemui.statusbar; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; +import com.android.systemui.R; /** * A view that can be used for both the dimmed and normal background of an notification. @@ -31,9 +35,15 @@ public class NotificationBackgroundView extends View { private Drawable mBackground; private int mClipTopAmount; private int mActualHeight; + private final int mTintedRippleColor; + private final int mNormalRippleColor; public NotificationBackgroundView(Context context, AttributeSet attrs) { super(context, attrs); + mTintedRippleColor = context.getResources().getColor( + R.color.notification_ripple_tinted_color); + mNormalRippleColor = context.getResources().getColor( + R.color.notification_ripple_untinted_color); } @Override @@ -64,6 +74,13 @@ public class NotificationBackgroundView extends View { } } + @Override + public void drawableHotspotChanged(float x, float y) { + if (mBackground != null) { + mBackground.setHotspot(x, y); + } + } + /** * Sets a background drawable. As we need to change our bounds independently of layout, we need * the notion of a background independently of the regular View background.. @@ -80,12 +97,25 @@ public class NotificationBackgroundView extends View { invalidate(); } - public void setCustomBackground(int drawableResId, int tintColor) { - final Drawable d = getResources().getDrawable(drawableResId); + public void setCustomBackground(int drawableResId) { + final Drawable d = mContext.getDrawable(drawableResId); + setCustomBackground(d); + } + + public void setTint(int tintColor) { + int rippleColor; if (tintColor != 0) { - d.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP); + mBackground.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP); + rippleColor = mTintedRippleColor; + } else { + mBackground.clearColorFilter(); + rippleColor = mNormalRippleColor; } - setCustomBackground(d); + if (mBackground instanceof RippleDrawable) { + RippleDrawable ripple = (RippleDrawable) mBackground; + ripple.setColor(ColorStateList.valueOf(rippleColor)); + } + invalidate(); } public void setActualHeight(int actualHeight) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index f919501..f3aba0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -51,13 +51,12 @@ public class NotificationContentView extends FrameLayout { private boolean mContractedVisible = true; - private Paint mFadePaint = new Paint(); + private final Paint mFadePaint = new Paint(); public NotificationContentView(Context context, AttributeSet attrs) { super(context, attrs); - mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height); - mActualHeight = mSmallHeight; mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD)); + reset(); } @Override @@ -66,6 +65,15 @@ public class NotificationContentView extends FrameLayout { updateClipping(); } + public void reset() { + removeAllViews(); + mContractedChild = null; + mExpandedChild = null; + mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height); + mActualHeight = mSmallHeight; + mContractedVisible = true; + } + public void setContractedChild(View child) { if (mContractedChild != null) { removeView(mContractedChild); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index b2e79c9..be2c515 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -66,7 +66,7 @@ public class NotificationPanelView extends PanelView implements private View mKeyguardStatusView; private ObservableScrollView mScrollView; private TextView mClockView; - + private View mReserveNotificationSpace; private MirrorView mSystemIconsCopy; private NotificationStackScrollLayout mNotificationStackScroller; @@ -89,8 +89,10 @@ public class NotificationPanelView extends PanelView implements */ private boolean mIntercepting; private boolean mQsExpanded; + private boolean mQsExpandedWhenExpandingStarted; private boolean mQsFullyExpanded; private boolean mKeyguardShowing; + private int mStatusBarState; private float mInitialHeightOnTouch; private float mInitialTouchX; private float mInitialTouchY; @@ -112,7 +114,6 @@ public class NotificationPanelView extends PanelView implements private Interpolator mFastOutSlowInInterpolator; private Interpolator mFastOutLinearInterpolator; - private Interpolator mLinearOutSlowInInterpolator; private ObjectAnimator mClockAnimator; private int mClockAnimationTarget = -1; private int mTopPaddingAdjustment; @@ -125,7 +126,13 @@ public class NotificationPanelView extends PanelView implements private boolean mBlockTouches; private ArrayList<View> mSwipeTranslationViews = new ArrayList<>(); private int mNotificationScrimWaitDistance; - private boolean mOnNotificationsOnDown; + + /** + * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still + * need to take this into account in our panel height calculation. + */ + private int mScrollYOverride = -1; + private boolean mQsAnimatorExpand; public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -148,14 +155,14 @@ public class NotificationPanelView extends PanelView implements mClockView = (TextView) findViewById(R.id.clock_view); mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view); mScrollView.setListener(this); + mReserveNotificationSpace = findViewById(R.id.reserve_notification_space); mNotificationStackScroller = (NotificationStackScrollLayout) findViewById(R.id.notification_stack_scroller); mNotificationStackScroller.setOnHeightChangedListener(this); mNotificationStackScroller.setOverscrollTopChangedListener(this); + mNotificationStackScroller.setScrollView(mScrollView); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.fast_out_slow_in); - mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), - android.R.interpolator.linear_out_slow_in); mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.fast_out_linear_in); mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area); @@ -283,13 +290,6 @@ public class NotificationPanelView extends PanelView implements requestLayout(); } - /** - * @return Whether Quick Settings are currently expanded. - */ - public boolean isQsExpanded() { - return mQsExpanded; - } - public void setQsExpansionEnabled(boolean qsExpansionEnabled) { mQsExpansionEnabled = qsExpansionEnabled; } @@ -309,6 +309,18 @@ public class NotificationPanelView extends PanelView implements setQsExpansion(mQsMinExpansionHeight); } + public void animateCloseQs() { + if (mQsExpansionAnimator != null) { + if (!mQsAnimatorExpand) { + return; + } + float height = mQsExpansionHeight; + mQsExpansionAnimator.cancel(); + setQsExpansion(height); + } + flingSettings(0 /* vel */, false); + } + public void openQs() { cancelAnimation(); if (mQsExpansionEnabled) { @@ -356,10 +368,16 @@ public class NotificationPanelView extends PanelView implements mInitialTouchX = x; initVelocityTracker(); trackMovement(event); - mOnNotificationsOnDown = isOnNotifications(x, y); if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) { getParent().requestDisallowInterceptTouchEvent(true); } + if (mQsExpansionAnimator != null) { + onQsExpansionStarted(); + mInitialHeightOnTouch = mQsExpansionHeight; + mQsTracking = true; + mIntercepting = false; + mNotificationStackScroller.removeLongPressCallback(); + } break; case MotionEvent.ACTION_POINTER_UP: final int upPointer = event.getPointerId(event.getActionIndex()); @@ -404,8 +422,6 @@ public class NotificationPanelView extends PanelView implements if (mQsTracking) { flingQsWithCurrentVelocity(); mQsTracking = false; - } else if (mQsFullyExpanded && mOnNotificationsOnDown) { - flingSettings(0 /* vel */, false /* expand */); } mIntercepting = false; break; @@ -413,10 +429,6 @@ public class NotificationPanelView extends PanelView implements return !mQsExpanded && super.onInterceptTouchEvent(event); } - private boolean isOnNotifications(float x, float y) { - return mNotificationStackScroller.getChildAtPosition(x, y) != null; - } - @Override public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { @@ -443,8 +455,9 @@ public class NotificationPanelView extends PanelView implements private float getQsExpansionFraction() { return (mQsExpansionHeight - mQsMinExpansionHeight) - / (mQsMaxExpansionHeight - mQsMinExpansionHeight); + / (getTempQsMaxExpansion() - mQsMinExpansionHeight); } + @Override public boolean onTouchEvent(MotionEvent event) { if (mBlockTouches) { @@ -550,6 +563,8 @@ public class NotificationPanelView extends PanelView implements if ((fraction != 0f || y >= mInitialTouchY) && (fraction != 1f || y <= mInitialTouchY)) { flingQsWithCurrentVelocity(); + } else { + mScrollYOverride = -1; } if (mVelocityTracker != null) { mVelocityTracker.recycle(); @@ -570,7 +585,6 @@ public class NotificationPanelView extends PanelView implements } } - @Override public void onOverscrollTopChanged(float amount, boolean isRubberbanded) { cancelAnimation(); @@ -589,6 +603,7 @@ public class NotificationPanelView extends PanelView implements public void run() { mStackScrollerOverscrolling = false; mQsExpansionFromOverscroll = false; + updateQsState(); } }); } @@ -602,6 +617,9 @@ public class NotificationPanelView extends PanelView implements // Reset scroll position and apply that position to the expanded height. float height = mQsExpansionHeight - mScrollView.getScrollY() - overscrollAmount; + if (mScrollView.getScrollY() != 0) { + mScrollYOverride = mScrollView.getScrollY(); + } mScrollView.scrollTo(0, 0); setQsExpansion(height); } @@ -611,14 +629,19 @@ public class NotificationPanelView extends PanelView implements if (changed) { mQsExpanded = expanded; updateQsState(); + requestPanelHeightUpdate(); + mNotificationStackScroller.setInterceptDelegateEnabled(expanded); } } - public void setKeyguardShowing(boolean keyguardShowing) { + public void setBarState(int statusBarState) { + boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD + || statusBarState == StatusBarState.SHADE_LOCKED; if (!mKeyguardShowing && keyguardShowing) { setQsTranslation(mQsExpansionHeight); mHeader.setTranslationY(0f); } + mStatusBarState = statusBarState; mKeyguardShowing = keyguardShowing; updateQsState(); } @@ -626,12 +649,12 @@ public class NotificationPanelView extends PanelView implements private void updateQsState() { boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling; mHeader.setExpanded(expandVisually, mStackScrollerOverscrolling); - mNotificationStackScroller.setEnabled(!mQsExpanded || mQsExpansionFromOverscroll); + mNotificationStackScroller.setScrollingEnabled(mStatusBarState != StatusBarState.KEYGUARD + && (!mQsExpanded || mQsExpansionFromOverscroll)); mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE); mQsContainer.setVisibility( mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE); mScrollView.setTouchEnabled(mQsExpanded); - mNotificationStackScroller.setTouchEnabled(!mQsExpanded || mQsExpansionFromOverscroll); } private void setQsExpansion(float height) { @@ -643,7 +666,7 @@ public class NotificationPanelView extends PanelView implements setQsExpanded(false); } mQsExpansionHeight = height; - mHeader.setExpansion(height - mQsPeekHeight); + mHeader.setExpansion(getQsExpansionFraction()); setQsTranslation(height); requestScrollerTopPaddingUpdate(false /* animate */); updateNotificationScrim(height); @@ -702,6 +725,7 @@ public class NotificationPanelView extends PanelView implements private void flingSettings(float vel, boolean expand, final Runnable onFinishRunnable) { float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight; if (target == mQsExpansionHeight) { + mScrollYOverride = -1; if (onFinishRunnable != null) { onFinishRunnable.run(); } @@ -718,6 +742,7 @@ public class NotificationPanelView extends PanelView implements animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + mScrollYOverride = -1; mQsExpansionAnimator = null; if (onFinishRunnable != null) { onFinishRunnable.run(); @@ -726,6 +751,7 @@ public class NotificationPanelView extends PanelView implements }); animator.start(); mQsExpansionAnimator = animator; + mQsAnimatorExpand = expand; } /** @@ -794,16 +820,23 @@ public class NotificationPanelView extends PanelView implements @Override protected int getMaxPanelHeight() { + int min = mStatusBarMinHeight; if (mStatusBar.getBarState() != StatusBarState.KEYGUARD && mNotificationStackScroller.getNotGoneChildCount() == 0) { - return (int) ((mQsMinExpansionHeight + getOverExpansionAmount()) + int minHeight = (int) ((mQsMinExpansionHeight + getOverExpansionAmount()) * HEADER_RUBBERBAND_FACTOR); + min = Math.max(min, minHeight); } - // TODO: Figure out transition for collapsing when QS is open, adjust height here. - int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); - int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin - - mTopPaddingAdjustment; - maxHeight = Math.max(maxHeight, mStatusBarMinHeight); + int maxHeight; + if (mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) { + maxHeight = (int) calculatePanelHeightQsExpanded(); + } else { + int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); + maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin + - mTopPaddingAdjustment; + maxHeight += mNotificationStackScroller.getTopPaddingOverflow(); + } + maxHeight = Math.max(maxHeight, min); return maxHeight; } @@ -816,12 +849,59 @@ public class NotificationPanelView extends PanelView implements if (!mQsExpanded) { positionClockAndNotifications(); } + if (mQsExpanded && !mQsTracking && mQsExpansionAnimator == null + && !mQsExpansionFromOverscroll) { + float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding() + + mNotificationStackScroller.getMinStackHeight() + + mNotificationStackScroller.getNotificationTopPadding(); + float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); + float t = (expandedHeight - panelHeightQsCollapsed) + / (panelHeightQsExpanded - panelHeightQsCollapsed); + + setQsExpansion(mQsMinExpansionHeight + + t * (getTempQsMaxExpansion() - mQsMinExpansionHeight)); + } mNotificationStackScroller.setStackHeight(expandedHeight); updateHeader(); updateUnlockIcon(); updateNotificationTranslucency(); } + /** + * @return a temporary override of {@link #mQsMaxExpansionHeight}, which is needed when + * collapsing QS / the panel when QS was scrolled + */ + private int getTempQsMaxExpansion() { + int qsTempMaxExpansion = mQsMaxExpansionHeight; + if (mScrollYOverride != -1) { + qsTempMaxExpansion -= mScrollYOverride; + } + return qsTempMaxExpansion; + } + + private float calculatePanelHeightQsExpanded() { + float notificationHeight = mNotificationStackScroller.getHeight() + - mNotificationStackScroller.getEmptyBottomMargin() + - mNotificationStackScroller.getTopPadding(); + float totalHeight = mQsMaxExpansionHeight + notificationHeight + + mNotificationStackScroller.getNotificationTopPadding(); + if (totalHeight > mNotificationStackScroller.getHeight()) { + float fullyCollapsedHeight = mQsMaxExpansionHeight + + mNotificationStackScroller.getMinStackHeight() + + mNotificationStackScroller.getNotificationTopPadding() + - getScrollViewScrollY(); + totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight()); + } + return totalHeight; + } + + private int getScrollViewScrollY() { + if (mScrollYOverride != -1) { + return mScrollYOverride; + } else { + return mScrollView.getScrollY(); + } + } private void updateNotificationTranslucency() { float alpha = (mNotificationStackScroller.getNotificationsTopY() + mNotificationStackScroller.getItemHeight()) @@ -931,6 +1011,10 @@ public class NotificationPanelView extends PanelView implements super.onExpandingStarted(); mNotificationStackScroller.onExpansionStarted(); mIsExpanding = true; + mQsExpandedWhenExpandingStarted = mQsExpanded; + if (mQsExpanded) { + onQsExpansionStarted(); + } } @Override @@ -938,6 +1022,7 @@ public class NotificationPanelView extends PanelView implements super.onExpandingFinished(); mNotificationStackScroller.onExpansionStopped(); mIsExpanding = false; + mScrollYOverride = -1; if (mExpandedHeight == 0f) { mHeader.setListening(false); mQsPanel.setListening(false); @@ -1005,6 +1090,12 @@ public class NotificationPanelView extends PanelView implements @Override public void onHeightChanged(ExpandableView view) { + + // Block update if we are in quick settings and just the top padding changed + // (i.e. view == null). + if (view == null && mQsExpanded) { + return; + } requestPanelHeightUpdate(); } @@ -1012,6 +1103,7 @@ public class NotificationPanelView extends PanelView implements public void onScrollChanged() { if (mQsExpanded) { requestScrollerTopPaddingUpdate(false /* animate */); + requestPanelHeightUpdate(); } } @@ -1128,4 +1220,30 @@ public class NotificationPanelView extends PanelView implements return mQsMinExpansionHeight * HEADER_RUBBERBAND_FACTOR; } } + + @Override + protected float getCannedFlingDurationFactor() { + if (mQsExpanded) { + return 0.7f; + } else { + return 0.6f; + } + } + + @Override + protected boolean isTrackingBlocked() { + return mConflictingQsExpansionGesture && mQsExpanded; + } + + public void notifyVisibleChildrenChanged() { + if (mNotificationStackScroller.getNotGoneChildCount() != 0) { + mReserveNotificationSpace.setVisibility(View.VISIBLE); + } else { + mReserveNotificationSpace.setVisibility(View.GONE); + } + } + + public boolean isQsExpanded() { + return mQsExpanded; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java index ea5b309..45f3632 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java @@ -31,6 +31,7 @@ public class ObservableScrollView extends ScrollView { private int mLastOverscrollAmount; private boolean mDispatchingTouchEvent; private boolean mTouchEnabled = true; + private boolean mInTouchEvent; public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); @@ -49,7 +50,7 @@ public class ObservableScrollView extends ScrollView { } public boolean isDispatchingTouchEvent() { - return mDispatchingTouchEvent; + return mDispatchingTouchEvent || mInTouchEvent; } private int getMaxScrollY() { @@ -63,6 +64,22 @@ public class ObservableScrollView extends ScrollView { } @Override + public boolean onTouchEvent(MotionEvent ev) { + mInTouchEvent = true; + boolean result = super.onTouchEvent(ev); + mInTouchEvent = false; + return result; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + mInTouchEvent = true; + boolean result = super.onInterceptTouchEvent(ev); + mInTouchEvent = false; + return result; + } + + @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!mTouchEnabled) { return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 7d5d99d..2c5ece6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -289,7 +289,7 @@ public abstract class PanelView extends FrameLayout { } mJustPeeked = false; } - if (!mJustPeeked && (!waitForTouchSlop || mTracking)) { + if (!mJustPeeked && (!waitForTouchSlop || mTracking) && !isTrackingBlocked()) { setExpandedHeightInternal(newHeight); } @@ -475,7 +475,8 @@ public abstract class PanelView extends FrameLayout { // Make it shorter if we run a canned animation if (vel == 0) { - animator.setDuration((long) (animator.getDuration() / 1.75f)); + animator.setDuration((long) + (animator.getDuration() * getCannedFlingDurationFactor())); } } animator.addListener(new AnimatorListenerAdapter() { @@ -546,9 +547,12 @@ public abstract class PanelView extends FrameLayout { float currentMaxPanelHeight = getMaxPanelHeight(); // If the user isn't actively poking us, let's update the height - if (!mTracking && mHeightAnimator == null - && mExpandedHeight > 0 && currentMaxPanelHeight != mExpandedHeight - && !mPeekPending && mPeekAnimator == null) { + if ((!mTracking || isTrackingBlocked()) + && mHeightAnimator == null + && mExpandedHeight > 0 + && currentMaxPanelHeight != mExpandedHeight + && !mPeekPending + && mPeekAnimator == null) { setExpandedHeight(currentMaxPanelHeight); } } @@ -576,6 +580,12 @@ public abstract class PanelView extends FrameLayout { notifyBarPanelExpansionChanged(); } + /** + * @return true if the panel tracking should be temporarily blocked; this is used when a + * conflicting gesture (opening QS) is happening + */ + protected abstract boolean isTrackingBlocked(); + protected abstract void setOverExpansion(float overExpansion, boolean isPixels); protected abstract void onHeightUpdated(float expandedHeight); @@ -866,4 +876,6 @@ public abstract class PanelView extends FrameLayout { public abstract void resetViews(); protected abstract float getPeekHeight(); + + protected abstract float getCannedFlingDurationFactor(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 387f5a7..a6a8e0c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -32,7 +32,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARE import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IActivityManager; @@ -78,7 +77,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; -import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewPropertyAnimator; @@ -101,7 +99,6 @@ import com.android.systemui.EventLogTags; import com.android.systemui.R; import com.android.systemui.doze.DozeService; import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.qs.CircularClipper; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.ActivatableNotificationView; @@ -717,14 +714,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // Set up the quick settings tile panel mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel); if (mQSPanel != null) { - mQSPanel.setUtils(new CircularClipper.Utils() { - @Override - public ValueAnimator createRevealAnimator(View v, int centerX, int centerY, - float startRadius, float endRadius) { - return ViewAnimationUtils.createCircularReveal(v, centerX, centerY, - startRadius, endRadius); - } - }); final QSTileHost qsh = new QSTileHost(mContext, this, mBluetoothController, mLocationController, mRotationLockController, mNetworkController, mZenModeController, null /*tethering*/, @@ -1181,13 +1170,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, ent.row.setShowingPublic(showingPublic); if (ent.autoRedacted && ent.legacy) { if (showingPublic) { - ent.row.setBackgroundResourceIds( - com.android.internal.R.drawable.notification_material_bg, - com.android.internal.R.drawable.notification_material_bg_dim); + ent.row.setShowingLegacyBackground(false); } else { - ent.row.setBackgroundResourceIds( - com.android.internal.R.drawable.notification_bg, - com.android.internal.R.drawable.notification_bg_dim); + ent.row.setShowingLegacyBackground(true); } } toShow.add(ent.row); @@ -1319,6 +1304,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } + @Override + protected void updateRowStates() { + super.updateRowStates(); + mNotificationPanel.notifyVisibleChildrenChanged(); + } + protected void updateCarrierLabelVisibility(boolean force) { // TODO: Handle this for the notification stack scroller as well if (!mShowCarrierInPanel) return; @@ -1691,9 +1682,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mStatusBarWindow.cancelExpandHelper(); mStatusBarView.collapseAllPanels(true); - if (isFlippedToSettings()) { - flipToNotifications(true /*animate*/); - } } } @@ -1751,18 +1739,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } mNotificationPanel.expand(); - if (mStackScroller.getVisibility() != View.VISIBLE) { - flipToNotifications(true /*animate*/); - } if (false) postStartTracing(); } - public void flipToNotifications(boolean animate) { - // TODO: Animation - mNotificationPanel.closeQs(); - } - @Override public void animateExpandSettingsPanel() { if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); @@ -1779,13 +1759,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (false) postStartTracing(); } - public boolean isFlippedToSettings() { - if (mNotificationPanel != null) { - return mNotificationPanel.isQsExpanded(); - } - return false; - } - public void animateCollapseQuickSettings() { mStatusBarView.collapseAllPanels(true); } @@ -2863,14 +2836,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { mKeyguardBottomArea.setVisibility(View.VISIBLE); mHeader.setKeyguardShowing(true); - mNotificationPanel.setKeyguardShowing(true); mScrimController.setKeyguardShowing(true); } else { mKeyguardBottomArea.setVisibility(View.GONE); mHeader.setKeyguardShowing(false); - mNotificationPanel.setKeyguardShowing(false); mScrimController.setKeyguardShowing(false); } + mNotificationPanel.setBarState(mState); updateDozingState(); updateStackScrollerState(); updatePublicMode(); @@ -2905,7 +2877,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mStackScroller.setDimmed(onKeyguard, false /* animate */); mStackScroller.setVisibility(!mShowLockscreenNotifications && onKeyguard ? View.INVISIBLE : View.VISIBLE); - mStackScroller.setScrollingEnabled(!onKeyguard); mStackScroller.setExpandingEnabled(!onKeyguard); ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild(); mStackScroller.setActivatedChild(null); @@ -2930,9 +2901,20 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public boolean onBackPressed() { if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { - return mStatusBarKeyguardViewManager.onBackPressed(); + if (mStatusBarKeyguardViewManager.onBackPressed()) { + return true; + } + if (mNotificationPanel.isQsExpanded()) { + mNotificationPanel.animateCloseQs(); + return true; + } + return false; } else { - animateCollapsePanels(); + if (mNotificationPanel.isQsExpanded()) { + mNotificationPanel.animateCloseQs(); + } else { + animateCollapsePanels(); + } return true; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 60f38b5..04b1443 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -65,7 +65,6 @@ public class QSTileHost implements QSTile.Host { private final CurrentUserTracker mUserTracker; private final VolumeComponent mVolume; private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>(); - private final int mFeedbackStartDelay; private final FlashlightController mFlashlight; public QSTileHost(Context context, PhoneStatusBar statusBar, @@ -110,7 +109,6 @@ public class QSTileHost implements QSTile.Host { } }; mUserTracker.startTracking(); - mFeedbackStartDelay = mContext.getResources().getInteger(R.integer.feedback_start_delay); } @Override @@ -120,7 +118,7 @@ public class QSTileHost implements QSTile.Host { @Override public void startSettingsActivity(final Intent intent) { - mStatusBar.postStartSettingsActivity(intent, mFeedbackStartDelay); + mStatusBar.postStartSettingsActivity(intent, 0); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java index 3e2dcef..2119316 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.Intent; import android.graphics.Outline; @@ -26,10 +28,12 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; +import android.widget.Switch; import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.qs.QSPanel; +import com.android.systemui.qs.QSTile; import com.android.systemui.settings.BrightnessController; import com.android.systemui.settings.ToggleSlider; import com.android.systemui.statusbar.policy.UserInfoController; @@ -60,6 +64,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL private View mSignalCluster; private View mSettingsButton; private View mBrightnessContainer; + private View mQsDetailHeader; private View mEmergencyCallsOnly; private TextView mChargingInfo; @@ -104,6 +109,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL mBrightnessController = new BrightnessController(getContext(), (ImageView) findViewById(R.id.brightness_icon), (ToggleSlider) findViewById(R.id.brightness_slider)); + mQsDetailHeader = findViewById(R.id.qs_detail_header); + mQsDetailHeader.setAlpha(0); mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only); mChargingInfo = (TextView) findViewById(R.id.header_charging_info); loadDimens(); @@ -219,6 +226,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE); mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE); mBrightnessContainer.setVisibility(mExpanded ? View.VISIBLE : View.GONE); + mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE); if (mStatusIcons != null) { mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE); } @@ -300,8 +308,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } } - public void setExpansion(float height) { - height = (height - mCollapsedHeight) * EXPANSION_RUBBERBAND_FACTOR + mCollapsedHeight; + public void setExpansion(float t) { + float height = mCollapsedHeight + t * (mExpandedHeight - mCollapsedHeight); if (height < mCollapsedHeight) { height = mCollapsedHeight; } @@ -375,13 +383,6 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } } - private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() { - @Override - public void onShowingDetail(boolean showingDetail) { - mBrightnessContainer.animate().alpha(showingDetail ? 0 : 1).withLayer().start(); - } - }; - @Override public boolean shouldDelayChildPressedState() { return true; @@ -418,4 +419,69 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL public boolean hasOverlappingRendering() { return !mKeyguardShowing || mExpanded; } + + private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() { + @Override + public void onToggleStateChanged(final boolean state) { + post(new Runnable() { + @Override + public void run() { + handleToggleStateChanged(state); + } + }); + } + + @Override + public void onShowingDetail(final QSTile.DetailAdapter detail) { + post(new Runnable() { + @Override + public void run() { + handleShowingDetail(detail); + } + }); + } + + private void handleToggleStateChanged(boolean state) { + final Switch headerSwitch = (Switch) + mQsDetailHeader.findViewById(android.R.id.toggle); + headerSwitch.setChecked(state); + } + + private void handleShowingDetail(final QSTile.DetailAdapter detail) { + final boolean showingDetail = detail != null; + transition(mBrightnessContainer, !showingDetail); + transition(mQsDetailHeader, showingDetail); + if (showingDetail) { + final TextView headerTitle = (TextView) + mQsDetailHeader.findViewById(android.R.id.title); + headerTitle.setText(detail.getTitle()); + final Switch headerSwitch = (Switch) + mQsDetailHeader.findViewById(android.R.id.toggle); + final Boolean toggleState = detail.getToggleState(); + if (toggleState == null) { + headerSwitch.setVisibility(INVISIBLE); + mQsDetailHeader.setClickable(false); + } else { + headerSwitch.setVisibility(VISIBLE); + headerSwitch.setChecked(toggleState); + mQsDetailHeader.setClickable(true); + mQsDetailHeader.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + detail.setToggleState(!toggleState); + } + }); + } + } else { + mQsDetailHeader.setClickable(false); + } + } + + private void transition(final View v, final boolean in) { + if (in) { + v.bringToFront(); + } + v.animate().alpha(in ? 1 : 0).withLayer().start(); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 1f68860..7e11369 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -33,4 +33,23 @@ public interface NetworkController { String dataTypeContentDescriptionId, String description, boolean noSim); void onAirplaneModeChanged(boolean enabled); } + + void addAccessPointCallback(AccessPointCallback callback); + void removeAccessPointCallback(AccessPointCallback callback); + void scanForAccessPoints(); + void connect(AccessPoint ap); + + public interface AccessPointCallback { + void onAccessPointsChanged(AccessPoint[] accessPoints); + } + + public static class AccessPoint { + public static final int NO_NETWORK = -1; // see WifiManager + + public int networkId; + public int iconId; + public String ssid; + public boolean isConnected; + public int level; // 0 - 5 + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 4e54e41..4b94ebd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -169,6 +169,8 @@ public class NetworkControllerImpl extends BroadcastReceiver void setIsAirplaneMode(boolean is, int airplaneIcon); } + private final WifiAccessPointController mAccessPoints; + /** * Construct this controller object and register for updates. */ @@ -237,6 +239,7 @@ public class NetworkControllerImpl extends BroadcastReceiver updateAirplaneMode(); mLastLocale = mContext.getResources().getConfiguration().locale; + mAccessPoints = new WifiAccessPointController(mContext); } public boolean hasMobileDataFeature() { @@ -282,6 +285,26 @@ public class NetworkControllerImpl extends BroadcastReceiver } @Override + public void addAccessPointCallback(AccessPointCallback callback) { + mAccessPoints.addCallback(callback); + } + + @Override + public void removeAccessPointCallback(AccessPointCallback callback) { + mAccessPoints.removeCallback(callback); + } + + @Override + public void scanForAccessPoints() { + mAccessPoints.scan(); + } + + @Override + public void connect(AccessPoint ap) { + mAccessPoints.connect(ap); + } + + @Override public void setWifiEnabled(final boolean enabled) { new AsyncTask<Void, Void, Void>() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiAccessPointController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiAccessPointController.java new file mode 100644 index 0000000..09e7472 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiAccessPointController.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2014 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.systemui.statusbar.policy; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.ActionListener; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; + +import com.android.systemui.R; +import com.android.systemui.statusbar.policy.NetworkController.AccessPoint; +import com.android.systemui.statusbar.policy.NetworkController.AccessPointCallback; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class WifiAccessPointController { + private static final String TAG = "WifiAccessPointController"; + private static final boolean DEBUG = false; + + private static final int[] ICONS = { + R.drawable.ic_qs_wifi_0, + R.drawable.ic_qs_wifi_full_1, + R.drawable.ic_qs_wifi_full_2, + R.drawable.ic_qs_wifi_full_3, + R.drawable.ic_qs_wifi_full_4, + }; + + private final Context mContext; + private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>(); + private final WifiManager mWifiManager; + private final Receiver mReceiver = new Receiver(); + + private boolean mScanning; + + public WifiAccessPointController(Context context) { + mContext = context; + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + } + + public void addCallback(AccessPointCallback callback) { + if (callback == null || mCallbacks.contains(callback)) return; + if (DEBUG) Log.d(TAG, "addCallback " + callback); + mCallbacks.add(callback); + mReceiver.setListening(!mCallbacks.isEmpty()); + } + + public void removeCallback(AccessPointCallback callback) { + if (callback == null) return; + if (DEBUG) Log.d(TAG, "removeCallback " + callback); + mCallbacks.remove(callback); + mReceiver.setListening(!mCallbacks.isEmpty()); + } + + public void scan() { + if (mScanning) return; + if (DEBUG) Log.d(TAG, "scan!"); + mScanning = mWifiManager.startScan(); + } + + public void connect(AccessPoint ap) { + if (ap == null || ap.networkId < 0) return; + if (DEBUG) Log.d(TAG, "connect networkId=" + ap.networkId); + mWifiManager.connect(ap.networkId, new ActionListener() { + @Override + public void onSuccess() { + if (DEBUG) Log.d(TAG, "connect success"); + } + + @Override + public void onFailure(int reason) { + if (DEBUG) Log.d(TAG, "connect failure reason=" + reason); + } + }); + } + + private void fireCallback(AccessPoint[] aps) { + for (AccessPointCallback callback : mCallbacks) { + callback.onAccessPointsChanged(aps); + } + } + + private static String trimDoubleQuotes(String v) { + return v != null && v.length() >= 2 && v.charAt(0) == '\"' + && v.charAt(v.length() - 1) == '\"' ? v.substring(1, v.length() - 1) : v; + } + + private int getConnectedNetworkId() { + final WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); + return wifiInfo != null ? wifiInfo.getNetworkId() : AccessPoint.NO_NETWORK; + } + + private ArrayMap<String, WifiConfiguration> getConfiguredNetworksBySsid() { + final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); + if (configs == null || configs.size() == 0) return ArrayMap.EMPTY; + final ArrayMap<String, WifiConfiguration> rt = new ArrayMap<String, WifiConfiguration>(); + for (WifiConfiguration config : configs) { + rt.put(trimDoubleQuotes(config.SSID), config); + } + return rt; + } + + private void updateAccessPoints() { + final int connectedNetworkId = getConnectedNetworkId(); + if (DEBUG) Log.d(TAG, "connectedNetworkId: " + connectedNetworkId); + final List<ScanResult> scanResults = mWifiManager.getScanResults(); + final ArrayMap<String, WifiConfiguration> configured = getConfiguredNetworksBySsid(); + if (DEBUG) Log.d(TAG, "scanResults: " + scanResults); + final List<AccessPoint> aps = new ArrayList<AccessPoint>(scanResults.size()); + final ArraySet<String> ssids = new ArraySet<String>(); + for (ScanResult scanResult : scanResults) { + final String ssid = scanResult.SSID; + if (TextUtils.isEmpty(ssid) || ssids.contains(ssid)) continue; + if (!configured.containsKey(ssid)) continue; + ssids.add(ssid); + final WifiConfiguration config = configured.get(ssid); + final int level = WifiManager.calculateSignalLevel(scanResult.level, ICONS.length); + final AccessPoint ap = new AccessPoint(); + ap.networkId = config != null ? config.networkId : AccessPoint.NO_NETWORK; + ap.ssid = ssid; + ap.iconId = ICONS[level]; + ap.isConnected = ap.networkId != AccessPoint.NO_NETWORK + && ap.networkId == connectedNetworkId; + ap.level = level; + aps.add(ap); + } + Collections.sort(aps, mByStrength); + fireCallback(aps.toArray(new AccessPoint[aps.size()])); + } + + private final Comparator<AccessPoint> mByStrength = new Comparator<AccessPoint> () { + @Override + public int compare(AccessPoint lhs, AccessPoint rhs) { + return -Integer.compare(score(lhs), score(rhs)); + } + + private int score(AccessPoint ap) { + return ap.level + (ap.isConnected ? 10 : 0); + } + }; + + private final class Receiver extends BroadcastReceiver { + private boolean mRegistered; + + public void setListening(boolean listening) { + if (listening && !mRegistered) { + if (DEBUG) Log.d(TAG, "Registering receiver"); + final IntentFilter filter = new IntentFilter(); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + filter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION); + filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); + filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.RSSI_CHANGED_ACTION); + mContext.registerReceiver(this, filter); + mRegistered = true; + } else if (!listening && mRegistered) { + if (DEBUG) Log.d(TAG, "Unregistering receiver"); + mContext.unregisterReceiver(this); + mRegistered = false; + } + } + + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) Log.d(TAG, "onReceive " + intent.getAction()); + if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) { + updateAccessPoints(); + mScanning = false; + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 94fdd1f..4086022 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -30,6 +30,8 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.animation.AnimationUtils; import android.widget.OverScroller; +import android.widget.ScrollView; + import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SwipeHelper; @@ -150,7 +152,7 @@ public class NotificationStackScrollLayout extends ViewGroup private float mMinTopOverScrollToEscape; private int mIntrinsicPadding; private int mNotificationTopPadding; - private int mMinStackHeight; + private float mTopPaddingOverflow; private boolean mDontReportNextOverScroll; /** @@ -166,7 +168,10 @@ public class NotificationStackScrollLayout extends ViewGroup * animating. */ private boolean mOnlyScrollingInThisMotion; - private boolean mTouchEnabled = true; + private ViewGroup mScrollView; + private boolean mInterceptDelegateEnabled; + private boolean mDelegateToScrollView; + private ViewTreeObserver.OnPreDrawListener mChildrenUpdater = new ViewTreeObserver.OnPreDrawListener() { @Override @@ -258,7 +263,6 @@ public class NotificationStackScrollLayout extends ViewGroup R.dimen.min_top_overscroll_to_qs); mNotificationTopPadding = getResources().getDimensionPixelSize( R.dimen.notifications_top_padding); - mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height); } private void updatePadding(boolean dimmed) { @@ -421,7 +425,7 @@ public class NotificationStackScrollLayout extends ViewGroup int minStackHeight = itemHeight + bottomStackPeekSize; int stackHeight; if (newStackHeight - mTopPadding >= minStackHeight || getNotGoneChildCount() == 0) { - setTranslationY(0); + setTranslationY(mTopPaddingOverflow); stackHeight = newStackHeight; } else { @@ -466,6 +470,14 @@ public class NotificationStackScrollLayout extends ViewGroup mLongClickListener = listener; } + public void setScrollView(ViewGroup scrollView) { + mScrollView = scrollView; + } + + public void setInterceptDelegateEnabled(boolean interceptDelegateEnabled) { + mInterceptDelegateEnabled = interceptDelegateEnabled; + } + public void onChildDismissed(View v) { if (DEBUG) Log.v(TAG, "onChildDismissed: " + v); final View veto = v.findViewById(R.id.veto); @@ -536,7 +548,7 @@ public class NotificationStackScrollLayout extends ViewGroup } float childTop = slidingChild.getTranslationY(); float top = childTop + slidingChild.getClipTopAmount(); - float bottom = childTop + slidingChild.getActualHeight(); + float bottom = top + slidingChild.getActualHeight(); int left = slidingChild.getLeft(); int right = slidingChild.getRight(); @@ -619,13 +631,17 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public boolean onTouchEvent(MotionEvent ev) { - if (!isEnabled()) { - return false; - } boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL || ev.getActionMasked()== MotionEvent.ACTION_UP; + if (mDelegateToScrollView) { + if (isCancelOrUp) { + mDelegateToScrollView = false; + } + transformTouchEvent(ev, this, mScrollView); + return mScrollView.onTouchEvent(ev); + } boolean expandWantsIt = false; - if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) { + if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) { if (isCancelOrUp) { mExpandHelper.onlyObserveMovements(false); } @@ -1195,7 +1211,7 @@ public class NotificationStackScrollLayout extends ViewGroup return view.getHeight(); } - private int getContentHeight() { + public int getContentHeight() { return mContentHeight; } @@ -1271,17 +1287,32 @@ public class NotificationStackScrollLayout extends ViewGroup public void updateTopPadding(float qsHeight, int scrollY, boolean animate) { float start = qsHeight - scrollY + mNotificationTopPadding; float stackHeight = getHeight() - start; - if (stackHeight <= mMinStackHeight) { - float overflow = mMinStackHeight - stackHeight; - stackHeight = mMinStackHeight; + int minStackHeight = getMinStackHeight(); + if (stackHeight <= minStackHeight) { + float overflow = minStackHeight - stackHeight; + stackHeight = minStackHeight; start = getHeight() - stackHeight; setTranslationY(overflow); + mTopPaddingOverflow = overflow; } else { setTranslationY(0); + mTopPaddingOverflow = 0; } setTopPadding(clampPadding((int) start), animate); } + public int getNotificationTopPadding() { + return mNotificationTopPadding; + } + + public int getMinStackHeight() { + return mCollapsedSize + mBottomStackPeekSize; + } + + public float getTopPaddingOverflow() { + return mTopPaddingOverflow; + } + public int getPeekHeight() { return mIntrinsicPadding + mCollapsedSize + mBottomStackPeekSize; } @@ -1327,11 +1358,25 @@ public class NotificationStackScrollLayout extends ViewGroup } } + private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) { + ev.offsetLocation(sourceView.getX(), sourceView.getY()); + ev.offsetLocation(-targetView.getX(), -targetView.getY()); + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { + if (mInterceptDelegateEnabled) { + transformTouchEvent(ev, this, mScrollView); + if (mScrollView.onInterceptTouchEvent(ev)) { + mDelegateToScrollView = true; + removeLongPressCallback(); + return true; + } + transformTouchEvent(ev, mScrollView, this); + } initDownStates(ev); boolean expandWantsIt = false; - if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) { + if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) { expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev); } boolean scrollWantsIt = false; @@ -1854,6 +1899,10 @@ public class NotificationStackScrollLayout extends ViewGroup mIntrinsicPadding = intrinsicPadding; } + public int getIntrinsicPadding() { + return mIntrinsicPadding; + } + /** * @return the y position of the first notification */ @@ -1861,18 +1910,6 @@ public class NotificationStackScrollLayout extends ViewGroup return mTopPadding + getTranslationY(); } - public void setTouchEnabled(boolean touchEnabled) { - mTouchEnabled = touchEnabled; - } - - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - if (!mTouchEnabled) { - return false; - } - return super.dispatchTouchEvent(ev); - } - @Override public boolean shouldDelayChildPressedState() { return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index a8c25d8..7c522d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -113,7 +113,7 @@ public class StackScrollAlgorithm { mBottomStackSlowDownLength = context.getResources() .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length); mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.notification_material_rounded_rect_radius); + R.dimen.notification_material_rounded_rect_radius); } @@ -155,6 +155,17 @@ public class StackScrollAlgorithm { updateDimmedActivated(ambientState, resultState, algorithmState); updateClipping(resultState, algorithmState); updateScrimAmount(resultState, algorithmState, ambientState.getScrimAmount()); + updateSpeedBumpState(resultState, algorithmState, ambientState.getSpeedBumpIndex()); + } + + private void updateSpeedBumpState(StackScrollState resultState, + StackScrollAlgorithmState algorithmState, int speedBumpIndex) { + int childCount = algorithmState.visibleChildren.size(); + for (int i = 0; i < childCount; i++) { + View child = algorithmState.visibleChildren.get(i); + StackScrollState.ViewState childViewState = resultState.getViewStateForView(child); + childViewState.belowSpeedBump = speedBumpIndex != -1 && i > speedBumpIndex; + } } private void updateScrimAmount(StackScrollState resultState, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java index 9ae038a..f48739c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -151,6 +151,9 @@ public class StackScrollState { // apply dark child.setDark(state.dark, false /* animate */); + // apply speed bump state + child.setBelowSpeedBump(state.belowSpeedBump); + // apply scrimming child.setScrimAmount(state.scrimAmount); @@ -224,6 +227,7 @@ public class StackScrollState { float scale; boolean dimmed; boolean dark; + boolean belowSpeedBump; /** * A value between 0 and 1 indicating how much the view should be scrimmed. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index f732cf0..0c84675 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -179,6 +179,9 @@ public class StackStateAnimator { // start dark animation child.setDark(viewState.dark, mAnimationFilter.animateDark); + // apply speed bump state + child.setBelowSpeedBump(viewState.belowSpeedBump); + // apply scrimming child.setScrimAmount(viewState.scrimAmount); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java index 99cba4d..53daaae 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java @@ -358,7 +358,7 @@ public class VolumePanel extends Handler { // embedded mode mDialog = null; mView = LayoutInflater.from(mContext).inflate( - com.android.systemui.R.layout.volume_panel, parent, true); + com.android.systemui.R.layout.volume_panel, parent, false); } mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel); mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel); @@ -384,6 +384,10 @@ public class VolumePanel extends Handler { listenToRingerMode(); } + public View getContentView() { + return mView; + } + private void setLayoutDirection(int layoutDirection) { mPanel.setLayoutDirection(layoutDirection); updateStates(); @@ -628,7 +632,8 @@ public class VolumePanel extends Handler { if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel); if (mZenPanel == null) { mZenPanel = (ZenModePanel) mZenPanelStub.inflate(); - mZenPanel.init(mZenController, mDialog != null ? 'D' : 'E'); + final boolean isDialog = mDialog != null; + mZenPanel.init(mZenController, isDialog ? 'D' : 'E', isDialog); mZenPanel.setCallback(new ZenModePanel.Callback() { @Override public void onMoreSettings() { diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java index 798e7fa..9917944 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java @@ -67,6 +67,7 @@ public class ZenModePanel extends LinearLayout { private char mLogTag = '?'; private String mTag; private LinearLayout mConditions; + private View mMoreSettings; private Callback mCallback; private ZenModeController mController; private boolean mRequestingConditions; @@ -91,7 +92,8 @@ public class ZenModePanel extends LinearLayout { protected void onFinishInflate() { super.onFinishInflate(); mConditions = (LinearLayout) findViewById(android.R.id.content); - findViewById(android.R.id.button2).setOnClickListener(new View.OnClickListener() { + mMoreSettings = findViewById(android.R.id.button2); + mMoreSettings.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { fireMoreSettings(); @@ -154,12 +156,13 @@ public class ZenModePanel extends LinearLayout { } } - public void init(ZenModeController controller, char logTag) { + public void init(ZenModeController controller, char logTag, boolean moreSettings) { mController = controller; mLogTag = logTag; updateTag(); mExitConditionId = mController.getExitConditionId(); if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId); + mMoreSettings.setVisibility(moreSettings ? VISIBLE : GONE); mConditions.removeAllViews(); mController.addCallback(mZenCallback); if (mShowing) { diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java index cf28f05..3595f79 100644 --- a/services/core/java/com/android/server/hdmi/FeatureAction.java +++ b/services/core/java/com/android/server/hdmi/FeatureAction.java @@ -207,7 +207,7 @@ abstract class FeatureAction { protected final void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) { - mService.pollDevices(callback, pickStrategy, retryCount); + mService.pollDevices(callback, getSourceAddress(), pickStrategy, retryCount); } /** diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index 49f2d6f..7dfea6a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -200,7 +200,8 @@ final class HdmiCecController { int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS; if (curAddress != HdmiCec.ADDR_UNREGISTERED && deviceType == HdmiCec.getTypeFromAddress(curAddress)) { - if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) { + if (!sendPollMessage(curAddress, curAddress, + RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) { logicalAddress = curAddress; break; } @@ -358,16 +359,18 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. * * @param callback an interface used to get a list of all remote devices' address + * @param sourceAddress a logical address of source device where sends polling message * @param pickStrategy strategy how to pick polling candidates * @param retryCount the number of retry used to send polling message to remote devices */ @ServiceThreadOnly - void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) { + void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy, + int retryCount) { assertRunOnServiceThread(); // Extract polling candidates. No need to poll against local devices. List<Integer> pollingCandidates = pickPollCandidates(pickStrategy); - runDevicePolling(pollingCandidates, retryCount, callback); + runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback); } /** @@ -428,7 +431,8 @@ final class HdmiCecController { } @ServiceThreadOnly - private void runDevicePolling(final List<Integer> candidates, final int retryCount, + private void runDevicePolling(final int sourceAddress, + final List<Integer> candidates, final int retryCount, final DevicePollingCallback callback) { assertRunOnServiceThread(); runOnIoThread(new Runnable() { @@ -436,7 +440,7 @@ final class HdmiCecController { public void run() { final ArrayList<Integer> allocated = new ArrayList<>(); for (Integer address : candidates) { - if (sendPollMessage(address, retryCount)) { + if (sendPollMessage(sourceAddress, address, retryCount)) { allocated.add(address); } } @@ -453,15 +457,14 @@ final class HdmiCecController { } @IoThreadOnly - private boolean sendPollMessage(int address, int retryCount) { + private boolean sendPollMessage(int sourceAddress, int destinationAddress, int retryCount) { assertRunOnIoThread(); for (int i = 0; i < retryCount; ++i) { - // <Polling Message> is a message which has empty body and - // uses same address for both source and destination address. + // <Polling Message> is a message which has empty body. // If sending <Polling Message> failed (NAK), it becomes // new logical address for the device because no device uses // it as logical address of the device. - if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY) + if (nativeSendCecCommand(mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY) == HdmiConstants.SEND_RESULT_SUCCESS) { return true; } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index a2df852..63df9a2 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -448,14 +448,17 @@ public final class HdmiControlService extends SystemService { * devices. * * @param callback an interface used to get a list of all remote devices' address + * @param sourceAddress a logical address of source device where sends polling message * @param pickStrategy strategy how to pick polling candidates * @param retryCount the number of retry used to send polling message to remote devices * @throw IllegalArgumentException if {@code pickStrategy} is invalid value */ @ServiceThreadOnly - void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) { + void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy, + int retryCount) { assertRunOnServiceThread(); - mCecController.pollDevices(callback, checkPollStrategy(pickStrategy), retryCount); + mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy), + retryCount); } private int checkPollStrategy(int pickStrategy) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index bc14888..c8496e4 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1551,8 +1551,16 @@ public class NotificationManagerService extends SystemService { mNotificationsByKey.remove(old.sbn.getKey()); r.isUpdate = true; } + mNotificationsByKey.put(n.getKey(), r); + // Ensure if this is a foreground service that the proper additional + // flags are set. + if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { + notification.flags |= Notification.FLAG_ONGOING_EVENT + | Notification.FLAG_NO_CLEAR; + } + applyZenModeLocked(r); Collections.sort(mNotificationList, mRankingComparator); @@ -1571,13 +1579,6 @@ public class NotificationManagerService extends SystemService { + n.getPackageName()); } - // Ensure if this is a foreground service that the proper additional - // flags are set. - if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { - notification.flags |= Notification.FLAG_ONGOING_EVENT - | Notification.FLAG_NO_CLEAR; - } - buzzBeepBlinkLocked(r); } } @@ -1872,10 +1873,9 @@ public class NotificationManagerService extends SystemService { } } - // let zen mode evaluate this record and then make note of that for the future + // let zen mode evaluate this record private void applyZenModeLocked(NotificationRecord record) { - record.setIntercepted(mZenModeHelper.shouldIntercept(record, record.wasTouchedByZen())); - record.setTouchedByZen(); + record.setIntercepted(mZenModeHelper.shouldIntercept(record)); } // lock on mNotificationList diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 13fb986..6b60ea4 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -51,8 +51,6 @@ public final class NotificationRecord { // is this notification currently being intercepted by Zen Mode? private boolean mIntercept; - // InterceptedNotifications needs to know if this has been previously evaluated. - private boolean mTouchedByZen; // The timestamp used for ranking. private long mRankingTimeMs; @@ -71,7 +69,6 @@ public final class NotificationRecord { public void copyRankingInformation(NotificationRecord previous) { mContactAffinity = previous.mContactAffinity; mRecentlyIntrusive = previous.mRecentlyIntrusive; - mTouchedByZen = previous.mTouchedByZen; mIntercept = previous.mIntercept; mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs()); } @@ -204,14 +201,6 @@ public final class NotificationRecord { return mIntercept; } - public boolean wasTouchedByZen() { - return mTouchedByZen; - } - - public void setTouchedByZen() { - mTouchedByZen = true; - } - /** * Returns the timestamp to use for time-based sorting in the ranker. */ diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index b95db9c..7bac3dc 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -129,12 +129,8 @@ public class ZenModeHelper { mCallbacks.add(callback); } - public boolean shouldIntercept(NotificationRecord record, boolean previouslySeen) { + public boolean shouldIntercept(NotificationRecord record) { if (mZenMode != Global.ZEN_MODE_OFF) { - if (previouslySeen && !record.isIntercepted()) { - // notifications never transition from not intercepted to intercepted - return false; - } if (isSystem(record)) { return false; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 0658eee..11e546f 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -41,6 +41,7 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SELinux; +import android.os.UserHandle; import android.system.ErrnoException; import android.system.OsConstants; import android.system.StructStat; @@ -272,7 +273,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } }; - mPm.installStage(mPackageName, this.sessionDir, localObserver, params.installFlags); + mPm.installStage(mPackageName, this.sessionDir, localObserver, params, installerPackageName, + installerUid, new UserHandle(userId)); } /** diff --git a/services/core/java/com/android/server/pm/PackageKeySetData.java b/services/core/java/com/android/server/pm/PackageKeySetData.java index d470807..11ed5d2 100644 --- a/services/core/java/com/android/server/pm/PackageKeySetData.java +++ b/services/core/java/com/android/server/pm/PackageKeySetData.java @@ -18,7 +18,6 @@ package com.android.server.pm; import com.android.internal.util.ArrayUtils; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -35,19 +34,17 @@ public class PackageKeySetData { private long[] mDefinedKeySets; - private final Map<String, Long> mKeySetAliases; + private final Map<String, Long> mKeySetAliases = new HashMap<String, Long>(); PackageKeySetData() { mProperSigningKeySet = KEYSET_UNASSIGNED; - mKeySetAliases = new HashMap<String, Long>(); } PackageKeySetData(PackageKeySetData original) { - mSigningKeySets = original.getSigningKeySets().clone(); - mUpgradeKeySets = original.getUpgradeKeySets().clone(); - mDefinedKeySets = original.getDefinedKeySets().clone(); - mKeySetAliases = new HashMap<String, Long>(); - mKeySetAliases.putAll(original.getAliases()); + mSigningKeySets = ArrayUtils.cloneOrNull(original.mSigningKeySets); + mUpgradeKeySets = ArrayUtils.cloneOrNull(original.mUpgradeKeySets); + mDefinedKeySets = ArrayUtils.cloneOrNull(original.mDefinedKeySets); + mKeySetAliases.putAll(original.mKeySetAliases); } protected void setProperSigningKeySet(long ks) { @@ -149,4 +146,4 @@ public class PackageKeySetData { /* should never be the case that mUpgradeKeySets.length == 0 */ return (mUpgradeKeySets != null && mUpgradeKeySets.length > 0); } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index bc45ae0..2d9176d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.pm.PackageParser.isPackageFilename; import static android.os.Process.PACKAGE_INFO_GID; import static android.os.Process.SYSTEM_UID; import static android.system.OsConstants.S_IRGRP; @@ -46,6 +47,7 @@ import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.EventLogTags; import com.android.server.IntentResolver; @@ -92,6 +94,7 @@ import android.content.pm.ManifestDigest; import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; +import android.content.pm.PackageInstallerParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser.ActivityIntentInfo; import android.content.pm.PackageParser.PackageParserException; @@ -349,6 +352,7 @@ public class PackageManagerService extends IPackageManager.Stub { // LOCK HELD. Can be called with mInstallLock held. final Installer mInstaller; + /** Directory where installed third-party apps stored */ final File mAppInstallDir; /** @@ -361,6 +365,7 @@ public class PackageManagerService extends IPackageManager.Stub { // apps. final File mDrmAppPrivateInstallDir; + /** Directory where third-party apps are staged before install */ final File mAppStagingDir; // ---------------------------------------------------------------- @@ -1143,17 +1148,18 @@ public class PackageManagerService extends IPackageManager.Stub { if ((state != null) && !state.timeoutExtended()) { final InstallArgs args = state.getInstallArgs(); - Slog.i(TAG, "Verification timed out for " + args.packageURI.toString()); + final Uri fromUri = Uri.fromFile(args.fromFile); + + Slog.i(TAG, "Verification timed out for " + fromUri); mPendingVerification.remove(verificationId); int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) { - Slog.i(TAG, "Continuing with installation of " - + args.packageURI.toString()); + Slog.i(TAG, "Continuing with installation of " + fromUri); state.setVerifierResponse(Binder.getCallingUid(), PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); - broadcastPackageVerified(verificationId, args.packageURI, + broadcastPackageVerified(verificationId, fromUri, PackageManager.VERIFICATION_ALLOW, state.getInstallArgs().getUser()); try { @@ -1162,7 +1168,7 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.e(TAG, "Could not contact the ContainerService"); } } else { - broadcastPackageVerified(verificationId, args.packageURI, + broadcastPackageVerified(verificationId, fromUri, PackageManager.VERIFICATION_REJECT, state.getInstallArgs().getUser()); } @@ -1189,11 +1195,12 @@ public class PackageManagerService extends IPackageManager.Stub { mPendingVerification.remove(verificationId); final InstallArgs args = state.getInstallArgs(); + final Uri fromUri = Uri.fromFile(args.fromFile); int ret; if (state.isInstallAllowed()) { ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; - broadcastPackageVerified(verificationId, args.packageURI, + broadcastPackageVerified(verificationId, fromUri, response.code, state.getInstallArgs().getUser()); try { ret = args.copyApk(mContainerService, true); @@ -1528,14 +1535,14 @@ public class PackageManagerService extends IPackageManager.Stub { // overlay packages if they reside in VENDOR_OVERLAY_DIR. File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR); mVendorOverlayInstallObserver = new AppDirObserver( - vendorOverlayDir.getPath(), OBSERVER_EVENTS, true, false); + vendorOverlayDir.getPath(), OBSERVER_EVENTS, true, false); mVendorOverlayInstallObserver.startWatching(); scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_TRUSTED_OVERLAY, 0); // Find base frameworks (resource packages without code). mFrameworkInstallObserver = new AppDirObserver( - frameworkDir.getPath(), OBSERVER_EVENTS, true, false); + frameworkDir.getPath(), OBSERVER_EVENTS, true, false); mFrameworkInstallObserver.startWatching(); scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR @@ -1547,14 +1554,14 @@ public class PackageManagerService extends IPackageManager.Stub { mPrivilegedInstallObserver = new AppDirObserver( privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true); mPrivilegedInstallObserver.startWatching(); - scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM - | PackageParser.PARSE_IS_SYSTEM_DIR - | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0); + scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM + | PackageParser.PARSE_IS_SYSTEM_DIR + | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0); // Collect ordinary system packages. File systemAppDir = new File(Environment.getRootDirectory(), "app"); mSystemInstallObserver = new AppDirObserver( - systemAppDir.getPath(), OBSERVER_EVENTS, true, false); + systemAppDir.getPath(), OBSERVER_EVENTS, true, false); mSystemInstallObserver.startWatching(); scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); @@ -1567,7 +1574,7 @@ public class PackageManagerService extends IPackageManager.Stub { // failed to look up canonical path, continue with original one } mVendorInstallObserver = new AppDirObserver( - vendorAppDir.getPath(), OBSERVER_EVENTS, true, false); + vendorAppDir.getPath(), OBSERVER_EVENTS, true, false); mVendorInstallObserver.startWatching(); scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); @@ -1825,6 +1832,10 @@ public class PackageManagerService extends IPackageManager.Stub { void cleanupInstallFailedPackage(PackageSetting ps) { Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name); removeDataDirsLI(ps.name); + + // TODO: try cleaning up codePath directory contents first, since it + // might be a cluster + if (ps.codePath != null) { if (!ps.codePath.delete()) { Slog.w(TAG, "Unable to remove old code file: " + ps.codePath); @@ -2071,15 +2082,12 @@ public class PackageManagerService extends IPackageManager.Stub { if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) { return null; } - // App code is gone, so we aren't worried about split paths + // Only data remains, so we aren't worried about code paths pkg = new PackageParser.Package(packageName); pkg.applicationInfo.packageName = packageName; pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY; - pkg.applicationInfo.sourceDir = ps.codePathString; - pkg.applicationInfo.publicSourceDir = ps.resourcePathString; pkg.applicationInfo.dataDir = getDataPathForPackage(packageName, 0).getPath(); - pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; pkg.applicationInfo.cpuAbi = ps.cpuAbiString; } return generatePackageInfo(pkg, flags, userId); @@ -4041,20 +4049,21 @@ public class PackageManagerService extends IPackageManager.Stub { private boolean createIdmapForPackagePairLI(PackageParser.Package pkg, PackageParser.Package opkg) { if (!opkg.mTrustedOverlay) { - Slog.w(TAG, "Skipping target and overlay pair " + pkg.codePath + " and " + - opkg.codePath + ": overlay not trusted"); + Slog.w(TAG, "Skipping target and overlay pair " + pkg.baseCodePath + " and " + + opkg.baseCodePath + ": overlay not trusted"); return false; } HashMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName); if (overlaySet == null) { - Slog.e(TAG, "was about to create idmap for " + pkg.codePath + " and " + - opkg.codePath + " but target package has no known overlays"); + Slog.e(TAG, "was about to create idmap for " + pkg.baseCodePath + " and " + + opkg.baseCodePath + " but target package has no known overlays"); return false; } final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); // TODO: generate idmap for split APKs - if (mInstaller.idmap(pkg.codePath, opkg.codePath, sharedGid) != 0) { - Slog.e(TAG, "Failed to generate idmap for " + pkg.codePath + " and " + opkg.codePath); + if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid) != 0) { + Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and " + + opkg.baseCodePath); return false; } PackageParser.Package[] overlayArray = @@ -4075,8 +4084,8 @@ public class PackageManagerService extends IPackageManager.Stub { } private void scanDirLI(File dir, int flags, int scanMode, long currentTime) { - String[] files = dir.list(); - if (files == null) { + final File[] files = dir.listFiles(); + if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + dir); return; } @@ -4086,10 +4095,8 @@ public class PackageManagerService extends IPackageManager.Stub { + " flags=0x" + Integer.toHexString(flags)); } - int i; - for (i=0; i<files.length; i++) { - File file = new File(dir, files[i]); - if (!isPackageFilename(files[i])) { + for (File file : files) { + if (!isPackageFilename(file)) { // Ignore entries which are not apk's continue; } @@ -4258,7 +4265,7 @@ public class PackageManagerService extends IPackageManager.Stub { + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); - InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps), + InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, getAppInstructionSetFromSettings(ps)); synchronized (mInstallLock) { @@ -4323,7 +4330,7 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Package " + ps.name + " at " + scanFile + "reverting from " + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); - InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps), + InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, getAppInstructionSetFromSettings(ps)); synchronized (mInstallLock) { @@ -4343,29 +4350,27 @@ public class PackageManagerService extends IPackageManager.Stub { } } - final String codePath = pkg.codePath; + final String baseCodePath = pkg.baseCodePath; final String[] splitCodePaths = pkg.splitCodePaths; - String resPath = null; - String[] splitResPaths = null; + // TODO: extend to support forward-locked splits + String baseResPath = null; if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) { if (ps != null && ps.resourcePathString != null) { - resPath = ps.resourcePathString; - splitResPaths = deriveSplitResPaths(pkg.splitCodePaths); + baseResPath = ps.resourcePathString; } else { // Should not happen at all. Just log an error. Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName); } } else { - resPath = pkg.codePath; - splitResPaths = pkg.splitCodePaths; + baseResPath = pkg.baseCodePath; } // Set application objects path explicitly. - pkg.applicationInfo.sourceDir = codePath; - pkg.applicationInfo.publicSourceDir = resPath; + pkg.applicationInfo.sourceDir = baseCodePath; + pkg.applicationInfo.publicSourceDir = baseResPath; pkg.applicationInfo.splitSourceDirs = splitCodePaths; - pkg.applicationInfo.splitPublicSourceDirs = splitResPaths; + pkg.applicationInfo.splitPublicSourceDirs = splitCodePaths; // Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode @@ -6419,10 +6424,6 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private static final boolean isPackageFilename(String name) { - return name != null && name.endsWith(".apk"); - } - private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) { for (int i=pkgInfo.permissions.size()-1; i>=0; i--) { if (pkgInfo.permissions.get(i).info.name.equals(perm)) { @@ -7701,8 +7702,13 @@ public class PackageManagerService extends IPackageManager.Stub { verificationParams.setInstallerUid(uid); + if (!"file".equals(packageURI.getScheme())) { + throw new UnsupportedOperationException("Only file:// URIs are supported"); + } + final File fromFile = new File(packageURI.getPath()); + final Message msg = mHandler.obtainMessage(INIT_COPY); - msg.obj = new InstallParams(packageURI, observer, observer2, filteredFlags, + msg.obj = new InstallParams(fromFile, observer, observer2, filteredFlags, installerPackageName, verificationParams, encryptionParams, user, packageAbiOverride); mHandler.sendMessage(msg); @@ -7820,11 +7826,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } - void installStage(String basePackageName, File stageDir, IPackageInstallObserver2 observer, - int flags) { - // TODO: install stage! + void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer2, + PackageInstallerParams params, String installerPackageName, int installerUid, + UserHandle user) { + Slog.e(TAG, "TODO: install stage!"); try { - observer.packageInstalled(basePackageName, null, + observer2.packageInstalled(packageName, null, PackageManager.INSTALL_FAILED_INTERNAL_ERROR); } catch (RemoteException ignored) { } @@ -8407,30 +8414,38 @@ public class PackageManagerService extends IPackageManager.Stub { } class InstallParams extends HandlerParams { + /** + * Location where install is coming from, before it has been + * copied/renamed into place. This could be a single monolithic APK + * file, or a cluster directory. This location may be untrusted. + */ + private final File mFromFile; + + /** + * Local copy of {@link #mFromFile}, if generated. + */ + private File mLocalFromFile; + final IPackageInstallObserver observer; final IPackageInstallObserver2 observer2; int flags; - - private final Uri mPackageURI; final String installerPackageName; final VerificationParams verificationParams; private InstallArgs mArgs; private int mRet; - private File mTempPackage; final ContainerEncryptionParams encryptionParams; final String packageAbiOverride; final String packageInstructionSetOverride; - InstallParams(Uri packageURI, - IPackageInstallObserver observer, IPackageInstallObserver2 observer2, - int flags, String installerPackageName, VerificationParams verificationParams, - ContainerEncryptionParams encryptionParams, UserHandle user, - String packageAbiOverride) { + InstallParams(File fromFile, IPackageInstallObserver observer, + IPackageInstallObserver2 observer2, int flags, String installerPackageName, + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams, + UserHandle user, String packageAbiOverride) { super(user); - this.mPackageURI = packageURI; - this.flags = flags; + mFromFile = Preconditions.checkNotNull(fromFile); this.observer = observer; this.observer2 = observer2; + this.flags = flags; this.installerPackageName = installerPackageName; this.verificationParams = verificationParams; this.encryptionParams = encryptionParams; @@ -8443,7 +8458,7 @@ public class PackageManagerService extends IPackageManager.Stub { public String toString() { return "InstallParams{" + Integer.toHexString(System.identityHashCode(this)) - + " " + mPackageURI + "}"; + + " " + mFromFile + "}"; } public ManifestDigest getManifestDigest() { @@ -8543,76 +8558,55 @@ public class PackageManagerService extends IPackageManager.Stub { Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed"); } - try { - mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, mPackageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); - - final File packageFile; - if (encryptionParams != null || !"file".equals(mPackageURI.getScheme())) { - mTempPackage = createTempPackageFile(mDrmAppPrivateInstallDir); - if (mTempPackage != null) { - ParcelFileDescriptor out; - try { - out = ParcelFileDescriptor.open(mTempPackage, - ParcelFileDescriptor.MODE_READ_WRITE); - } catch (FileNotFoundException e) { - out = null; - Slog.e(TAG, "Failed to create temporary file for : " + mPackageURI); - } - - // Make a temporary file for decryption. - ret = mContainerService - .copyResource(mPackageURI, encryptionParams, out); + if (encryptionParams != null) { + // Make a temporary file for decryption. + mLocalFromFile = createTempPackageFile(mDrmAppPrivateInstallDir); + if (mLocalFromFile != null) { + ParcelFileDescriptor out = null; + try { + out = ParcelFileDescriptor.open(mLocalFromFile, + ParcelFileDescriptor.MODE_READ_WRITE); + ret = mContainerService.copyResource(mFromFile.getAbsolutePath(), + encryptionParams, out); + } catch (FileNotFoundException e) { + Slog.e(TAG, "Failed to create temporary file for: " + mFromFile); + } finally { IoUtils.closeQuietly(out); - - packageFile = mTempPackage; - - FileUtils.setPermissions(packageFile.getAbsolutePath(), - FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP - | FileUtils.S_IROTH, - -1, -1); - } else { - packageFile = null; } - } else { - packageFile = new File(mPackageURI.getPath()); + + FileUtils.setPermissions(mLocalFromFile, 0644, -1, -1); } + } - if (packageFile != null) { - // Remote call to find out default install location - final String packageFilePath = packageFile.getAbsolutePath(); - pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags, - lowThreshold, packageAbiOverride); + // Remote call to find out default install location + final String fromPath = getFromFile().getAbsolutePath(); + pkgLite = mContainerService.getMinimalPackageInfo(fromPath, flags, lowThreshold, + packageAbiOverride); - /* - * If we have too little free space, try to free cache - * before giving up. - */ - if (pkgLite.recommendedInstallLocation - == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { - final long size = mContainerService.calculateInstalledSize( - packageFilePath, isForwardLocked(), packageAbiOverride); - if (mInstaller.freeCache(size + lowThreshold) >= 0) { - pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, - flags, lowThreshold, packageAbiOverride); - } - /* - * The cache free must have deleted the file we - * downloaded to install. - * - * TODO: fix the "freeCache" call to not delete - * the file we care about. - */ - if (pkgLite.recommendedInstallLocation - == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { - pkgLite.recommendedInstallLocation - = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; - } - } + /* + * If we have too little free space, try to free cache + * before giving up. + */ + if (pkgLite.recommendedInstallLocation + == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { + final long size = mContainerService.calculateInstalledSize( + fromPath, isForwardLocked(), packageAbiOverride); + if (mInstaller.freeCache(size + lowThreshold) >= 0) { + pkgLite = mContainerService.getMinimalPackageInfo(fromPath, + flags, lowThreshold, packageAbiOverride); + } + /* + * The cache free must have deleted the file we + * downloaded to install. + * + * TODO: fix the "freeCache" call to not delete + * the file we care about. + */ + if (pkgLite.recommendedInstallLocation + == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { + pkgLite.recommendedInstallLocation + = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } - } finally { - mContext.revokeUriPermission(mPackageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); } } @@ -8672,9 +8666,10 @@ public class PackageManagerService extends IPackageManager.Stub { final int requiredUid = mRequiredVerifierPackage == null ? -1 : getPackageUid(mRequiredVerifierPackage, userIdentifier); if (requiredUid != -1 && isVerificationEnabled(userIdentifier, flags)) { + // TODO: send verifier the install session instead of uri final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); - verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE); + verification.setDataAndType(Uri.fromFile(getFromFile()), PACKAGE_MIME_TYPE); verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); final List<ResolveInfo> receivers = queryIntentReceivers(verification, @@ -8802,10 +8797,9 @@ public class PackageManagerService extends IPackageManager.Stub { if (mArgs != null) { processPendingInstall(mArgs, mRet); - if (mTempPackage != null) { - if (!mTempPackage.delete()) { - Slog.w(TAG, "Couldn't delete temporary file: " + - mTempPackage.getAbsolutePath()); + if (mLocalFromFile != null) { + if (!mLocalFromFile.delete()) { + Slog.w(TAG, "Couldn't delete temporary file: " + mLocalFromFile); } } } @@ -8821,11 +8815,11 @@ public class PackageManagerService extends IPackageManager.Stub { return (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0; } - public Uri getPackageUri() { - if (mTempPackage != null) { - return Uri.fromFile(mTempPackage); + public File getFromFile() { + if (mLocalFromFile != null) { + return mLocalFromFile; } else { - return mPackageURI; + return mFromFile; } } } @@ -8856,8 +8850,9 @@ public class PackageManagerService extends IPackageManager.Stub { this.packageName = packageName; this.uid = uid; if (srcArgs != null) { - Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath())); - targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir, instructionSet); + final String codePath = srcArgs.getCodePath(); + targetArgs = createInstallArgsForMoveTarget(codePath, flags, packageName, dataDir, + instructionSet); } else { targetArgs = null; } @@ -8958,6 +8953,8 @@ public class PackageManagerService extends IPackageManager.Stub { } private InstallArgs createInstallArgs(InstallParams params) { + // TODO: extend to support incoming zero-copy locations + if (installOnSd(params.flags) || params.isForwardLocked()) { return new AsecInstallArgs(params); } else { @@ -8965,14 +8962,18 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath, - String nativeLibraryPath, String instructionSet) { + /** + * Create args that describe an existing installed package. Typically used + * when cleaning up old installs, or used as a move source. + */ + private InstallArgs createInstallArgsForExisting(int flags, String codePath, + String resourcePath, String nativeLibraryPath, String instructionSet) { final boolean isInAsec; if (installOnSd(flags)) { /* Apps on SD card are always in ASEC containers. */ isInAsec = true; } else if (installForwardLocked(flags) - && !fullCodePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) { + && !codePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) { /* * Forward-locked apps are only in ASEC containers if they're the * new style @@ -8983,44 +8984,49 @@ public class PackageManagerService extends IPackageManager.Stub { } if (isInAsec) { - return new AsecInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath, + return new AsecInstallArgs(codePath, resourcePath, nativeLibraryPath, instructionSet, installOnSd(flags), installForwardLocked(flags)); } else { - return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath, - instructionSet); + return new FileInstallArgs(codePath, resourcePath, nativeLibraryPath, instructionSet); } } - // Used by package mover - private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir, - String instructionSet) { + private InstallArgs createInstallArgsForMoveTarget(String codePath, int flags, String pkgName, + String dataDir, String instructionSet) { + final File codeFile = new File(codePath); if (installOnSd(flags) || installForwardLocked(flags)) { - String cid = getNextCodePath(packageURI.getPath(), pkgName, "/" + String cid = getNextCodePath(codePath, pkgName, "/" + AsecInstallArgs.RES_FILE_NAME); - return new AsecInstallArgs(packageURI, cid, instructionSet, installOnSd(flags), + return new AsecInstallArgs(codeFile, cid, instructionSet, installOnSd(flags), installForwardLocked(flags)); } else { - return new FileInstallArgs(packageURI, pkgName, dataDir, instructionSet); + return new FileInstallArgs(codeFile, pkgName, dataDir, instructionSet); } } static abstract class InstallArgs { + /** + * Location where install is coming from, before it has been + * copied/renamed into place. This could be a single monolithic APK + * file, or a cluster directory. This location is typically untrusted. + */ + final File fromFile; + final IPackageInstallObserver observer; final IPackageInstallObserver2 observer2; // Always refers to PackageManager flags only final int flags; - final Uri packageURI; final String installerPackageName; final ManifestDigest manifestDigest; final UserHandle user; final String instructionSet; final String abiOverride; - InstallArgs(Uri packageURI, - IPackageInstallObserver observer, IPackageInstallObserver2 observer2, - int flags, String installerPackageName, ManifestDigest manifestDigest, - UserHandle user, String instructionSet, String abiOverride) { - this.packageURI = packageURI; + InstallArgs(File fromFile, IPackageInstallObserver observer, + IPackageInstallObserver2 observer2, int flags, String installerPackageName, + ManifestDigest manifestDigest, UserHandle user, String instructionSet, + String abiOverride) { + this.fromFile = fromFile; this.flags = flags; this.observer = observer; this.observer2 = observer2; @@ -9031,24 +9037,23 @@ public class PackageManagerService extends IPackageManager.Stub { this.abiOverride = abiOverride; } - abstract void createCopyFile(); abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException; abstract int doPreInstall(int status); abstract boolean doRename(int status, String pkgName, String oldCodePath); - abstract int doPostInstall(int status, int uid); + + /** @see PackageSettingBase#codePathString */ abstract String getCodePath(); + /** @see PackageSettingBase#resourcePathString */ abstract String getResourcePath(); + /** @see PackageSettingBase#nativeLibraryPathString */ abstract String getNativeLibraryPath(); + // Need installer lock especially for dex file removal. abstract void cleanUpResourcesLI(); abstract boolean doPostDeleteLI(boolean delete); abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException; - String[] getSplitCodePaths() { - return null; - } - /** * Called before the source arguments are copied. This is used mostly * for MoveParams when it needs to read the source file to put it in the @@ -9078,20 +9083,27 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /** + * Logic to handle installation of non-ASEC applications, including copying + * and renaming logic. + */ class FileInstallArgs extends InstallArgs { + // TODO: teach about handling cluster directories + File installDir; String codeFileName; String resourceFileName; String libraryPath; boolean created = false; + /** New install */ FileInstallArgs(InstallParams params) { - super(params.getPackageUri(), params.observer, params.observer2, params.flags, - params.installerPackageName, params.getManifestDigest(), - params.getUser(), params.packageInstructionSetOverride, - params.packageAbiOverride); + super(params.getFromFile(), params.observer, params.observer2, params.flags, + params.installerPackageName, params.getManifestDigest(), params.getUser(), + params.packageInstructionSetOverride, params.packageAbiOverride); } + /** Existing install */ FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, String instructionSet) { super(null, null, null, 0, null, null, null, instructionSet, null); @@ -9102,8 +9114,9 @@ public class PackageManagerService extends IPackageManager.Stub { libraryPath = nativeLibraryPath; } - FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) { - super(packageURI, null, null, 0, null, null, null, instructionSet, null); + /** New install from existing */ + FileInstallArgs(File fromFile, String pkgName, String dataDir, String instructionSet) { + super(fromFile, null, null, 0, null, null, null, instructionSet, null); installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; String apkName = getNextCodePath(null, pkgName, ".apk"); codeFileName = new File(installDir, apkName + ".apk").getPath(); @@ -9128,13 +9141,8 @@ public class PackageManagerService extends IPackageManager.Stub { lowThreshold = dsm.getMemoryLowThreshold(); } - try { - mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); - return imcs.checkInternalFreeStorage(packageURI, isFwdLocked(), lowThreshold); - } finally { - mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - } + return imcs.checkInternalFreeStorage(fromFile.getAbsolutePath(), isFwdLocked(), + lowThreshold); } void createCopyFile() { @@ -9175,12 +9183,9 @@ public class PackageManagerService extends IPackageManager.Stub { // Copy the resource now int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; try { - mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); - ret = imcs.copyResource(packageURI, null, out); + ret = imcs.copyResource(fromFile.getAbsolutePath(), null, out); } finally { IoUtils.closeQuietly(out); - mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } if (isFwdLocked()) { @@ -9432,7 +9437,13 @@ public class PackageManagerService extends IPackageManager.Stub { return subStr1.substring(sidx+1, eidx); } + /** + * Logic to handle installation of ASEC applications, including copying and + * renaming logic. + */ class AsecInstallArgs extends InstallArgs { + // TODO: teach about handling cluster directories + static final String RES_FILE_NAME = "pkg.apk"; static final String PUBLIC_RES_FILE_NAME = "res.zip"; @@ -9441,13 +9452,14 @@ public class PackageManagerService extends IPackageManager.Stub { String resourcePath; String libraryPath; + /** New install */ AsecInstallArgs(InstallParams params) { - super(params.getPackageUri(), params.observer, params.observer2, params.flags, - params.installerPackageName, params.getManifestDigest(), - params.getUser(), params.packageInstructionSetOverride, - params.packageAbiOverride); + super(params.getFromFile(), params.observer, params.observer2, params.flags, + params.installerPackageName, params.getManifestDigest(), params.getUser(), + params.packageInstructionSetOverride, params.packageAbiOverride); } + /** Existing install */ AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, String instructionSet, boolean isExternal, boolean isForwardLocked) { super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) @@ -9469,9 +9481,10 @@ public class PackageManagerService extends IPackageManager.Stub { setCachePath(PackageHelper.getSdDir(cid)); } - AsecInstallArgs(Uri packageURI, String cid, String instructionSet, + /** New install from existing */ + AsecInstallArgs(File fromFile, String cid, String instructionSet, boolean isExternal, boolean isForwardLocked) { - super(packageURI, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) + super(fromFile, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null, null, instructionSet, null); this.cid = cid; @@ -9482,13 +9495,8 @@ public class PackageManagerService extends IPackageManager.Stub { } boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { - try { - mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); - return imcs.checkExternalFreeStorage(packageURI, isFwdLocked(), abiOverride); - } finally { - mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - } + return imcs.checkExternalFreeStorage(fromFile.getAbsolutePath(), isFwdLocked(), + abiOverride); } private final boolean isExternal() { @@ -9506,16 +9514,9 @@ public class PackageManagerService extends IPackageManager.Stub { PackageHelper.destroySdDir(cid); } - final String newCachePath; - try { - mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, - Intent.FLAG_GRANT_READ_URI_PERMISSION); - newCachePath = imcs.copyResourceToContainer(packageURI, cid, getEncryptKey(), - RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked(), - abiOverride); - } finally { - mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - } + final String newCachePath = imcs.copyResourceToContainer(fromFile.getAbsolutePath(), + cid, getEncryptKey(), RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), + isFwdLocked(), abiOverride); if (newCachePath != null) { setCachePath(newCachePath); @@ -10078,7 +10079,7 @@ public class PackageManagerService extends IPackageManager.Stub { // We didn't need to disable the .apk as a current system package, // which means we are replacing another update that is already // installed. We need to make sure to delete the older one's .apk. - res.removedInfo.args = createInstallArgs(0, + res.removedInfo.args = createInstallArgsForExisting(0, deletedPackage.applicationInfo.sourceDir, deletedPackage.applicationInfo.publicSourceDir, deletedPackage.applicationInfo.nativeLibraryDir, @@ -10143,7 +10144,7 @@ public class PackageManagerService extends IPackageManager.Stub { // TODO: extend to move split APK dex files if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { final String instructionSet = getAppInstructionSet(newPackage.applicationInfo); - int retCode = mInstaller.movedex(oldCodePath, newPackage.codePath, + int retCode = mInstaller.movedex(oldCodePath, newPackage.baseCodePath, instructionSet); if (retCode != 0) { /* @@ -10156,7 +10157,7 @@ public class PackageManagerService extends IPackageManager.Stub { */ newPackage.mDexOptNeeded = true; mInstaller.rmdex(oldCodePath, instructionSet); - mInstaller.rmdex(newPackage.codePath, instructionSet); + mInstaller.rmdex(newPackage.baseCodePath, instructionSet); } } return PackageManager.INSTALL_SUCCEEDED; @@ -10221,8 +10222,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private void installPackageLI(InstallArgs args, - boolean newInstall, PackageInstalledInfo res) { + private void installPackageLI(InstallArgs args, boolean newInstall, PackageInstalledInfo res) { int pFlags = args.flags; String installerPackageName = args.installerPackageName; File tmpPackageFile = new File(args.getCodePath()); @@ -10367,14 +10367,18 @@ public class PackageManagerService extends IPackageManager.Stub { res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return; } + // Set application objects path explicitly after the rename + // TODO: derive split paths from original scan after rename pkg.codePath = args.getCodePath(); + pkg.baseCodePath = args.getCodePath(); + pkg.splitCodePaths = null; pkg.applicationInfo.sourceDir = args.getCodePath(); pkg.applicationInfo.publicSourceDir = args.getResourcePath(); - pkg.applicationInfo.splitSourceDirs = args.getSplitCodePaths(); - pkg.applicationInfo.splitPublicSourceDirs = deriveSplitResPaths( - pkg.applicationInfo.splitSourceDirs); + pkg.applicationInfo.splitSourceDirs = null; + pkg.applicationInfo.splitPublicSourceDirs = null; pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath(); + if (replace) { replacePackageLI(pkg, parseFlags, scanMode, args.user, installerPackageName, res, args.abiOverride); @@ -10847,8 +10851,8 @@ public class PackageManagerService extends IPackageManager.Stub { // Delete application code and resources if (deleteCodeAndResources && (outInfo != null)) { - outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString, - ps.resourcePathString, ps.nativeLibraryPathString, + outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), + ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString, getAppInstructionSetFromSettings(ps)); } return true; @@ -11253,7 +11257,8 @@ public class PackageManagerService extends IPackageManager.Stub { publicSrcDir = applicationInfo.publicSourceDir; } } - int res = mInstaller.getSizeInfo(packageName, userHandle, p.codePath, libDirPath, + // TODO: extend to measure size of split APKs + int res = mInstaller.getSizeInfo(packageName, userHandle, p.baseCodePath, libDirPath, publicSrcDir, asecPath, getAppInstructionSetFromSettings(ps), pStats); if (res < 0) { @@ -12843,9 +12848,9 @@ public class PackageManagerService extends IPackageManager.Stub { } else { Message msg = mHandler.obtainMessage(INIT_COPY); final String instructionSet = getAppInstructionSet(pkg.applicationInfo); - InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir, - pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir, - instructionSet); + InstallArgs srcArgs = createInstallArgsForExisting(currFlags, + pkg.applicationInfo.sourceDir, pkg.applicationInfo.publicSourceDir, + pkg.applicationInfo.nativeLibraryDir, instructionSet); MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName, pkg.applicationInfo.dataDir, instructionSet, pkg.applicationInfo.uid, user); msg.obj = mp; @@ -12943,11 +12948,13 @@ public class PackageManagerService extends IPackageManager.Stub { if (returnCode == PackageManager.MOVE_SUCCEEDED) { pkg.codePath = newCodePath; + pkg.baseCodePath = newCodePath; // Move dex files around if (moveDexFilesLI(oldCodePath, pkg) != PackageManager.INSTALL_SUCCEEDED) { // Moving of dex files failed. Set // error code and abort move. pkg.codePath = oldCodePath; + pkg.baseCodePath = oldCodePath; returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE; } } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 7fee372..2091c16 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -45,6 +45,12 @@ class PackageSettingBase extends GrantedPermissions { final String name; final String realName; + + /** + * Path where this package was found on disk. For monolithic packages + * this is path to single base APK file; for cluster packages this is + * path to the cluster directory. + */ File codePath; String codePathString; File resourcePath; diff --git a/telecomm/java/android/telecomm/CallCapabilities.java b/telecomm/java/android/telecomm/CallCapabilities.java index 5aff19c..2e0152a 100644 --- a/telecomm/java/android/telecomm/CallCapabilities.java +++ b/telecomm/java/android/telecomm/CallCapabilities.java @@ -42,11 +42,8 @@ public final class CallCapabilities { /** Call supports generic conference mode. */ public static final int GENERIC_CONFERENCE = 0x00000080; - /** Call currently supports switch between connections. */ - public static final int CONNECTION_HANDOFF = 0x00000100; - public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL - | RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE | CONNECTION_HANDOFF; + | RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE; public static String toString(int capabilities) { StringBuilder builder = new StringBuilder(); @@ -75,9 +72,6 @@ public final class CallCapabilities { if ((capabilities & GENERIC_CONFERENCE) != 0) { builder.append(" GENERIC_CONFERENCE"); } - if ((capabilities & CONNECTION_HANDOFF) != 0) { - builder.append(" HANDOFF"); - } builder.append("]"); return builder.toString(); } diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java index 44380e2..e77fb23 100644 --- a/telecomm/java/android/telecomm/CallService.java +++ b/telecomm/java/android/telecomm/CallService.java @@ -31,11 +31,6 @@ import com.android.internal.telecomm.ICallServiceAdapter; * Base implementation of CallService which can be used to provide calls for the system * in-call UI. CallService is a one-way service from the framework's CallsManager to any app * that would like to provide calls managed by the default system in-call user interface. - * When the service is bound by the framework, CallsManager will call setCallServiceAdapter - * which will provide CallService with an instance of {@link CallServiceAdapter} to be used - * for communicating back to CallsManager. Subsequently, more specific methods of the service - * will be called to perform various call actions including making an outgoing call and - * disconnected existing calls. * TODO(santoscordon): Needs more about AndroidManifest.xml service registrations before * we can unhide this API. * @@ -61,6 +56,7 @@ public abstract class CallService extends Service { private static final int MSG_CONFERENCE = 13; private static final int MSG_SPLIT_FROM_CONFERENCE = 14; private static final int MSG_ON_POST_DIAL_CONTINUE = 15; + private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 16; /** * Default Handler used to consolidate binder method calls onto a single thread. @@ -147,6 +143,9 @@ public abstract class CallService extends Service { case MSG_SPLIT_FROM_CONFERENCE: splitFromConference((String) msg.obj); break; + case MSG_ON_PHONE_ACCOUNT_CLICKED: + onPhoneAccountClicked((String) msg.obj); + break; default: break; } @@ -244,6 +243,12 @@ public abstract class CallService extends Service { args.argi1 = proceed ? 1 : 0; mMessageHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget(); } + + @Override + public void onPhoneAccountClicked(String callId) { + mMessageHandler.obtainMessage(MSG_ON_PHONE_ACCOUNT_CLICKED, callId).sendToTarget(); + } + } /** @@ -272,152 +277,59 @@ public abstract class CallService extends Service { return mBinder; } - /** - * @return The attached {@link CallServiceAdapter} if the service is bound, null otherwise. - */ + /** @hide */ protected final CallServiceAdapter getAdapter() { return mAdapter; } - /** - * Lifecycle callback which is called when this {@link CallService} has been attached to a - * {@link CallServiceAdapter}, indicating {@link #getAdapter()} is now safe to use. - * - * @param adapter The adapter now attached to this call service. - */ - protected void onAdapterAttached(CallServiceAdapter adapter) { - } + /** @hide */ + protected abstract void onAdapterAttached(CallServiceAdapter adapter); - /** - * Attempts to call the relevant party using the specified call's handle, be it a phone number, - * SIP address, or some other kind of user ID. Note that the set of handle types is - * dynamically extensible since call providers should be able to implement arbitrary - * handle-calling systems. - * - * @param callInfo The details of the relevant call. - */ - public abstract void call(CallInfo callInfo); + /** @hide */ + protected abstract void call(CallInfo callInfo); - /** - * Aborts the outgoing call attempt. Invoked in the unlikely event that Telecomm decides to - * abort an attempt to place a call. Only ever be invoked after {@link #call} invocations. - * After this is invoked, Telecomm does not expect any more updates about the call and will - * actively ignore any such update. This is different from {@link #disconnect} where Telecomm - * expects confirmation via CallServiceAdapter.markCallAsDisconnected. - * - * @param callId The identifier of the call to abort. - */ - public abstract void abort(String callId); + /** @hide */ + protected abstract void abort(String callId); - /** - * Receives a new call ID to use with an incoming call. Invoked by Telecomm after it is notified - * that this call service has a pending incoming call, see - * {@link TelecommConstants#ACTION_INCOMING_CALL}. The call service must first give Telecomm - * additional information about the call through {@link CallServiceAdapter#notifyIncomingCall}. - * Following that, the call service can update the call at will using the specified call ID. - * - * If a {@link Bundle} was passed (via {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}) in - * with the {@link TelecommConstants#ACTION_INCOMING_CALL} intent, <code>extras</code> will be - * populated with this {@link Bundle}. Otherwise, an empty Bundle will be returned. - * - * @param callId The ID of the call. - * @param extras The optional extras which were passed in with the intent, or an empty Bundle. - */ - public abstract void setIncomingCallId(String callId, Bundle extras); + /** @hide */ + protected abstract void setIncomingCallId(String callId, Bundle extras); - /** - * Answers a ringing call identified by callId. Telecomm invokes this method as a result of the - * user hitting the "answer" button in the incoming call screen. - * - * @param callId The ID of the call. - */ - public abstract void answer(String callId); + /** @hide */ + protected abstract void answer(String callId); - /** - * Rejects a ringing call identified by callId. Telecomm invokes this method as a result of the - * user hitting the "reject" button in the incoming call screen. - * - * @param callId The ID of the call. - */ - public abstract void reject(String callId); + /** @hide */ + protected abstract void reject(String callId); - /** - * Disconnects the specified call. - * - * @param callId The ID of the call to disconnect. - */ - public abstract void disconnect(String callId); + /** @hide */ + protected abstract void disconnect(String callId); - /** - * Puts the specified call on hold. - * - * @param callId The ID of the call to put on hold. - */ - public abstract void hold(String callId); + /** @hide */ + protected abstract void hold(String callId); - /** - * Removes the specified call from hold. - * - * @param callId The ID of the call to unhold. - */ - public abstract void unhold(String callId); + /** @hide */ + protected abstract void unhold(String callId); - /** - * Plays a dual-tone multi-frequency signaling (DTMF) tone in a call. - * - * @param callId The unique ID of the call in which the tone will be played. - * @param digit A character representing the DTMF digit for which to play the tone. This - * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}. - */ - public abstract void playDtmfTone(String callId, char digit); + /** @hide */ + protected abstract void playDtmfTone(String callId, char digit); - /** - * Stops any dual-tone multi-frequency sinaling (DTMF) tone currently playing. - * - * DTMF tones are played by calling {@link #playDtmfTone(String,char)}. If no DTMF tone is - * currently playing, this method will do nothing. - * - * @param callId The unique ID of the call in which any currently playing tone will be stopped. - */ - public abstract void stopDtmfTone(String callId); + /** @hide */ + protected abstract void stopDtmfTone(String callId); - /** - * Called when the audio state changes. - * - * @param activeCallId The identifier of the call that was active during the state change. - * @param audioState The new {@link CallAudioState}. - */ - public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState); + /** @hide */ + protected abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState); - /** - * Conferences the specified call. - * - * @param conferenceCallId The unique ID of the conference call onto which the specified calls - * should be added. - * @param callId The call to conference. - * @hide - */ - public abstract void conference(String conferenceCallId, String callId); + /** @hide */ + protected abstract void conference(String conferenceCallId, String callId); - /** - * Removes the specified call from a conference call. - * - * @param callId The call to remove from the conference call - * @hide - */ - public abstract void splitFromConference(String callId); + /** @hide */ + protected abstract void splitFromConference(String callId); - public void onPostDialContinue(String callId, boolean proceed) {} + /** @hide */ + protected abstract void onPostDialContinue(String callId, boolean proceed); - public void onPostDialWait(Connection conn, String remaining) {} + /** @hide */ + protected abstract void onFeaturesChanged(String callId, int features); - /** - * Called when changes to the features of a call occurs. Features are defined in - * {@link android.telecomm.CallFeatures}. The active features for the call are represented as - * bits in the features bit-mask. - * - * @param callId The call to set the features for. - * @param features The new features of the call. - */ - public abstract void onFeaturesChanged(String callId, int features); + /** @hide */ + protected abstract void onPhoneAccountClicked(String callId); } diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java index 1b3c0a3..dcab566 100644 --- a/telecomm/java/android/telecomm/CallServiceAdapter.java +++ b/telecomm/java/android/telecomm/CallServiceAdapter.java @@ -33,8 +33,9 @@ import java.util.Set; /** * Provides methods for ICallService implementations to interact with the system phone app. - * TODO(santoscordon): Need final public-facing comments in this file. * TODO(santoscordon): Rename this to CallServiceAdapterDemultiplexer (or something). + * + * @hide */ public final class CallServiceAdapter implements DeathRecipient { private final Set<ICallServiceAdapter> mAdapters = new HashSet<>(); @@ -298,20 +299,6 @@ public final class CallServiceAdapter implements DeathRecipient { } /** - * Instructs Telecomm to handoff the call to another call service. - * - * @param callId The identifier of the call to handoff. - */ - public void handoffCall(String callId) { - for (ICallServiceAdapter adapter : mAdapters) { - try { - adapter.handoffCall(callId); - } catch (RemoteException e) { - } - } - } - - /** * Indicates that a new conference call has been created. * * @param callId The unique ID of the conference call. diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java index 73aac2c..32dcf6c 100644 --- a/telecomm/java/android/telecomm/Connection.java +++ b/telecomm/java/android/telecomm/Connection.java @@ -611,6 +611,12 @@ public abstract class Connection { */ protected void onChildrenChanged(List<Connection> children) {} + /** + * Called when the phone account UI was clicked. + */ + protected void onPhoneAccountClicked() {} + + private void addChild(Connection connection) { Log.d(this, "adding child %s", connection); mChildConnections.add(connection); diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java index 693e9fb..d6970e2 100644 --- a/telecomm/java/android/telecomm/ConnectionService.java +++ b/telecomm/java/android/telecomm/ConnectionService.java @@ -180,7 +180,7 @@ public abstract class ConnectionService extends CallService { /** @hide */ @Override - public final void call(final CallInfo callInfo) { + protected final void call(final CallInfo callInfo) { Log.d(this, "call %s", callInfo); onCreateConnections( new ConnectionRequest( @@ -211,14 +211,14 @@ public abstract class ConnectionService extends CallService { /** @hide */ @Override - public final void abort(String callId) { + protected final void abort(String callId) { Log.d(this, "abort %s", callId); findConnectionForAction(callId, "abort").abort(); } /** @hide */ @Override - public final void setIncomingCallId(final String callId, Bundle extras) { + protected final void setIncomingCallId(final String callId, Bundle extras) { Log.d(this, "setIncomingCallId %s %s", callId, extras); onCreateIncomingConnection( new ConnectionRequest( @@ -261,63 +261,63 @@ public abstract class ConnectionService extends CallService { /** @hide */ @Override - public final void answer(String callId) { + protected final void answer(String callId) { Log.d(this, "answer %s", callId); findConnectionForAction(callId, "answer").answer(); } /** @hide */ @Override - public final void reject(String callId) { + protected final void reject(String callId) { Log.d(this, "reject %s", callId); findConnectionForAction(callId, "reject").reject(); } /** @hide */ @Override - public final void disconnect(String callId) { + protected final void disconnect(String callId) { Log.d(this, "disconnect %s", callId); findConnectionForAction(callId, "disconnect").disconnect(); } /** @hide */ @Override - public final void hold(String callId) { + protected final void hold(String callId) { Log.d(this, "hold %s", callId); findConnectionForAction(callId, "hold").hold(); } /** @hide */ @Override - public final void unhold(String callId) { + protected final void unhold(String callId) { Log.d(this, "unhold %s", callId); findConnectionForAction(callId, "unhold").unhold(); } /** @hide */ @Override - public final void playDtmfTone(String callId, char digit) { + protected final void playDtmfTone(String callId, char digit) { Log.d(this, "playDtmfTone %s %c", callId, digit); findConnectionForAction(callId, "playDtmfTone").playDtmfTone(digit); } /** @hide */ @Override - public final void stopDtmfTone(String callId) { + protected final void stopDtmfTone(String callId) { Log.d(this, "stopDtmfTone %s", callId); findConnectionForAction(callId, "stopDtmfTone").stopDtmfTone(); } /** @hide */ @Override - public final void onAudioStateChanged(String callId, CallAudioState audioState) { + protected final void onAudioStateChanged(String callId, CallAudioState audioState) { Log.d(this, "onAudioStateChanged %s %s", callId, audioState); findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState); } /** @hide */ @Override - public final void conference(final String conferenceCallId, String callId) { + protected final void conference(final String conferenceCallId, String callId) { Log.d(this, "conference %s, %s", conferenceCallId, callId); Connection connection = findConnectionForAction(callId, "conference"); @@ -352,7 +352,7 @@ public abstract class ConnectionService extends CallService { /** @hide */ @Override - public final void splitFromConference(String callId) { + protected final void splitFromConference(String callId) { Log.d(this, "splitFromConference(%s)", callId); Connection connection = findConnectionForAction(callId, "splitFromConference"); @@ -366,7 +366,7 @@ public abstract class ConnectionService extends CallService { /** @hide */ @Override - public final void onPostDialContinue(String callId, boolean proceed) { + protected final void onPostDialContinue(String callId, boolean proceed) { Log.d(this, "onPostDialContinue(%s)", callId); Connection connection = findConnectionForAction(callId, "onPostDialContinue"); @@ -377,9 +377,21 @@ public abstract class ConnectionService extends CallService { connection.onPostDialContinue(proceed); } - /** - * @hide - */ + /** @hide */ + @Override + protected final void onFeaturesChanged(String callId, int features) { + Log.d(this, "onFeaturesChanged %s %d", callId, features); + findConnectionForAction(callId, "onFeaturesChanged").setFeatures(features); + } + + /** @hide */ + @Override + protected void onPhoneAccountClicked(String callId) { + Log.d(this, "onPhoneAccountClicked %s", callId); + findConnectionForAction(callId, "onPhoneAccountClicked").onPhoneAccountClicked(); + } + + /** @hide */ @Override protected final void onAdapterAttached(CallServiceAdapter adapter) { if (mAreAccountsInitialized) { @@ -569,18 +581,4 @@ public abstract class ConnectionService extends CallService { Log.w(this, "%s - Cannot find Connection %s", action, callId); return NULL_CONNECTION; } - - /** - * Handles changes to the features of a connection. - * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a - * bit-mask. - * - * @param callId The call to set the features for. - * @param features The new features of the call. - */ - @Override - public final void onFeaturesChanged(String callId, int features) { - Log.d(this, "onFeaturesChanged %s %d", callId, features); - findConnectionForAction(callId, "onFeaturesChanged").setFeatures(features); - } } diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java index ce52d19..aef05fd 100644 --- a/telecomm/java/android/telecomm/InCallAdapter.java +++ b/telecomm/java/android/telecomm/InCallAdapter.java @@ -189,13 +189,13 @@ public final class InCallAdapter { } /** - * Instructs Telecomm to handoff the call to another call service. + * Instructs Telecomm that the phone account UI was clicked. * - * @param callId The identifier of the call to handoff. + * @param callId The identifier of the call. */ - public void handoffCall(String callId) { + public void phoneAccountClicked(String callId) { try { - mAdapter.handoffCall(callId); + mAdapter.phoneAccountClicked(callId); } catch (RemoteException e) { } } diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java index fc96f55..e08d450 100644 --- a/telecomm/java/android/telecomm/InCallCall.java +++ b/telecomm/java/android/telecomm/InCallCall.java @@ -43,7 +43,6 @@ public final class InCallCall implements Parcelable { private final GatewayInfo mGatewayInfo; private final PhoneAccount mAccount; private final CallServiceDescriptor mCurrentCallServiceDescriptor; - private final CallServiceDescriptor mHandoffCallServiceDescriptor; private final ICallVideoProvider mCallVideoProvider; private RemoteCallVideoProvider mRemoteCallVideoProvider; private final String mParentCallId; @@ -61,9 +60,8 @@ public final class InCallCall implements Parcelable { long connectTimeMillis, Uri handle, GatewayInfo gatewayInfo, - PhoneAccount subscription, + PhoneAccount account, CallServiceDescriptor descriptor, - CallServiceDescriptor handoffDescriptor, ICallVideoProvider callVideoProvider, String parentCallId, List<String> childCallIds, @@ -77,9 +75,8 @@ public final class InCallCall implements Parcelable { mConnectTimeMillis = connectTimeMillis; mHandle = handle; mGatewayInfo = gatewayInfo; - mAccount = subscription; + mAccount = account; mCurrentCallServiceDescriptor = descriptor; - mHandoffCallServiceDescriptor = handoffDescriptor; mCallVideoProvider = callVideoProvider; mParentCallId = parentCallId; mChildCallIds = childCallIds; @@ -150,14 +147,6 @@ public final class InCallCall implements Parcelable { } /** - * The descriptor for the call service that this call is being switched to, null if handoff is - * not in progress. - */ - public CallServiceDescriptor getHandoffCallServiceDescriptor() { - return mHandoffCallServiceDescriptor; - } - - /** * Returns an object for remotely communicating through the call video provider's binder. * @return The call video provider. */ @@ -217,7 +206,6 @@ public final class InCallCall implements Parcelable { GatewayInfo gatewayInfo = source.readParcelable(classLoader); PhoneAccount account = source.readParcelable(classLoader); CallServiceDescriptor descriptor = source.readParcelable(classLoader); - CallServiceDescriptor handoffDescriptor = source.readParcelable(classLoader); ICallVideoProvider callVideoProvider = ICallVideoProvider.Stub.asInterface(source.readStrongBinder()); String parentCallId = source.readString(); @@ -226,8 +214,7 @@ public final class InCallCall implements Parcelable { int features = source.readInt(); return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg, cannedSmsResponses, capabilities, connectTimeMillis, handle, gatewayInfo, - account, descriptor, handoffDescriptor, callVideoProvider, parentCallId, - childCallIds, features); + account, descriptor, callVideoProvider, parentCallId, childCallIds, features); } @Override @@ -256,7 +243,6 @@ public final class InCallCall implements Parcelable { destination.writeParcelable(mGatewayInfo, 0); destination.writeParcelable(mAccount, 0); destination.writeParcelable(mCurrentCallServiceDescriptor, 0); - destination.writeParcelable(mHandoffCallServiceDescriptor, 0); destination.writeStrongBinder( mCallVideoProvider != null ? mCallVideoProvider.asBinder() : null); destination.writeString(mParentCallId); diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java index b3ef927..81bee98 100644 --- a/telecomm/java/android/telecomm/RemoteConnectionService.java +++ b/telecomm/java/android/telecomm/RemoteConnectionService.java @@ -177,12 +177,6 @@ public final class RemoteConnectionService implements DeathRecipient { } /** ${inheritDoc} */ - @Override - public void handoffCall(String connectionId) { - // unnecessary. - } - - /** ${inheritDoc} */ @Override public void queryRemoteConnectionServices(RemoteServiceCallback callback) { try { diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java index 616c4a9..80901cf 100644 --- a/telecomm/java/android/telecomm/TelecommConstants.java +++ b/telecomm/java/android/telecomm/TelecommConstants.java @@ -27,13 +27,13 @@ public final class TelecommConstants { /** * <p>Activity action: Starts the UI for handing an incoming call. This intent starts the * in-call UI by notifying the Telecomm system that an incoming call exists for a specific call - * service (see {@link android.telecomm.CallService}). Telecomm reads the Intent extras to find - * and bind to the appropriate {@link android.telecomm.CallService} which Telecomm will - * ultimately use to control and get information about the call.</p> + * service (see {@link android.telecomm.ConnectionService}). Telecomm reads the Intent extras + * to find and bind to the appropriate {@link android.telecomm.ConnectionService} which + * Telecomm will ultimately use to control and get information about the call.</p> * * <p>Input: get*Extra field {@link #EXTRA_CALL_SERVICE_DESCRIPTOR} contains the component name - * of the {@link android.telecomm.CallService} that Telecomm should bind to. Telecomm will then - * ask the call service for more information about the call prior to showing any UI. + * of the {@link android.telecomm.ConnectionService} that Telecomm should bind to. Telecomm + * will then ask the call service for more information about the call prior to showing any UI. * * TODO(santoscordon): Needs permissions. * TODO(santoscordon): Consider moving this into a simple method call on a system service. @@ -46,7 +46,7 @@ public final class TelecommConstants { public static final String ACTION_CALL_SERVICE_PROVIDER = CallServiceProvider.class.getName(); /** - * The service action used to bind to {@link CallService} implementations. + * The service action used to bind to {@link ConnectionService} implementations. */ public static final String ACTION_CALL_SERVICE = CallService.class.getName(); @@ -73,8 +73,8 @@ public final class TelecommConstants { /** * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains - * metadata about the call. This {@link Bundle} will be returned to the {@link CallService} as - * part of {@link CallService#setIncomingCallId(String,Bundle)}. + * metadata about the call. This {@link Bundle} will be returned to the + * {@link ConnectionService}. */ public static final String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS"; diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/ICallService.aidl index 62ebd54..ac4e66c 100644 --- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl +++ b/telecomm/java/com/android/internal/telecomm/ICallService.aidl @@ -59,4 +59,6 @@ oneway interface ICallService { void splitFromConference(String callId); void onPostDialContinue(String callId, boolean proceed); + + void onPhoneAccountClicked(String callId); } diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl index e78762d..eee64da 100644 --- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl @@ -60,8 +60,6 @@ oneway interface ICallServiceAdapter { void onPostDialWait(String callId, String remaining); - void handoffCall(String callId); - void queryRemoteConnectionServices(RemoteServiceCallback callback); void setCallVideoProvider(String callId, ICallVideoProvider callVideoProvider); diff --git a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl index b66995a..7f3767c 100644 --- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl @@ -46,7 +46,7 @@ oneway interface IInCallAdapter { void postDialContinue(String callId, boolean proceed); - void handoffCall(String callId); + void phoneAccountClicked(String callId); void conference(String callId); diff --git a/telephony/java/com/android/internal/telephony/IMms.aidl b/telephony/java/com/android/internal/telephony/IMms.aidl index a745420..4da90a5 100644 --- a/telephony/java/com/android/internal/telephony/IMms.aidl +++ b/telephony/java/com/android/internal/telephony/IMms.aidl @@ -44,4 +44,25 @@ interface IMms { * broadcast when the message is downloaded, or the download is failed */ void downloadMessage(String callingPkg, String locationUrl, in PendingIntent downloadedIntent); + + /** + * Update the status of a pending (send-by-IP) MMS message handled by the carrier app. + * If the carrier app fails to send this message, it would be resent via carrier network. + * + * @param messageRef the reference number of the MMS message. + * @param success True if and only if the message was sent successfully. If its value is + * false, this message should be resent via carrier network + */ + void updateMmsSendStatus(int messageRef, boolean success); + + /** + * Update the status of a pending (download-by-IP) MMS message handled by the carrier app. + * If the carrier app fails to download this message, it would be re-downloaded via carrier + * network. + * + * @param messageRef the reference number of the MMS message. + * @param pdu non-empty if downloaded successfully, otherwise, it is empty and the message + * will be downloaded via carrier network + */ + void updateMmsDownloadStatus(int messageRef, in byte[] pdu); } |