diff options
79 files changed, 1995 insertions, 388 deletions
diff --git a/api/current.txt b/api/current.txt index 0f35a7b..8d71375 100644 --- a/api/current.txt +++ b/api/current.txt @@ -239,6 +239,8 @@ package android { field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 field public static final int accessibilityFlags = 16843652; // 0x1010384 field public static final int accessibilityLiveRegion = 16843758; // 0x10103ee + field public static final int accessibilityTraversalAfter = 16844036; // 0x1010504 + field public static final int accessibilityTraversalBefore = 16844035; // 0x1010503 field public static final int accountPreferences = 16843423; // 0x101029f field public static final int accountType = 16843407; // 0x101028f field public static final int action = 16842797; // 0x101002d @@ -28093,6 +28095,7 @@ package android.telecom { method public final java.util.List<android.telecom.Connection> getConferenceableConnections(); method public final java.util.List<android.telecom.Connection> getConnections(); method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle(); + method public android.telecom.Connection getPrimaryConnection(); method public final int getState(); method public void onAudioStateChanged(android.telecom.AudioState); method public void onDisconnect(); @@ -28182,6 +28185,7 @@ package android.telecom { public abstract class ConnectionService extends android.app.Service { ctor public ConnectionService(); method public final void addConference(android.telecom.Conference); + method public final void addExistingConnection(android.telecom.PhoneAccountHandle, android.telecom.Connection); method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection); method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); @@ -28191,6 +28195,7 @@ package android.telecom { method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public void onRemoteConferenceAdded(android.telecom.RemoteConference); + method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection); field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService"; } @@ -28389,6 +28394,7 @@ package android.telecom { method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle); method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage(); method public boolean handleMmi(java.lang.String); + method public boolean handleMmi(android.telecom.PhoneAccountHandle, java.lang.String); method public boolean hasMultipleCallCapableAccounts(); method public boolean isInCall(); method public void registerPhoneAccount(android.telecom.PhoneAccount); @@ -32152,6 +32158,7 @@ package android.util { method public static int complexToDimensionPixelSize(int, android.util.DisplayMetrics); method public static float complexToFloat(int); method public static float complexToFraction(int, float, float); + method public int getComplexUnit(); method public float getDimension(android.util.DisplayMetrics); method public final float getFloat(); method public float getFraction(float, float); @@ -33607,6 +33614,8 @@ package android.view { method public static int generateViewId(); method public int getAccessibilityLiveRegion(); method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider(); + method public int getAccessibilityTraversalAfter(); + method public int getAccessibilityTraversalBefore(); method public float getAlpha(); method public android.view.animation.Animation getAnimation(); method public android.os.IBinder getApplicationWindowToken(); @@ -33883,6 +33892,8 @@ package android.view { method public void sendAccessibilityEventUnchecked(android.view.accessibility.AccessibilityEvent); method public void setAccessibilityDelegate(android.view.View.AccessibilityDelegate); method public void setAccessibilityLiveRegion(int); + method public void setAccessibilityTraversalAfter(int); + method public void setAccessibilityTraversalBefore(int); method public void setActivated(boolean); method public void setAlpha(float); method public void setAnimation(android.view.animation.Animation); @@ -35162,6 +35173,8 @@ package android.view.accessibility { method public java.lang.CharSequence getText(); method public int getTextSelectionEnd(); method public int getTextSelectionStart(); + method public android.view.accessibility.AccessibilityNodeInfo getTraversalAfter(); + method public android.view.accessibility.AccessibilityNodeInfo getTraversalBefore(); method public java.lang.String getViewIdResourceName(); method public android.view.accessibility.AccessibilityWindowInfo getWindow(); method public int getWindowId(); @@ -35232,6 +35245,10 @@ package android.view.accessibility { method public void setSource(android.view.View, int); method public void setText(java.lang.CharSequence); method public void setTextSelection(int, int); + method public void setTraversalAfter(android.view.View); + method public void setTraversalAfter(android.view.View, int); + method public void setTraversalBefore(android.view.View); + method public void setTraversalBefore(android.view.View, int); method public void setViewIdResourceName(java.lang.String); method public void setVisibleToUser(boolean); method public void writeToParcel(android.os.Parcel, int); @@ -38429,6 +38446,8 @@ package android.widget { method public boolean onLoadClass(java.lang.Class); method public void reapply(android.content.Context, android.view.View); method public void removeAllViews(int); + method public void setAccessibilityTraversalAfter(int, int); + method public void setAccessibilityTraversalBefore(int, int); method public void setBitmap(int, java.lang.String, android.graphics.Bitmap); method public void setBoolean(int, java.lang.String, boolean); method public void setBundle(int, java.lang.String, android.os.Bundle); diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 5e9d8f7..7fd586f 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -96,8 +96,9 @@ public final class Pm { "Error: Could not access the Package Manager. Is the system running?"; public static void main(String[] args) { + int exitCode = 1; try { - new Pm().run(args); + exitCode = new Pm().run(args); } catch (Exception e) { Log.e(TAG, "Error", e); System.err.println("Error: " + e); @@ -105,20 +106,20 @@ public final class Pm { System.err.println(PM_NOT_RUNNING_ERR); } } + System.exit(exitCode); } - public void run(String[] args) throws IOException, RemoteException { + public int run(String[] args) throws IOException, RemoteException { boolean validCommand = false; if (args.length < 1) { - showUsage(); - return; + return showUsage(); } mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user")); mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); if (mPm == null) { System.err.println(PM_NOT_RUNNING_ERR); - return; + return 1; } mInstaller = mPm.getPackageInstaller(); @@ -127,155 +128,129 @@ public final class Pm { mNextArg = 1; if ("list".equals(op)) { - runList(); - return; + return runList(); } if ("path".equals(op)) { - runPath(); - return; + return runPath(); } if ("dump".equals(op)) { - runDump(); - return; + return runDump(); } if ("install".equals(op)) { - runInstall(); - return; + return runInstall(); } if ("install-create".equals(op)) { - runInstallCreate(); - return; + return runInstallCreate(); } if ("install-write".equals(op)) { - runInstallWrite(); - return; + return runInstallWrite(); } if ("install-commit".equals(op)) { - runInstallCommit(); - return; + return runInstallCommit(); } if ("install-abandon".equals(op) || "install-destroy".equals(op)) { - runInstallAbandon(); - return; + return runInstallAbandon(); } if ("set-installer".equals(op)) { - runSetInstaller(); - return; + return runSetInstaller(); } if ("uninstall".equals(op)) { - runUninstall(); - return; + return runUninstall(); } if ("clear".equals(op)) { - runClear(); - return; + return runClear(); } if ("enable".equals(op)) { - runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); - return; + return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); } if ("disable".equals(op)) { - runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); - return; + return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); } if ("disable-user".equals(op)) { - runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER); - return; + return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER); } if ("disable-until-used".equals(op)) { - runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED); - return; + return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED); } if ("hide".equals(op)) { - runSetHiddenSetting(true); - return; + return runSetHiddenSetting(true); } if ("unhide".equals(op)) { - runSetHiddenSetting(false); - return; + return runSetHiddenSetting(false); } if ("grant".equals(op)) { - runGrantRevokePermission(true); - return; + return runGrantRevokePermission(true); } if ("revoke".equals(op)) { - runGrantRevokePermission(false); - return; + return runGrantRevokePermission(false); } if ("set-permission-enforced".equals(op)) { - runSetPermissionEnforced(); - return; + return runSetPermissionEnforced(); } if ("set-install-location".equals(op)) { - runSetInstallLocation(); - return; + return runSetInstallLocation(); } if ("get-install-location".equals(op)) { - runGetInstallLocation(); - return; + return runGetInstallLocation(); } if ("trim-caches".equals(op)) { - runTrimCaches(); - return; + return runTrimCaches(); } if ("create-user".equals(op)) { - runCreateUser(); - return; + return runCreateUser(); } if ("remove-user".equals(op)) { - runRemoveUser(); - return; + return runRemoveUser(); } if ("get-max-users".equals(op)) { - runGetMaxUsers(); - return; + return runGetMaxUsers(); } if ("force-dex-opt".equals(op)) { - runForceDexOpt(); - return; + return runForceDexOpt(); } try { if (args.length == 1) { if (args[0].equalsIgnoreCase("-l")) { validCommand = true; - runListPackages(false); + return runListPackages(false); } else if (args[0].equalsIgnoreCase("-lf")){ validCommand = true; - runListPackages(true); + return runListPackages(true); } } else if (args.length == 2) { if (args[0].equalsIgnoreCase("-p")) { validCommand = true; - displayPackageFilePath(args[1]); + return displayPackageFilePath(args[1]); } } + return 1; } finally { if (validCommand == false) { if (op != null) { @@ -296,35 +271,36 @@ public final class Pm { * pm list libraries * pm list instrumentation */ - private void runList() { + private int runList() { String type = nextArg(); if (type == null) { System.err.println("Error: didn't specify type of data to list"); - return; + return 1; } if ("package".equals(type) || "packages".equals(type)) { - runListPackages(false); + return runListPackages(false); } else if ("permission-groups".equals(type)) { - runListPermissionGroups(); + return runListPermissionGroups(); } else if ("permissions".equals(type)) { - runListPermissions(); + return runListPermissions(); } else if ("features".equals(type)) { - runListFeatures(); + return runListFeatures(); } else if ("libraries".equals(type)) { - runListLibraries(); + return runListLibraries(); } else if ("instrumentation".equals(type)) { - runListInstrumentation(); + return runListInstrumentation(); } else if ("users".equals(type)) { - runListUsers(); + return runListUsers(); } else { System.err.println("Error: unknown list type '" + type + "'"); + return 1; } } /** * Lists all the installed packages. */ - private void runListPackages(boolean showApplicationPackage) { + private int runListPackages(boolean showApplicationPackage) { int getFlags = 0; boolean listDisabled = false, listEnabled = false; boolean listSystem = false, listThirdParty = false; @@ -355,12 +331,12 @@ public final class Pm { getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES; } else { System.err.println("Error: Unknown option: " + opt); - return; + return 1; } } } catch (RuntimeException ex) { System.err.println("Error: " + ex.toString()); - return; + return 1; } String filter = nextArg(); @@ -393,9 +369,11 @@ public final class Pm { System.out.println(); } } + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } @@ -411,7 +389,7 @@ public final class Pm { * * pm list features */ - private void runListFeatures() { + private int runListFeatures() { try { List<FeatureInfo> list = new ArrayList<FeatureInfo>(); FeatureInfo[] rawList = mPm.getSystemAvailableFeatures(); @@ -438,9 +416,11 @@ public final class Pm { else System.out.println("reqGlEsVersion=0x" + Integer.toHexString(fi.reqGlEsVersion)); } + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } @@ -449,7 +429,7 @@ public final class Pm { * * pm list libraries */ - private void runListLibraries() { + private int runListLibraries() { try { List<String> list = new ArrayList<String>(); String[] rawList = mPm.getSystemSharedLibraryNames(); @@ -474,9 +454,11 @@ public final class Pm { System.out.print("library:"); System.out.println(lib); } + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } @@ -485,7 +467,7 @@ public final class Pm { * * pm list instrumentation [package] [-f] */ - private void runListInstrumentation() { + private int runListInstrumentation() { int flags = 0; // flags != 0 is only used to request meta-data boolean showPackage = false; String targetPackage = null; @@ -499,12 +481,12 @@ public final class Pm { targetPackage = opt; } else { System.err.println("Error: Unknown option: " + opt); - return; + return 1; } } } catch (RuntimeException ex) { System.err.println("Error: " + ex.toString()); - return; + return 1; } try { @@ -531,16 +513,18 @@ public final class Pm { System.out.print(ii.targetPackage); System.out.println(")"); } + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } /** * Lists all the known permission groups. */ - private void runListPermissionGroups() { + private int runListPermissionGroups() { try { List<PermissionGroupInfo> pgs = mPm.getAllPermissionGroups(0); @@ -550,9 +534,11 @@ public final class Pm { System.out.print("permission group:"); System.out.println(pgi.name); } + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } @@ -572,7 +558,7 @@ public final class Pm { /** * Lists all the permissions in a group. */ - private void runListPermissions() { + private int runListPermissions() { try { boolean labels = false; boolean groups = false; @@ -595,7 +581,7 @@ public final class Pm { dangerousOnly = true; } else { System.err.println("Error: Unknown option: " + opt); - return; + return 1; } } @@ -637,9 +623,11 @@ public final class Pm { doListPermissions(groupList, groups, labels, summary, -10000, 10000); } + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } @@ -739,22 +727,23 @@ public final class Pm { } } - private void runPath() { + private int runPath() { String pkg = nextArg(); if (pkg == null) { System.err.println("Error: no package specified"); - return; + return 1; } - displayPackageFilePath(pkg); + return displayPackageFilePath(pkg); } - private void runDump() { + private int runDump() { String pkg = nextArg(); if (pkg == null) { System.err.println("Error: no package specified"); - return; + return 1; } ActivityManager.dumpPackageStateStatic(FileDescriptor.out, pkg); + return 0; } class LocalPackageInstallObserver extends PackageInstallObserver { @@ -822,31 +811,34 @@ public final class Pm { return Integer.toString(result); } - private void runSetInstallLocation() { + private int runSetInstallLocation() { int loc; String arg = nextArg(); if (arg == null) { System.err.println("Error: no install location specified."); - return; + return 1; } try { loc = Integer.parseInt(arg); } catch (NumberFormatException e) { System.err.println("Error: install location has to be a number."); - return; + return 1; } try { if (!mPm.setInstallLocation(loc)) { System.err.println("Error: install location has to be a number."); + return 1; } + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } - private void runGetInstallLocation() { + private int runGetInstallLocation() { try { int loc = mPm.getInstallLocation(); String locStr = "invalid"; @@ -858,13 +850,15 @@ public final class Pm { locStr = "external"; } System.out.println(loc + "[" + locStr + "]"); + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } - private void runInstall() { + private int runInstall() { int installFlags = 0; int userId = UserHandle.USER_ALL; String installerPackageName = null; @@ -884,7 +878,7 @@ public final class Pm { installerPackageName = nextOptionData(); if (installerPackageName == null) { System.err.println("Error: no value specified for -i"); - return; + return 1; } } else if (opt.equals("-t")) { installFlags |= PackageManager.INSTALL_ALLOW_TEST; @@ -900,13 +894,13 @@ public final class Pm { originatingUriString = nextOptionData(); if (originatingUriString == null) { System.err.println("Error: must supply argument for --originating-uri"); - return; + return 1; } } else if (opt.equals("--referrer")) { referrer = nextOptionData(); if (referrer == null) { System.err.println("Error: must supply argument for --referrer"); - return; + return 1; } } else if (opt.equals("--abi")) { abi = checkAbiArgument(nextOptionData()); @@ -914,7 +908,7 @@ public final class Pm { userId = Integer.parseInt(nextOptionData()); } else { System.err.println("Error: Unknown option: " + opt); - return; + return 1; } } @@ -944,7 +938,7 @@ public final class Pm { System.err.println("\tpkg: " + apkFilePath); if (apkFilePath == null) { System.err.println("Error: no package specified"); - return; + return 1; } // Populate verificationURI, optionally present @@ -973,19 +967,22 @@ public final class Pm { } if (obs.result == PackageManager.INSTALL_SUCCEEDED) { System.out.println("Success"); + return 0; } else { System.err.println("Failure [" + installFailureToString(obs) + "]"); + return 1; } } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } - private void runInstallCreate() throws RemoteException { + private int runInstallCreate() throws RemoteException { int userId = UserHandle.USER_ALL; String installerPackageName = null; @@ -1040,9 +1037,10 @@ public final class Pm { // NOTE: adb depends on parsing this string System.out.println("Success: created install session [" + sessionId + "]"); + return 0; } - private void runInstallWrite() throws IOException, RemoteException { + private int runInstallWrite() throws IOException, RemoteException { long sizeBytes = -1; String opt; @@ -1097,6 +1095,7 @@ public final class Pm { session.fsync(out); System.out.println("Success: streamed " + total + " bytes"); + return 0; } finally { IoUtils.closeQuietly(out); IoUtils.closeQuietly(in); @@ -1104,7 +1103,7 @@ public final class Pm { } } - private void runInstallCommit() throws RemoteException { + private int runInstallCommit() throws RemoteException { final int sessionId = Integer.parseInt(nextArg()); PackageInstaller.Session session = null; @@ -1119,18 +1118,19 @@ public final class Pm { PackageInstaller.STATUS_FAILURE); if (status == PackageInstaller.STATUS_SUCCESS) { System.out.println("Success"); + return 0; } else { Log.e(TAG, "Failure details: " + result.getExtras()); - System.out.println("Failure [" + System.err.println("Failure [" + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); - return; + return 1; } } finally { IoUtils.closeQuietly(session); } } - private void runInstallAbandon() throws RemoteException { + private int runInstallAbandon() throws RemoteException { final int sessionId = Integer.parseInt(nextArg()); PackageInstaller.Session session = null; @@ -1138,12 +1138,13 @@ public final class Pm { session = new PackageInstaller.Session(mInstaller.openSession(sessionId)); session.abandon(); System.out.println("Success"); + return 0; } finally { IoUtils.closeQuietly(session); } } - private void runSetInstaller() throws RemoteException { + private int runSetInstaller() throws RemoteException { final String targetPackage = nextArg(); final String installerPackageName = nextArg(); @@ -1154,9 +1155,10 @@ public final class Pm { mPm.setInstallerPackageName(targetPackage, installerPackageName); System.out.println("Success"); + return 0; } - public void runCreateUser() { + public int runCreateUser() { String name; int userId = -1; int flags = 0; @@ -1167,7 +1169,7 @@ public final class Pm { if (optionData == null || !isNumber(optionData)) { System.err.println("Error: no USER_ID specified"); showUsage(); - return; + return 1; } else { userId = Integer.parseInt(optionData); } @@ -1176,13 +1178,13 @@ public final class Pm { } else { System.err.println("Error: unknown option " + opt); showUsage(); - return; + return 1; } } String arg = nextArg(); if (arg == null) { System.err.println("Error: no user name specified."); - return; + return 1; } name = arg; try { @@ -1194,75 +1196,85 @@ public final class Pm { } if (info != null) { System.out.println("Success: created user id " + info.id); + return 1; } else { System.err.println("Error: couldn't create User."); + return 1; } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } - } - public void runRemoveUser() { + public int runRemoveUser() { int userId; String arg = nextArg(); if (arg == null) { System.err.println("Error: no user id specified."); - return; + return 1; } try { userId = Integer.parseInt(arg); } catch (NumberFormatException e) { System.err.println("Error: user id '" + arg + "' is not a number."); - return; + return 1; } try { if (mUm.removeUser(userId)) { System.out.println("Success: removed user"); + return 0; } else { System.err.println("Error: couldn't remove user id " + userId); + return 1; } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } - public void runListUsers() { + public int runListUsers() { try { IActivityManager am = ActivityManagerNative.getDefault(); List<UserInfo> users = mUm.getUsers(false); if (users == null) { System.err.println("Error: couldn't get users"); + return 1; } else { System.out.println("Users:"); for (int i = 0; i < users.size(); i++) { String running = am.isUserRunning(users.get(i).id, false) ? " running" : ""; System.out.println("\t" + users.get(i).toString() + running); } + return 0; } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } - public void runGetMaxUsers() { + public int runGetMaxUsers() { System.out.println("Maximum supported users: " + UserManager.getMaxSupportedUsers()); + return 0; } - public void runForceDexOpt() { + public int runForceDexOpt() { final String packageName = nextArg(); try { mPm.forceDexOpt(packageName); + return 0; } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } - private void runUninstall() throws RemoteException { + private int runUninstall() throws RemoteException { int flags = 0; int userId = UserHandle.USER_ALL; @@ -1277,11 +1289,11 @@ public final class Pm { } else { showUsage(); System.err.println("Error: Invalid user: " + param); - return; + return 1; } } else { System.err.println("Error: Unknown option: " + opt); - return; + return 1; } } @@ -1289,7 +1301,7 @@ public final class Pm { if (pkg == null) { System.err.println("Error: no package specified"); showUsage(); - return; + return 1; } if (userId == UserHandle.USER_ALL) { @@ -1302,11 +1314,11 @@ public final class Pm { } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); - return; + return 1; } if (info == null) { System.err.println("Failure - not installed for " + userId); - return; + return 1; } final boolean isSystem = (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; @@ -1326,10 +1338,12 @@ public final class Pm { PackageInstaller.STATUS_FAILURE); if (status == PackageInstaller.STATUS_SUCCESS) { System.out.println("Success"); + return 0; } else { Log.e(TAG, "Failure details: " + result.getExtras()); - System.out.println("Failure [" + System.err.println("Failure [" + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); + return 1; } } @@ -1347,7 +1361,7 @@ public final class Pm { } } - private void runClear() { + private int runClear() { int userId = 0; String option = nextOption(); if (option != null && option.equals("--user")) { @@ -1355,7 +1369,7 @@ public final class Pm { if (optionData == null || !isNumber(optionData)) { System.err.println("Error: no USER_ID specified"); showUsage(); - return; + return 1; } else { userId = Integer.parseInt(optionData); } @@ -1365,7 +1379,7 @@ public final class Pm { if (pkg == null) { System.err.println("Error: no package specified"); showUsage(); - return; + return 1; } ClearDataObserver obs = new ClearDataObserver(); @@ -1381,13 +1395,16 @@ public final class Pm { } if (obs.result) { - System.err.println("Success"); + System.out.println("Success"); + return 0; } else { System.err.println("Failed"); + return 1; } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } @@ -1416,7 +1433,7 @@ public final class Pm { return true; } - private void runSetEnabledSetting(int state) { + private int runSetEnabledSetting(int state) { int userId = 0; String option = nextOption(); if (option != null && option.equals("--user")) { @@ -1424,7 +1441,7 @@ public final class Pm { if (optionData == null || !isNumber(optionData)) { System.err.println("Error: no USER_ID specified"); showUsage(); - return; + return 1; } else { userId = Integer.parseInt(optionData); } @@ -1434,34 +1451,38 @@ public final class Pm { if (pkg == null) { System.err.println("Error: no package or component specified"); showUsage(); - return; + return 1; } ComponentName cn = ComponentName.unflattenFromString(pkg); if (cn == null) { try { mPm.setApplicationEnabledSetting(pkg, state, 0, userId, "shell:" + android.os.Process.myUid()); - System.err.println("Package " + pkg + " new state: " + System.out.println("Package " + pkg + " new state: " + enabledSettingToString( mPm.getApplicationEnabledSetting(pkg, userId))); + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } else { try { mPm.setComponentEnabledSetting(cn, state, 0, userId); - System.err.println("Component " + cn.toShortString() + " new state: " + System.out.println("Component " + cn.toShortString() + " new state: " + enabledSettingToString( mPm.getComponentEnabledSetting(cn, userId))); + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } } - private void runSetHiddenSetting(boolean state) { + private int runSetHiddenSetting(boolean state) { int userId = 0; String option = nextOption(); if (option != null && option.equals("--user")) { @@ -1469,7 +1490,7 @@ public final class Pm { if (optionData == null || !isNumber(optionData)) { System.err.println("Error: no USER_ID specified"); showUsage(); - return; + return 1; } else { userId = Integer.parseInt(optionData); } @@ -1479,30 +1500,32 @@ public final class Pm { if (pkg == null) { System.err.println("Error: no package or component specified"); showUsage(); - return; + return 1; } try { mPm.setApplicationHiddenSettingAsUser(pkg, state, userId); - System.err.println("Package " + pkg + " new hidden state: " + System.out.println("Package " + pkg + " new hidden state: " + mPm.getApplicationHiddenSettingAsUser(pkg, userId)); + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } } - private void runGrantRevokePermission(boolean grant) { + private int runGrantRevokePermission(boolean grant) { String pkg = nextArg(); if (pkg == null) { System.err.println("Error: no package specified"); showUsage(); - return; + return 1; } String perm = nextArg(); if (perm == null) { System.err.println("Error: no permission specified"); showUsage(); - return; + return 1; } try { if (grant) { @@ -1510,41 +1533,49 @@ public final class Pm { } else { mPm.revokePermission(pkg, perm); } + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } catch (IllegalArgumentException e) { System.err.println("Bad argument: " + e.toString()); showUsage(); + return 1; } catch (SecurityException e) { System.err.println("Operation not allowed: " + e.toString()); + return 1; } } - private void runSetPermissionEnforced() { + private int runSetPermissionEnforced() { final String permission = nextArg(); if (permission == null) { System.err.println("Error: no permission specified"); showUsage(); - return; + return 1; } final String enforcedRaw = nextArg(); if (enforcedRaw == null) { System.err.println("Error: no enforcement specified"); showUsage(); - return; + return 1; } final boolean enforced = Boolean.parseBoolean(enforcedRaw); try { mPm.setPermissionEnforced(permission, enforced); + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } catch (IllegalArgumentException e) { System.err.println("Bad argument: " + e.toString()); showUsage(); + return 1; } catch (SecurityException e) { System.err.println("Operation not allowed: " + e.toString()); + return 1; } } @@ -1563,12 +1594,12 @@ public final class Pm { } - private void runTrimCaches() { + private int runTrimCaches() { String size = nextArg(); if (size == null) { System.err.println("Error: no size specified"); showUsage(); - return; + return 1; } int len = size.length(); long multiplier = 1; @@ -1583,7 +1614,7 @@ public final class Pm { } else { System.err.println("Invalid suffix: " + c); showUsage(); - return; + return 1; } size = size.substring(0, len-1); } @@ -1593,7 +1624,7 @@ public final class Pm { } catch (NumberFormatException e) { System.err.println("Error: expected number at: " + size); showUsage(); - return; + return 1; } ClearDataObserver obs = new ClearDataObserver(); try { @@ -1606,14 +1637,18 @@ public final class Pm { } } } + return 0; } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); + return 1; } catch (IllegalArgumentException e) { System.err.println("Bad argument: " + e.toString()); showUsage(); + return 1; } catch (SecurityException e) { System.err.println("Operation not allowed: " + e.toString()); + return 1; } } @@ -1621,7 +1656,7 @@ public final class Pm { * Displays the package file for a package. * @param pckg */ - private void displayPackageFilePath(String pckg) { + private int displayPackageFilePath(String pckg) { try { PackageInfo info = mPm.getPackageInfo(pckg, 0, 0); if (info != null && info.applicationInfo != null) { @@ -1632,12 +1667,14 @@ public final class Pm { System.out.print("package:"); System.out.println(splitSourceDir); } + return 0; } } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); } + return 1; } private Resources getResources(PackageItemInfo pii) { @@ -1752,7 +1789,7 @@ public final class Pm { return arg; } - private static void showUsage() { + private static int showUsage() { System.err.println("usage: pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]"); System.err.println(" pm list permission-groups"); System.err.println(" pm list permissions [-g] [-f] [-d] [-u] [GROUP]"); @@ -1873,5 +1910,6 @@ public final class Pm { System.err.println("pm remove-user: remove the user with the given USER_IDENTIFIER,"); System.err.println(" deleting all data associated with that user"); System.err.println(""); + return 1; } } diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index 82b6e35..9062892 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -638,6 +638,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { if (decorView != null) { decorView.getLocationOnScreen(decorLoc); } + Matrix tempMatrix = new Matrix(); for (String name: names) { Bundle sharedElementBundle = state.getBundle(name); if (sharedElementBundle != null) { @@ -647,7 +648,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { snapshot = mListener.onCreateSnapshotView(context, parcelable); } if (snapshot != null) { - setSharedElementState(snapshot, name, state, null, null, decorLoc); + setSharedElementState(snapshot, name, state, tempMatrix, null, decorLoc); } snapshots.add(snapshot); } diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 7894887..ecf19c7 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -133,16 +133,17 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { return; } mAreViewsReady = true; + final ViewGroup decor = getDecor(); // Ensure the views have been laid out before capturing the views -- we need the epicenter. - if (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()) { + if (decor == null || (decor.isAttachedToWindow() && + (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) { viewsReady(sharedElements); } else { - final View sharedElement = sharedElements.valueAt(0); - sharedElement.getViewTreeObserver() + decor.getViewTreeObserver() .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { - sharedElement.getViewTreeObserver().removeOnPreDrawListener(this); + decor.getViewTreeObserver().removeOnPreDrawListener(this); viewsReady(sharedElements); return true; } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a30ae57..74502fc 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3133,9 +3133,10 @@ public class DevicePolicyManager { } /** - * Called by a profile owner to disable account management for a specific type of account. + * Called by a device owner or profile owner to disable account management for a specific type + * of account. * - * <p>The calling device admin must be a profile owner. If it is not, a + * <p>The calling device admin must be a device owner or profile owner. If it is not, a * security exception will be thrown. * * <p>When account management is disabled for an account type, adding or removing an account diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 5ee0b67..c164340 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -499,20 +499,4 @@ public class LauncherApps { obtainMessage(MSG_UNAVAILABLE, info).sendToTarget(); } } - - /** - * TODO Remove after 2014-09-22 - * @hide - */ - public void addCallback(Callback callback) { - registerCallback(callback); - } - - /** - * TODO Remove after 2014-09-22 - * @hide - */ - public void removeCallback(Callback callback) { - unregisterCallback(callback); - } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 369efac..c54a5ba 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6629,7 +6629,8 @@ public final class Settings { WIFI_NUM_OPEN_NETWORKS_KEPT, EMERGENCY_TONE, CALL_AUTO_RETRY, - DOCK_AUDIO_MEDIA_ENABLED + DOCK_AUDIO_MEDIA_ENABLED, + LOW_POWER_MODE_TRIGGER_LEVEL }; // Populated lazily, guarded by class object: diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index d42ed03..74d4245 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -312,6 +312,18 @@ public class TypedValue { } /** + * Return the complex unit type for this value. For example, a dimen type + * with value 12sp will return {@link #COMPLEX_UNIT_SP}. Only use for values + * whose type is {@link #TYPE_DIMENSION}. + * + * @return The complex unit type. + */ + public int getComplexUnit() + { + return COMPLEX_UNIT_MASK & (data>>TypedValue.COMPLEX_UNIT_SHIFT); + } + + /** * Converts an unpacked complex data value holding a dimension to its final floating * point value. The two parameters <var>unit</var> and <var>value</var> * are as in {@link #TYPE_DIMENSION}. diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 3e7aae0..9fc80fc 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -85,4 +85,9 @@ oneway interface IWindow { * is done. */ void doneAnimating(); + + /** + * Called for non-application windows when the enter animation has completed. + */ + void dispatchWindowShown(); } diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 0711aed..00a8884 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -191,7 +191,8 @@ public class ThreadedRenderer extends HardwareRenderer { final float lightX = width / 2.0f; mWidth = width; mHeight = height; - if (surfaceInsets != null && !surfaceInsets.isEmpty()) { + if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0 + || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) { mHasInsets = true; mInsetLeft = surfaceInsets.left; mInsetTop = surfaceInsets.top; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e4f95a4..1d09696 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3109,6 +3109,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private MatchLabelForPredicate mMatchLabelForPredicate; /** + * Specifies a view before which this one is visited in accessibility traversal. + */ + private int mAccessibilityTraversalBeforeId = NO_ID; + + /** + * Specifies a view after which this one is visited in accessibility traversal. + */ + private int mAccessibilityTraversalAfterId = NO_ID; + + /** * Predicate for matching a view by its id. */ private MatchIdPredicate mMatchIdPredicate; @@ -3888,6 +3898,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, case com.android.internal.R.styleable.View_contentDescription: setContentDescription(a.getString(attr)); break; + case com.android.internal.R.styleable.View_accessibilityTraversalBefore: + setAccessibilityTraversalBefore(a.getResourceId(attr, NO_ID)); + break; + case com.android.internal.R.styleable.View_accessibilityTraversalAfter: + setAccessibilityTraversalAfter(a.getResourceId(attr, NO_ID)); + break; case com.android.internal.R.styleable.View_labelFor: setLabelFor(a.getResourceId(attr, NO_ID)); break; @@ -5611,6 +5627,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (rootView == null) { rootView = this; } + View label = rootView.findLabelForView(this, mID); if (label != null) { info.setLabeledBy(label); @@ -5639,6 +5656,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + if (mAccessibilityTraversalBeforeId != View.NO_ID) { + View rootView = getRootView(); + if (rootView == null) { + rootView = this; + } + View next = rootView.findViewInsideOutShouldExist(this, + mAccessibilityTraversalBeforeId); + if (next != null) { + info.setTraversalBefore(next); + } + } + + if (mAccessibilityTraversalAfterId != View.NO_ID) { + View rootView = getRootView(); + if (rootView == null) { + rootView = this; + } + View next = rootView.findViewInsideOutShouldExist(this, + mAccessibilityTraversalAfterId); + if (next != null) { + info.setTraversalAfter(next); + } + } + info.setVisibleToUser(isVisibleToUser()); info.setPackageName(mContext.getPackageName()); @@ -6043,6 +6084,94 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Sets the id of a view before which this one is visited in accessibility traversal. + * A screen-reader must visit the content of this view before the content of the one + * it precedes. For example, if view B is set to be before view A, then a screen-reader + * will traverse the entire content of B before traversing the entire content of A, + * regardles of what traversal strategy it is using. + * <p> + * Views that do not have specified before/after relationships are traversed in order + * determined by the screen-reader. + * </p> + * <p> + * Setting that this view is before a view that is not important for accessibility + * or if this view is not important for accessibility will have no effect as the + * screen-reader is not aware of unimportant views. + * </p> + * + * @param beforeId The id of a view this one precedes in accessibility traversal. + * + * @attr ref android.R.styleable#View_accessibilityTraversalBefore + * + * @see #setImportantForAccessibility(int) + */ + @RemotableViewMethod + public void setAccessibilityTraversalBefore(int beforeId) { + if (mAccessibilityTraversalBeforeId == beforeId) { + return; + } + mAccessibilityTraversalBeforeId = beforeId; + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } + + /** + * Gets the id of a view before which this one is visited in accessibility traversal. + * + * @return The id of a view this one precedes in accessibility traversal if + * specified, otherwise {@link #NO_ID}. + * + * @see #setAccessibilityTraversalBefore(int) + */ + public int getAccessibilityTraversalBefore() { + return mAccessibilityTraversalBeforeId; + } + + /** + * Sets the id of a view after which this one is visited in accessibility traversal. + * A screen-reader must visit the content of the other view before the content of this + * one. For example, if view B is set to be after view A, then a screen-reader + * will traverse the entire content of A before traversing the entire content of B, + * regardles of what traversal strategy it is using. + * <p> + * Views that do not have specified before/after relationships are traversed in order + * determined by the screen-reader. + * </p> + * <p> + * Setting that this view is after a view that is not important for accessibility + * or if this view is not important for accessibility will have no effect as the + * screen-reader is not aware of unimportant views. + * </p> + * + * @param afterId The id of a view this one succedees in accessibility traversal. + * + * @attr ref android.R.styleable#View_accessibilityTraversalAfter + * + * @see #setImportantForAccessibility(int) + */ + @RemotableViewMethod + public void setAccessibilityTraversalAfter(int afterId) { + if (mAccessibilityTraversalAfterId == afterId) { + return; + } + mAccessibilityTraversalAfterId = afterId; + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } + + /** + * Gets the id of a view after which this one is visited in accessibility traversal. + * + * @return The id of a view this one succeedes in accessibility traversal if + * specified, otherwise {@link #NO_ID}. + * + * @see #setAccessibilityTraversalAfter(int) + */ + public int getAccessibilityTraversalAfter() { + return mAccessibilityTraversalAfterId; + } + + /** * Gets the id of a view for which this view serves as a label for * accessibility purposes. * @@ -6061,11 +6190,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @RemotableViewMethod public void setLabelFor(int id) { + if (mLabelForId == id) { + return; + } mLabelForId = id; if (mLabelForId != View.NO_ID && mID == View.NO_ID) { mID = generateViewId(); } + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 27f78b6..f0d5252 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3089,6 +3089,7 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_INVALIDATE_WORLD = 23; private final static int MSG_WINDOW_MOVED = 24; private final static int MSG_SYNTHESIZE_INPUT_EVENT = 25; + private final static int MSG_DISPATCH_WINDOW_SHOWN = 26; final class ViewRootHandler extends Handler { @Override @@ -3138,6 +3139,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_WINDOW_MOVED"; case MSG_SYNTHESIZE_INPUT_EVENT: return "MSG_SYNTHESIZE_INPUT_EVENT"; + case MSG_DISPATCH_WINDOW_SHOWN: + return "MSG_DISPATCH_WINDOW_SHOWN"; } return super.getMessageName(message); } @@ -3366,6 +3369,9 @@ public final class ViewRootImpl implements ViewParent, invalidateWorld(mView); } } break; + case MSG_DISPATCH_WINDOW_SHOWN: { + handleDispatchWindowShown(); + } } } } @@ -5212,6 +5218,10 @@ public final class ViewRootImpl implements ViewParent, } } + public void handleDispatchWindowShown() { + mAttachInfo.mTreeObserver.dispatchOnWindowShown(); + } + public void getLastTouchPoint(Point outLocation) { outLocation.x = (int) mLastTouchPoint.x; outLocation.y = (int) mLastTouchPoint.y; @@ -6072,6 +6082,10 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } + public void dispatchWindowShown() { + mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN); + } + public void dispatchCloseSystemDialogs(String reason) { Message msg = Message.obtain(); msg.what = MSG_CLOSE_SYSTEM_DIALOGS; @@ -6582,6 +6596,14 @@ public final class ViewRootImpl implements ViewParent, viewAncestor.dispatchDoneAnimating(); } } + + @Override + public void dispatchWindowShown() { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchWindowShown(); + } + } } public static final class CalledFromWrongThreadException extends AndroidRuntimeException { diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index a9444b4..b85fec8 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -44,10 +44,15 @@ public final class ViewTreeObserver { private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners; private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners; + private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners; // These listeners cannot be mutated during dispatch private ArrayList<OnDrawListener> mOnDrawListeners; + /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after + * that the listener will be immediately called. */ + private boolean mWindowShown; + private boolean mAlive = true; /** @@ -174,6 +179,19 @@ public final class ViewTreeObserver { } /** + * Interface definition for a callback noting when a system window has been displayed. + * This is only used for non-Activity windows. Activity windows can use + * Activity.onEnterAnimationComplete() to get the same signal. + * @hide + */ + public interface OnWindowShownListener { + /** + * Callback method to be invoked when a non-activity window is fully shown. + */ + void onWindowShown(); + } + + /** * Parameters used with OnComputeInternalInsetsListener. * * We are not yet ready to commit to this API and support it, so @@ -375,6 +393,14 @@ public final class ViewTreeObserver { } } + if (observer.mOnWindowShownListeners != null) { + if (mOnWindowShownListeners != null) { + mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners); + } else { + mOnWindowShownListeners = observer.mOnWindowShownListeners; + } + } + observer.kill(); } @@ -568,6 +594,45 @@ public final class ViewTreeObserver { } /** + * Register a callback to be invoked when the view tree window has been shown + * + * @param listener The callback to add + * + * @throws IllegalStateException If {@link #isAlive()} returns false + * @hide + */ + public void addOnWindowShownListener(OnWindowShownListener listener) { + checkIsAlive(); + + if (mOnWindowShownListeners == null) { + mOnWindowShownListeners = new CopyOnWriteArray<OnWindowShownListener>(); + } + + mOnWindowShownListeners.add(listener); + if (mWindowShown) { + listener.onWindowShown(); + } + } + + /** + * Remove a previously installed window shown callback + * + * @param victim The callback to remove + * + * @throws IllegalStateException If {@link #isAlive()} returns false + * + * @see #addOnWindowShownListener(OnWindowShownListener) + * @hide + */ + public void removeOnWindowShownListener(OnWindowShownListener victim) { + checkIsAlive(); + if (mOnWindowShownListeners == null) { + return; + } + mOnWindowShownListeners.remove(victim); + } + + /** * <p>Register a callback to be invoked when the view tree is about to be drawn.</p> * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p> @@ -854,6 +919,27 @@ public final class ViewTreeObserver { } /** + * Notifies registered listeners that the window is now shown + * @hide + */ + @SuppressWarnings("unchecked") + public final void dispatchOnWindowShown() { + mWindowShown = true; + final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners; + if (listeners != null && listeners.size() > 0) { + CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).onWindowShown(); + } + } finally { + listeners.end(); + } + } + } + + /** * Notifies registered listeners that the drawing pass is about to start. */ public final void dispatchOnDraw() { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 5b48c0d..f4f047e 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1324,7 +1324,7 @@ public interface WindowManager extends ViewManager { * * @hide */ - public Rect surfaceInsets = new Rect(); + public final Rect surfaceInsets = new Rect(); /** * The desired bitmap format. May be one of the constants in diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 3987fbc..b5afdf7 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -547,6 +547,8 @@ public class AccessibilityNodeInfo implements Parcelable { private long mParentNodeId = ROOT_NODE_ID; private long mLabelForId = ROOT_NODE_ID; private long mLabeledById = ROOT_NODE_ID; + private long mTraversalBefore = ROOT_NODE_ID; + private long mTraversalAfter = ROOT_NODE_ID; private int mBooleanProperties; private final Rect mBoundsInParent = new Rect(); @@ -1046,6 +1048,126 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Gets the node before which this one is visited during traversal. A screen-reader + * must visit the content of this node before the content of the one it precedes. + * + * @return The succeeding node if such or <code>null</code>. + * + * @see #setTraversalBefore(android.view.View) + * @see #setTraversalBefore(android.view.View, int) + */ + public AccessibilityNodeInfo getTraversalBefore() { + enforceSealed(); + return getNodeForAccessibilityId(mTraversalBefore); + } + + /** + * Sets the view before whose node this one should be visited during traversal. A + * screen-reader must visit the content of this node before the content of the one + * it precedes. + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param view The view providing the preceding node. + * + * @see #getTraversalBefore() + */ + public void setTraversalBefore(View view) { + setTraversalBefore(view, UNDEFINED_ITEM_ID); + } + + /** + * Sets the node before which this one is visited during traversal. A screen-reader + * must visit the content of this node before the content of the one it precedes. + * The successor is a virtual descendant of the given <code>root</code>. If + * <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root is set + * as the successor. + * <p> + * A virtual descendant is an imaginary View that is reported as a part of the view + * hierarchy for accessibility purposes. This enables custom views that draw complex + * content to report them selves as a tree of virtual views, thus conveying their + * logical structure. + * </p> + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param root The root of the virtual subtree. + * @param virtualDescendantId The id of the virtual descendant. + */ + public void setTraversalBefore(View root, int virtualDescendantId) { + enforceNotSealed(); + final int rootAccessibilityViewId = (root != null) + ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; + mTraversalBefore = makeNodeId(rootAccessibilityViewId, virtualDescendantId); + } + + /** + * Gets the node after which this one is visited in accessibility traversal. + * A screen-reader must visit the content of the other node before the content + * of this one. + * + * @return The succeeding node if such or <code>null</code>. + * + * @see #setTraversalAfter(android.view.View) + * @see #setTraversalAfter(android.view.View, int) + */ + public AccessibilityNodeInfo getTraversalAfter() { + enforceSealed(); + return getNodeForAccessibilityId(mTraversalAfter); + } + + /** + * Sets the view whose node is visited after this one in accessibility traversal. + * A screen-reader must visit the content of the other node before the content + * of this one. + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param view The previous view. + * + * @see #getTraversalAfter() + */ + public void setTraversalAfter(View view) { + setTraversalAfter(view, UNDEFINED_ITEM_ID); + } + + /** + * Sets the node after which this one is visited in accessibility traversal. + * A screen-reader must visit the content of the other node before the content + * of this one. If <code>virtualDescendantId</code> equals to {@link View#NO_ID} + * the root is set as the predecessor. + * <p> + * A virtual descendant is an imaginary View that is reported as a part of the view + * hierarchy for accessibility purposes. This enables custom views that draw complex + * content to report them selves as a tree of virtual views, thus conveying their + * logical structure. + * </p> + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param root The root of the virtual subtree. + * @param virtualDescendantId The id of the virtual descendant. + */ + public void setTraversalAfter(View root, int virtualDescendantId) { + enforceNotSealed(); + final int rootAccessibilityViewId = (root != null) + ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; + mTraversalAfter = makeNodeId(rootAccessibilityViewId, virtualDescendantId); + } + + /** * Sets the maximum text length, or -1 for no limit. * <p> * Typically used to indicate that an editable text field has a limit on @@ -1229,13 +1351,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getParent() { enforceSealed(); - if (!canPerformRequestOverConnection(mParentNodeId)) { - return null; - } - AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, - mWindowId, mParentNodeId, false, FLAG_PREFETCH_PREDECESSORS - | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + return getNodeForAccessibilityId(mParentNodeId); } /** @@ -2055,13 +2171,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getLabelFor() { enforceSealed(); - if (!canPerformRequestOverConnection(mLabelForId)) { - return null; - } - AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, - mWindowId, mLabelForId, false, FLAG_PREFETCH_PREDECESSORS - | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + return getNodeForAccessibilityId(mLabelForId); } /** @@ -2113,13 +2223,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getLabeledBy() { enforceSealed(); - if (!canPerformRequestOverConnection(mLabeledById)) { - return null; - } - AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, - mWindowId, mLabeledById, false, FLAG_PREFETCH_PREDECESSORS - | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + return getNodeForAccessibilityId(mLabeledById); } /** @@ -2453,6 +2557,9 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeLong(mParentNodeId); parcel.writeLong(mLabelForId); parcel.writeLong(mLabeledById); + parcel.writeLong(mTraversalBefore); + parcel.writeLong(mTraversalAfter); + parcel.writeInt(mConnectionId); final LongArray childIds = mChildNodeIds; @@ -2571,6 +2678,8 @@ public class AccessibilityNodeInfo implements Parcelable { mParentNodeId = other.mParentNodeId; mLabelForId = other.mLabelForId; mLabeledById = other.mLabeledById; + mTraversalBefore = other.mTraversalBefore; + mTraversalAfter = other.mTraversalAfter; mWindowId = other.mWindowId; mConnectionId = other.mConnectionId; mBoundsInParent.set(other.mBoundsInParent); @@ -2633,6 +2742,9 @@ public class AccessibilityNodeInfo implements Parcelable { mParentNodeId = parcel.readLong(); mLabelForId = parcel.readLong(); mLabeledById = parcel.readLong(); + mTraversalBefore = parcel.readLong(); + mTraversalAfter = parcel.readLong(); + mConnectionId = parcel.readInt(); final int childrenSize = parcel.readInt(); @@ -2725,6 +2837,8 @@ public class AccessibilityNodeInfo implements Parcelable { mParentNodeId = ROOT_NODE_ID; mLabelForId = ROOT_NODE_ID; mLabeledById = ROOT_NODE_ID; + mTraversalBefore = ROOT_NODE_ID; + mTraversalAfter = ROOT_NODE_ID; mWindowId = UNDEFINED_ITEM_ID; mConnectionId = UNDEFINED_CONNECTION_ID; mMaxTextLength = -1; @@ -2911,6 +3025,8 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId)); builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); builder.append("; mParentNodeId: " + mParentNodeId); + builder.append("; traversalBefore: ").append(mTraversalBefore); + builder.append("; traversalAfter: ").append(mTraversalAfter); int granularities = mMovementGranularities; builder.append("; MovementGranularities: ["); @@ -2963,6 +3079,16 @@ public class AccessibilityNodeInfo implements Parcelable { return builder.toString(); } + private AccessibilityNodeInfo getNodeForAccessibilityId(long accessibilityId) { + if (!canPerformRequestOverConnection(accessibilityId)) { + return null; + } + AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, + mWindowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS + | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + } + /** * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}. * Each action has a unique id that is mandatory and optional data. diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 6c107a2..6927660 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2663,7 +2663,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * @return True if the selector should be shown */ boolean shouldShowSelector() { - return (!isInTouchMode()) || (touchModeDrawsInPressedState() && isPressed()); + return (isFocused() && !isInTouchMode()) || (touchModeDrawsInPressedState() && isPressed()); } private void drawSelector(Canvas canvas) { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 80f364b..dd7fa18 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -2528,6 +2528,26 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}. + * + * @param viewId The id of the view whose before view in accessibility traversal to set. + * @param nextId The id of the next in the accessibility traversal. + **/ + public void setAccessibilityTraversalBefore(int viewId, int nextId) { + setInt(viewId, "setAccessibilityTraversalBefore", nextId); + } + + /** + * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}. + * + * @param viewId The id of the view whose after view in accessibility traversal to set. + * @param nextId The id of the next in the accessibility traversal. + **/ + public void setAccessibilityTraversalAfter(int viewId, int nextId) { + setInt(viewId, "setAccessibilityTraversalAfter", nextId); + } + + /** * Equivalent to calling View.setLabelFor(int). * * @param viewId The id of the view whose property to set. diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 3630cc7..35e03c3 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -37,6 +37,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets; @@ -589,9 +590,10 @@ public class AlertController { mScrollView.removeView(mMessageView); if (mListView != null) { - final int childIndex = mScrollView.indexOfChild(mScrollView); - contentPanel.removeViewAt(childIndex); - contentPanel.addView(mListView, childIndex, + final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent(); + final int childIndex = scrollParent.indexOfChild(mScrollView); + scrollParent.removeViewAt(childIndex); + scrollParent.addView(mListView, childIndex, new LayoutParams(MATCH_PARENT, MATCH_PARENT)); } else { contentPanel.setVisibility(View.GONE); diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index ccffa19..7df76e5 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -278,9 +278,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel); if (rdl != null) { - rdl.setOnClickOutsideListener(new View.OnClickListener() { + rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() { @Override - public void onClick(View v) { + public void onDismissed() { finish(); } }); diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 50a7a5e..993ab58 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -102,4 +102,8 @@ public class BaseIWindow extends IWindow.Stub { @Override public void doneAnimating() { } + + @Override + public void dispatchWindowShown() { + } } diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 375822f..25b4945 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -63,18 +63,22 @@ public class ResolverDrawerLayout extends ViewGroup { private float mCollapseOffset; private int mCollapsibleHeight; + private int mUncollapsibleHeight; private int mTopOffset; private boolean mIsDragging; private boolean mOpenOnClick; private boolean mOpenOnLayout; + private boolean mDismissOnScrollerFinished; private final int mTouchSlop; private final float mMinFlingVelocity; private final OverScroller mScroller; private final VelocityTracker mVelocityTracker; - private OnClickListener mClickOutsideListener; + private OnDismissedListener mOnDismissedListener; + private RunOnDismissedListener mRunOnDismissedListener; + private float mInitialTouchX; private float mInitialTouchY; private float mLastTouchY; @@ -143,8 +147,8 @@ public class ResolverDrawerLayout extends ViewGroup { return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight; } - public void setOnClickOutsideListener(OnClickListener listener) { - mClickOutsideListener = listener; + public void setOnDismissedListener(OnDismissedListener listener) { + mOnDismissedListener = listener; } @Override @@ -194,7 +198,7 @@ public class ResolverDrawerLayout extends ViewGroup { } if (mIsDragging) { - mScroller.abortAnimation(); + abortAnimation(); } return mIsDragging || mOpenOnClick; } @@ -213,12 +217,9 @@ public class ResolverDrawerLayout extends ViewGroup { mInitialTouchX = x; mInitialTouchY = mLastTouchY = y; mActivePointerId = ev.getPointerId(0); - if (findChildUnder(mInitialTouchX, mInitialTouchY) == null && - mClickOutsideListener != null) { - mIsDragging = handled = true; - } - handled |= mCollapsibleHeight > 0; - mScroller.abortAnimation(); + mIsDragging = findChildUnder(mInitialTouchX, mInitialTouchY) != null; + handled = (!mIsDragging && mOnDismissedListener != null) || mCollapsibleHeight > 0; + abortAnimation(); } break; @@ -264,11 +265,12 @@ public class ResolverDrawerLayout extends ViewGroup { break; case MotionEvent.ACTION_UP: { + final boolean wasDragging = mIsDragging; mIsDragging = false; - if (!mIsDragging && findChildUnder(mInitialTouchX, mInitialTouchY) == null && + if (!wasDragging && findChildUnder(mInitialTouchX, mInitialTouchY) == null && findChildUnder(ev.getX(), ev.getY()) == null) { - if (mClickOutsideListener != null) { - mClickOutsideListener.onClick(this); + if (mOnDismissedListener != null) { + dispatchOnDismissed(); resetTouch(); return true; } @@ -281,7 +283,13 @@ public class ResolverDrawerLayout extends ViewGroup { mVelocityTracker.computeCurrentVelocity(1000); final float yvel = mVelocityTracker.getYVelocity(mActivePointerId); if (Math.abs(yvel) > mMinFlingVelocity) { - smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel); + if (mOnDismissedListener != null + && yvel > 0 && mCollapseOffset > mCollapsibleHeight) { + smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel); + mDismissOnScrollerFinished = true; + } else { + smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel); + } } else { smoothScrollTo( mCollapseOffset < mCollapsibleHeight / 2 ? 0 : mCollapsibleHeight, 0); @@ -327,17 +335,27 @@ public class ResolverDrawerLayout extends ViewGroup { @Override public void computeScroll() { super.computeScroll(); - if (!mScroller.isFinished()) { - final boolean keepGoing = mScroller.computeScrollOffset(); + if (mScroller.computeScrollOffset()) { + final boolean keepGoing = !mScroller.isFinished(); performDrag(mScroller.getCurrY() - mCollapseOffset); if (keepGoing) { postInvalidateOnAnimation(); + } else if (mDismissOnScrollerFinished && mOnDismissedListener != null) { + mRunOnDismissedListener = new RunOnDismissedListener(); + post(mRunOnDismissedListener); } } } + private void abortAnimation() { + mScroller.abortAnimation(); + mRunOnDismissedListener = null; + mDismissOnScrollerFinished = false; + } + private float performDrag(float dy) { - final float newPos = Math.max(0, Math.min(mCollapseOffset + dy, mCollapsibleHeight)); + final float newPos = Math.max(0, Math.min(mCollapseOffset + dy, + mCollapsibleHeight + mUncollapsibleHeight)); if (newPos != mCollapseOffset) { dy = newPos - mCollapseOffset; final int childCount = getChildCount(); @@ -356,11 +374,18 @@ public class ResolverDrawerLayout extends ViewGroup { return 0; } - private void smoothScrollTo(int yOffset, float velocity) { - if (getMaxCollapsedHeight() == 0) { - return; + void dispatchOnDismissed() { + if (mOnDismissedListener != null) { + mOnDismissedListener.onDismissed(); } - mScroller.abortAnimation(); + if (mRunOnDismissedListener != null) { + removeCallbacks(mRunOnDismissedListener); + mRunOnDismissedListener = null; + } + } + + private void smoothScrollTo(int yOffset, float velocity) { + abortAnimation(); final int sy = (int) mCollapseOffset; int dy = yOffset - sy; if (dy == 0) { @@ -490,6 +515,7 @@ public class ResolverDrawerLayout extends ViewGroup { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeOnTouchModeChangeListener(mTouchModeChangeListener); + abortAnimation(); } @Override @@ -585,6 +611,7 @@ public class ResolverDrawerLayout extends ViewGroup { mCollapsibleHeight = Math.max(0, heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); + mUncollapsibleHeight = heightUsed - mCollapsibleHeight; if (isLaidOut()) { mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); @@ -734,4 +761,15 @@ public class ResolverDrawerLayout extends ViewGroup { } }; } + + public interface OnDismissedListener { + public void onDismissed(); + } + + private class RunOnDismissedListener implements Runnable { + @Override + public void run() { + dispatchOnDismissed(); + } + } } diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/core/jni/android/graphics/pdf/PdfEditor.cpp index 5f60c9e..2b756e2 100644 --- a/core/jni/android/graphics/pdf/PdfEditor.cpp +++ b/core/jni/android/graphics/pdf/PdfEditor.cpp @@ -19,6 +19,9 @@ #include "fpdfview.h" #include "fpdfedit.h" #include "fpdfsave.h" +#include "fsdk_rendercontext.h" +#include "fpdf_transformpage.h" +#include "SkMatrix.h" #include <android_runtime/AndroidRuntime.h> #include <vector> @@ -29,6 +32,20 @@ namespace android { +enum PageBox {PAGE_BOX_MEDIA, PAGE_BOX_CROP}; + +static struct { + jfieldID x; + jfieldID y; +} gPointClassInfo; + +static struct { + jfieldID left; + jfieldID top; + jfieldID right; + jfieldID bottom; +} gRectClassInfo; + static Mutex sLock; static int sUnmatchedInitRequestCount = 0; @@ -144,18 +161,201 @@ static void nativeWrite(JNIEnv* env, jclass thiz, jlong documentPtr, jint fd) { } } +static void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + CPDF_Page* page = (CPDF_Page*) FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + double width = 0; + double height = 0; + + const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return; + } + + CFX_AffineMatrix matrix; + + SkMatrix* skTransform = reinterpret_cast<SkMatrix*>(transformPtr); + + SkScalar transformValues[6]; + skTransform->asAffine(transformValues); + + // PDF's coordinate system origin is left-bottom while in graphics it + // is the top-left. So, translate the PDF coordinates to ours. + matrix.Set(1, 0, 0, -1, 0, page->GetPageHeight()); + + // Apply the transformation what was created in our coordinates. + matrix.Concat(transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY], + transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY], + transformValues[SkMatrix::kATransX], transformValues[SkMatrix::kATransY]); + + // Translate the result back to PDF coordinates. + matrix.Concat(1, 0, 0, -1, 0, page->GetPageHeight()); + + FS_MATRIX transform = {matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f}; + FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom}; + + FPDFPage_TransFormWithClip(page, &transform, &clip); + + FPDF_ClosePage(page); +} + +static void nativeGetPageSize(JNIEnv* env, jclass thiz, jlong documentPtr, + jint pageIndex, jobject outSize) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + double width = 0; + double height = 0; + + const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return; + } + + env->SetIntField(outSize, gPointClassInfo.x, width); + env->SetIntField(outSize, gPointClassInfo.y, height); + + FPDF_ClosePage(page); +} + +static jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + FPDF_BOOL success = FPDF_VIEWERREF_GetPrintScaling(document); + return success ? JNI_TRUE : JNI_FALSE; +} + +static bool nativeGetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + PageBox pageBox, jobject outBox) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return false; + } + + float left; + float top; + float right; + float bottom; + + const FPDF_BOOL success = (pageBox == PAGE_BOX_MEDIA) + ? FPDFPage_GetMediaBox(page, &left, &top, &right, &bottom) + : FPDFPage_GetCropBox(page, &left, &top, &right, &bottom); + + FPDF_ClosePage(page); + + if (!success) { + return false; + } + + env->SetIntField(outBox, gRectClassInfo.left, (int) left); + env->SetIntField(outBox, gRectClassInfo.top, (int) top); + env->SetIntField(outBox, gRectClassInfo.right, (int) right); + env->SetIntField(outBox, gRectClassInfo.bottom, (int) bottom); + + return true; +} + +static jboolean nativeGetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject outMediaBox) { + const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, + outMediaBox); + return success ? JNI_TRUE : JNI_FALSE; +} + +static jboolean nativeGetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject outMediaBox) { + const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, + outMediaBox); + return success ? JNI_TRUE : JNI_FALSE; +} + +static void nativeSetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + PageBox pageBox, jobject box) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot open page"); + return; + } + + const int left = env->GetIntField(box, gRectClassInfo.left); + const int top = env->GetIntField(box, gRectClassInfo.top); + const int right = env->GetIntField(box, gRectClassInfo.right); + const int bottom = env->GetIntField(box, gRectClassInfo.bottom); + + if (pageBox == PAGE_BOX_MEDIA) { + FPDFPage_SetMediaBox(page, left, top, right, bottom); + } else { + FPDFPage_SetCropBox(page, left, top, right, bottom); + } + + FPDF_ClosePage(page); +} + +static void nativeSetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject mediaBox) { + nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, mediaBox); +} + +static void nativeSetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, + jobject mediaBox) { + nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, mediaBox); +} + static JNINativeMethod gPdfEditor_Methods[] = { {"nativeOpen", "(IJ)J", (void*) nativeOpen}, {"nativeClose", "(J)V", (void*) nativeClose}, {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage}, - {"nativeWrite", "(JI)V", (void*) nativeWrite} + {"nativeWrite", "(JI)V", (void*) nativeWrite}, + {"nativeSetTransformAndClip", "(JIJIIII)V", (void*) nativeSetTransformAndClip}, + {"nativeGetPageSize", "(JILandroid/graphics/Point;)V", (void*) nativeGetPageSize}, + {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, + {"nativeGetPageMediaBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageMediaBox}, + {"nativeSetPageMediaBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageMediaBox}, + {"nativeGetPageCropBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageCropBox}, + {"nativeSetPageCropBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageCropBox} }; int register_android_graphics_pdf_PdfEditor(JNIEnv* env) { - return android::AndroidRuntime::registerNativeMethods( + const int result = android::AndroidRuntime::registerNativeMethods( env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods, NELEM(gPdfEditor_Methods)); + + jclass pointClass = env->FindClass("android/graphics/Point"); + gPointClassInfo.x = env->GetFieldID(pointClass, "x", "I"); + gPointClassInfo.y = env->GetFieldID(pointClass, "y", "I"); + + jclass rectClass = env->FindClass("android/graphics/Rect"); + gRectClassInfo.left = env->GetFieldID(rectClass, "left", "I"); + gRectClassInfo.top = env->GetFieldID(rectClass, "top", "I"); + gRectClassInfo.right = env->GetFieldID(rectClass, "right", "I"); + gRectClassInfo.bottom = env->GetFieldID(rectClass, "bottom", "I"); + + return result; }; }; diff --git a/core/res/res/layout/select_dialog_item_material.xml b/core/res/res/layout/select_dialog_item_material.xml index fe326f3..b45edc6 100644 --- a/core/res/res/layout/select_dialog_item_material.xml +++ b/core/res/res/layout/select_dialog_item_material.xml @@ -28,6 +28,6 @@ android:textAppearance="?android:attr/textAppearanceListItemSmall" android:textColor="?android:attr/textColorAlertDialogListItem" android:gravity="center_vertical" - android:paddingStart="@dimen/alert_dialog_padding_material" - android:paddingEnd="@dimen/alert_dialog_padding_material" + android:paddingStart="?attr/listPreferredItemPaddingStart" + android:paddingEnd="?attr/listPreferredItemPaddingEnd" android:ellipsize="marquee" /> diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml index 3c03814..d7484e1 100644 --- a/core/res/res/values-mcc204-mnc04/config.xml +++ b/core/res/res/values-mcc204-mnc04/config.xml @@ -31,4 +31,11 @@ <item>"*611:+19085594899,BAE0000000000000"</item> <item>"*86:+1MDN,BAE0000000000000"</item> </string-array> + + <!-- Flag indicating whether strict threshold is used, or lenient threshold is used, + when evaluating RSRP for LTE antenna bar display + 0. Strict threshold + 1. Lenient threshold + --> + <integer name="config_LTE_RSRP_threshold_type">0</integer> </resources> diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml index d0a57b3..8cb2928 100644 --- a/core/res/res/values-mcc311-mnc480/config.xml +++ b/core/res/res/values-mcc311-mnc480/config.xml @@ -49,4 +49,11 @@ <item>"*611:+19085594899,"</item> <item>"*86:+1MDN,"</item> </string-array> + + <!-- Flag indicating whether strict threshold is used, or lenient threshold is used, + when evaluating RSRP for LTE antenna bar display + 0. Strict threshold + 1. Lenient threshold + --> + <integer name="config_LTE_RSRP_threshold_type">0</integer> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index c4131b3..91a8598 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2363,6 +2363,18 @@ representation this attribute can be used for providing such. --> <attr name="contentDescription" format="string" localization="suggested" /> + <!-- Sets the id of a view before which this one is visited in accessibility traversal. + A screen-reader must visit the content of this view before the content of the one + it precedes. + @see android.view.View#setAccessibilityTraversalBefore(int)} --> + <attr name="accessibilityTraversalBefore" format="integer" /> + + <!-- Sets the id of a view after which this one is visited in accessibility traversal. + A screen-reader must visit the content of the other view before the content of + this one. + @see android.view.View#setAccessibilityTraversalAfter(int)} --> + <attr name="accessibilityTraversalAfter" format="integer" /> + <!-- Name of the method in this View's context to invoke when the view is clicked. This name must correspond to a public method that takes exactly one parameter of type View. For instance, if you specify diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9332105d..60d8210 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1885,4 +1885,11 @@ <bool name="config_switch_phone_on_voice_reg_state_change">true</bool> <bool name="config_sms_force_7bit_encoding">false</bool> + + <!-- Flag indicating whether strict threshold is used, or lenient threshold is used, + when evaluating RSRP for LTE antenna bar display + 0. Strict threshold + 1. Lenient threshold + --> + <integer name="config_LTE_RSRP_threshold_type">1</integer> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index c0b7fd0..c0a5ab2 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2597,5 +2597,7 @@ <public type="attr" name="resizeClip"/> <public type="attr" name="collapseContentDescription"/> + <public type="attr" name="accessibilityTraversalBefore" /> + <public type="attr" name="accessibilityTraversalAfter" /> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0432425..afe7c78 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2094,4 +2094,7 @@ <java-symbol type="layout" name="simple_account_item" /> <java-symbol type="id" name="scrollIndicatorUp" /> <java-symbol type="id" name="scrollIndicatorDown" /> + + <!-- From SignalStrength --> + <java-symbol type="integer" name="config_LTE_RSRP_threshold_type" /> </resources> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index 1864f89..5640fc1 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -1019,10 +1019,10 @@ please see themes_device_defaults.xml. <item name="textAppearance">@style/TextAppearance.Material</item> <item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item> - <item name="listPreferredItemPaddingLeft">16dip</item> - <item name="listPreferredItemPaddingRight">16dip</item> - <item name="listPreferredItemPaddingStart">16dip</item> - <item name="listPreferredItemPaddingEnd">16dip</item> + <item name="listPreferredItemPaddingLeft">24dip</item> + <item name="listPreferredItemPaddingRight">24dip</item> + <item name="listPreferredItemPaddingStart">24dip</item> + <item name="listPreferredItemPaddingEnd">24dip</item> <item name="listDivider">@null</item> @@ -1135,10 +1135,10 @@ please see themes_device_defaults.xml. <item name="textAppearance">@style/TextAppearance.Material</item> <item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item> - <item name="listPreferredItemPaddingLeft">16dip</item> - <item name="listPreferredItemPaddingRight">16dip</item> - <item name="listPreferredItemPaddingStart">16dip</item> - <item name="listPreferredItemPaddingEnd">16dip</item> + <item name="listPreferredItemPaddingLeft">24dip</item> + <item name="listPreferredItemPaddingRight">24dip</item> + <item name="listPreferredItemPaddingStart">24dip</item> + <item name="listPreferredItemPaddingEnd">24dip</item> <item name="listDivider">@null</item> diff --git a/docs/html/tools/testing/activity_testing.jd b/docs/html/tools/testing/activity_testing.jd index 88ac9b2..8baa35d 100644 --- a/docs/html/tools/testing/activity_testing.jd +++ b/docs/html/tools/testing/activity_testing.jd @@ -242,7 +242,7 @@ parent.link=index.html This changes when you run tests against the application. With instrumentation-based classes, you can invoke methods against the UI of the application under test. The other test classes don't allow this. To run an entire test method on the UI thread, you can annotate the thread - with <code>@UIThreadTest</code>. Notice that this will run <em>all</em> of the method statements + with <code>@UiThreadTest</code>. Notice that this will run <em>all</em> of the method statements on the UI thread. Methods that do not interact with the UI are not allowed; for example, you can't invoke <code>Instrumentation.waitForIdleSync()</code>. </p> diff --git a/graphics/java/android/graphics/pdf/PdfEditor.java b/graphics/java/android/graphics/pdf/PdfEditor.java index 9837139..0b84d29 100644 --- a/graphics/java/android/graphics/pdf/PdfEditor.java +++ b/graphics/java/android/graphics/pdf/PdfEditor.java @@ -17,6 +17,10 @@ package android.graphics.pdf; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.Rect; import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.OsConstants; @@ -98,6 +102,109 @@ public final class PdfEditor { } /** + * Sets a transformation and clip for a given page. The transformation matrix if + * non-null must be affine as per {@link android.graphics.Matrix#isAffine()}. If + * the clip is null, then no clipping is performed. + * + * @param pageIndex The page whose transform to set. + * @param transform The transformation to apply. + * @param clip The clip to apply. + */ + public void setTransformAndClip(int pageIndex, @Nullable Matrix transform, + @Nullable Rect clip) { + throwIfClosed(); + throwIfPageNotInDocument(pageIndex); + throwIfNotNullAndNotAfine(transform); + if (transform == null) { + transform = Matrix.IDENTITY_MATRIX; + } + if (clip == null) { + Point size = new Point(); + getPageSize(pageIndex, size); + nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance, + 0, 0, size.x, size.y); + } else { + nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance, + clip.left, clip.top, clip.right, clip.bottom); + } + } + + /** + * Gets the size of a given page in mils (1/72"). + * + * @param pageIndex The page index. + * @param outSize The size output. + */ + public void getPageSize(int pageIndex, @NonNull Point outSize) { + throwIfClosed(); + throwIfOutSizeNull(outSize); + throwIfPageNotInDocument(pageIndex); + nativeGetPageSize(mNativeDocument, pageIndex, outSize); + } + + /** + * Gets the media box of a given page in mils (1/72"). + * + * @param pageIndex The page index. + * @param outMediaBox The media box output. + */ + public boolean getPageMediaBox(int pageIndex, @NonNull Rect outMediaBox) { + throwIfClosed(); + throwIfOutMediaBoxNull(outMediaBox); + throwIfPageNotInDocument(pageIndex); + return nativeGetPageMediaBox(mNativeDocument, pageIndex, outMediaBox); + } + + /** + * Sets the media box of a given page in mils (1/72"). + * + * @param pageIndex The page index. + * @param mediaBox The media box. + */ + public void setPageMediaBox(int pageIndex, @NonNull Rect mediaBox) { + throwIfClosed(); + throwIfMediaBoxNull(mediaBox); + throwIfPageNotInDocument(pageIndex); + nativeSetPageMediaBox(mNativeDocument, pageIndex, mediaBox); + } + + /** + * Gets the crop box of a given page in mils (1/72"). + * + * @param pageIndex The page index. + * @param outCropBox The crop box output. + */ + public boolean getPageCropBox(int pageIndex, @NonNull Rect outCropBox) { + throwIfClosed(); + throwIfOutCropBoxNull(outCropBox); + throwIfPageNotInDocument(pageIndex); + return nativeGetPageCropBox(mNativeDocument, pageIndex, outCropBox); + } + + /** + * Sets the crop box of a given page in mils (1/72"). + * + * @param pageIndex The page index. + * @param cropBox The crop box. + */ + public void setPageCropBox(int pageIndex, @NonNull Rect cropBox) { + throwIfClosed(); + throwIfCropBoxNull(cropBox); + throwIfPageNotInDocument(pageIndex); + nativeSetPageCropBox(mNativeDocument, pageIndex, cropBox); + } + + /** + * Gets whether the document prefers to be scaled for printing. + * + * @return Whether to scale the document. + */ + public boolean shouldScaleForPrinting() { + throwIfClosed(); + return nativeScaleForPrinting(mNativeDocument); + } + + /** * Writes the PDF file to the provided destination. * <p> * <strong>Note:</strong> This method takes ownership of the passed in file @@ -154,9 +261,57 @@ public final class PdfEditor { } } + private void throwIfNotNullAndNotAfine(Matrix matrix) { + if (matrix != null && !matrix.isAffine()) { + throw new IllegalStateException("Matrix must be afine"); + } + } + + private void throwIfOutSizeNull(Point outSize) { + if (outSize == null) { + throw new NullPointerException("outSize cannot be null"); + } + } + + private void throwIfOutMediaBoxNull(Rect outMediaBox) { + if (outMediaBox == null) { + throw new NullPointerException("outMediaBox cannot be null"); + } + } + + private void throwIfMediaBoxNull(Rect mediaBox) { + if (mediaBox == null) { + throw new NullPointerException("mediaBox cannot be null"); + } + } + + private void throwIfOutCropBoxNull(Rect outCropBox) { + if (outCropBox == null) { + throw new NullPointerException("outCropBox cannot be null"); + } + } + + private void throwIfCropBoxNull(Rect cropBox) { + if (cropBox == null) { + throw new NullPointerException("cropBox cannot be null"); + } + } + private static native long nativeOpen(int fd, long size); private static native void nativeClose(long documentPtr); private static native int nativeGetPageCount(long documentPtr); private static native int nativeRemovePage(long documentPtr, int pageIndex); private static native void nativeWrite(long documentPtr, int fd); + private static native void nativeSetTransformAndClip(long documentPtr, int pageIndex, + long transformPtr, int clipLeft, int clipTop, int clipRight, int clipBottom); + private static native void nativeGetPageSize(long documentPtr, int pageIndex, Point outSize); + private static native boolean nativeGetPageMediaBox(long documentPtr, int pageIndex, + Rect outMediaBox); + private static native void nativeSetPageMediaBox(long documentPtr, int pageIndex, + Rect mediaBox); + private static native boolean nativeGetPageCropBox(long documentPtr, int pageIndex, + Rect outMediaBox); + private static native void nativeSetPageCropBox(long documentPtr, int pageIndex, + Rect mediaBox); + private static native boolean nativeScaleForPrinting(long documentPtr); } diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 17d3251..20c4978 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -161,6 +161,12 @@ public final class AudioAttributes implements Parcelable { * Usage value to use when the usage is for game audio. */ public final static int USAGE_GAME = 14; + /** + * @hide + * Usage value to use when feeding audio to the platform and replacing "traditional" audio + * source, such as audio capture devices. + */ + public final static int USAGE_VIRTUAL_SOURCE = 15; /** * Flag defining a behavior where the audibility of the sound will be ensured by the system. @@ -374,6 +380,7 @@ public final class AudioAttributes implements Parcelable { case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: case USAGE_ASSISTANCE_SONIFICATION: case USAGE_GAME: + case USAGE_VIRTUAL_SOURCE: mUsage = usage; break; default: diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 716ff99..8fc0b8e 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2663,9 +2663,13 @@ public class AudioManager { } IAudioService service = getService(); try { - if (!service.registerAudioPolicy(policy.getConfig(), policy.token())) { + String regId = service.registerAudioPolicy(policy.getConfig(), policy.token()); + if (regId == null) { return ERROR; + } else { + policy.setRegistration(regId); } + // successful registration } catch (RemoteException e) { Log.e(TAG, "Dead object in registerAudioPolicyAsync()", e); return ERROR; diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 6a69517..2f68382 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -48,6 +48,7 @@ import android.hardware.hdmi.HdmiTvClient; import android.hardware.usb.UsbManager; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; +import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioPolicyConfig; import android.media.session.MediaSessionLegacyHelper; import android.os.Binder; @@ -118,6 +119,10 @@ public class AudioService extends IAudioService.Stub { /** Debug audio mode */ protected static final boolean DEBUG_MODE = Log.isLoggable(TAG + ".MOD", Log.DEBUG); + + /** Debug audio policy feature */ + protected static final boolean DEBUG_AP = Log.isLoggable(TAG + ".AP", Log.DEBUG); + /** Debug volumes */ protected static final boolean DEBUG_VOL = Log.isLoggable(TAG + ".VOL", Log.DEBUG); @@ -5634,31 +5639,33 @@ public class AudioService extends IAudioService.Stub { //========================================================================================== // Audio policy management //========================================================================================== - public boolean registerAudioPolicy(AudioPolicyConfig policyConfig, IBinder cb) { + public String registerAudioPolicy(AudioPolicyConfig policyConfig, IBinder cb) { //Log.v(TAG, "registerAudioPolicy for " + cb + " got policy:" + policyConfig); + String regId = null; boolean hasPermissionForPolicy = (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( android.Manifest.permission.MODIFY_AUDIO_ROUTING)); if (!hasPermissionForPolicy) { Slog.w(TAG, "Can't register audio policy for pid " + Binder.getCallingPid() + " / uid " + Binder.getCallingUid() + ", need MODIFY_AUDIO_ROUTING"); - return false; + return null; } synchronized (mAudioPolicies) { - AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, cb); try { + AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, cb); cb.linkToDeath(app, 0/*flags*/); + regId = app.connectMixes(); mAudioPolicies.put(cb, app); } catch (RemoteException e) { // audio policy owner has already died! Slog.w(TAG, "Audio policy registration failed, could not link to " + cb + " binder death", e); - return false; + return null; } } - // TODO implement registration with native audio policy (including permission check) - return true; + return regId; } + public void unregisterAudioPolicyAsync(IBinder cb) { synchronized (mAudioPolicies) { AudioPolicyProxy app = mAudioPolicies.remove(cb); @@ -5668,27 +5675,59 @@ public class AudioService extends IAudioService.Stub { } else { cb.unlinkToDeath(app, 0/*flags*/); } + app.disconnectMixes(); } - // TODO implement registration with native audio policy + // TODO implement clearing mix attribute matching info in native audio policy } - public class AudioPolicyProxy implements IBinder.DeathRecipient { + /** + * This internal class inherits from AudioPolicyConfig which contains all the mixes and + * their configurations. + */ + public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient { private static final String TAG = "AudioPolicyProxy"; AudioPolicyConfig mConfig; IBinder mToken; AudioPolicyProxy(AudioPolicyConfig config, IBinder token) { - mConfig = config; + super(config); + setRegistration(new String(config.toString() + ":ap:" + mAudioPolicyCounter++)); mToken = token; } public void binderDied() { synchronized (mAudioPolicies) { - Log.v(TAG, "audio policy " + mToken + " died"); + Log.i(TAG, "audio policy " + mToken + " died"); mAudioPolicies.remove(mToken); + disconnectMixes(); + } + } + + String connectMixes() { + updateMixes(AudioSystem.DEVICE_STATE_AVAILABLE); + return mRegistrationId; + } + + void disconnectMixes() { + updateMixes(AudioSystem.DEVICE_STATE_UNAVAILABLE); + } + + void updateMixes(int connectionState) { + for (AudioMix mix : mMixes) { + // TODO implement sending the mix attribute matching info to native audio policy + if (DEBUG_AP) { + Log.v(TAG, "AudioPolicyProxy connect mix state=" + connectionState + + " addr=" + mix.getRegistration()); } + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_REMOTE_SUBMIX, + connectionState, + mix.getRegistration()); + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, + connectionState, + mix.getRegistration()); } } }; private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies = new HashMap<IBinder, AudioPolicyProxy>(); + private int mAudioPolicyCounter = 0; // always accessed synchronized on mAudioPolicies } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 2d8042c..317cc21 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -207,6 +207,6 @@ interface IAudioService { boolean isHdmiSystemAudioSupported(); - boolean registerAudioPolicy(in AudioPolicyConfig policyConfig, IBinder cb); + String registerAudioPolicy(in AudioPolicyConfig policyConfig, IBinder cb); oneway void unregisterAudioPolicyAsync(in IBinder cb); } diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java index f7967f1..bb52682 100644 --- a/media/java/android/media/audiopolicy/AudioMix.java +++ b/media/java/android/media/audiopolicy/AudioMix.java @@ -24,13 +24,14 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * @hide CANDIDATE FOR PUBLIC API + * @hide */ public class AudioMix { private AudioMixingRule mRule; private AudioFormat mFormat; private int mRouteFlags; + private String mRegistrationId; /** * All parameters are guaranteed valid through the Builder. @@ -39,6 +40,7 @@ public class AudioMix { mRule = rule; mFormat = format; mRouteFlags = routeFlags; + mRegistrationId = null; } /** @@ -65,6 +67,15 @@ public class AudioMix { return mRule; } + void setRegistration(String regId) { + mRegistrationId = regId; + } + + /** @hide */ + public String getRegistration() { + return mRegistrationId; + } + /** @hide */ @IntDef(flag = true, value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } ) diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java index ced7881..2e06a80 100644 --- a/media/java/android/media/audiopolicy/AudioMixingRule.java +++ b/media/java/android/media/audiopolicy/AudioMixingRule.java @@ -23,7 +23,7 @@ import java.util.Iterator; /** - * @hide CANDIDATE FOR PUBLIC API + * @hide * * Here's an example of creating a mixing rule for all media playback: * <pre> diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 314eb88..255d828 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -17,18 +17,26 @@ package android.media.audiopolicy; import android.annotation.IntDef; +import android.content.Context; +import android.content.pm.PackageManager; +import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioManager; +import android.media.AudioRecord; +import android.media.AudioSystem; +import android.media.AudioTrack; +import android.media.MediaRecorder; import android.os.Binder; import android.os.IBinder; import android.util.Log; +import android.util.Slog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; /** - * @hide CANDIDATE FOR PUBLIC API + * @hide * AudioPolicy provides access to the management of audio routing and audio focus. */ public class AudioPolicy { @@ -49,11 +57,13 @@ public class AudioPolicy { public static final int POLICY_STATUS_REGISTERED = 2; private int mStatus; - private AudioPolicyStatusListener mStatusListener = null; + private String mRegistrationId; + private AudioPolicyStatusListener mStatusListener; private final IBinder mToken = new Binder(); /** @hide */ public IBinder token() { return mToken; } + private Context mContext; private AudioPolicyConfig mConfig; /** @hide */ @@ -62,13 +72,14 @@ public class AudioPolicy { /** * The parameter is guaranteed non-null through the Builder */ - private AudioPolicy(AudioPolicyConfig config) { + private AudioPolicy(AudioPolicyConfig config, Context context) { mConfig = config; if (mConfig.mMixes.isEmpty()) { mStatus = POLICY_STATUS_INVALID; } else { mStatus = POLICY_STATUS_UNREGISTERED; } + mContext = context; } /** @@ -76,12 +87,15 @@ public class AudioPolicy { */ public static class Builder { private ArrayList<AudioMix> mMixes; + private Context mContext; /** * Constructs a new Builder with no audio mixes. + * @param context the context for the policy */ - public Builder() { + public Builder(Context context) { mMixes = new ArrayList<AudioMix>(); + mContext = context; } /** @@ -99,10 +113,115 @@ public class AudioPolicy { } public AudioPolicy build() { - return new AudioPolicy(new AudioPolicyConfig(mMixes)); + return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext); } } + /** @hide */ + public void setRegistration(String regId) { + mRegistrationId = regId; + mConfig.setRegistration(regId); + } + + private boolean policyReadyToUse() { + if (mContext == null) { + Log.e(TAG, "Cannot use AudioPolicy without context"); + return false; + } + if (mRegistrationId == null) { + Log.e(TAG, "Cannot use unregistered AudioPolicy"); + return false; + } + if (!(PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_AUDIO_ROUTING))) { + Slog.w(TAG, "Cannot use AudioPolicy for pid " + Binder.getCallingPid() + " / uid " + + Binder.getCallingUid() + ", needs MODIFY_AUDIO_ROUTING"); + return false; + } + return true; + } + + private void checkMixReadyToUse(AudioMix mix, boolean forTrack) + throws IllegalArgumentException{ + if (mix == null) { + String msg = forTrack ? "Invalid null AudioMix for AudioTrack creation" + : "Invalid null AudioMix for AudioRecord creation"; + throw new IllegalArgumentException(msg); + } + if (!mConfig.mMixes.contains(mix)) { + throw new IllegalArgumentException("Invalid mix: not part of this policy"); + } + if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) != AudioMix.ROUTE_FLAG_LOOP_BACK) + { + throw new IllegalArgumentException("Invalid AudioMix: not defined for loop back"); + } + } + + /** + * @hide + * Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}. + * Audio buffers recorded through the created instance will contain the mix of the audio + * streams that fed the given mixer. + * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with + * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy. + * @return a new {@link AudioRecord} instance whose data format is the one defined in the + * {@link AudioMix}, or null if this policy was not successfully registered + * with {@link AudioManager#registerAudioPolicy(AudioPolicy)}. + * @throws IllegalArgumentException + */ + public AudioRecord createAudioRecordSink(AudioMix mix) throws IllegalArgumentException { + if (!policyReadyToUse()) { + Log.e(TAG, "Cannot create AudioRecord sink for AudioMix"); + return null; + } + checkMixReadyToUse(mix, false/*not for an AudioTrack*/); + // create the AudioRecord, configured for loop back, using the same format as the mix + AudioRecord ar = new AudioRecord( + new AudioAttributes.Builder() + .setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX) + .addTag(mix.getRegistration()) + .build(), + mix.getFormat(), + AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(), + // using stereo for buffer size to avoid the current poor support for masks + AudioFormat.CHANNEL_IN_STEREO, mix.getFormat().getEncoding()), + AudioManager.AUDIO_SESSION_ID_GENERATE + ); + return ar; + } + + /** + * @hide + * Create an {@link AudioTrack} instance that is associated with the given {@link AudioMix}. + * Audio buffers played through the created instance will be sent to the given mix + * to be recorded through the recording APIs. + * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with + * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy. + * @returna new {@link AudioTrack} instance whose data format is the one defined in the + * {@link AudioMix}, or null if this policy was not successfully registered + * with {@link AudioManager#registerAudioPolicy(AudioPolicy)}. + * @throws IllegalArgumentException + */ + public AudioTrack createAudioTrackSource(AudioMix mix) throws IllegalArgumentException { + if (!policyReadyToUse()) { + Log.e(TAG, "Cannot create AudioTrack source for AudioMix"); + return null; + } + checkMixReadyToUse(mix, true/*for an AudioTrack*/); + // create the AudioTrack, configured for loop back, using the same format as the mix + AudioTrack at = new AudioTrack( + new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_VIRTUAL_SOURCE) + .addTag(mix.getRegistration()) + .build(), + mix.getFormat(), + AudioTrack.getMinBufferSize(mix.getFormat().getSampleRate(), + mix.getFormat().getChannelMask(), mix.getFormat().getEncoding()), + AudioTrack.MODE_STREAM, + AudioManager.AUDIO_SESSION_ID_GENERATE + ); + return at; + } public int getStatus() { return mStatus; @@ -118,10 +237,9 @@ public class AudioPolicy { } /** @hide */ - @Override - public String toString () { + public String toLogFriendlyString() { String textDump = new String("android.media.audiopolicy.AudioPolicy:\n"); - textDump += "config=" + mConfig.toString(); + textDump += "config=" + mConfig.toLogFriendlyString(); return (textDump); } diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java index 2fc6d58..a9a4175 100644 --- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java +++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java @@ -36,7 +36,13 @@ public class AudioPolicyConfig implements Parcelable { private static final String TAG = "AudioPolicyConfig"; - ArrayList<AudioMix> mMixes; + protected ArrayList<AudioMix> mMixes; + + protected String mRegistrationId = null; + + protected AudioPolicyConfig(AudioPolicyConfig conf) { + mMixes = conf.mMixes; + } AudioPolicyConfig(ArrayList<AudioMix> mixes) { mMixes = mixes; @@ -117,7 +123,6 @@ public class AudioPolicyConfig implements Parcelable { } } - /** @hide */ public static final Parcelable.Creator<AudioPolicyConfig> CREATOR = new Parcelable.Creator<AudioPolicyConfig>() { /** @@ -133,9 +138,7 @@ public class AudioPolicyConfig implements Parcelable { } }; - /** @hide */ - @Override - public String toString () { + public String toLogFriendlyString () { String textDump = new String("android.media.audiopolicy.AudioPolicyConfig:\n"); textDump += mMixes.size() + " AudioMix:\n"; for(AudioMix mix : mMixes) { @@ -166,4 +169,13 @@ public class AudioPolicyConfig implements Parcelable { } return textDump; } + + public void setRegistration(String regId) { + mRegistrationId = regId; + int mixIndex = 0; + for (AudioMix mix : mMixes) { + mix.setRegistration(mRegistrationId + "mix:" + mixIndex++); + } + } + } diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfEditor.aidl b/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfEditor.aidl index b450ccb..01cabe1 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfEditor.aidl +++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfEditor.aidl @@ -18,6 +18,7 @@ package com.android.printspooler.renderer; import android.os.ParcelFileDescriptor; import android.print.PageRange; +import android.print.PrintAttributes; /** * Interface for communication with a remote pdf editor. @@ -25,6 +26,7 @@ import android.print.PageRange; interface IPdfEditor { int openDocument(in ParcelFileDescriptor source); void removePages(in PageRange[] pages); + void applyPrintAttributes(in PrintAttributes attributes); void write(in ParcelFileDescriptor destination); void closeDocument(); } diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java index 00e5051..0462e4d 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java @@ -87,7 +87,7 @@ public final class PdfManipulationService extends Service { } mRenderer = new PdfRenderer(source); return mRenderer.getPageCount(); - } catch (IOException|IllegalStateException e) { + } catch (IOException | IllegalStateException e) { IoUtils.closeQuietly(source); Log.e(LOG_TAG, "Cannot open file", e); return MALFORMED_PDF_FILE_ERROR; @@ -217,7 +217,7 @@ public final class PdfManipulationService extends Service { } mEditor = new PdfEditor(source); return mEditor.getPageCount(); - } catch (IOException|IllegalStateException e) { + } catch (IOException | IllegalStateException e) { IoUtils.closeQuietly(source); Log.e(LOG_TAG, "Cannot open file", e); throw new RemoteException(e.toString()); @@ -246,6 +246,111 @@ public final class PdfManipulationService extends Service { } @Override + public void applyPrintAttributes(PrintAttributes attributes) { + synchronized (mLock) { + throwIfNotOpened(); + if (DEBUG) { + Log.i(LOG_TAG, "applyPrintAttributes()"); + } + + Rect mediaBox = new Rect(); + Rect cropBox = new Rect(); + Matrix transform = new Matrix(); + + final boolean contentPortrait = attributes.getMediaSize().isPortrait(); + + final boolean layoutDirectionRtl = getResources().getConfiguration() + .getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + + // We do not want to rotate the media box, so take into account orientation. + final int dstWidthPts = contentPortrait + ? pointsFromMils(attributes.getMediaSize().getWidthMils()) + : pointsFromMils(attributes.getMediaSize().getHeightMils()); + final int dstHeightPts = contentPortrait + ? pointsFromMils(attributes.getMediaSize().getHeightMils()) + : pointsFromMils(attributes.getMediaSize().getWidthMils()); + + final boolean scaleForPrinting = mEditor.shouldScaleForPrinting(); + + final int pageCount = mEditor.getPageCount(); + for (int i = 0; i < pageCount; i++) { + if (!mEditor.getPageMediaBox(i, mediaBox)) { + Log.e(LOG_TAG, "Malformed PDF file"); + return; + } + + final int srcWidthPts = mediaBox.width(); + final int srcHeightPts = mediaBox.height(); + + // Update the media box with the desired size. + mediaBox.right = dstWidthPts; + mediaBox.bottom = dstHeightPts; + mEditor.setPageMediaBox(i, mediaBox); + + // Make sure content is top-left after media box resize. + transform.setTranslate(0, srcHeightPts - dstHeightPts); + + // Rotate the content if in landscape. + if (!contentPortrait) { + transform.postRotate(270); + transform.postTranslate(0, dstHeightPts); + } + + // Scale the content if document allows it. + final float scale; + if (scaleForPrinting) { + if (contentPortrait) { + scale = Math.min((float) dstWidthPts / srcWidthPts, + (float) dstHeightPts / srcHeightPts); + transform.postScale(scale, scale); + } else { + scale = Math.min((float) dstWidthPts / srcHeightPts, + (float) dstHeightPts / srcWidthPts); + transform.postScale(scale, scale, mediaBox.left, mediaBox.bottom); + } + } else { + scale = 1.0f; + } + + // Update the crop box relatively to the media box change, if needed. + if (mEditor.getPageCropBox(i, cropBox)) { + cropBox.left = (int) (cropBox.left * scale + 0.5f); + cropBox.top = (int) (cropBox.top * scale + 0.5f); + cropBox.right = (int) (cropBox.right * scale + 0.5f); + cropBox.bottom = (int) (cropBox.bottom * scale + 0.5f); + cropBox.intersect(mediaBox); + mEditor.setPageCropBox(i, cropBox); + } + + // If in RTL mode put the content in the logical top-right corner. + if (layoutDirectionRtl) { + final float dx = contentPortrait + ? dstWidthPts - (int) (srcWidthPts * scale + 0.5f) : 0; + final float dy = contentPortrait + ? 0 : - (dstHeightPts - (int) (srcWidthPts * scale + 0.5f)); + transform.postTranslate(dx, dy); + } + + // Adjust the physical margins if needed. + Margins minMargins = attributes.getMinMargins(); + final int paddingLeftPts = pointsFromMils(minMargins.getLeftMils()); + final int paddingTopPts = pointsFromMils(minMargins.getTopMils()); + final int paddingRightPts = pointsFromMils(minMargins.getRightMils()); + final int paddingBottomPts = pointsFromMils(minMargins.getBottomMils()); + + Rect clip = new Rect(mediaBox); + clip.left += paddingLeftPts; + clip.top += paddingTopPts; + clip.right -= paddingRightPts; + clip.bottom -= paddingBottomPts; + + // Apply the accumulated transforms. + mEditor.setTransformAndClip(i, transform, clip); + } + } + } + + @Override public void write(ParcelFileDescriptor destination) throws RemoteException { synchronized (mLock) { try { diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index 15ea9a7..f361884 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -592,7 +592,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mDestinationSpinner.post(new Runnable() { @Override public void run() { - shredPagesAndFinish(uri); + transformDocumentAndFinish(uri); } }); } else if (resultCode == RESULT_CANCELED) { @@ -922,7 +922,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) { startCreateDocumentActivity(); } else { - shredPagesAndFinish(null); + transformDocumentAndFinish(null); } } @@ -1597,8 +1597,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat return true; } - private void shredPagesAndFinish(final Uri writeToUri) { - new PageShredder(this, mPrintJob, mFileProvider, new Runnable() { + private void transformDocumentAndFinish(final Uri writeToUri) { + // If saving to PDF, apply the attibutes as we are acting as a print service. + PrintAttributes attributes = mDestinationSpinnerAdapter.getPdfPrinter() == mCurrentPrinter + ? mPrintJob.getAttributes() : null; + new DocumentTransformer(this, mPrintJob, mFileProvider, attributes, new Runnable() { @Override public void run() { if (writeToUri != null) { @@ -1606,7 +1609,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } doFinish(); } - }).shred(); + }).transform(); } private void doFinish() { @@ -2329,7 +2332,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } } - private static final class PageShredder implements ServiceConnection { + private static final class DocumentTransformer implements ServiceConnection { private static final String TEMP_FILE_PREFIX = "print_job"; private static final String TEMP_FILE_EXTENSION = ".pdf"; @@ -2341,20 +2344,24 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private final PageRange[] mPagesToShred; + private final PrintAttributes mAttributesToApply; + private final Runnable mCallback; - public PageShredder(Context context, PrintJobInfo printJob, - MutexFileProvider fileProvider, Runnable callback) { + public DocumentTransformer(Context context, PrintJobInfo printJob, + MutexFileProvider fileProvider, PrintAttributes attributes, + Runnable callback) { mContext = context; mPrintJob = printJob; mFileProvider = fileProvider; mCallback = callback; mPagesToShred = computePagesToShred(mPrintJob); + mAttributesToApply = attributes; } - public void shred() { + public void transform() { // If we have only the pages we want, done. - if (mPagesToShred.length <= 0) { + if (mPagesToShred.length <= 0 && mAttributesToApply == null) { mCallback.run(); return; } @@ -2376,14 +2383,14 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // final and this code is the last one to touch // them as shredding is the very last step, so the // UI is not interactive at this point. - shredPages(editor); + doTransform(editor); updatePrintJob(); return null; } @Override protected void onPostExecute(Void aVoid) { - mContext.unbindService(PageShredder.this); + mContext.unbindService(DocumentTransformer.this); mCallback.run(); } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -2394,7 +2401,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat /* do nothing */ } - private void shredPages(IPdfEditor editor) { + private void doTransform(IPdfEditor editor) { File tempFile = null; ParcelFileDescriptor src = null; ParcelFileDescriptor dst = null; @@ -2413,6 +2420,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // Drop the pages. editor.removePages(mPagesToShred); + // Apply print attributes if needed. + if (mAttributesToApply != null) { + editor.applyPrintAttributes(mAttributesToApply); + } + // Write the modified PDF to a temp file. tempFile = File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_EXTENSION, mContext.getCacheDir()); diff --git a/packages/SystemUI/res/drawable/ic_dismiss_all.xml b/packages/SystemUI/res/drawable/ic_dismiss_all.xml index 8e5e572..c32e5b11 100644 --- a/packages/SystemUI/res/drawable/ic_dismiss_all.xml +++ b/packages/SystemUI/res/drawable/ic_dismiss_all.xml @@ -16,6 +16,7 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" + android:autoMirrored="true" android:viewportWidth="48.0" android:viewportHeight="48.0"> <path diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_1.xml b/packages/SystemUI/res/drawable/ic_qs_signal_1.xml index adbda4a..7fb423e 100644 --- a/packages/SystemUI/res/drawable/ic_qs_signal_1.xml +++ b/packages/SystemUI/res/drawable/ic_qs_signal_1.xml @@ -21,14 +21,14 @@ Copyright (C) 2014 The Android Open Source Project android:viewportHeight="24.0"> <path android:fillColor="#FFFFFFFF" - android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/> + android:pathData="M19.7,20.0l2.0,0.0l0.0,2.0l-2.0,0.0z"/> <path android:fillColor="#FFFFFFFF" - android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/> + android:pathData="M19.7,10.0l2.0,0.0l0.0,8.1l-2.0,0.0z"/> <path - android:fillColor="#FFFFFFFF" - android:pathData="M11.300000,12.700000l-9.300000,9.300000 9.300000,0.000000z"/> + android:fillColor="#4DFFFFFF" + android:pathData="M17.7,8.0l4.299999,0.0 0.0,-6.0 -20.0,20.0 15.700001,0.0z"/> <path - android:pathData="M17.700001,8.000000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z" - android:fillColor="#4DFFFFFF"/> + android:fillColor="#FFFFFFFF" + android:pathData="M10.1,13.9l-8.1,8.1 8.1,0.0z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml index a71e33a..8baa4eb 100644 --- a/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml +++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml @@ -21,8 +21,8 @@ Copyright (C) 2014 The Android Open Source Project android:viewportHeight="24.0"> <path android:fillColor="#4DFFFFFF" - android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/> + android:pathData="M2.0,22.0l20.0,0.0 0.0,-20.0z"/> <path android:fillColor="#FFFFFFFF" - android:pathData="M11.300000,12.700000l-9.300000,9.300000 9.300000,0.000000z"/> + android:pathData="M10.1,13.9l-8.1,8.1 8.1,0.0z"/> </vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_1.xml b/packages/SystemUI/res/drawable/stat_sys_signal_1.xml index a53e946..d1124ee 100644 --- a/packages/SystemUI/res/drawable/stat_sys_signal_1.xml +++ b/packages/SystemUI/res/drawable/stat_sys_signal_1.xml @@ -21,14 +21,14 @@ Copyright (C) 2014 The Android Open Source Project android:viewportHeight="24.0"> <path android:fillColor="#FFFFFFFF" - android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/> + android:pathData="M19.7,20.0l2.0,0.0l0.0,2.0l-2.0,0.0z"/> <path android:fillColor="#FFFFFFFF" - android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/> + android:pathData="M19.7,10.0l2.0,0.0l0.0,8.1l-2.0,0.0z"/> <path - android:fillColor="#FFFFFFFF" - android:pathData="M11.300000,12.700000l-9.300000,9.300000 9.300000,0.000000z"/> + android:fillColor="#4DFFFFFF" + android:pathData="M17.7,8.0l4.299999,0.0 0.0,-6.0 -20.0,20.0 15.700001,0.0z"/> <path - android:pathData="M17.700001,8.000000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z" - android:fillColor="#4DFFFFFF"/> + android:fillColor="#FFFFFFFF" + android:pathData="M10.1,13.9l-8.1,8.1 8.1,0.0z"/> </vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_1_fully.xml b/packages/SystemUI/res/drawable/stat_sys_signal_1_fully.xml index 6bc55cd..29eda94 100644 --- a/packages/SystemUI/res/drawable/stat_sys_signal_1_fully.xml +++ b/packages/SystemUI/res/drawable/stat_sys_signal_1_fully.xml @@ -21,8 +21,8 @@ Copyright (C) 2014 The Android Open Source Project android:viewportHeight="24.0"> <path android:fillColor="#4DFFFFFF" - android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/> + android:pathData="M2.0,22.0l20.0,0.0 0.0,-20.0z"/> <path android:fillColor="#FFFFFFFF" - android:pathData="M11.300000,12.700000l-9.300000,9.300000 9.300000,0.000000z"/> + android:pathData="M10.1,13.9l-8.1,8.1 8.1,0.0z"/> </vector> diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index 0f8fe1c..d42ac61 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -351,6 +351,9 @@ public class ExpandHelper implements Gefingerpoken { mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } mVelocityTracker.addMovement(event); break; default: diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 0d393bf..f206e56 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -311,11 +311,14 @@ public class SwipeHelper implements Gefingerpoken { final View animView = mCallback.getChildContentView(view); final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view); float newPos; + boolean isLayoutRtl = view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; if (velocity < 0 || (velocity == 0 && getTranslation(animView) < 0) // if we use the Menu to dismiss an item in landscape, animate up - || (velocity == 0 && getTranslation(animView) == 0 && mSwipeDirection == Y)) { + || (velocity == 0 && getTranslation(animView) == 0 && mSwipeDirection == Y) + // if the language is rtl we prefer swiping to the left + || (velocity == 0 && getTranslation(animView) == 0 && isLayoutRtl)) { newPos = -getSize(animView); } else { newPos = getSize(animView); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index e4faa6a..02b9378 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -508,7 +508,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView if (mAppearAnimator != null) { mAppearAnimator.cancel(); } - mAnimationTranslationY = translationDirection * mActualHeight; + mAnimationTranslationY = translationDirection * getActualHeight(); if (mAppearAnimationFraction == -1.0f) { // not initialized yet, we start anew if (isAppearing) { @@ -601,14 +601,15 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView float top; float bottom; + final int actualHeight = getActualHeight(); if (mAnimationTranslationY > 0.0f) { - bottom = mActualHeight - heightFraction * mAnimationTranslationY * 0.1f + bottom = actualHeight - heightFraction * mAnimationTranslationY * 0.1f - translateYTotalAmount; top = bottom * heightFraction; } else { - top = heightFraction * (mActualHeight + mAnimationTranslationY) * 0.1f - + top = heightFraction * (actualHeight + mAnimationTranslationY) * 0.1f - translateYTotalAmount; - bottom = mActualHeight * (1 - heightFraction) + top * heightFraction; + bottom = actualHeight * (1 - heightFraction) + top * heightFraction; } mAppearAnimationRect.set(left, top, right, bottom); setOutlineRect(left, top + mAppearAnimationTranslation, right, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java index 897dbf2..479c2fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java @@ -36,4 +36,11 @@ public class DismissView extends StackScrollerDecorView { public void setOnButtonClickListener(OnClickListener listener) { mContent.setOnClickListener(listener); } + + public boolean isOnEmptySpace(float touchX, float touchY) { + return touchX < mContent.getX() + || touchX > mContent.getX() + mContent.getWidth() + || touchY < mContent.getY() + || touchY > mContent.getY() + mContent.getHeight(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 556c423..a4e5e74 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -23,10 +23,8 @@ import android.graphics.drawable.Drawable; import android.service.notification.StatusBarNotification; import android.util.AttributeSet; import android.view.View; -import android.view.ViewGroup; import android.view.ViewStub; import android.view.accessibility.AccessibilityEvent; - import android.widget.ImageView; import com.android.systemui.R; @@ -156,9 +154,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } public void resetHeight() { + super.resetHeight(); mMaxExpandHeight = 0; mWasReset = true; - mActualHeight = 0; onHeightReset(); requestLayout(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java index f85d32b..a18fff2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java @@ -41,7 +41,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { outline.setRect(0, mClipTopAmount, getWidth(), - Math.max(mActualHeight, mClipTopAmount)); + Math.max(getActualHeight(), mClipTopAmount)); } else { outline.setRect(mOutlineRect); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index c8f756e..edfbe86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -36,7 +36,7 @@ public abstract class ExpandableView extends FrameLayout { private final int mMaxNotificationHeight; private OnHeightChangedListener mOnHeightChangedListener; - protected int mActualHeight; + private int mActualHeight; protected int mClipTopAmount; private boolean mActualHeightInitialized; private ArrayList<View> mMatchParentViews = new ArrayList<View>(); @@ -103,6 +103,11 @@ public abstract class ExpandableView extends FrameLayout { } } + protected void resetHeight() { + mActualHeight = 0; + mActualHeightInitialized = false; + } + protected int getInitialHeight() { return getHeight(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 502490f..9b11f9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -27,11 +27,11 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; +import android.view.ViewTreeObserver; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; - import com.android.systemui.R; /** @@ -60,6 +60,16 @@ public class NotificationContentView extends FrameLayout { private boolean mDark; private final Paint mFadePaint = new Paint(); + private boolean mAnimate; + private ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener + = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + mAnimate = true; + getViewTreeObserver().removeOnPreDrawListener(this); + return true; + } + }; public NotificationContentView(Context context, AttributeSet attrs) { super(context, attrs); @@ -73,6 +83,12 @@ public class NotificationContentView extends FrameLayout { updateClipping(); } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + updateVisibility(); + } + public void reset() { if (mContractedChild != null) { mContractedChild.animate().cancel(); @@ -117,9 +133,31 @@ public class NotificationContentView extends FrameLayout { selectLayout(false /* animate */, true /* force */); } + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + updateVisibility(); + } + + private void updateVisibility() { + setVisible(isShown()); + } + + private void setVisible(final boolean isVisible) { + if (isVisible) { + + // We only animate if we are drawn at least once, otherwise the view might animate when + // it's shown the first time + getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener); + } else { + getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener); + mAnimate = false; + } + } + public void setActualHeight(int actualHeight) { mActualHeight = actualHeight; - selectLayout(true /* animate */, false /* force */); + selectLayout(mAnimate /* animate */, false /* force */); updateClipping(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 682c01c..6cb5bcc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -159,7 +159,7 @@ public class DemoStatusIcons extends LinearLayout implements DemoMode { } } StatusBarIcon icon = new StatusBarIcon(iconPkg, UserHandle.CURRENT, iconId, 0, 0, "Demo"); - StatusBarIconView v = new StatusBarIconView(getContext(), null); + StatusBarIconView v = new StatusBarIconView(getContext(), null, null); v.setTag(slot); v.set(icon); addView(v, 0, new LinearLayout.LayoutParams(mIconSize, mIconSize)); 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 0bde7ef..d9e44c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -52,7 +52,7 @@ import com.android.systemui.statusbar.stack.StackStateAnimator; public class NotificationPanelView extends PanelView implements ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener, View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener, - KeyguardAffordanceHelper.Callback { + KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener { // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is // changed. @@ -197,6 +197,7 @@ public class NotificationPanelView extends PanelView implements findViewById(R.id.notification_stack_scroller); mNotificationStackScroller.setOnHeightChangedListener(this); mNotificationStackScroller.setOverscrollTopChangedListener(this); + mNotificationStackScroller.setOnEmptySpaceClickListener(this); mNotificationStackScroller.setScrollView(mScrollView); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.fast_out_slow_in); @@ -526,13 +527,15 @@ public class NotificationPanelView extends PanelView implements mIntercepting = false; break; } + return super.onInterceptTouchEvent(event); + } - // Allow closing the whole panel when in SHADE state. - if (mStatusBarState == StatusBarState.SHADE) { - return super.onInterceptTouchEvent(event); - } else { - return !mQsExpanded && super.onInterceptTouchEvent(event); - } + @Override + protected boolean isInContentBounds(float x, float y) { + float yTransformed = y - mNotificationStackScroller.getY(); + float stackScrollerX = mNotificationStackScroller.getX(); + return mNotificationStackScroller.isInContentBounds(yTransformed) && stackScrollerX < x + && x < stackScrollerX + mNotificationStackScroller.getWidth(); } private void resetDownStates(MotionEvent event) { @@ -636,10 +639,9 @@ public class NotificationPanelView extends PanelView implements } private boolean isInQsArea(float x, float y) { - return mStatusBarState != StatusBarState.SHADE || - (x >= mScrollView.getLeft() && x <= mScrollView.getRight()) && - (y <= mNotificationStackScroller.getBottomMostNotificationBottom() - || y <= mQsContainer.getY() + mQsContainer.getHeight()); + return (x >= mScrollView.getLeft() && x <= mScrollView.getRight()) && + (y <= mNotificationStackScroller.getBottomMostNotificationBottom() + || y <= mQsContainer.getY() + mQsContainer.getHeight()); } private void handleQsDown(MotionEvent event) { @@ -1110,9 +1112,26 @@ public class NotificationPanelView extends PanelView implements } private float calculateQsTopPadding() { - // We can only do the smoother transition on Keyguard when we also are not collapsing from a - // scrolled quick settings. - if (mKeyguardShowing && mScrollYOverride == -1) { + if (mKeyguardShowing + && (mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted)) { + + // Either QS pushes the notifications down when fully expanded, or QS is fully above the + // notifications (mostly on tablets). maxNotifications denotes the normal top padding + // on Keyguard, maxQs denotes the top padding from the quick settings panel. We need to + // take the maximum and linearly interpolate with the panel expansion for a nice motion. + int maxNotifications = mClockPositionResult.stackScrollerPadding + - mClockPositionResult.stackScrollerPaddingAdjustment + - mNotificationTopPadding; + int maxQs = getTempQsMaxExpansion(); + int max = mStatusBarState == StatusBarState.KEYGUARD + ? Math.max(maxNotifications, maxQs) + : maxQs; + return (int) interpolate(getExpandedFraction(), + mQsMinExpansionHeight, max); + } else if (mKeyguardShowing && mScrollYOverride == -1) { + + // We can only do the smoother transition on Keyguard when we also are not collapsing + // from a scrolled quick settings. return interpolate(getQsExpansionFraction(), mNotificationStackScroller.getIntrinsicPadding() - mNotificationTopPadding, mQsMaxExpansionHeight); @@ -1124,7 +1143,9 @@ public class NotificationPanelView extends PanelView implements private void requestScrollerTopPaddingUpdate(boolean animate) { mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), mScrollView.getScrollY(), - mAnimateNextTopPaddingChange || animate); + mAnimateNextTopPaddingChange || animate, + mKeyguardShowing + && (mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted)); mAnimateNextTopPaddingChange = false; } @@ -1254,18 +1275,27 @@ public class NotificationPanelView extends PanelView implements @Override protected void onHeightUpdated(float expandedHeight) { - if (!mQsExpanded) { + if (!mQsExpanded || mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted) { positionClockAndNotifications(); } if (mTwoFingerQsExpand || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null && !mQsExpansionFromOverscroll) { - float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding() - + mNotificationStackScroller.getMinStackHeight() - + mNotificationStackScroller.getNotificationTopPadding(); - float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); - float t = (expandedHeight - panelHeightQsCollapsed) - / (panelHeightQsExpanded - panelHeightQsCollapsed); + float t; + if (mKeyguardShowing) { + + // On Keyguard, interpolate the QS expansion linearly to the panel expansion + t = expandedHeight / getMaxPanelHeight(); + } else { + // In Shade, interpolate linearly such that QS is closed whenever panel height is + // minimum QS expansion + minStackHeight + float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding() + + mNotificationStackScroller.getMinStackHeight() + + mNotificationStackScroller.getNotificationTopPadding(); + float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); + t = (expandedHeight - panelHeightQsCollapsed) + / (panelHeightQsExpanded - panelHeightQsCollapsed); + } setQsExpansion(mQsMinExpansionHeight + t * (getTempQsMaxExpansion() - mQsMinExpansionHeight)); } @@ -1299,8 +1329,10 @@ public class NotificationPanelView extends PanelView implements float notificationHeight = mNotificationStackScroller.getHeight() - mNotificationStackScroller.getEmptyBottomMargin() - mNotificationStackScroller.getTopPadding(); - float totalHeight = mQsMaxExpansionHeight + notificationHeight - + mNotificationStackScroller.getNotificationTopPadding(); + float totalHeight = Math.max( + mQsMaxExpansionHeight + mNotificationStackScroller.getNotificationTopPadding(), + mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment) + + notificationHeight; if (totalHeight > mNotificationStackScroller.getHeight()) { float fullyCollapsedHeight = mQsMaxExpansionHeight + mNotificationStackScroller.getMinStackHeight() @@ -1433,7 +1465,7 @@ public class NotificationPanelView extends PanelView implements super.onExpandingStarted(); mNotificationStackScroller.onExpansionStarted(); mIsExpanding = true; - mQsExpandedWhenExpandingStarted = mQsExpanded; + mQsExpandedWhenExpandingStarted = mQsFullyExpanded; if (mQsExpanded) { onQsExpansionStarted(); } @@ -1487,11 +1519,12 @@ public class NotificationPanelView extends PanelView implements @Override protected void onTrackingStarted() { super.onTrackingStarted(); + if (mQsFullyExpanded) { + mTwoFingerQsExpand = true; + } if (mStatusBar.getBarState() == StatusBarState.KEYGUARD || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) { mAfforanceHelper.animateHideLeftRightIcon(); - } else if (mQsExpanded) { - mTwoFingerQsExpand = true; } } @@ -1867,4 +1900,9 @@ public class NotificationPanelView extends PanelView implements public void onScreenTurnedOn() { mKeyguardStatusView.refreshTime(); } + + @Override + public void onEmptySpaceClicked(float x, float y) { + onEmptySpaceClick(x); + } } 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 0cf2d05..c706ef0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -70,6 +70,7 @@ public abstract class PanelView extends FrameLayout { private boolean mOverExpandedBeforeFling; private boolean mTouchAboveFalsingThreshold; private int mUnlockFalsingThreshold; + private boolean mTouchStartedInEmptyArea; private ValueAnimator mHeightAnimator; private ObjectAnimator mPeekAnimator; @@ -409,6 +410,7 @@ public abstract class PanelView extends FrameLayout { } mInitialTouchY = y; mInitialTouchX = x; + mTouchStartedInEmptyArea = !isInContentBounds(x, y); mTouchSlopExceeded = false; mJustPeeked = false; mPanelClosedOnDown = mExpandedHeight == 0.0f; @@ -432,7 +434,7 @@ public abstract class PanelView extends FrameLayout { case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; trackMovement(event); - if (scrolledToBottom) { + if (scrolledToBottom || mTouchStartedInEmptyArea) { if (h < -mTouchSlop && h < -Math.abs(x - mInitialTouchX)) { cancelHeightAnimator(); mInitialOffsetOnTouch = mExpandedHeight; @@ -452,6 +454,11 @@ public abstract class PanelView extends FrameLayout { return false; } + /** + * @return Whether a pair of coordinates are inside the visible view content bounds. + */ + protected abstract boolean isInContentBounds(float x, float y); + private void cancelHeightAnimator() { if (mHeightAnimator != null) { mHeightAnimator.cancel(); @@ -632,10 +639,10 @@ public abstract class PanelView extends FrameLayout { } mExpandedHeight = Math.max(0, mExpandedHeight); - onHeightUpdated(mExpandedHeight); mExpandedFraction = Math.min(1f, fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion); + onHeightUpdated(mExpandedHeight); notifyBarPanelExpansionChanged(); } @@ -903,7 +910,7 @@ public abstract class PanelView extends FrameLayout { * * @return whether the panel will be expanded after the action performed by this method */ - private boolean onEmptySpaceClick(float x) { + protected boolean onEmptySpaceClick(float x) { if (mHintAnimationRunning) { return true; } 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 4a20406..87ce565 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -40,6 +40,7 @@ import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.SpeedBumpView; +import com.android.systemui.statusbar.StackScrollerDecorView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.policy.ScrollAdapter; @@ -70,6 +71,12 @@ public class NotificationStackScrollLayout extends ViewGroup private SwipeHelper mSwipeHelper; private boolean mSwipingInProgress; private int mCurrentStackHeight = Integer.MAX_VALUE; + + /** + * mCurrentStackHeight is the actual stack height, mLastSetStackHeight is the stack height set + * externally from {@link #setStackHeight} + */ + private float mLastSetStackHeight; private int mOwnScrollY; private int mMaxLayoutHeight; @@ -84,6 +91,9 @@ public class NotificationStackScrollLayout extends ViewGroup private int mLastMotionY; private int mDownX; private int mActivePointerId; + private boolean mTouchIsClick; + private float mInitialTouchX; + private float mInitialTouchY; private int mSidePaddings; private Paint mDebugPaint; @@ -133,6 +143,7 @@ public class NotificationStackScrollLayout extends ViewGroup private OnChildLocationsChangedListener mListener; private OnOverscrollTopChangedListener mOverscrollTopChangedListener; private ExpandableView.OnHeightChangedListener mOnHeightChangedListener; + private OnEmptySpaceClickListener mOnEmptySpaceClickListener; private boolean mNeedsAnimation; private boolean mTopPaddingNeedsAnimation; private boolean mDimmedNeedsAnimation; @@ -367,6 +378,9 @@ public class NotificationStackScrollLayout extends ViewGroup if (childViewState == null) { return ViewState.LOCATION_UNKNOWN; } + if (childViewState.gone) { + return ViewState.LOCATION_GONE; + } return childViewState.location; } @@ -445,6 +459,7 @@ public class NotificationStackScrollLayout extends ViewGroup * @param height the new height of the stack */ public void setStackHeight(float height) { + mLastSetStackHeight = height; setIsExpanded(height > 0.0f); int newStackHeight = (int) height; int minStackHeight = getMinStackHeight(); @@ -581,7 +596,9 @@ public class NotificationStackScrollLayout extends ViewGroup final int count = getChildCount(); for (int childIdx = 0; childIdx < count; childIdx++) { ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx); - if (slidingChild.getVisibility() == GONE) { + if (slidingChild.getVisibility() == GONE + || slidingChild instanceof StackScrollerDecorView + || slidingChild == mSpeedBumpView) { continue; } float childTop = slidingChild.getTranslationY(); @@ -687,6 +704,7 @@ public class NotificationStackScrollLayout extends ViewGroup transformTouchEvent(ev, this, mScrollView); return mScrollView.onTouchEvent(ev); } + handleEmptySpaceClick(ev); boolean expandWantsIt = false; if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) { if (isCancelOrUp) { @@ -1338,7 +1356,19 @@ public class NotificationStackScrollLayout extends ViewGroup && initialVelocity > 0; } - public void updateTopPadding(float qsHeight, int scrollY, boolean animate) { + /** + * Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into + * account. + * + * @param qsHeight the top padding imposed by the quick settings panel + * @param scrollY how much the notifications are scrolled inside the QS/notifications scroll + * container + * @param animate whether to animate the change + * @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and + * {@code qsHeight} is the final top padding + */ + public void updateTopPadding(float qsHeight, int scrollY, boolean animate, + boolean ignoreIntrinsicPadding) { float start = qsHeight - scrollY + mNotificationTopPadding; float stackHeight = getHeight() - start; int minStackHeight = getMinStackHeight(); @@ -1346,13 +1376,13 @@ public class NotificationStackScrollLayout extends ViewGroup float overflow = minStackHeight - stackHeight; stackHeight = minStackHeight; start = getHeight() - stackHeight; - setTranslationY(overflow); mTopPaddingOverflow = overflow; } else { - setTranslationY(0); mTopPaddingOverflow = 0; } - setTopPadding(clampPadding((int) start), animate); + setTopPadding(ignoreIntrinsicPadding ? (int) start : clampPadding((int) start), + animate); + setStackHeight(mLastSetStackHeight); } public int getNotificationTopPadding() { @@ -1430,6 +1460,7 @@ public class NotificationStackScrollLayout extends ViewGroup transformTouchEvent(ev, mScrollView, this); } initDownStates(ev); + handleEmptySpaceClick(ev); boolean expandWantsIt = false; if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) { expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev); @@ -1448,11 +1479,31 @@ public class NotificationStackScrollLayout extends ViewGroup return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev); } + private void handleEmptySpaceClick(MotionEvent ev) { + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_MOVE: + if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop + || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop )) { + mTouchIsClick = false; + } + break; + case MotionEvent.ACTION_UP: + if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD && mTouchIsClick && + isBelowLastNotification(mInitialTouchX, mInitialTouchY)) { + mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY); + } + break; + } + } + private void initDownStates(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mExpandedInThisMotion = false; mOnlyScrollingInThisMotion = !mScroller.isFinished(); mDisallowScrollingInThisMotion = false; + mTouchIsClick = true; + mInitialTouchX = ev.getX(); + mInitialTouchY = ev.getY(); } } @@ -1886,7 +1937,14 @@ public class NotificationStackScrollLayout extends ViewGroup * @return Whether the specified motion event is actually happening over the content. */ private boolean isInContentBounds(MotionEvent event) { - return event.getY() < getHeight() - getEmptyBottomMargin(); + return isInContentBounds(event.getY()); + } + + /** + * @return Whether a y coordinate is inside the content. + */ + public boolean isInContentBounds(float y) { + return y < getHeight() - getEmptyBottomMargin(); } private void setIsBeingDragged(boolean isDragged) { @@ -1995,6 +2053,10 @@ public class NotificationStackScrollLayout extends ViewGroup this.mOnHeightChangedListener = mOnHeightChangedListener; } + public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) { + mOnEmptySpaceClickListener = listener; + } + public void onChildAnimationFinished() { requestChildrenUpdate(); } @@ -2245,6 +2307,24 @@ public class NotificationStackScrollLayout extends ViewGroup } } + private boolean isBelowLastNotification(float touchX, float touchY) { + ExpandableView lastChildNotGone = (ExpandableView) getLastChildNotGone(); + if (lastChildNotGone == null) { + return touchY > mIntrinsicPadding; + } + if (lastChildNotGone != mDismissView && lastChildNotGone != mEmptyShadeView) { + return touchY > lastChildNotGone.getY() + lastChildNotGone.getActualHeight(); + } else if (lastChildNotGone == mEmptyShadeView) { + return touchY > mEmptyShadeView.getY(); + } else { + float dismissY = mDismissView.getY(); + boolean belowDismissView = touchY > dismissY + mDismissView.getActualHeight(); + return belowDismissView || (touchY > dismissY + && mDismissView.isOnEmptySpace(touchX - mDismissView.getX(), + touchY - dismissY)); + } + } + /** * A listener that is notified when some child locations might have changed. */ @@ -2253,6 +2333,13 @@ public class NotificationStackScrollLayout extends ViewGroup } /** + * A listener that is notified when the empty space below the notifications is clicked on + */ + public interface OnEmptySpaceClickListener { + public void onEmptySpaceClicked(float x, float y); + } + + /** * A listener that gets notified when the overscroll at the top has changed. */ public interface OnOverscrollTopChangedListener { 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 026c2f3..4611370 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -234,6 +234,8 @@ public class StackScrollState { public static final int LOCATION_MAIN_AREA = 0x08; public static final int LOCATION_BOTTOM_STACK_PEEKING = 0x10; public static final int LOCATION_BOTTOM_STACK_HIDDEN = 0x20; + /** The view isn't layouted at all. */ + public static final int LOCATION_GONE = 0x40; float alpha; float yTranslation; diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 1623eac..edea274 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -774,7 +774,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (defIm == null && mMethodList.size() > 0) { defIm = InputMethodUtils.getMostApplicableDefaultIME( mSettings.getEnabledInputMethodListLocked()); - Slog.i(TAG, "No default found, using " + defIm.getId()); + if (defIm != null) { + Slog.i(TAG, "Default found, using " + defIm.getId()); + } else { + Slog.i(TAG, "No default found"); + } } if (defIm != null) { setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false); diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 89e3f49..8e46c4d 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -415,15 +415,9 @@ public class Watchdog extends Thread { dumpKernelStackTraces(); } - // Trigger the kernel to dump all blocked threads to the kernel log - try { - FileWriter sysrq_trigger = new FileWriter("/proc/sysrq-trigger"); - sysrq_trigger.write("w"); - sysrq_trigger.close(); - } catch (IOException e) { - Slog.e(TAG, "Failed to write to /proc/sysrq-trigger"); - Slog.e(TAG, e.getMessage()); - } + // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log + doSysRq('w'); + doSysRq('l'); // Try to add the error to the dropbox, but assuming that the ActivityManager // itself may be deadlocked. (which has happened, causing this statement to @@ -488,6 +482,16 @@ public class Watchdog extends Thread { } } + private void doSysRq(char c) { + try { + FileWriter sysrq_trigger = new FileWriter("/proc/sysrq-trigger"); + sysrq_trigger.write(c); + sysrq_trigger.close(); + } catch (IOException e) { + Slog.w(TAG, "Failed to write to /proc/sysrq-trigger", e); + } + } + private File dumpKernelStackTraces() { String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null); if (tracesPath == null || tracesPath.length() == 0) { diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java index dfc8df5..36263ec 100644 --- a/services/core/java/com/android/server/am/UserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java @@ -17,17 +17,11 @@ package com.android.server.am; import android.app.AlertDialog; -import android.app.Service; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; import android.content.res.Resources; -import android.os.Handler; -import android.os.Message; -import android.util.Slog; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewTreeObserver; import android.view.WindowManager; import android.widget.TextView; @@ -39,11 +33,10 @@ import com.android.internal.R; * in the background rather than just freeze the screen and not know if the user-switch affordance * was being handled. */ -final class UserSwitchingDialog extends AlertDialog { +final class UserSwitchingDialog extends AlertDialog + implements ViewTreeObserver.OnWindowShownListener { private static final String TAG = "ActivityManagerUserSwitchingDialog"; - private static final int MSG_START_USER = 1; - private final ActivityManagerService mService; private final int mUserId; @@ -74,19 +67,21 @@ final class UserSwitchingDialog extends AlertDialog { @Override public void show() { + // Slog.v(TAG, "show called"); super.show(); - // TODO: Instead of just an arbitrary delay, wait for a signal that the window was fully - // displayed by the window manager - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_USER), 250); + final View decorView = getWindow().getDecorView(); + if (decorView != null) { + decorView.getViewTreeObserver().addOnWindowShownListener(this); + } } - private final Handler mHandler = new Handler() { - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_START_USER: - mService.startUserInForeground(mUserId, UserSwitchingDialog.this); - break; - } + @Override + public void onWindowShown() { + // Slog.v(TAG, "onWindowShown called"); + mService.startUserInForeground(mUserId, this); + final View decorView = getWindow().getDecorView(); + if (decorView != null) { + decorView.getViewTreeObserver().removeOnWindowShownListener(this); } - }; + } } diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 07fe7ba..7d1da01 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -265,14 +265,9 @@ public class PacManager { } Intent intent = new Intent(); intent.setClassName(PAC_PACKAGE, PAC_SERVICE); - // Already bound no need to bind again. if ((mProxyConnection != null) && (mConnection != null)) { - if (mLastPort != -1) { - sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort)); - } else { - Log.e(TAG, "Received invalid port from Local Proxy," - + " PAC will not be operational"); - } + // Already bound no need to bind again, just download the new file. + IoThread.getHandler().post(mPacDownloader); return; } mConnection = new ServiceConnection() { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 13fb96f..96e56e9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2487,7 +2487,9 @@ public class WindowManagerService extends IWindowManager.Stub } } - win.mWinAnimator.mEnterAnimationPending = true; + final WindowStateAnimator winAnimator = win.mWinAnimator; + winAnimator.mEnterAnimationPending = true; + winAnimator.mEnteringAnimation = true; if (displayContent.isDefaultDisplay) { mPolicy.getContentInsetHintLw(attrs, outContentInsets); @@ -3099,6 +3101,7 @@ public class WindowManagerService extends IWindowManager.Stub if (oldVisibility == View.GONE) { winAnimator.mEnterAnimationPending = true; } + winAnimator.mEnteringAnimation = true; if (toBeDisplayed) { if (win.isDrawnLw() && okToDisplay()) { winAnimator.applyEnterAnimationLocked(); @@ -3167,6 +3170,7 @@ public class WindowManagerService extends IWindowManager.Stub } } else { winAnimator.mEnterAnimationPending = false; + winAnimator.mEnteringAnimation = false; if (winAnimator.mSurfaceControl != null) { if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win + ": mExiting=" + win.mExiting); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 0c727f3..fc3f2c2 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -40,6 +40,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.os.Debug; +import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; import android.view.Display; @@ -141,6 +142,11 @@ class WindowStateAnimator { // an enter animation. boolean mEnterAnimationPending; + /** Used to indicate that this window is undergoing an enter animation. Used for system + * windows to make the callback to View.dispatchOnWindowShownCallback(). Set when the + * window is first added or shown, cleared when the callback has been made. */ + boolean mEnteringAnimation; + boolean keyguardGoingAwayAnimation; /** This is set when there is no Surface */ @@ -428,6 +434,14 @@ class WindowStateAnimator { mWin.mChildWindows.get(i).mWinAnimator.finishExit(); } + if (mEnteringAnimation && mWin.mAppToken == null) { + try { + mEnteringAnimation = false; + mWin.mClient.dispatchWindowShown(); + } catch (RemoteException e) { + } + } + if (!mWin.mExiting) { return; } diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index 15cb786..003d5cd 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -334,6 +334,19 @@ public abstract class Conference { } /** + * Retrieves the primary connection associated with the conference. The primary connection is + * the connection from which the conference will retrieve its current state. + * + * @return The primary connection. + */ + public Connection getPrimaryConnection() { + if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) { + return null; + } + return mUnmodifiableChildConnections.get(0); + } + + /** * Inform this Conference that the state of its audio output has been changed externally. * * @param state The new audio state. diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 649533e..6e41c33 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -822,6 +822,40 @@ public abstract class ConnectionService extends Service { } /** + * Adds a connection created by the {@link ConnectionService} and informs telecom of the new + * connection. + * + * @param phoneAccountHandle The phone account handle for the connection. + * @param connection The connection to add. + */ + public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle, + Connection connection) { + + String id = addExistingConnectionInternal(connection); + if (id != null) { + List<String> emptyList = new ArrayList<>(0); + + ParcelableConnection parcelableConnection = new ParcelableConnection( + phoneAccountHandle, + connection.getState(), + connection.getCallCapabilities(), + connection.getAddress(), + connection.getAddressPresentation(), + connection.getCallerDisplayName(), + connection.getCallerDisplayNamePresentation(), + connection.getVideoProvider() == null ? + null : connection.getVideoProvider().getInterface(), + connection.getVideoState(), + connection.isRingbackRequested(), + connection.getAudioModeIsVoip(), + connection.getStatusHints(), + connection.getDisconnectCause(), + emptyList); + mAdapter.addExistingConnection(id, parcelableConnection); + } + } + + /** * Returns all the active {@code Connection}s for which this {@code ConnectionService} * has taken responsibility. * @@ -906,6 +940,12 @@ public abstract class ConnectionService extends Service { public void onRemoteConferenceAdded(RemoteConference conference) {} /** + * Called when an existing connection is added remotely. + * @param connection The existing connection which was added. + */ + public void onRemoteExistingConnectionAdded(RemoteConnection connection) {} + + /** * @hide */ public boolean containsConference(Conference conference) { @@ -917,6 +957,11 @@ public abstract class ConnectionService extends Service { onRemoteConferenceAdded(remoteConference); } + /** {@hide} */ + void addRemoteExistingConnection(RemoteConnection remoteConnection) { + onRemoteExistingConnectionAdded(remoteConnection); + } + private void onAccountsInitialized() { mAreAccountsInitialized = true; for (Runnable r : mPreInitializationConnectionRequests) { @@ -925,6 +970,18 @@ public abstract class ConnectionService extends Service { mPreInitializationConnectionRequests.clear(); } + /** + * Adds an existing connection to the list of connections, identified by a new UUID. + * + * @param connection The connection. + * @return The UUID of the connection (e.g. the call-id). + */ + private String addExistingConnectionInternal(Connection connection) { + String id = UUID.randomUUID().toString(); + addConnection(id, connection); + return id; + } + private void addConnection(String callId, Connection connection) { mConnectionById.put(callId, connection); mIdByConnection.put(connection, callId); diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java index c676172..e67af8c 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java @@ -344,4 +344,20 @@ final class ConnectionServiceAdapter implements DeathRecipient { } } } + + /** + * Informs telecom of an existing connection which was added by the {@link ConnectionService}. + * + * @param callId The unique ID of the call being added. + * @param connection The connection. + */ + void addExistingConnection(String callId, ParcelableConnection connection) { + Log.v(this, "addExistingConnection: %s", callId); + for (IConnectionServiceAdapter adapter : mAdapters) { + try { + adapter.addExistingConnection(callId, connection); + } catch (RemoteException ignored) { + } + } + } } diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java index 217dbc3..519a400 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java @@ -57,6 +57,7 @@ final class ConnectionServiceAdapterServant { private static final int MSG_SET_ADDRESS = 18; private static final int MSG_SET_CALLER_DISPLAY_NAME = 19; private static final int MSG_SET_CONFERENCEABLE_CONNECTIONS = 20; + private static final int MSG_ADD_EXISTING_CONNECTION = 21; private final IConnectionServiceAdapter mDelegate; @@ -199,6 +200,16 @@ final class ConnectionServiceAdapterServant { } break; } + case MSG_ADD_EXISTING_CONNECTION: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.addExistingConnection( + (String) args.arg1, (ParcelableConnection) args.arg2); + } finally { + args.recycle(); + } + break; + } } } }; @@ -345,6 +356,15 @@ final class ConnectionServiceAdapterServant { args.arg2 = conferenceableConnectionIds; mHandler.obtainMessage(MSG_SET_CONFERENCEABLE_CONNECTIONS, args).sendToTarget(); } + + @Override + public final void addExistingConnection( + String connectionId, ParcelableConnection connection) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = connectionId; + args.arg2 = connection; + mHandler.obtainMessage(MSG_ADD_EXISTING_CONNECTION, args).sendToTarget(); + } }; public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) { diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java index 9a094df..816e2bf 100644 --- a/telecomm/java/android/telecom/RemoteConnection.java +++ b/telecomm/java/android/telecom/RemoteConnection.java @@ -407,6 +407,29 @@ public final class RemoteConnection { } /** + * @hide + */ + RemoteConnection(String callId, IConnectionService connectionService, + ParcelableConnection connection) { + mConnectionId = callId; + mConnectionService = connectionService; + mConnected = true; + mState = connection.getState(); + mDisconnectCause = connection.getDisconnectCause(); + mRingbackRequested = connection.isRingbackRequested(); + mCallCapabilities = connection.getCapabilities(); + mVideoState = connection.getVideoState(); + mVideoProvider = new RemoteConnection.VideoProvider(connection.getVideoProvider()); + mIsVoipAudioMode = connection.getIsVoipAudioMode(); + mStatusHints = connection.getStatusHints(); + mAddress = connection.getHandle(); + mAddressPresentation = connection.getHandlePresentation(); + mCallerDisplayName = connection.getCallerDisplayName(); + mCallerDisplayNamePresentation = connection.getCallerDisplayNamePresentation(); + mConference = null; + } + + /** * Create a RemoteConnection which is used for failed connections. Note that using it for any * "real" purpose will almost certainly fail. Callers should note the failure and act * accordingly (moving on to another RemoteConnection, for example) @@ -415,7 +438,7 @@ public final class RemoteConnection { * @hide */ RemoteConnection(DisconnectCause disconnectCause) { - this("NULL", null, null); + mConnectionId = "NULL"; mConnected = false; mState = Connection.STATE_DISCONNECTED; mDisconnectCause = disconnectCause; diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index af4ee22..4bb78c0 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -41,8 +41,9 @@ import java.util.UUID; */ final class RemoteConnectionService { + // Note: Casting null to avoid ambiguous constructor reference. private static final RemoteConnection NULL_CONNECTION = - new RemoteConnection("NULL", null, null); + new RemoteConnection("NULL", null, (ConnectionRequest) null); private static final RemoteConference NULL_CONFERENCE = new RemoteConference("NULL", null); @@ -286,6 +287,15 @@ final class RemoteConnectionService { .setConferenceableConnections(conferenceable); } } + + @Override + public void addExistingConnection(String callId, ParcelableConnection connection) { + // TODO: add contents of this method + RemoteConnection remoteConnction = new RemoteConnection(callId, + mOutgoingConnectionServiceRpc, connection); + + mOurConnectionServiceImpl.addRemoteExistingConnection(remoteConnction); + } }; private final ConnectionServiceAdapterServant mServant = diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index b771879..4eac5ac 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -895,6 +895,7 @@ public class TelecomManager { * Processes the specified dial string as an MMI code. * MMI codes are any sequence of characters entered into the dialpad that contain a "*" or "#". * Some of these sequences launch special behavior through handled by Telephony. + * This method uses the default subscription. * <p> * Requires that the method-caller be set as the system dialer app. * </p> @@ -915,6 +916,31 @@ public class TelecomManager { } /** + * Processes the specified dial string as an MMI code. + * MMI codes are any sequence of characters entered into the dialpad that contain a "*" or "#". + * Some of these sequences launch special behavior through handled by Telephony. + * <p> + * Requires that the method-caller be set as the system dialer app. + * </p> + * + * @param accountHandle The handle for the account the MMI code should apply to. + * @param dialString The digits to dial. + * @return True if the digits were processed as an MMI code, false otherwise. + * + */ + public boolean handleMmi(PhoneAccountHandle accountHandle, String dialString) { + ITelecomService service = getTelecomService(); + if (service != null) { + try { + return service.handlePinMmiForPhoneAccount(accountHandle, dialString); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecomService#handlePinMmi", e); + } + } + return false; + } + + /** * Removes the missed-call notification if one is present. * <p> * Requires that the method-caller be set as the system dialer app. diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl index 5daa568..0d6b3d9 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl @@ -77,4 +77,6 @@ oneway interface IConnectionServiceAdapter { void setCallerDisplayName(String callId, String callerDisplayName, int presentation); void setConferenceableConnections(String callId, in List<String> conferenceableCallIds); + + void addExistingConnection(String callId, in ParcelableConnection connection); } diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 91f44b9..fd47213 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -169,6 +169,11 @@ interface ITelecomService { boolean handlePinMmi(String dialString); /** + * @see TelecomServiceImpl#handleMmi + */ + boolean handlePinMmiForPhoneAccount(in PhoneAccountHandle accountHandle, String dialString); + + /** * @see TelecomServiceImpl#isTtySupported */ boolean isTtySupported(); diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index 3363ca6..17db3fb 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -20,6 +20,7 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.telephony.Rlog; +import android.content.res.Resources; /** * Contains phone signal strength related information. @@ -50,6 +51,11 @@ public class SignalStrength implements Parcelable { //Use int max, as -1 is a valid value in signal strength public static final int INVALID = 0x7FFFFFFF; + private static final int RSRP_THRESH_TYPE_STRICT = 0; + private static final int[] RSRP_THRESH_STRICT = new int[] {-140, -115, -105, -95, -85, -44}; + private static final int[] RSRP_THRESH_LENIENT = new int[] {-140, -128, -118, -108, -98, -44}; + + private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5 private int mGsmBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 private int mCdmaDbm; // This value is the RSSI value @@ -745,12 +751,21 @@ public class SignalStrength implements Parcelable { */ int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1; - if (mLteRsrp > -44) rsrpIconLevel = -1; - else if (mLteRsrp >= -85) rsrpIconLevel = SIGNAL_STRENGTH_GREAT; - else if (mLteRsrp >= -95) rsrpIconLevel = SIGNAL_STRENGTH_GOOD; - else if (mLteRsrp >= -105) rsrpIconLevel = SIGNAL_STRENGTH_MODERATE; - else if (mLteRsrp >= -115) rsrpIconLevel = SIGNAL_STRENGTH_POOR; - else if (mLteRsrp >= -140) rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + int rsrpThreshType = Resources.getSystem().getInteger(com.android.internal.R.integer. + config_LTE_RSRP_threshold_type); + int[] threshRsrp; + if (rsrpThreshType == RSRP_THRESH_TYPE_STRICT) { + threshRsrp = RSRP_THRESH_STRICT; + } else { + threshRsrp = RSRP_THRESH_LENIENT; + } + + if (mLteRsrp > threshRsrp[5]) rsrpIconLevel = -1; + else if (mLteRsrp >= threshRsrp[4]) rsrpIconLevel = SIGNAL_STRENGTH_GREAT; + else if (mLteRsrp >= threshRsrp[3]) rsrpIconLevel = SIGNAL_STRENGTH_GOOD; + else if (mLteRsrp >= threshRsrp[2]) rsrpIconLevel = SIGNAL_STRENGTH_MODERATE; + else if (mLteRsrp >= threshRsrp[1]) rsrpIconLevel = SIGNAL_STRENGTH_POOR; + else if (mLteRsrp >= threshRsrp[0]) rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; /* * Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5 @@ -789,6 +804,7 @@ public class SignalStrength implements Parcelable { else if (mLteSignalStrength >= 8) rssiIconLevel = SIGNAL_STRENGTH_GOOD; else if (mLteSignalStrength >= 5) rssiIconLevel = SIGNAL_STRENGTH_MODERATE; else if (mLteSignalStrength >= 0) rssiIconLevel = SIGNAL_STRENGTH_POOR; + if (DBG) log("getLTELevel - rssi:" + mLteSignalStrength + " rssiIconLevel:" + rssiIconLevel); return rssiIconLevel; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index a3546ed..0246a2d 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -3336,6 +3336,17 @@ public class TelephonyManager { /** @hide */ @SystemApi + public boolean handlePinMmiForSubscriber(int subId, String dialString) { + try { + return getITelephony().handlePinMmiForSubscriber(subId, dialString); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#handlePinMmi", e); + } + return false; + } + + /** @hide */ + @SystemApi public void toggleRadioOnOff() { try { getITelephony().toggleRadioOnOff(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java index 997b199..4c4454d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java @@ -95,6 +95,10 @@ public final class BridgeWindow implements IWindow { } @Override + public void dispatchWindowShown() { + } + + @Override public IBinder asBinder() { // pass for now. return null; |