summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt10
-rw-r--r--api/system-current.txt12
-rw-r--r--cmds/dpm/src/com/android/commands/dpm/Dpm.java37
-rw-r--r--core/java/android/animation/ValueAnimator.java9
-rw-r--r--core/java/android/app/Activity.java10
-rw-r--r--core/java/android/app/INotificationManager.aidl8
-rw-r--r--core/java/android/app/NotificationManager.java40
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java76
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl6
-rw-r--r--core/java/android/content/Intent.java7
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java2
-rw-r--r--core/java/android/net/DhcpResults.java9
-rw-r--r--core/java/android/net/LinkProperties.java3
-rw-r--r--core/java/android/preference/PreferenceFragment.java3
-rw-r--r--core/java/android/preference/PreferenceGroup.java9
-rw-r--r--core/java/android/service/notification/ConditionProviderService.java26
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java2
-rw-r--r--core/java/android/text/BoringLayout.java5
-rw-r--r--core/java/android/view/SurfaceView.java3
-rw-r--r--core/java/android/view/WindowManagerInternal.java3
-rw-r--r--core/java/android/webkit/WebView.java24
-rw-r--r--core/java/android/webkit/WebViewProvider.java3
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java9
-rw-r--r--core/java/android/widget/DropDownListView.java17
-rw-r--r--core/java/android/widget/FastScroller.java1
-rw-r--r--core/java/android/widget/MenuItemHoverListener.java21
-rw-r--r--core/java/android/widget/MenuPopupWindow.java53
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java15
-rw-r--r--core/java/com/android/internal/view/menu/CascadingMenuPopup.java476
-rw-r--r--core/java/com/android/internal/view/menu/MenuBuilder.java17
-rw-r--r--core/java/com/android/internal/view/menu/MenuPresenter.java6
-rw-r--r--core/java/com/android/internal/widget/NonClientDecorView.java50
-rw-r--r--core/res/res/layout/list_content.xml3
-rw-r--r--core/res/res/layout/preference_list_fragment.xml1
-rw-r--r--core/res/res/values-fa/strings.xml16
-rw-r--r--core/res/res/values-it/strings.xml2
-rw-r--r--core/res/res/values-sv/strings.xml68
-rw-r--r--core/res/res/values-sw/strings.xml2
-rw-r--r--core/res/res/values-vi/strings.xml12
-rw-r--r--core/res/res/values/colors_legacy.xml2
-rw-r--r--core/tests/coretests/src/android/net/LinkPropertiesTest.java26
-rw-r--r--core/tests/coretests/src/android/util/OrientationUtil.java67
-rw-r--r--core/tests/coretests/src/android/widget/TextViewActivityTest.java27
-rw-r--r--data/keyboards/Vendor_18d1_Product_5018.kcm321
-rw-r--r--data/keyboards/Vendor_18d1_Product_5018.kl84
-rw-r--r--libs/hwui/Android.mk1
-rw-r--r--libs/hwui/tests/Benchmark.h54
-rw-r--r--libs/hwui/tests/TestContext.cpp23
-rw-r--r--libs/hwui/tests/TestContext.h2
-rw-r--r--libs/hwui/tests/TreeContentAnimation.cpp385
-rw-r--r--libs/hwui/tests/main.cpp388
-rw-r--r--media/java/android/media/MediaActionSound.java2
-rw-r--r--media/java/android/media/MediaPlayer.java5
-rw-r--r--media/java/android/media/MediaRecorder.java26
-rw-r--r--media/jni/android_media_MediaRecorder.cpp18
-rw-r--r--packages/DocumentsUI/AndroidManifest.xml1
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java16
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/CopyService.java12
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java39
-rw-r--r--packages/SystemUI/res/drawable/qs_customizer_background.xml19
-rw-r--r--packages/SystemUI/res/layout/qs_customize_panel.xml11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java60
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java118
-rw-r--r--services/accessibility/java/com/android/server/accessibility/TouchExplorer.java237
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java42
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java16
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java23
-rw-r--r--services/core/java/com/android/server/job/JobServiceContext.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java34
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java123
-rw-r--r--services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java18
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java4
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java64
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java1
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java140
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java27
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java59
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java41
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java122
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java11
-rw-r--r--services/net/java/android/net/dhcp/DhcpClient.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java246
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java3
-rw-r--r--telecomm/java/android/telecom/PhoneAccount.java37
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java8
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java73
-rw-r--r--tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java7
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java1
106 files changed, 3099 insertions, 1425 deletions
diff --git a/api/current.txt b/api/current.txt
index 2331170c6748..ef18bf9f05f6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5080,7 +5080,7 @@ package android.app {
}
public class NotificationManager {
- method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule);
+ method public android.app.AutomaticZenRule addAutomaticZenRule(android.app.AutomaticZenRule);
method public void cancel(int);
method public void cancel(java.lang.String, int);
method public void cancelAll();
@@ -5093,9 +5093,9 @@ package android.app {
method public void notify(int, android.app.Notification);
method public void notify(java.lang.String, int, android.app.Notification);
method public boolean removeAutomaticZenRule(java.lang.String);
- method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String);
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+ method public boolean updateAutomaticZenRule(android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
@@ -28851,10 +28851,8 @@ package android.service.notification {
method public abstract void onRequestConditions(int);
method public abstract void onSubscribe(android.net.Uri);
method public abstract void onUnsubscribe(android.net.Uri);
- field public static final java.lang.String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
- field public static final java.lang.String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+ field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
- field public static final java.lang.String META_DATA_DEFAULT_CONDITION_ID = "android.service.zen.automatic.defaultConditionId";
field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
@@ -30480,6 +30478,7 @@ package android.telecom {
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public android.net.Uri getAddress();
method public int getCapabilities();
+ method public android.os.Bundle getExtras();
method public int getHighlightColor();
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getLabel();
@@ -30512,6 +30511,7 @@ package android.telecom {
method public android.telecom.PhoneAccount build();
method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
method public android.telecom.PhoneAccount.Builder setCapabilities(int);
+ method public android.telecom.PhoneAccount.Builder setExtras(android.os.Bundle);
method public android.telecom.PhoneAccount.Builder setHighlightColor(int);
method public android.telecom.PhoneAccount.Builder setIcon(android.graphics.drawable.Icon);
method public android.telecom.PhoneAccount.Builder setShortDescription(java.lang.CharSequence);
diff --git a/api/system-current.txt b/api/system-current.txt
index dacc338b0ce3..0b216bdddb30 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5197,7 +5197,7 @@ package android.app {
}
public class NotificationManager {
- method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule);
+ method public android.app.AutomaticZenRule addAutomaticZenRule(android.app.AutomaticZenRule);
method public void cancel(int);
method public void cancel(java.lang.String, int);
method public void cancelAll();
@@ -5210,9 +5210,9 @@ package android.app {
method public void notify(int, android.app.Notification);
method public void notify(java.lang.String, int, android.app.Notification);
method public boolean removeAutomaticZenRule(java.lang.String);
- method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String);
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+ method public boolean updateAutomaticZenRule(android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
@@ -30940,10 +30940,8 @@ package android.service.notification {
method public abstract void onRequestConditions(int);
method public abstract void onSubscribe(android.net.Uri);
method public abstract void onUnsubscribe(android.net.Uri);
- field public static final java.lang.String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
- field public static final java.lang.String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+ field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
- field public static final java.lang.String META_DATA_DEFAULT_CONDITION_ID = "android.service.zen.automatic.defaultConditionId";
field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
@@ -32679,6 +32677,7 @@ package android.telecom {
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public android.net.Uri getAddress();
method public int getCapabilities();
+ method public android.os.Bundle getExtras();
method public int getHighlightColor();
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getLabel();
@@ -32712,6 +32711,7 @@ package android.telecom {
method public android.telecom.PhoneAccount build();
method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
method public android.telecom.PhoneAccount.Builder setCapabilities(int);
+ method public android.telecom.PhoneAccount.Builder setExtras(android.os.Bundle);
method public android.telecom.PhoneAccount.Builder setHighlightColor(int);
method public android.telecom.PhoneAccount.Builder setIcon(android.graphics.drawable.Icon);
method public android.telecom.PhoneAccount.Builder setShortDescription(java.lang.CharSequence);
@@ -41882,6 +41882,7 @@ package android.webkit {
method public void super_scrollTo(int, int);
method public boolean super_setFrame(int, int, int, int);
method public void super_setLayoutParams(android.view.ViewGroup.LayoutParams);
+ method public void super_startActivityForResult(android.content.Intent, int);
}
public static abstract class WebView.VisualStateCallback {
@@ -42116,6 +42117,7 @@ package android.webkit {
public static abstract interface WebViewProvider.ViewDelegate {
method public abstract boolean dispatchKeyEvent(android.view.KeyEvent);
method public abstract android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
+ method public abstract void onActivityResult(int, int, android.content.Intent);
method public abstract void onAttachedToWindow();
method public abstract void onConfigurationChanged(android.content.res.Configuration);
method public abstract android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.EditorInfo);
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 214dc5d35a24..ea530094e691 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -44,6 +44,7 @@ public final class Dpm extends BaseCommand {
private IDevicePolicyManager mDevicePolicyManager;
private int mUserId = UserHandle.USER_SYSTEM;
+ private String mName = "";
private ComponentName mComponent = null;
@Override
@@ -52,8 +53,8 @@ public final class Dpm extends BaseCommand {
"usage: dpm [subcommand] [options]\n" +
"usage: dpm set-active-admin [ --user <USER_ID> ] <COMPONENT>\n" +
// STOPSHIP Finalize it
- "usage: dpm set-device-owner [ --user <USER_ID> *EXPERIMENTAL* ] <COMPONENT>\n" +
- "usage: dpm set-profile-owner [ --user <USER_ID> ] <COMPONENT>\n" +
+ "usage: dpm set-device-owner [ --user <USER_ID> *EXPERIMENTAL* ] [ --name <NAME> ] <COMPONENT>\n" +
+ "usage: dpm set-profile-owner [ --user <USER_ID> ] [ --name <NAME> ] <COMPONENT>\n" +
"\n" +
"dpm set-active-admin: Sets the given component as active admin" +
" for an existing user.\n" +
@@ -90,47 +91,51 @@ public final class Dpm extends BaseCommand {
}
}
- private void parseArgs(boolean canHaveUser) {
- String nextArg = nextArgRequired();
- if (canHaveUser && "--user".equals(nextArg)) {
- mUserId = parseInt(nextArgRequired());
- nextArg = nextArgRequired();
+ private void parseArgs(boolean canHaveUser, boolean canHaveName) {
+ String opt;
+ while ((opt = nextOption()) != null) {
+ if (canHaveUser && "--user".equals(opt)) {
+ mUserId = parseInt(nextArgRequired());
+ } else if (canHaveName && "--name".equals(opt)) {
+ mName = nextArgRequired();
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
}
- mComponent = parseComponentName(nextArg);
+ mComponent = parseComponentName(nextArgRequired());
}
private void runSetActiveAdmin() throws RemoteException {
- parseArgs(true);
+ parseArgs(/*canHaveUser=*/ true, /*canHaveName=*/ false);
mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
System.out.println("Success: Active admin set to component " + mComponent.toShortString());
}
private void runSetDeviceOwner() throws RemoteException {
- parseArgs(true);
+ parseArgs(/*canHaveUser=*/ true, /*canHaveName=*/ true);
mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
- String packageName = mComponent.getPackageName();
try {
- if (!mDevicePolicyManager.setDeviceOwner(packageName, null /*ownerName*/, mUserId)) {
+ if (!mDevicePolicyManager.setDeviceOwner(mComponent, mName, mUserId)) {
throw new RuntimeException(
- "Can't set package " + packageName + " as device owner.");
+ "Can't set package " + mComponent + " as device owner.");
}
} catch (Exception e) {
// Need to remove the admin that we just added.
mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
throw e;
}
- System.out.println("Success: Device owner set to package " + packageName);
+ System.out.println("Success: Device owner set to package " + mComponent);
System.out.println("Active admin set to component " + mComponent.toShortString());
}
private void runSetProfileOwner() throws RemoteException {
- parseArgs(true);
+ parseArgs(/*canHaveUser=*/ true, /*canHaveName=*/ true);
mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
try {
- if (!mDevicePolicyManager.setProfileOwner(mComponent, "" /*ownerName*/, mUserId)) {
+ if (!mDevicePolicyManager.setProfileOwner(mComponent, mName, mUserId)) {
throw new RuntimeException("Can't set component " + mComponent.toShortString() +
" as profile owner for user " + mUserId);
}
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 1995ef58e499..9b905bdffc0a 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -625,14 +625,19 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
/**
* Gets the current position of the animation in time, which is equal to the current
* time minus the time that the animation started. An animation that is not yet started will
- * return a value of zero.
+ * return a value of zero, unless the animation has has its play time set via
+ * {@link #setCurrentPlayTime(long)} or {@link #setCurrentFraction(float)}, in which case
+ * it will return the time that was set.
*
* @return The current position in time of the animation.
*/
public long getCurrentPlayTime() {
- if (!mInitialized || !mStarted) {
+ if (!mInitialized || (!mStarted && mSeekFraction < 0)) {
return 0;
}
+ if (mSeekFraction >= 0) {
+ return (long) (mUnscaledDuration * mSeekFraction);
+ }
return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f60250cc6eb1..d47c0aa3e58b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -687,9 +687,15 @@ public class Activity extends ContextThemeWrapper
/** @hide Task isn't finished when activity is finished */
public static final int DONT_FINISH_TASK_WITH_ACTIVITY = 0;
- /** @hide Task is finished if the finishing activity is the root of the task */
+ /**
+ * @hide Task is finished if the finishing activity is the root of the task. To preserve the
+ * past behavior the task is also removed from recents.
+ */
public static final int FINISH_TASK_WITH_ROOT_ACTIVITY = 1;
- /** @hide Task is finished along with the finishing activity*/
+ /**
+ * @hide Task is finished along with the finishing activity, but it is not removed from
+ * recents.
+ */
public static final int FINISH_TASK_WITH_ACTIVITY = 2;
static final String FRAGMENTS_TAG = "android:fragments";
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 920fbe9cfc1c..e95a35a23b6c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -93,11 +93,11 @@ interface INotificationManager
String[] getPackagesRequestingNotificationPolicyAccess();
boolean isNotificationPolicyAccessGrantedForPackage(String pkg);
void setNotificationPolicyAccessGranted(String pkg, boolean granted);
- AutomaticZenRule getAutomaticZenRule(String name);
+ AutomaticZenRule getAutomaticZenRule(String id);
List<AutomaticZenRule> getAutomaticZenRules();
- boolean addOrUpdateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
- boolean renameAutomaticZenRule(String oldName, String newName);
- boolean removeAutomaticZenRule(String name);
+ AutomaticZenRule addAutomaticZenRule(in AutomaticZenRule automaticZenRule);
+ boolean updateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
+ boolean removeAutomaticZenRule(String id);
byte[] getBackupPayload(int user);
void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index cbf198bb9415..cb0ff33ee9b3 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -436,48 +436,47 @@ public class NotificationManager
}
/**
- * Returns the AutomaticZenRule with the given name, if it exists and the caller has access.
+ * Returns the AutomaticZenRule with the given id, if it exists and the caller has access.
*
* <p>
* Only available if policy access is granted to this package.
* See {@link #isNotificationPolicyAccessGranted}.
*
* <p>
- * Returns null if there are no zen rules that match the given name, or if the calling package
+ * Returns null if there are no zen rules that match the given id, or if the calling package
* doesn't own the matching rule. See {@link AutomaticZenRule#getOwner}.
*/
- public AutomaticZenRule getAutomaticZenRule(String name) {
+ public AutomaticZenRule getAutomaticZenRule(String id) {
INotificationManager service = getService();
try {
- return service.getAutomaticZenRule(name);
+ return service.getAutomaticZenRule(id);
} catch (RemoteException e) {
}
return null;
}
/**
- * Creates or updates the given zen rule.
+ * Creates the given zen rule.
*
* <p>
* Only available if policy access is granted to this package.
* See {@link #isNotificationPolicyAccessGranted}.
*
- * <p>
- * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
- * @param automaticZenRule the rule to create or update.
- * @return Whether the rule was successfully created or updated.
+ * @param automaticZenRule the rule to create.
+ * @return A fully populated {@link AutomaticZenRule} if the rule was persisted successfully,
+ * null otherwise.
*/
- public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule) {
+ public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule) {
INotificationManager service = getService();
try {
- return service.addOrUpdateAutomaticZenRule(automaticZenRule);
+ return service.addAutomaticZenRule(automaticZenRule);
} catch (RemoteException e) {
}
- return false;
+ return null;
}
/**
- * Renames a zen rule.
+ * Updates the given zen rule.
*
* <p>
* Only available if policy access is granted to this package.
@@ -485,21 +484,20 @@ public class NotificationManager
*
* <p>
* Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
- * @param oldName The name of the rule to update.
- * @param newName The new name for the rule.
+ * @param automaticZenRule the rule to update.
* @return Whether the rule was successfully updated.
*/
- public boolean renameAutomaticZenRule(String oldName, String newName) {
+ public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule) {
INotificationManager service = getService();
try {
- return service.renameAutomaticZenRule(oldName, newName);
+ return service.updateAutomaticZenRule(automaticZenRule);
} catch (RemoteException e) {
}
return false;
}
/**
- * Deletes the automatic zen rule with the given name.
+ * Deletes the automatic zen rule with the given id.
*
* <p>
* Only available if policy access is granted to this package.
@@ -507,13 +505,13 @@ public class NotificationManager
*
* <p>
* Callers can only delete rules that they own. See {@link AutomaticZenRule#getOwner}.
- * @param name the name of the rule to delete.
+ * @param id the id of the rule to delete.
* @return Whether the rule was successfully deleted.
*/
- public boolean removeAutomaticZenRule(String name) {
+ public boolean removeAutomaticZenRule(String id) {
INotificationManager service = getService();
try {
- return service.removeAutomaticZenRule(name);
+ return service.removeAutomaticZenRule(id);
} catch (RemoteException e) {
}
return false;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 50764f740bc6..9e9d9496b461 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -341,7 +341,8 @@ public class DevicePolicyManager {
/**
* A String extra indicating the security type of the wifi network in
- * {@link #EXTRA_PROVISIONING_WIFI_SSID}.
+ * {@link #EXTRA_PROVISIONING_WIFI_SSID} and could be one of {@code NONE}, {@code WPA} or
+ * {@code WEP}.
*
* <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
* provisioning via an NFC bump.
@@ -2573,28 +2574,28 @@ public class DevicePolicyManager {
/**
* @hide
* Sets the given package as the device owner.
- * Same as {@link #setDeviceOwner(String, String)} but without setting a device owner name.
- * @param packageName the package name of the application to be registered as the device owner.
+ * Same as {@link #setDeviceOwner(ComponentName, String)} but without setting a device owner name.
+ * @param who the component name to be registered as device owner.
* @return whether the package was successfully registered as the device owner.
* @throws IllegalArgumentException if the package name is null or invalid
* @throws IllegalStateException If the preconditions mentioned are not met.
*/
- public boolean setDeviceOwner(String packageName) {
- return setDeviceOwner(packageName, null);
+ public boolean setDeviceOwner(ComponentName who) {
+ return setDeviceOwner(who, null);
}
/**
* @hide
*/
- public boolean setDeviceOwner(String packageName, int userId) {
- return setDeviceOwner(packageName, null, userId);
+ public boolean setDeviceOwner(ComponentName who, int userId) {
+ return setDeviceOwner(who, null, userId);
}
/**
* @hide
*/
- public boolean setDeviceOwner(String packageName, String ownerName) {
- return setDeviceOwner(packageName, ownerName, UserHandle.USER_SYSTEM);
+ public boolean setDeviceOwner(ComponentName who, String ownerName) {
+ return setDeviceOwner(who, ownerName, UserHandle.USER_SYSTEM);
}
/**
@@ -2605,18 +2606,18 @@ public class DevicePolicyManager {
* this method.
* Calling this after the setup phase of the primary user has completed is allowed only if
* the caller is the shell uid, and there are no additional users and no accounts.
- * @param packageName the package name of the application to be registered as the device owner.
+ * @param who the component name to be registered as device owner.
* @param ownerName the human readable name of the institution that owns this device.
* @param userId ID of the user on which the device owner runs.
* @return whether the package was successfully registered as the device owner.
* @throws IllegalArgumentException if the package name is null or invalid
* @throws IllegalStateException If the preconditions mentioned are not met.
*/
- public boolean setDeviceOwner(String packageName, String ownerName, int userId)
+ public boolean setDeviceOwner(ComponentName who, String ownerName, int userId)
throws IllegalArgumentException, IllegalStateException {
if (mService != null) {
try {
- return mService.setDeviceOwner(packageName, ownerName, userId);
+ return mService.setDeviceOwner(who, ownerName, userId);
} catch (RemoteException re) {
Log.w(TAG, "Failed to set device owner");
}
@@ -2635,14 +2636,15 @@ public class DevicePolicyManager {
* the setup process.
* @param packageName the package name of the app, to compare with the registered device owner
* app, if any.
- * @return whether or not the package is registered as the device owner app.
+ * @return whether or not the package is registered as the device owner app. Note this method
+ * does *not* check weather the device owner is actually running on the current user.
*/
public boolean isDeviceOwnerApp(String packageName) {
if (mService != null) {
try {
- return mService.isDeviceOwner(packageName);
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to check device owner");
+ return mService.isDeviceOwnerPackage(packageName);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
}
}
return false;
@@ -2657,6 +2659,17 @@ public class DevicePolicyManager {
}
/**
+ * Check whether a given component is registered as a device owner.
+ * Note this method does *not* check weather the device owner is actually running on the current
+ * user.
+ *
+ * @hide
+ */
+ public boolean isDeviceOwner(ComponentName who) {
+ return (who != null) && who.equals(getDeviceOwner());
+ }
+
+ /**
* Clears the current device owner. The caller must be the device owner.
*
* This function should be used cautiously as once it is called it cannot
@@ -2675,12 +2688,28 @@ public class DevicePolicyManager {
}
}
- /** @hide */
+ /**
+ * Returns the device owner package name. Note this method will still return the device owner
+ * package name even if it's running on a different user.
+ *
+ * @hide
+ */
@SystemApi
public String getDeviceOwner() {
+ final ComponentName componentName = getDeviceOwnerComponent();
+ return componentName == null ? null : componentName.getPackageName();
+ }
+
+ /**
+ * Returns the device owner name. Note this method will still return the device owner
+ * name even if it's running on a different user.
+ *
+ * @hide
+ */
+ public String getDeviceOwnerName() {
if (mService != null) {
try {
- return mService.getDeviceOwner();
+ return mService.getDeviceOwnerName();
} catch (RemoteException re) {
Log.w(TAG, "Failed to get device owner");
}
@@ -2688,11 +2717,16 @@ public class DevicePolicyManager {
return null;
}
- /** @hide */
- public String getDeviceOwnerName() {
+ /**
+ * Returns the device owner component name. Note this method will still return the device owner
+ * component name even if it's running on a different user.
+ *
+ * @hide
+ */
+ public ComponentName getDeviceOwnerComponent() {
if (mService != null) {
try {
- return mService.getDeviceOwnerName();
+ return mService.getDeviceOwner();
} catch (RemoteException re) {
Log.w(TAG, "Failed to get device owner");
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 74fb11dbc60c..ccaa8cb82170 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -113,9 +113,9 @@ interface IDevicePolicyManager {
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
- boolean setDeviceOwner(String packageName, String ownerName, int userId);
- boolean isDeviceOwner(String packageName);
- String getDeviceOwner();
+ boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
+ boolean isDeviceOwnerPackage(String packageName);
+ ComponentName getDeviceOwner();
String getDeviceOwnerName();
void clearDeviceOwner(String packageName);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 670ca803a367..6740d434d012 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3168,6 +3168,13 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_HOME = "android.intent.category.HOME";
/**
+ * This is the home activity that is displayed when the device is finished setting up and ready
+ * for use.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_HOME_MAIN = "android.intent.category.HOME_MAIN";
+ /**
* This is the setup wizard activity, that is the first activity that is displayed
* when the user sets up the device for the first time.
* @hide
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 20b0be11e23c..8f45f72de6e2 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -46,7 +46,7 @@ import java.lang.annotation.RetentionPolicy;
* provide substantially improved capabilities over the older camera
* API. Applications that target the limited level devices will run unchanged on
* the full-level devices; if your application requires a full-level device for
- * proper operation, declare the "android.hardware.camera2.full" feature in your
+ * proper operation, declare the "android.hardware.camera.level.full" feature in your
* manifest.</p>
*
* @see CameraManager#openCamera
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 87c063ff3eb0..97bd5d284c71 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -21,7 +21,6 @@ import android.os.Parcel;
import android.text.TextUtils;
import android.util.Log;
-import java.net.InetAddress;
import java.net.Inet4Address;
import java.util.Objects;
@@ -34,7 +33,7 @@ import java.util.Objects;
public class DhcpResults extends StaticIpConfiguration {
private static final String TAG = "DhcpResults";
- public InetAddress serverAddress;
+ public Inet4Address serverAddress;
/** Vendor specific information (from RFC 2132). */
public String vendorInfo;
@@ -142,7 +141,7 @@ public class DhcpResults extends StaticIpConfiguration {
private static void readFromParcel(DhcpResults dhcpResults, Parcel in) {
StaticIpConfiguration.readFromParcel(dhcpResults, in);
dhcpResults.leaseDuration = in.readInt();
- dhcpResults.serverAddress = NetworkUtils.unparcelInetAddress(in);
+ dhcpResults.serverAddress = (Inet4Address) NetworkUtils.unparcelInetAddress(in);
dhcpResults.vendorInfo = in.readString();
}
@@ -183,8 +182,8 @@ public class DhcpResults extends StaticIpConfiguration {
public boolean setServerAddress(String addrString) {
try {
- serverAddress = NetworkUtils.numericToInetAddress(addrString);
- } catch (IllegalArgumentException e) {
+ serverAddress = (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
+ } catch (IllegalArgumentException|ClassCastException e) {
Log.e(TAG, "setServerAddress failed with addrString " + addrString);
return true;
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index c4de4a21e093..6bfa2a495b82 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -667,7 +667,8 @@ public final class LinkProperties implements Parcelable {
* @return {@code true} if there is an IPv4 address, {@code false} otherwise.
*/
private boolean hasIPv4AddressOnInterface(String iface) {
- return (mIfaceName.equals(iface) && hasIPv4Address()) ||
+ // mIfaceName can be null.
+ return (Objects.equals(iface, mIfaceName) && hasIPv4Address()) ||
(iface != null && mStackedLinks.containsKey(iface) &&
mStackedLinks.get(iface).hasIPv4Address());
}
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 66642debc032..db04c7125adc 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -214,6 +214,9 @@ public abstract class PreferenceFragment extends Fragment implements
@Override
public void onDestroyView() {
+ if (mList != null) {
+ mList.setOnKeyListener(null);
+ }
mList = null;
mHandler.removeCallbacks(mRequestFocus);
mHandler.removeMessages(MSG_BIND_PREFERENCES);
diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java
index d6e9e61f358b..13c36614729e 100644
--- a/core/java/android/preference/PreferenceGroup.java
+++ b/core/java/android/preference/PreferenceGroup.java
@@ -148,16 +148,15 @@ public abstract class PreferenceGroup extends Preference implements GenericInfla
}
}
- int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
- if (insertionIndex < 0) {
- insertionIndex = insertionIndex * -1 - 1;
- }
-
if (!onPrepareAddPreference(preference)) {
return false;
}
synchronized(this) {
+ int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
+ if (insertionIndex < 0) {
+ insertionIndex = insertionIndex * -1 - 1;
+ }
mPreferenceList.add(insertionIndex, preference);
}
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 2a8fb2c14695..c679eda2fff2 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -35,8 +35,7 @@ import android.util.Log;
* the {@link android.Manifest.permission#BIND_CONDITION_PROVIDER_SERVICE} permission
* and include an intent filter with the {@link #SERVICE_INTERFACE} action. If you want users to be
* able to create and update conditions for this service to monitor, include the
- * {@link #META_DATA_RULE_TYPE}, {@link #META_DATA_DEFAULT_CONDITION_ID}, and
- * {@link #META_DATA_CONFIGURATION_ACTIVITY} tags. For example:</p>
+ * {@link #META_DATA_RULE_TYPE} and {@link #META_DATA_CONFIGURATION_ACTIVITY} tags. For example:</p>
* <pre>
* &lt;service android:name=".MyConditionProvider"
* android:label="&#64;string/service_name"
@@ -49,10 +48,6 @@ import android.util.Log;
* android:value="@string/my_condition_rule">
* &lt;/meta-data>
* &lt;meta-data
- * android:name="android.service.zen.automatic.defaultConditionId"
- * android:value="condition://com.my.package/mycondition">
- * &lt;/meta-data>
- * &lt;meta-data
* android:name="android.service.zen.automatic.configurationActivity"
* android:value="com.my.package/.MyConditionConfigurationActivity">
* &lt;/meta-data>
@@ -82,13 +77,6 @@ public abstract class ConditionProviderService extends Service {
public static final String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
/**
- * The name of the {@code meta-data} tag containing a default Condition {@link Uri} that can
- * be parsed by this service.
- */
- public static final String META_DATA_DEFAULT_CONDITION_ID =
- "android.service.zen.automatic.defaultConditionId";
-
- /**
* The name of the {@code meta-data} tag containing the {@link ComponentName} of an activity
* that allows users to configure the conditions provided by this service.
*/
@@ -96,17 +84,9 @@ public abstract class ConditionProviderService extends Service {
"android.service.zen.automatic.configurationActivity";
/**
- * A condition {@link Uri} extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}. If the
- * condition Uri is modified by that activity, it must be included in the result Intent extras
- * in order to be persisted.
- */
- public static final String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
-
- /**
- * A String rule name extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}. This extra is
- * informative only, and will be ignored if included in the result Intent extras.
+ * A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}.
*/
- public static final String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+ public static final String EXTRA_RULE_ID = "android.content.automatic.ruleId";
/**
* Called when this service is connected.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 9a0f7fc811f2..e054a61e7b62 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -896,7 +896,7 @@ public class ZenModeConfig implements Parcelable {
return Global.isValidZenMode(rt) ? rt : defValue;
}
- public String newRuleId() {
+ public static String newRuleId() {
return UUID.randomUUID().toString().replace("-", "");
}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index aa8b71c5ea42..fd9188b79599 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -364,6 +364,11 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
}
@Override
+ public float getLineWidth(int line) {
+ return (line == 0 ? mMax : 0);
+ }
+
+ @Override
public final Directions getLineDirections(int line) {
return Layout.DIRS_ALL_LEFT_TO_RIGHT;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 7d48a9a11eda..db68c29d025f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -447,10 +447,11 @@ public class SurfaceView extends View {
final boolean formatChanged = mFormat != mRequestedFormat;
final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
final boolean visibleChanged = mVisible != mRequestedVisible;
+ final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height;
if (force || creating || formatChanged || sizeChanged || visibleChanged
|| mLeft != mLocation[0] || mTop != mLocation[1]
- || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
+ || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded || layoutSizeChanged) {
if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
+ " format=" + formatChanged + " size=" + sizeChanged
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index a6d99325b094..3882bca043e8 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -263,4 +263,7 @@ public abstract class WindowManagerInternal {
*/
public abstract void setOnHardKeyboardStatusChangeListener(
OnHardKeyboardStatusChangeListener listener);
+
+ /** Returns true if the stack with the input Id is currently visible. */
+ public abstract boolean isStackVisible(int stackId);
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d0c50c9309c2..358f939c62e2 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.Widget;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -1850,7 +1851,7 @@ public class WebView extends AbsoluteLayout
* <a href="https://html.spec.whatwg.org/multipage/comms.html#messagechannel">here
* </a>
*
- * The returned message channels are entangled and already in started state.
+ * <p>The returned message channels are entangled and already in started state.</p>
*
* @return the two message ports that form the message channel.
*/
@@ -2157,6 +2158,10 @@ public class WebView extends AbsoluteLayout
WebView.super.setLayoutParams(params);
}
+ public void super_startActivityForResult(Intent intent, int requestCode) {
+ WebView.super.startActivityForResult(intent, requestCode);
+ }
+
// ---- Access to non-public methods ----
public void overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
@@ -2594,6 +2599,23 @@ public class WebView extends AbsoluteLayout
mProvider.getViewDelegate().onFinishTemporaryDetach();
}
+ /**
+ * Receive the result from a previous call to {@link #startActivityForResult(Intent, int)}.
+ *
+ * @param requestCode The integer request code originally supplied to
+ * startActivityForResult(), allowing you to identify who this
+ * result came from.
+ * @param resultCode The integer result code returned by the child activity
+ * through its setResult().
+ * @param data An Intent, which can return result data to the caller
+ * (various data can be attached to Intent "extras").
+ * @hide
+ */
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mProvider.getViewDelegate().onActivityResult(requestCode, resultCode, data);
+ }
+
/** @hide */
@Override
protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 27033ad61186..3ce034cf24cb 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -18,6 +18,7 @@ package android.webkit;
import android.annotation.SystemApi;
import android.content.res.Configuration;
+import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -380,6 +381,8 @@ public interface WebViewProvider {
public void onStartTemporaryDetach();
public void onFinishTemporaryDetach();
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data);
}
interface ScrollDelegate {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 932b3540925b..0e3a69f5f0cd 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -401,12 +401,11 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
}
LayoutParams createOrReuseLayoutParams(View v) {
- final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
- if (currentLp instanceof ViewGroup.LayoutParams) {
- LayoutParams lp = (LayoutParams) currentLp;
- return lp;
+ final LayoutParams currentLp = v.getLayoutParams();
+ if (currentLp != null) {
+ return currentLp;
}
- return new ViewGroup.LayoutParams(0, 0);
+ return new LayoutParams(0, 0);
}
void refreshChildren() {
diff --git a/core/java/android/widget/DropDownListView.java b/core/java/android/widget/DropDownListView.java
index 69649ac71868..c869ccbdef58 100644
--- a/core/java/android/widget/DropDownListView.java
+++ b/core/java/android/widget/DropDownListView.java
@@ -159,6 +159,23 @@ public class DropDownListView extends ListView {
return super.onHoverEvent(ev);
}
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ final int position = pointToPosition(x, y);
+ if (position == INVALID_POSITION) {
+ return super.onTouchEvent(event);
+ }
+
+ if (position != mSelectedPosition) {
+ setSelectedPositionInt(position);
+ setNextSelectedPositionInt(position);
+ }
+
+ return super.onTouchEvent(event);
+ }
+
/**
* Handles forwarded events.
*
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index c40289e44bcf..559181bdb823 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -383,6 +383,7 @@ class FastScroller {
break;
}
}
+ ta.recycle();
updateAppearance();
}
diff --git a/core/java/android/widget/MenuItemHoverListener.java b/core/java/android/widget/MenuItemHoverListener.java
index 77d6674ead21..87c5c852973e 100644
--- a/core/java/android/widget/MenuItemHoverListener.java
+++ b/core/java/android/widget/MenuItemHoverListener.java
@@ -2,8 +2,6 @@ package android.widget;
import com.android.internal.view.menu.MenuBuilder;
-import android.annotation.NonNull;
-
/**
* An interface notified when a menu item is hovered. Useful for cases when hover should trigger
* some behavior at a higher level, like managing the opening and closing of submenus.
@@ -11,22 +9,5 @@ import android.annotation.NonNull;
* @hide
*/
public interface MenuItemHoverListener {
- /**
- * Called when hover exits a menu item.
- * <p>
- * If hover is moving to another item, this method will be called before
- * {@link #onItemHoverEnter(MenuBuilder, int)} for the newly-hovered item.
- *
- * @param menu the item's parent menu
- * @param position the position of the item within the menu
- */
- void onItemHoverExit(@NonNull MenuBuilder menu, int position);
-
- /**
- * Called when hover enters a menu item.
- *
- * @param menu the item's parent menu
- * @param position the position of the item within the menu
- */
- void onItemHoverEnter(@NonNull MenuBuilder menu, int position);
+ public void onItemHovered(MenuBuilder menu, int position);
}
diff --git a/core/java/android/widget/MenuPopupWindow.java b/core/java/android/widget/MenuPopupWindow.java
index 6b740d9268f9..1fb62d0fa6c9 100644
--- a/core/java/android/widget/MenuPopupWindow.java
+++ b/core/java/android/widget/MenuPopupWindow.java
@@ -16,7 +16,6 @@
package android.widget;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -73,18 +72,10 @@ public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverLis
}
@Override
- public void onItemHoverEnter(@NonNull MenuBuilder menu, int position) {
+ public void onItemHovered(MenuBuilder menu, int position) {
// Forward up the chain
if (mHoverListener != null) {
- mHoverListener.onItemHoverEnter(menu, position);
- }
- }
-
- @Override
- public void onItemHoverExit(@NonNull MenuBuilder menu, int position) {
- // Forward up the chain
- if (mHoverListener != null) {
- mHoverListener.onItemHoverExit(menu, position);
+ mHoverListener.onItemHovered(menu, position);
}
}
@@ -97,8 +88,6 @@ public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverLis
private MenuItemHoverListener mHoverListener;
- private int mPreviouslyHoveredPosition = INVALID_POSITION;
-
public MenuDropDownListView(Context context, boolean hijackFocus) {
super(context, hijackFocus);
@@ -146,17 +135,25 @@ public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverLis
@Override
public boolean onHoverEvent(MotionEvent ev) {
- final int position;
- if (ev.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
- position = INVALID_POSITION;
- } else {
- position = pointToPosition((int) ev.getX(), (int) ev.getY());
+ boolean dispatchHover = false;
+ final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
+
+ final int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_HOVER_ENTER
+ || action == MotionEvent.ACTION_HOVER_MOVE) {
+ if (position != INVALID_POSITION && position != mSelectedPosition) {
+ final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
+ if (hoveredItem.isEnabled()) {
+ dispatchHover = true;
+ }
+ }
}
- // Dispatch any changes in hovered position to the listener.
- if (mHoverListener != null && mPreviouslyHoveredPosition != position) {
- final ListAdapter adapter = getAdapter();
- final MenuAdapter menuAdapter;
+ boolean superVal = super.onHoverEvent(ev);
+
+ if (dispatchHover && mHoverListener != null) {
+ ListAdapter adapter = getAdapter();
+ MenuAdapter menuAdapter;
if (adapter instanceof HeaderViewListAdapter) {
menuAdapter = (MenuAdapter) ((HeaderViewListAdapter) adapter)
.getWrappedAdapter();
@@ -164,18 +161,10 @@ public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverLis
menuAdapter = (MenuAdapter) adapter;
}
- final MenuBuilder menu = menuAdapter.getAdapterMenu();
- if (mPreviouslyHoveredPosition != INVALID_POSITION) {
- mHoverListener.onItemHoverExit(menu, mPreviouslyHoveredPosition);
- }
- if (position != INVALID_POSITION) {
- mHoverListener.onItemHoverEnter(menu, position);
- }
+ mHoverListener.onItemHovered(menuAdapter.getAdapterMenu(), position);
}
- mPreviouslyHoveredPosition = position;
-
- return super.onHoverEvent(ev);
+ return superVal;
}
}
} \ No newline at end of file
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 23c40473f621..ab372d30e271 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -488,8 +488,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
public void clearContentView() {
- if (mNonClientDecorView != null && mNonClientDecorView.getChildCount() > 1) {
- mNonClientDecorView.removeViewAt(1);
+ if (mNonClientDecorView != null) {
+ if (mNonClientDecorView.getChildCount() > 1) {
+ mNonClientDecorView.removeViewAt(1);
+ }
+ } else {
+ // This window doesn't have non client decor, so we need to just remove the children
+ // of the decor view.
+ mDecor.removeAllViews();
}
}
@@ -4202,10 +4208,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
attrs.type == TYPE_APPLICATION;
mWorkspaceId = getWorkspaceId();
- // Only a non floating application window on one of the allowed worksapces can get a non
+ // Only a non floating application window on one of the allowed workspaces can get a non
// client decor.
- if (!isFloating() && isApplication && mWorkspaceId != HOME_STACK_ID &&
- mWorkspaceId < FIRST_DYNAMIC_STACK_ID) {
+ if (!isFloating() && isApplication && mWorkspaceId < FIRST_DYNAMIC_STACK_ID) {
// Dependent on the brightness of the used title we either use the
// dark or the light button frame.
if (nonClientDecorView == null) {
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index 1a7d91ebc2e0..293e2ade7229 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -5,32 +5,28 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
-import android.annotation.AttrRes;
import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StyleRes;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Parcelable;
-import android.os.SystemClock;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnKeyListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.DropDownListView;
import android.widget.FrameLayout;
-import android.widget.HeaderViewListAdapter;
-import android.widget.ListAdapter;
import android.widget.MenuItemHoverListener;
+import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.MenuPopupWindow;
+import android.widget.MenuPopupWindow.MenuDropDownListView;
import android.widget.PopupWindow;
import android.widget.PopupWindow.OnDismissListener;
import android.widget.TextView;
@@ -51,10 +47,6 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
private static final int HORIZ_POSITION_LEFT = 0;
private static final int HORIZ_POSITION_RIGHT = 1;
- /**
- * Delay between hovering over a menu item with a mouse and receiving
- * side-effects (ex. opening a sub-menu or closing unrelated menus).
- */
private static final int SUBMENU_TIMEOUT_MS = 200;
private final Context mContext;
@@ -62,14 +54,9 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
private final int mPopupStyleAttr;
private final int mPopupStyleRes;
private final boolean mOverflowOnly;
+ private final int mLayoutDirection;
private final Handler mSubMenuHoverHandler;
- /**
- * List of open menus. The first item is the root menu and each
- * subsequent item is a direct submenu of the previous item.
- */
- private final List<CascadingMenuInfo> mAddedMenus = new ArrayList<>();
-
private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
@@ -79,8 +66,8 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
dismiss();
} else if (isShowing()) {
// Recompute window sizes and positions.
- for (CascadingMenuInfo info : mAddedMenus) {
- info.window.show();
+ for (MenuPopupWindow popup : mPopupWindows) {
+ popup.show();
}
}
}
@@ -107,22 +94,13 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
private final MenuItemHoverListener mMenuItemHoverListener = new MenuItemHoverListener() {
@Override
- public void onItemHoverExit(@NonNull MenuBuilder menu, int position) {
- // If the mouse moves between two windows, hover enter/exit pairs
- // may be received out of order. So, instead of canceling all
- // pending runnables, only cancel runnables for the host menu.
- mSubMenuHoverHandler.removeCallbacksAndMessages(menu);
- }
-
- @Override
- public void onItemHoverEnter(@NonNull final MenuBuilder menu, final int position) {
- // Something new was hovered, cancel all scheduled runnables.
- mSubMenuHoverHandler.removeCallbacksAndMessages(null);
-
- // Find the position of the hovered menu within the added menus.
+ public void onItemHovered(MenuBuilder menu, int position) {
int menuIndex = -1;
- for (int i = 0, count = mAddedMenus.size(); i < count; i++) {
- if (menu == mAddedMenus.get(i).menu) {
+ for (int i = 0; i < mListViews.size(); i++) {
+ final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(i);
+ final MenuAdapter adapter = toMenuAdapter(view.getAdapter());
+
+ if (adapter.getAdapterMenu() == menu) {
menuIndex = i;
break;
}
@@ -132,44 +110,82 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
return;
}
- final CascadingMenuInfo nextInfo;
- final int nextIndex = menuIndex + 1;
- if (nextIndex < mAddedMenus.size()) {
- nextInfo = mAddedMenus.get(nextIndex);
- } else {
- nextInfo = null;
- }
-
- final Runnable runnable = new Runnable() {
- @Override
- public void run() {
- // Close any other submenus that might be open at the
- // current or a deeper level.
- if (nextInfo != null) {
- // Disable exit animations to prevent overlapping
- // fading out submenus.
- nextInfo.window.setExitTransition(null);
- nextInfo.window.setAnimationStyle(0);
- nextInfo.menu.close(false);
+ final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(menuIndex);
+ final ListMenuItemView selectedItemView = (ListMenuItemView) view.getSelectedView();
+
+ if (selectedItemView != null && selectedItemView.isEnabled()
+ && selectedItemView.getItemData().hasSubMenu()) {
+ // If the currently selected item corresponds to a submenu, schedule to open the
+ // submenu on a timeout.
+
+ mSubMenuHoverHandler.removeCallbacksAndMessages(null);
+ mSubMenuHoverHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ // Make sure the submenu item is still the one selected.
+ if (view.getSelectedView() == selectedItemView
+ && selectedItemView.isEnabled()
+ && selectedItemView.getItemData().hasSubMenu()) {
+ // Close any other submenus that might be open at the current or
+ // a deeper level.
+ int nextIndex = mListViews.indexOf(view) + 1;
+ if (nextIndex < mListViews.size()) {
+ MenuAdapter nextSubMenuAdapter =
+ toMenuAdapter(mListViews.get(nextIndex).getAdapter());
+ // Disable exit animation, to prevent overlapping fading out
+ // submenus.
+ mPopupWindows.get(nextIndex).setExitTransition(null);
+ nextSubMenuAdapter.getAdapterMenu().close();
+ }
+
+ // Then open the selected submenu.
+ view.performItemClick(
+ selectedItemView,
+ view.getSelectedItemPosition(),
+ view.getSelectedItemId());
+ }
}
-
- // Then open the selected submenu, if there is one.
- final MenuItem menuItem = menu.getItem(position);
- if (menuItem.isEnabled() && menuItem.hasSubMenu()) {
- menu.performItemAction(menuItem, 0);
+ }, SUBMENU_TIMEOUT_MS);
+ } else if (menuIndex + 1 < mListViews.size()) {
+ // If the currently selected item does NOT corresponds to a submenu, check if there
+ // is a submenu already open that is one level deeper. If so, schedule to close it
+ // on a timeout.
+
+ final MenuDropDownListView nextView =
+ (MenuDropDownListView) mListViews.get(menuIndex + 1);
+ final MenuAdapter nextAdapter = toMenuAdapter(nextView.getAdapter());
+
+ mSubMenuHoverHandler.removeCallbacksAndMessages(null);
+ mSubMenuHoverHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ // Make sure the menu wasn't already closed by something else and that
+ // it wasn't re-hovered by the user since this was scheduled.
+ int nextMenuIndex = mListViews.indexOf(nextView);
+
+ if (nextMenuIndex != -1 && nextView.getSelectedView() == null) {
+ // Disable exit animation, to prevent overlapping fading out submenus.
+ for (int i = nextMenuIndex; i < mPopupWindows.size(); i++) {
+ final MenuPopupWindow popupWindow = mPopupWindows.get(i);
+ popupWindow.setExitTransition(null);
+ popupWindow.setAnimationStyle(0);
+ }
+ nextAdapter.getAdapterMenu().close();
+ }
}
- }
- };
- final long uptimeMillis = SystemClock.uptimeMillis() + SUBMENU_TIMEOUT_MS;
- mSubMenuHoverHandler.postAtTime(runnable, menu, uptimeMillis);
+ }, SUBMENU_TIMEOUT_MS);
+ }
}
};
- private int mRawDropDownGravity = Gravity.NO_GRAVITY;
private int mDropDownGravity = Gravity.NO_GRAVITY;
private View mAnchorView;
private View mShownAnchorView;
+ private List<DropDownListView> mListViews;
+ private List<MenuPopupWindow> mPopupWindows;
private int mLastPosition;
+ private List<Integer> mPositions;
+ private List<int[]> mOffsets;
private int mInitXOffset;
private int mInitYOffset;
private boolean mForceShowIcon;
@@ -181,10 +197,10 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
/**
* Initializes a new cascading-capable menu popup.
*
- * @param anchor A parent view to get the {@link android.view.View#getWindowToken()} token from.
+ * @param parent A parent view to get the {@link android.view.View#getWindowToken()} token from.
*/
- public CascadingMenuPopup(@NonNull Context context, @NonNull View anchor,
- @AttrRes int popupStyleAttr, @StyleRes int popupStyleRes, boolean overflowOnly) {
+ public CascadingMenuPopup(Context context, View anchor, int popupStyleAttr,
+ int popupStyleRes, boolean overflowOnly) {
mContext = Preconditions.checkNotNull(context);
mAnchorView = Preconditions.checkNotNull(anchor);
mPopupStyleAttr = popupStyleAttr;
@@ -192,12 +208,18 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
mOverflowOnly = overflowOnly;
mForceShowIcon = false;
- mLastPosition = getInitialMenuPosition();
final Resources res = context.getResources();
+ final Configuration config = res.getConfiguration();
+ mLayoutDirection = config.getLayoutDirection();
+ mLastPosition = getInitialMenuPosition();
mMenuMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
res.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth));
+ mPopupWindows = new ArrayList<MenuPopupWindow>();
+ mListViews = new ArrayList<DropDownListView>();
+ mOffsets = new ArrayList<int[]>();
+ mPositions = new ArrayList<Integer>();
mSubMenuHoverHandler = new Handler();
}
@@ -224,28 +246,28 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
return;
}
- // Show any menus that have been added via #addMenu(MenuBuilder) but
- // which have not yet been shown. In a typical use case,
- // #addMenu(MenuBuilder) would be called once, followed by a call to
- // this #show() method -- which would actually show the popup on the
- // screen.
- for (int i = 0, count = mAddedMenus.size(); i < count; i++) {
- final CascadingMenuInfo info = mAddedMenus.get(i);
- final MenuPopupWindow popupWindow = info.window;
+ // Show any menus that have been added via #addMenu(MenuBuilder) but which have not yet been
+ // shown.
+ // In a typical use case, #addMenu(MenuBuilder) would be called once, followed by a call to
+ // this #show() method -- which would actually show the popup on the screen.
+ for (int i = 0; i < mPopupWindows.size(); i++) {
+ MenuPopupWindow popupWindow = mPopupWindows.get(i);
popupWindow.show();
+ DropDownListView listView = (DropDownListView) popupWindow.getListView();
+ mListViews.add(listView);
- final MenuBuilder menu = info.menu;
+ MenuBuilder menu = toMenuAdapter(listView.getAdapter()).getAdapterMenu();
if (i == 0 && mShowTitle && menu.getHeaderTitle() != null) {
FrameLayout titleItemView =
(FrameLayout) LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.popup_menu_header_item_layout,
- info.getListView(),
+ listView,
false);
TextView titleView = (TextView) titleItemView.findViewById(
com.android.internal.R.id.title);
titleView.setText(menu.getHeaderTitle());
titleItemView.setEnabled(false);
- info.getListView().addHeaderView(titleItemView, null, false);
+ listView.addHeaderView(titleItemView, null, false);
// Update to show the title.
popupWindow.show();
@@ -265,13 +287,12 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
@Override
public void dismiss() {
- // Need to make another list to avoid a concurrent modification
- // exception, as #onDismiss may clear mPopupWindows while we are
- // iterating.
- final List<CascadingMenuInfo> addedMenus = new ArrayList<>(mAddedMenus);
- for (CascadingMenuInfo info : addedMenus) {
- if (info.window.isShowing()) {
- info.window.dismiss();
+ // Need to make another list to avoid a concurrent modification exception, as #onDismiss
+ // may clear mPopupWindows while we are iterating.
+ List<MenuPopupWindow> popupWindows = new ArrayList<MenuPopupWindow>(mPopupWindows);
+ for (MenuPopupWindow popupWindow : popupWindows) {
+ if (popupWindow != null && popupWindow.isShowing()) {
+ popupWindow.dismiss();
}
}
}
@@ -291,8 +312,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
*/
@HorizPosition
private int getInitialMenuPosition() {
- final int layoutDirection = mAnchorView.getLayoutDirection();
- return layoutDirection == View.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT :
+ return mLayoutDirection == View.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT :
HORIZ_POSITION_RIGHT;
}
@@ -305,7 +325,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
*/
@HorizPosition
private int getNextMenuPosition(int nextMenuWidth) {
- ListView lastListView = mAddedMenus.get(mAddedMenus.size() - 1).getListView();
+ ListView lastListView = mListViews.get(mListViews.size() - 1);
final int[] screenLocation = new int[2];
lastListView.getLocationOnScreen(screenLocation);
@@ -330,140 +350,76 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
@Override
public void addMenu(MenuBuilder menu) {
+ boolean addSubMenu = mListViews.size() > 0;
+
menu.addMenuPresenter(this, mContext);
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly);
+ MenuPopupWindow popupWindow = createPopupWindow();
+
+ MenuAdapter adapter = new MenuAdapter(menu, LayoutInflater.from(mContext), mOverflowOnly);
adapter.setForceShowIcon(mForceShowIcon);
- final int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth);
- final MenuPopupWindow popupWindow = createPopupWindow();
popupWindow.setAdapter(adapter);
- popupWindow.setWidth(menuWidth);
- popupWindow.setDropDownGravity(mDropDownGravity);
- final CascadingMenuInfo parentInfo;
- final View parentView;
- if (mAddedMenus.size() > 0) {
- parentInfo = mAddedMenus.get(mAddedMenus.size() - 1);
- parentView = findParentViewForSubmenu(parentInfo, menu);
- } else {
- parentInfo = null;
- parentView = null;
- }
+ int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth);
- final int x;
- final int y;
- if (parentView != null) {
- // This menu is a cascading submenu anchored to a parent view.
+ int x = 0;
+ int y = 0;
+
+ if (addSubMenu) {
popupWindow.setTouchModal(false);
popupWindow.setEnterTransition(null);
- final @HorizPosition int nextMenuPosition = getNextMenuPosition(menuWidth);
- final boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT;
+ ListView lastListView = mListViews.get(mListViews.size() - 1);
+ @HorizPosition int nextMenuPosition = getNextMenuPosition(menuWidth);
+ boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT;
mLastPosition = nextMenuPosition;
- final int[] tempLocation = new int[2];
+ int[] lastLocation = new int[2];
+ lastListView.getLocationOnScreen(lastLocation);
- // This popup menu will be positioned relative to the top-left edge
- // of the view representing its parent menu.
- parentView.getLocationInWindow(tempLocation);
- final int parentOffsetLeft = parentInfo.window.getHorizontalOffset() + tempLocation[0];
- final int parentOffsetTop = parentInfo.window.getVerticalOffset() + tempLocation[1];
+ int[] lastOffset = mOffsets.get(mOffsets.size() - 1);
- // By now, mDropDownGravity is the resolved absolute gravity, so
- // this should work in both LTR and RTL.
+ // Note: By now, mDropDownGravity is the absolute gravity, so this should work in both
+ // LTR and RTL.
if ((mDropDownGravity & Gravity.RIGHT) == Gravity.RIGHT) {
if (showOnRight) {
- x = parentOffsetLeft + menuWidth;
+ x = lastOffset[0] + menuWidth;
} else {
- x = parentOffsetLeft - parentView.getWidth();
+ x = lastOffset[0] - lastListView.getWidth();
}
} else {
if (showOnRight) {
- x = parentOffsetLeft + parentView.getWidth();
+ x = lastOffset[0] + lastListView.getWidth();
} else {
- x = parentOffsetLeft - menuWidth;
+ x = lastOffset[0] - menuWidth;
}
}
- y = parentOffsetTop;
+ y = lastOffset[1] + lastListView.getSelectedView().getTop() -
+ lastListView.getChildAt(0).getTop();
} else {
x = mInitXOffset;
y = mInitYOffset;
}
+ popupWindow.setWidth(menuWidth);
popupWindow.setHorizontalOffset(x);
popupWindow.setVerticalOffset(y);
-
- final CascadingMenuInfo menuInfo = new CascadingMenuInfo(popupWindow, menu, mLastPosition);
- mAddedMenus.add(menuInfo);
+ mPopupWindows.add(popupWindow);
// NOTE: This case handles showing submenus once the CascadingMenuPopup has already
// been shown via a call to its #show() method. If it hasn't yet been show()n, then
// we deliberately do not yet show the popupWindow, as #show() will do that later.
if (isShowing()) {
popupWindow.show();
- }
- }
-
- /**
- * Returns the index within the specified parent menu of the menu item that
- * owns specified submenu.
- *
- * @param parent the parent menu
- * @param submenu the submenu for which the index should be returned
- * @return the index or {@code -1} if not present
- */
- private int findIndexOfSubmenuInMenu(
- @NonNull MenuBuilder parent, @NonNull MenuBuilder submenu) {
- for (int i = 0, count = parent.size(); i < count; i++) {
- final MenuItem item = parent.getItem(i);
- if (item.hasSubMenu() && submenu == item.getSubMenu()) {
- return i;
- }
+ DropDownListView listView = (DropDownListView) popupWindow.getListView();
+ mListViews.add(listView);
}
- return -1;
- }
-
- /**
- * Attempts to find the view for the menu item that owns the specified
- * submenu.
- *
- * @param parentInfo info for the parent menu
- * @param submenu the submenu whose parent view should be obtained
- * @return the parent view, or {@code null} if one could not be found
- */
- @Nullable
- private View findParentViewForSubmenu(
- @NonNull CascadingMenuInfo parentInfo, @NonNull MenuBuilder submenu) {
- final int parentIndex = findIndexOfSubmenuInMenu(parentInfo.menu, submenu);
- if (parentIndex < 0) {
- // Couldn't find the submenu.
- return null;
- }
-
- // The adapter may be wrapped. Adjust the index if necessary.
- final ListView listView = parentInfo.getListView();
- final ListAdapter listAdapter = listView.getAdapter();
- final int adjParentIndex;
- if (listAdapter instanceof HeaderViewListAdapter) {
- final int numHeaders = ((HeaderViewListAdapter) listAdapter).getHeadersCount();
- adjParentIndex = parentIndex + numHeaders;
- } else {
- adjParentIndex = parentIndex;
- }
-
- // Obtain the view at the menu item's adjusted index.
- final int firstPosition = listView.getFirstVisiblePosition();
- final int parentPosition = adjParentIndex - firstPosition;
- if (parentPosition < 0 || parentPosition >= listView.getChildCount()) {
- // Not visible on screen.
- return null;
- }
-
- return listView.getChildAt(parentPosition);
+ int[] offsets = {x, y};
+ mOffsets.add(offsets);
+ mPositions.add(mLastPosition);
}
/**
@@ -471,7 +427,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
*/
@Override
public boolean isShowing() {
- return mAddedMenus.size() > 0 && mAddedMenus.get(0).window.isShowing();
+ return mPopupWindows.size() > 0 && mPopupWindows.get(0).isShowing();
}
/**
@@ -479,28 +435,28 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
*/
@Override
public void onDismiss() {
- // The dismiss listener doesn't pass the calling window, so walk
- // through the stack to figure out which one was just dismissed.
- CascadingMenuInfo dismissedInfo = null;
- for (int i = 0, count = mAddedMenus.size(); i < count; i++) {
- final CascadingMenuInfo info = mAddedMenus.get(i);
- if (!info.window.isShowing()) {
- dismissedInfo = info;
+ int dismissedIndex = -1;
+ for (int i = 0; i < mPopupWindows.size(); i++) {
+ if (!mPopupWindows.get(i).isShowing()) {
+ dismissedIndex = i;
break;
}
}
- // Close all menus starting from the dismissed menu, passing false
- // since we are manually closing only a subset of windows.
- if (dismissedInfo != null) {
- dismissedInfo.menu.close(false);
+ if (dismissedIndex != -1) {
+ for (int i = dismissedIndex; i < mListViews.size(); i++) {
+ ListView view = mListViews.get(i);
+ ListAdapter adapter = view.getAdapter();
+ MenuAdapter menuAdapter = toMenuAdapter(adapter);
+ menuAdapter.mAdapterMenu.close();
+ }
}
}
@Override
public void updateMenuView(boolean cleared) {
- for (CascadingMenuInfo info : mAddedMenus) {
- toMenuAdapter(info.getListView().getAdapter()).notifyDataSetChanged();
+ for (ListView view : mListViews) {
+ toMenuAdapter(view.getAdapter()).notifyDataSetChanged();
}
}
@@ -512,17 +468,16 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
@Override
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
// Don't allow double-opening of the same submenu.
- for (CascadingMenuInfo info : mAddedMenus) {
- if (subMenu == info.menu) {
+ for (ListView view : mListViews) {
+ if (toMenuAdapter(view.getAdapter()).mAdapterMenu.equals(subMenu)) {
// Just re-focus that one.
- info.getListView().requestFocus();
+ view.requestFocus();
return true;
}
}
if (subMenu.hasVisibleItems()) {
- addMenu(subMenu);
-
+ this.addMenu(subMenu);
if (mPresenterCallback != null) {
mPresenterCallback.onOpenSubMenu(subMenu);
}
@@ -531,61 +486,52 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
return false;
}
- /**
- * Finds the index of the specified menu within the list of added menus.
- *
- * @param menu the menu to find
- * @return the index of the menu, or {@code -1} if not present
- */
- private int findIndexOfAddedMenu(@NonNull MenuBuilder menu) {
- for (int i = 0, count = mAddedMenus.size(); i < count; i++) {
- final CascadingMenuInfo info = mAddedMenus.get(i);
- if (menu == info.menu) {
- return i;
- }
- }
-
- return -1;
- }
-
@Override
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
- final int menuIndex = findIndexOfAddedMenu(menu);
- if (menuIndex < 0) {
- return;
- }
+ int menuIndex = -1;
+ boolean wasSelected = false;
+
+ for (int i = 0; i < mListViews.size(); i++) {
+ ListView view = mListViews.get(i);
+ MenuAdapter adapter = toMenuAdapter(view.getAdapter());
- // Close this and all descendant menus.
- final int nextMenuIndex = menuIndex + 1;
- if (nextMenuIndex < mAddedMenus.size()) {
- final CascadingMenuInfo info = mAddedMenus.get(nextMenuIndex);
+ if (menuIndex == -1 && menu == adapter.mAdapterMenu) {
+ menuIndex = i;
+ wasSelected = view.getSelectedView() != null;
+ }
- // Disable all exit animations.
- info.window.setExitTransition(null);
- info.window.setAnimationStyle(0);
- info.menu.close(false);
+ // Once the menu has been found, remove it and all submenus beneath it from the
+ // container view. Also remove the presenter.
+ if (menuIndex != -1) {
+ adapter.mAdapterMenu.removeMenuPresenter(this);
+ }
}
- final CascadingMenuInfo info = mAddedMenus.remove(menuIndex);
- info.menu.removeMenuPresenter(this);
- info.window.dismiss();
+ // Then, actually remove the views for these [sub]menu(s) from our list of views.
+ if (menuIndex != -1) {
+ for (int i = menuIndex; i < mPopupWindows.size(); i++) {
+ mPopupWindows.get(i).dismiss();
+ }
+ mPopupWindows.subList(menuIndex, mPopupWindows.size()).clear();
+ mListViews.subList(menuIndex, mListViews.size()).clear();
+ mOffsets.subList(menuIndex, mOffsets.size()).clear();
- final int count = mAddedMenus.size();
- if (count > 0) {
- mLastPosition = mAddedMenus.get(count - 1).position;
- } else {
- mLastPosition = getInitialMenuPosition();
+ mPositions.subList(menuIndex, mPositions.size()).clear();
+ if (mPositions.size() > 0) {
+ mLastPosition = mPositions.get(mPositions.size() - 1);
+ } else {
+ mLastPosition = getInitialMenuPosition();
+ }
}
- // If this was the root menu or we're supposed to close all menus,
- // dismiss the window and close all menus from the root.
- if (count == 0 || allMenusAreClosing) {
+ if (mListViews.size() == 0 || wasSelected) {
dismiss();
-
if (mPresenterCallback != null) {
- mPresenterCallback.onCloseMenu(menu, true);
+ mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
}
+ }
+ if (mPopupWindows.size() == 0) {
if (mTreeObserver != null) {
if (mTreeObserver.isAlive()) {
mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
@@ -593,9 +539,8 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
mTreeObserver = null;
}
mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
-
- // If every [sub]menu was dismissed, that means the whole thing was
- // dismissed, so notify the owner.
+ // If every [sub]menu was dismissed, that means the whole thing was dismissed, so notify
+ // the owner.
mOnDismissListener.onDismiss();
}
}
@@ -616,22 +561,12 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
@Override
public void setGravity(int dropDownGravity) {
- if (mRawDropDownGravity != dropDownGravity) {
- mRawDropDownGravity = dropDownGravity;
- mDropDownGravity = Gravity.getAbsoluteGravity(
- dropDownGravity, mAnchorView.getLayoutDirection());
- }
+ mDropDownGravity = Gravity.getAbsoluteGravity(dropDownGravity, mLayoutDirection);
}
@Override
- public void setAnchorView(@NonNull View anchor) {
- if (mAnchorView != anchor) {
- mAnchorView = anchor;
-
- // Gravity resolution may have changed, update from raw gravity.
- mDropDownGravity = Gravity.getAbsoluteGravity(
- mRawDropDownGravity, mAnchorView.getLayoutDirection());
- }
+ public void setAnchorView(View anchor) {
+ mAnchorView = anchor;
}
@Override
@@ -641,7 +576,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
@Override
public ListView getListView() {
- return mAddedMenus.isEmpty() ? null : mAddedMenus.get(mAddedMenus.size() - 1).getListView();
+ return mListViews.size() > 0 ? mListViews.get(mListViews.size() - 1) : null;
}
@Override
@@ -658,21 +593,4 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
public void setShowTitle(boolean showTitle) {
mShowTitle = showTitle;
}
-
- private static class CascadingMenuInfo {
- public final MenuPopupWindow window;
- public final MenuBuilder menu;
- public final int position;
-
- public CascadingMenuInfo(@NonNull MenuPopupWindow window, @NonNull MenuBuilder menu,
- int position) {
- this.window = window;
- this.menu = menu;
- this.position = position;
- }
-
- public ListView getListView() {
- return window.getListView();
- }
- }
} \ No newline at end of file
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 0cfb1b9c053f..167392874173 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -936,14 +936,15 @@ public class MenuBuilder implements Menu {
}
/**
- * Closes the menu.
- *
- * @param closeAllMenus {@code true} if all displayed menus and submenus
- * should be completely closed (as when a menu item is
- * selected) or {@code false} if only this menu should
- * be closed
+ * Closes the visible menu.
+ *
+ * @param allMenusAreClosing Whether the menus are completely closing (true),
+ * or whether there is another menu coming in this menu's place
+ * (false). For example, if the menu is closing because a
+ * sub menu is about to be shown, <var>allMenusAreClosing</var>
+ * is false.
*/
- public final void close(boolean closeAllMenus) {
+ public final void close(boolean allMenusAreClosing) {
if (mIsClosing) return;
mIsClosing = true;
@@ -952,7 +953,7 @@ public class MenuBuilder implements Menu {
if (presenter == null) {
mPresenters.remove(ref);
} else {
- presenter.onCloseMenu(this, closeAllMenus);
+ presenter.onCloseMenu(this, allMenusAreClosing);
}
}
mIsClosing = false;
diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java
index c847c15607e1..f207b9834885 100644
--- a/core/java/com/android/internal/view/menu/MenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/MenuPresenter.java
@@ -97,10 +97,8 @@ public interface MenuPresenter {
* closing. Presenter implementations should close the representation
* of the menu indicated as necessary and notify a registered callback.
*
- * @param menu the menu or submenu that is closing
- * @param allMenusAreClosing {@code true} if all displayed menus and
- * submenus are closing, {@code false} if only
- * the specified menu is closing
+ * @param menu Menu or submenu that is closing.
+ * @param allMenusAreClosing True if all associated menus are closing.
*/
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 591da1c76484..6557ed159a0b 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -28,7 +28,6 @@ import android.view.MotionEvent;
import android.view.RenderNode;
import android.view.ThreadedRenderer;
import android.view.View;
-import android.view.ViewRootImpl;
import android.widget.LinearLayout;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
@@ -399,6 +398,8 @@ public class NonClientDecorView extends LinearLayout
* It starts with the creation and it ends once someone calls destroy().
* Any size changes can be passed by a call to setTargetRect will passed to the thread and
* executed via the Choreographer.
+ * TODO(b/24810450): Separate functionality from non-client-decor so that it can be used
+ * independently.
*/
private class ResizeFrameThread extends Thread implements Choreographer.FrameCallback {
// This is containing the last requested size by a resize command. Note that this size might
@@ -423,6 +424,7 @@ public class NonClientDecorView extends LinearLayout
private int mLastYOffset;
ResizeFrameThread(ThreadedRenderer renderer, Rect initialBounds) {
+ setName("ResizeFrame");
mRenderer = renderer;
// Create the render nodes for our frame and backdrop which can be resized independently
@@ -435,7 +437,9 @@ public class NonClientDecorView extends LinearLayout
// Set the initial bounds and draw once so that we do not get a broken frame.
mTargetRect.set(initialBounds);
- changeWindowSize(initialBounds);
+ synchronized (this) {
+ changeWindowSizeLocked(initialBounds);
+ }
// Kick off our draw thread.
start();
@@ -458,10 +462,12 @@ public class NonClientDecorView extends LinearLayout
* The window got replaced due to a configuration change.
*/
public void onConfigurationChange() {
- if (mRenderer != null) {
- // Enforce a window redraw.
- mOldTargetRect.set(0, 0, 0, 0);
- pingRenderLocked();
+ synchronized (this) {
+ if (mRenderer != null) {
+ // Enforce a window redraw.
+ mOldTargetRect.set(0, 0, 0, 0);
+ pingRenderLocked();
+ }
}
}
@@ -475,7 +481,8 @@ public class NonClientDecorView extends LinearLayout
// Invalidate the current content bounds.
mRenderer.setContentDrawBounds(0, 0, 0, 0);
- // Remove the render nodes again (see comment above - better to do that only once).
+ // Remove the render nodes again
+ // (see comment above - better to do that only once).
mRenderer.removeRenderNode(mFrameNode);
mRenderer.removeRenderNode(mBackdropNode);
@@ -509,24 +516,23 @@ public class NonClientDecorView extends LinearLayout
*/
@Override
public void doFrame(long frameTimeNanos) {
- if (mRenderer == null) {
- // Tell the looper to stop. We are done.
- Looper.myLooper().quit();
- return;
- }
- // Prevent someone from changing this while we are copying.
synchronized (this) {
+ if (mRenderer == null) {
+ // Tell the looper to stop. We are done.
+ Looper.myLooper().quit();
+ return;
+ }
mNewTargetRect.set(mTargetRect);
- }
- if (!mNewTargetRect.equals(mOldTargetRect)) {
- mOldTargetRect.set(mNewTargetRect);
- changeWindowSize(mNewTargetRect);
+ if (!mNewTargetRect.equals(mOldTargetRect)) {
+ mOldTargetRect.set(mNewTargetRect);
+ changeWindowSizeLocked(mNewTargetRect);
+ }
}
}
/**
* The content is about to be drawn and we got the location of where it will be shown.
- * If a "changeWindowSize" call has already been processed, we will re-issue the call
+ * If a "changeWindowSizeLocked" call has already been processed, we will re-issue the call
* if the previous call was ignored since the size was unknown.
* @param xOffset The x offset where the content is drawn to.
* @param yOffset The y offset where the content is drawn to.
@@ -547,8 +553,8 @@ public class NonClientDecorView extends LinearLayout
mLastYOffset + mLastCaptionHeight,
mLastXOffset + mLastContentWidth,
mLastYOffset + mLastCaptionHeight + mLastContentHeight);
- // If this was the first call and changeWindowSize got already called prior to us,
- // We should re-issue a changeWindowSize now.
+ // If this was the first call and changeWindowSizeLocked got already called prior
+ // to us, we should re-issue a changeWindowSizeLocked now.
if (firstCall && (mLastCaptionHeight != 0 || !mShowDecor)) {
mOldTargetRect.set(0, 0, 0, 0);
pingRenderLocked();
@@ -560,9 +566,7 @@ public class NonClientDecorView extends LinearLayout
* Resizing the frame to fit the new window size.
* @param newBounds The window bounds which needs to be drawn.
*/
- private void changeWindowSize(Rect newBounds) {
- long startTime = System.currentTimeMillis();
-
+ private void changeWindowSizeLocked(Rect newBounds) {
// While a configuration change is taking place the view hierarchy might become
// inaccessible. For that case we remember the previous metrics to avoid flashes.
// Note that even when there is no visible caption, the caption child will exist.
diff --git a/core/res/res/layout/list_content.xml b/core/res/res/layout/list_content.xml
index 14140322b7cd..45ade4d0c6d7 100644
--- a/core/res/res/layout/list_content.xml
+++ b/core/res/res/layout/list_content.xml
@@ -44,8 +44,7 @@
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:drawSelectorOnTop="false" />
+ android:layout_height="match_parent" />
<TextView android:id="@+android:id/internalEmpty"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index 4e895b0417ff..f073c3379727 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -41,6 +41,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/preference_fragment_padding_side"
+ android:textAppearance="?android:attr/textAppearanceMedium"
android:gravity="center"
android:visibility="gone" />
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 45d9dc36adae..5032c2538c94 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -51,7 +51,7 @@
<string name="serviceDisabled" msgid="1937553226592516411">"سرویس غیرفعال شده است."</string>
<string name="serviceRegistered" msgid="6275019082598102493">"ثبت با موفقیت انجام شد"</string>
<string name="serviceErased" msgid="1288584695297200972">"پاک کردن با موفقیت انجام شد."</string>
- <string name="passwordIncorrect" msgid="7612208839450128715">"رمز ورود اشتباه است."</string>
+ <string name="passwordIncorrect" msgid="7612208839450128715">"گذرواژه اشتباه است."</string>
<string name="mmiComplete" msgid="8232527495411698359">"‏MMI کامل شد."</string>
<string name="badPin" msgid="9015277645546710014">"‏پین قدیمی که نوشته‎اید صحیح نیست."</string>
<string name="badPuk" msgid="5487257647081132201">"‏PUK که نوشته‌اید صحیح نیست."</string>
@@ -74,7 +74,7 @@
<string name="CfMmi" msgid="5123218989141573515">"هدایت تماس"</string>
<string name="CwMmi" msgid="9129678056795016867">"انتظار تماس"</string>
<string name="BaMmi" msgid="455193067926770581">"محدودیت تماس"</string>
- <string name="PwdMmi" msgid="7043715687905254199">"تغییر رمز ورود"</string>
+ <string name="PwdMmi" msgid="7043715687905254199">"تغییر گذرواژه"</string>
<string name="PinMmi" msgid="3113117780361190304">"تغییر پین"</string>
<string name="CnipMmi" msgid="3110534680557857162">"شماره تماس حاضر"</string>
<string name="CnirMmi" msgid="3062102121430548731">"شماره تماس محدود شده"</string>
@@ -495,7 +495,7 @@
<string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"به دارنده امکان می‌دهد به سرویس‌های شرکت مخابراتی متصل شود. هرگز نباید برای برنامه‌های عادی مورد نیاز باشد."</string>
<string name="permlab_access_notification_policy" msgid="4247510821662059671">"دسترسی به حالت «مزاحم نشوید»"</string>
<string name="permdesc_access_notification_policy" msgid="3296832375218749580">"به برنامه امکان می‌دهد پیکربندی «مزاحم نشوید» را بخواند و بنویسد."</string>
- <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string>
+ <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین گذرواژه"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"کنترل طول و نوع نویسه‌هایی که در گذرواژه و پین قفل صفحه مجاز است."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاش‌های قفل گشایی صفحه"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"‏تعداد گذرواژه‎های نادرست تایپ شده را هنگام بازکردن قفل صفحه کنترل می‌کند، و اگر دفعات زیادی گذرواژه نادرست وارد شود رایانهٔ لوحی را قفل می‌کند و همه داده‎های رایانهٔ لوحی را پاک می‌کند."</string>
@@ -532,7 +532,7 @@
<item msgid="7897544654242874543">"محل کار"</item>
<item msgid="1103601433382158155">"نمابر محل کار"</item>
<item msgid="1735177144948329370">"نمابر خانه"</item>
- <item msgid="603878674477207394">"پیجر"</item>
+ <item msgid="603878674477207394">"پی‌جو"</item>
<item msgid="1650824275177931637">"سایر موارد"</item>
<item msgid="9192514806975898961">"سفارشی"</item>
</string-array>
@@ -575,7 +575,7 @@
<string name="phoneTypeWork" msgid="8863939667059911633">"محل کار"</string>
<string name="phoneTypeFaxWork" msgid="3517792160008890912">"نمابر محل کار"</string>
<string name="phoneTypeFaxHome" msgid="2067265972322971467">"نمابر خانه"</string>
- <string name="phoneTypePager" msgid="7582359955394921732">"پیجر"</string>
+ <string name="phoneTypePager" msgid="7582359955394921732">"پی‌جو"</string>
<string name="phoneTypeOther" msgid="1544425847868765990">"سایر موارد"</string>
<string name="phoneTypeCallback" msgid="2712175203065678206">"برگرداندن تماس"</string>
<string name="phoneTypeCar" msgid="8738360689616716982">"خودرو"</string>
@@ -700,9 +700,9 @@
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"‏تلاش‎های زیادی برای کشیدن الگو صورت گرفته است"</string>
<string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"‏برای بازگشایی قفل، با حساب Google خود وارد سیستم شوید."</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"نام کاربری (ایمیل)"</string>
- <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"رمز ورود"</string>
+ <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"گذرواژه"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ورود به سیستم"</string>
- <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"نام کاربر یا رمز ورود نامعتبر است."</string>
+ <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"نام کاربر یا گذرواژه نامعتبر است."</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"‏نام کاربری یا گذرواژهٔ خود را فراموش کردید؟\nاز "<b>"google.com/accounts/recovery"</b>" بازدید کنید."</string>
<string name="lockscreen_glogin_checking_password" msgid="7114627351286933867">"در حال بررسی..."</string>
<string name="lockscreen_unlock_label" msgid="737440483220667054">"بازگشایی قفل"</string>
@@ -786,7 +786,7 @@
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه می‌دهد تا پیام‌ها را به صندوق دریافت پست صوتی شما اضافه کند."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تغییر مجوزهای مکان جغرافیایی مرورگر"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"‏به برنامه اجازه می‎دهد تا مجوزهای جغرافیایی مرورگر را تغییر دهد. برنامه‎های مخرب می‎توانند از آن استفاده کنند تا اطلاعات موقعیت مکانی را به سایت‌های وب کتابخانه بفرستند."</string>
- <string name="save_password_message" msgid="767344687139195790">"می‌خواهید مرورگر این رمز ورود را به خاطر داشته باشد؟"</string>
+ <string name="save_password_message" msgid="767344687139195790">"می‌خواهید مرورگر این گذرواژه را به خاطر داشته باشد؟"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"اکنون نه"</string>
<string name="save_password_remember" msgid="6491879678996749466">"به خاطر سپردن"</string>
<string name="save_password_never" msgid="8274330296785855105">"هیچ‌وقت"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 72b1c54293d6..a034a256546c 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -290,7 +290,7 @@
<string name="permdesc_enableCarMode" msgid="4853187425751419467">"Consente all\'applicazione di abilitare la modalità automobile."</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"chiusura altre applicazioni"</string>
<string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Consente all\'applicazione di terminare i processi in background di altre applicazioni. Ciò potrebbe causare l\'interruzione di altre applicazioni."</string>
- <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"spostamento sopra altre app"</string>
+ <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"posizionamento davanti ad altre app"</string>
<string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Consente all\'applicazione di spostarsi sopra ad altre applicazioni o parti dell\'interfaccia utente. Potrebbe interferire con il tuo utilizzo dell\'interfaccia in qualsiasi applicazione o cambiare ciò che credi di vedere in altre applicazioni."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"esecuzione permanente delle applicazioni"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Consente all\'applicazione di rendere persistenti in memoria alcune sue parti. Ciò può limitare la memoria disponibile per altre applicazioni, rallentando il tablet."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 29fd6a1be775..e803cbb2dca0 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -163,7 +163,7 @@
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"För många <xliff:g id="CONTENT_TYPE">%s</xliff:g>-borttagningar."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Pekdatorns lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
<string name="low_memory" product="watch" msgid="4415914910770005166">"Klockans lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
- <string name="low_memory" product="tv" msgid="516619861191025923">"Lagringsutrymmet på tv:n är fullt. Ta bort några filer för att frigöra utrymme."</string>
+ <string name="low_memory" product="tv" msgid="516619861191025923">"Lagringsutrymmet på TV:n är fullt. Ta bort några filer för att frigöra utrymme."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Mobilens lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Nätverket kan vara övervakat"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Av en okänd tredje part"</string>
@@ -177,7 +177,7 @@
<string name="factory_reset_message" msgid="4905025204141900666">"Administratörsappen saknar delar eller är skadad och kan inte användas. Enheten kommer nu att rensas. Kontakta administratören om du behöver hjälp."</string>
<string name="me" msgid="6545696007631404292">"Jag"</string>
<string name="power_dialog" product="tablet" msgid="8545351420865202853">"Alternativ för surfplattan"</string>
- <string name="power_dialog" product="tv" msgid="6153888706430556356">"Tv-alternativ"</string>
+ <string name="power_dialog" product="tv" msgid="6153888706430556356">"TV-alternativ"</string>
<string name="power_dialog" product="default" msgid="1319919075463988638">"Telefonalternativ"</string>
<string name="silent_mode" msgid="7167703389802618663">"Tyst läge"</string>
<string name="turn_on_radio" msgid="3912793092339962371">"Aktivera trådlöst"</string>
@@ -195,7 +195,7 @@
<string name="reboot_to_reset_message" msgid="2432077491101416345">"Startar om …"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Avslutar…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Din surfplatta stängs av."</string>
- <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"Tv:n stängs av."</string>
+ <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"TV:n stängs av."</string>
<string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Klockan stängs av."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Din telefon stängs av."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Vill du stänga av?"</string>
@@ -276,7 +276,7 @@
<string name="permdesc_sendSms" msgid="7094729298204937667">"Tillåter att appen skickar SMS. Detta kan leda till oväntade avgifter. Skadliga appar kan skicka meddelanden utan ditt godkännande vilket kan kosta pengar."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"läsa dina textmeddelanden (SMS eller MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Tillåter att appen läser SMS som sparats på surfplattan eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string>
- <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Tillåter att appen läser sms som har sparats på tv:n eller SIM-kortet. På så sätt kan appen läsa alla sms oavsett innehåll eller sekretess."</string>
+ <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Tillåter att appen läser sms som har sparats på TV:n eller SIM-kortet. På så sätt kan appen läsa alla sms oavsett innehåll eller sekretess."</string>
<string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Tillåter att appen läser SMS som sparats på mobilen eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string>
<string name="permlab_receiveWapPush" msgid="5991398711936590410">"ta emot textmeddelanden (WAP)"</string>
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillåter att appen tar emot och hanterar WAP-meddelanden. Med den här behörigheten kan appen övervaka eller ta bort meddelanden som skickats till dig utan att visa dem för dig."</string>
@@ -294,7 +294,7 @@
<string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Tillåter att appen att dras ovanpå andra appar eller delar av användargränssnittet. De kan störa din användning av gränssnittet i olika appar eller ändra vad du tror visas i andra appar."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"se till att appen alltid körs"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör surfplattan långsam."</string>
- <string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Tillåter att en app gör vissa delar beständiga i minnet. Det kan begränsa mängden minne som är tillgänglig för andra appar och gör tv:n långsammare."</string>
+ <string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Tillåter att en app gör vissa delar beständiga i minnet. Det kan begränsa mängden minne som är tillgänglig för andra appar och gör TV:n långsammare."</string>
<string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör mobilen långsam."</string>
<string name="permlab_getPackageSize" msgid="7472921768357981986">"mäta appens lagringsplats"</string>
<string name="permdesc_getPackageSize" msgid="3921068154420738296">"Tillåter att appen hämtar kod, data och cachestorlekar"</string>
@@ -306,33 +306,33 @@
<string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"Tillåter att appen startar automatiskt när systemet har startats om. Detta kan innebära att det tar längre tid att starta mobilen och att mobilen blir långsammare i och med att appen hela tiden körs i bakgrunden."</string>
<string name="permlab_broadcastSticky" msgid="7919126372606881614">"Skicka sticky broadcast"</string>
<string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"Tillåter att appen skickar sticky broadcasts, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas."</string>
- <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Tillåter att appen skickar sticky broadcasts som finns kvar när sändningen är slut. Överdriven användning kan göra tv:n seg eller instabil eftersom den använder för mycket minne."</string>
+ <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Tillåter att appen skickar sticky broadcasts som finns kvar när sändningen är slut. Överdriven användning kan göra TV:n seg eller instabil eftersom den använder för mycket minne."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Tillåter att appen skickar sticky broadcast, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"läsa dina kontakter"</string>
<string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Tillåter att appen läser kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
- <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Tillåter att appen läser data om dina kontakter som sparats på tv:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med enskilda individer. Med den här behörigheten kan appar spara dina kontaktuppgifter och skadliga appar kan dela kontaktuppgifter utan att du vet om det."</string>
+ <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Tillåter att appen läser data om dina kontakter som sparats på TV:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med enskilda individer. Med den här behörigheten kan appar spara dina kontaktuppgifter och skadliga appar kan dela kontaktuppgifter utan att du vet om det."</string>
<string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Tillåter att appen läser kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"ändra kontakterna"</string>
<string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillåter att appen ändrar kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string>
- <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Tillåter att appen ändrar uppgifterna om dina kontakter som har sparats på tv:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med särskilda kontakter. Med den här behörigheten kan appar ta bort kontaktuppgifter."</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Tillåter att appen ändrar uppgifterna om dina kontakter som har sparats på TV:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med särskilda kontakter. Med den här behörigheten kan appar ta bort kontaktuppgifter."</string>
<string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillåter att appen ändrar kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"läs samtalslogg"</string>
<string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Tillåter att appen läser pekdatorns samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
- <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Tillåter att appen läser tv:ns samtalslista, bland annat data om inkommande och utgående samtal. Med den här behörigheten kan appar spara data i dina samtalslistor och skadliga appar kan dela data i samtalslistor utan att du vet om det."</string>
+ <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Tillåter att appen läser TV:ns samtalslista, bland annat data om inkommande och utgående samtal. Med den här behörigheten kan appar spara data i dina samtalslistor och skadliga appar kan dela data i samtalslistor utan att du vet om det."</string>
<string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Tillåter att appen läser mobilens samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"skriv samtalslogg"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Tillåter att appen gör ändringar i pekdatorns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string>
- <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Tillåter att appen gör ändringar i tv:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista."</string>
+ <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Tillåter att appen gör ändringar i TV:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Tillåter att appen gör ändringar i mobilens samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string>
<string name="permlab_bodySensors" msgid="4871091374767171066">"kroppssens. (för hjärtat m.m.)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ger appen åtkomst till information från sensorer om ditt fysiska tillstånd, till exempel din puls."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"läsa kalenderuppgifter plus konfidentiell information"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillåter att appen läser alla kalenderuppgifter som sparats på surfplattan, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Tillåter att appen läser alla kalenderhändelser som sparats på tv:n, bland annat de som tillhör vänner eller kollegor. På så sätt kan appen dela eller spara dina kalenderhändelser oavsett sekretess eller känslighet."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Tillåter att appen läser alla kalenderhändelser som sparats på TV:n, bland annat de som tillhör vänner eller kollegor. På så sätt kan appen dela eller spara dina kalenderhändelser oavsett sekretess eller känslighet."</string>
<string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Tillåter att appen läser alla kalenderuppgifter som sparats på mobilen, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string>
<string name="permlab_writeCalendar" msgid="8438874755193825647">"lägga till eller ändra kalenderuppgifter och skicka e-post till gäster utan ägarens vetskap"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på surfplattan, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Tillåter att appen lägger till, tar bort och ändrar händelser som du kan ändra på tv:n, inklusive dina vänners eller kollegors uppgifter. Appen kan på så sätt skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Tillåter att appen lägger till, tar bort och ändrar händelser som du kan ändra på TV:n, inklusive dina vänners eller kollegors uppgifter. Appen kan på så sätt skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap."</string>
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på mobilen, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få åtkomst till extra kommandon för platsleverantör"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tillåter att appen får åtkomst till extra kommandon för platsleverantör. Detta kan innebära att appen tillåts störa funktionen för GPS eller andra platskällor."</string>
@@ -359,14 +359,14 @@
<string name="permlab_readPhoneState" msgid="9178228524507610486">"läsa telefonens status och identitet"</string>
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tillåter att appen kommer åt enhetens telefonfunktioner. Med den här behörigheten tillåts appen att identifiera mobilens telefonnummer och enhets-ID, om ett samtal pågår och vilket nummer samtalet är kopplat till."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"förhindra att surfplattan går in i viloläge"</string>
- <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"förhindra att tv:n försätts i viloläge"</string>
+ <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"förhindra att TV:n försätts i viloläge"</string>
<string name="permlab_wakeLock" product="default" msgid="573480187941496130">"förhindra att telefonen sätts i viloläge"</string>
<string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Tillåter att appen förhindrar att surfplattan går in i viloläge."</string>
- <string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Tillåter att appen förhindrar att tv:n försätts i viloläge."</string>
+ <string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Tillåter att appen förhindrar att TV:n försätts i viloläge."</string>
<string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Tillåter att appen förhindrar att mobilen går in i viloläge."</string>
<string name="permlab_transmitIr" msgid="7545858504238530105">"tillåt IR-sändning"</string>
<string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"Tillåter att appen använder surfplattans IR-sändare."</string>
- <string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Tillåter att appen använder den infraröda sändaren på tv:n."</string>
+ <string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Tillåter att appen använder den infraröda sändaren på TV:n."</string>
<string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"Tillåter att appen använder mobilens IR-sändare."</string>
<string name="permlab_setWallpaper" msgid="6627192333373465143">"ange bakgrund"</string>
<string name="permdesc_setWallpaper" msgid="7373447920977624745">"Tillåter att appen anger systemets bakgrund."</string>
@@ -374,11 +374,11 @@
<string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"Tillåter att appen ger tips om systemets bakgrundsstorlek."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"ange tidszon"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"Tillåter att appen ändrar pekdatorns tidszon."</string>
- <string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Tillåter att appen ändrar tidszonen på tv:n."</string>
+ <string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Tillåter att appen ändrar tidszonen på TV:n."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"Tillåter att appen ändrar mobilens tidszon."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"hitta konton på enheten"</string>
<string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Tillåter att appen hämtar en lista över alla kända konton på surfplattan. Detta kan inkludera konton som har skapats av appar som du har installerat."</string>
- <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Tillåter att appen hämtar listan med konton som tv:n kan identifiera. Den kan innehålla konton som skapats av appar som du har installerat."</string>
+ <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Tillåter att appen hämtar listan med konton som TV:n kan identifiera. Den kan innehålla konton som skapats av appar som du har installerat."</string>
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Tillåter att appen hämtar en lista över alla kända konton på mobilen. Detta kan inkludera konton som har skapats av appar som du har installerat."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"visa nätverksanslutningar"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Tillåter att appen kommer åt information om nätverksanslutningarna, till exempel vilka nätverk som finns och är anslutna."</string>
@@ -394,21 +394,21 @@
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"Tillåter att appen ansluter till och kopplar från Wi-Fi-åtkomstpunkter samt gör ändringar i enhetens konfiguration för Wi-Fi-nätverk."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"tillåt Wi-Fi multicast-mottagning"</string>
<string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här surfplattan. Detta drar mer batteri än när multicastläget inte används."</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Tillåter att appen tar emot paket som skickats till alla enheter i ett Wi-Fi-nätverk med multicastadress, inte bara till tv:n. Detta drar mer batteri än när multicastläget inte används."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Tillåter att appen tar emot paket som skickats till alla enheter i ett Wi-Fi-nätverk med multicastadress, inte bara till TV:n. Detta drar mer batteri än när multicastläget inte används."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här mobilen. Detta drar mer batteri än när multicastläget inte används."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"få åtkomst till Bluetooth-inställningar"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Tillåter att appen konfigurerar den lokala Bluetooth-surfplattan samt upptäcker och parkopplar den med fjärranslutna enheter."</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Tillåter att appen konfigurerar den lokala Bluetooth-tv:n och identifierar och kopplar den till fjärrenheter."</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Tillåter att appen konfigurerar den lokala Bluetooth-TV:n och identifierar och kopplar den till fjärrenheter."</string>
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Tillåter att appen konfigurerar den lokala Bluetooth-mobilen samt upptäcker och parkopplar den med fjärranslutna enheter."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"ansluta till och koppla från WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Tillåter att appen avgör om WiMAX är aktiverat och kommer åt information om eventuella anslutna WiMAX-nätverk."</string>
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"ändra WiMAX-status"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tillåter att appen ansluter surfplattan till eller kopplar från WiMAX-nätverk."</string>
- <string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Tillåter att appen ansluter tv:n till och kopplar från tv:n från WiMAX-nätverk."</string>
+ <string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Tillåter att appen ansluter TV:n till och kopplar från TV:n från WiMAX-nätverk."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Tillåter att appen ansluter mobilen till eller kopplar från WiMAX-nätverk."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"koppla till Bluetooth-enheter"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Tillåter att appen kommer åt pekdatorns Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string>
- <string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Tillåter att appen visar konfigurationen av Bluetooth på tv:n och godkänner alla anslutningar till kopplade enheter."</string>
+ <string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Tillåter att appen visar konfigurationen av Bluetooth på TV:n och godkänner alla anslutningar till kopplade enheter."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Tillåter att appen kommer åt mobilens Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"kontrollera närfältskommunikationen"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"Tillåter att appen kommunicerar med etiketter, kort och läsare för närfältskommunikation (NFC)."</string>
@@ -499,10 +499,10 @@
<string name="policydesc_limitPassword" msgid="2502021457917874968">"Styr tillåten längd och tillåtna tecken i lösenord och pinkoder för skärmlåset."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Övervaka försök att låsa upp skärmen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås surfplattan eller ta bort alla data från surfplattan om för många felaktiga försök görs."</string>
- <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Övervakar antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och låser tv:n eller rensar alla uppgifter på tv:n om för många felaktiga lösenord har skrivits in."</string>
+ <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Övervakar antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och låser TV:n eller rensar alla uppgifter på TV:n om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Övervaka antalet felaktiga lösenord som angivits för skärmlåset och lås mobilen eller ta bort alla data från mobilen om för många felaktiga försök görs."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås surfplattan eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås tv:n eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås TV:n eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås mobilen eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
<string name="policylab_resetPassword" msgid="4934707632423915395">"Ändra skärmlåset"</string>
<string name="policydesc_resetPassword" msgid="1278323891710619128">"Ändra skärmlåset."</string>
@@ -510,11 +510,11 @@
<string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrollera hur och när skärmlåset aktiveras."</string>
<string name="policylab_wipeData" msgid="3910545446758639713">"Radera all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Ta bort data från surfplattan utan förvarning genom att återställa standardinställningarna."</string>
- <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Rensar uppgifterna på tv:n utan föregående varning genom att återställa standardinställningarna."</string>
+ <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Rensar uppgifterna på TV:n utan föregående varning genom att återställa standardinställningarna."</string>
<string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Ta bort data från mobilen utan förvarning genom att återställa standardinställningarna."</string>
<string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Radera användaruppgifter"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Rensa användarens uppgifter på den här surfplattan utan förvarning."</string>
- <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Rensa användarens uppgifter på den här tv:n utan förvarning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Rensa användarens uppgifter på den här TV:n utan förvarning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="6787904546711590238">"Rensa användarens data på den här mobilen utan förvarning."</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ange global proxyserver"</string>
<string name="policydesc_setGlobalProxy" msgid="8459859731153370499">"Ange enhetens globala proxy som ska användas när policyn aktiveras. Det är bara enhetens ägare som kan ange global proxy."</string>
@@ -663,7 +663,7 @@
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har försökt låsa upp med Ansiktslås för många gånger"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Inget SIM-kort"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Inget SIM-kort i surfplattan."</string>
- <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Det finns inget SIM-kort i tv:n."</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Det finns inget SIM-kort i TV:n."</string>
<string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Inget SIM-kort i telefonen."</string>
<string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Sätt i ett SIM-kort."</string>
<string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM-kort saknas eller kan inte läsas. Sätt i ett SIM-kort."</string>
@@ -686,13 +686,13 @@
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp surfplattan med din Google-inloggning.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp tv:n genom att logga in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n genom att logga in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp mobilen med uppgifterna som du använder när du loggar in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer surfplattan att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer tv:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer mobilen att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER">%d</xliff:g> gånger. Tv:n kommer nu att återställas till standardinställningarna."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER">%d</xliff:g> gånger. TV:n kommer nu att återställas till standardinställningarna."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Glömt ditt grafiska lösenord?"</string>
@@ -778,7 +778,7 @@
<string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Tillåter att appen läser historiken för besökta sidor och alla bokmärken i webbläsaren. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"skriva bokmärken och historik på webben"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på surfplattan. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string>
- <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Tillåter att appen ändrar webbläsarens historik eller bokmärken som har sparats på tv:n. Appen kan därmed rensa eller ändra uppgifter i webbläsaren. Obs! Den här behörigheten kanske inte gäller för webbläsare från tredje part eller andra appar med webbfunktioner."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Tillåter att appen ändrar webbläsarens historik eller bokmärken som har sparats på TV:n. Appen kan därmed rensa eller ändra uppgifter i webbläsaren. Obs! Den här behörigheten kanske inte gäller för webbläsare från tredje part eller andra appar med webbfunktioner."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på telefonen. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ställa in ett alarm"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Tillåter att appen ställer in ett alarm i en befintlig alarmapp. Vissa alarmappar har inte den här funktionen."</string>
@@ -988,7 +988,7 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ange den obligatoriska PIN-koden:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kod:"</string>
<string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Surfplattans Wi-Fi-anslutning kommer tillfälligt att avbrytas när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
- <string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"Tv:n kopplas tillfälligt från Wi-Fi-nätverket när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"TV:n kopplas tillfälligt från Wi-Fi-nätverket när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Mobilen kommer tillfälligt att kopplas från Wi-Fi när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="select_character" msgid="3365550120617701745">"Infoga tecken"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Skickar SMS"</string>
@@ -1289,13 +1289,13 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs surfplattan till fabriksinställningarna. Du förlorar då alla användardata."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer tv:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs mobilen till fabriksinställningarna. Du förlorar då alla användardata."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string>
- <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER">%d</xliff:g> gånger. Tv:n kommer nu att återställas till standardinställningarna."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER">%d</xliff:g> gånger. TV:n kommer nu att återställas till standardinställningarna."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp surfplattan med ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp tv:n via ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n via ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ta bort"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d367887275ac..af04a46a69a3 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -589,7 +589,7 @@
<string name="phoneTypeTelex" msgid="3367879952476250512">"Teleksi"</string>
<string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Nambari ya Simu ya Mkononi ya Kazini"</string>
- <string name="phoneTypeWorkPager" msgid="649938731231157056">"Kiunda ujumbe cha Kazini"</string>
+ <string name="phoneTypeWorkPager" msgid="649938731231157056">"Peja ya Kazini"</string>
<string name="phoneTypeAssistant" msgid="5596772636128562884">"Msaidizi"</string>
<string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string>
<string name="eventTypeCustom" msgid="7837586198458073404">"Maalum"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 439b9f284e76..83b0d1813fde 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -530,8 +530,8 @@
<item msgid="8901098336658710359">"Nhà riêng"</item>
<item msgid="869923650527136615">"Di Động"</item>
<item msgid="7897544654242874543">"Cơ quan"</item>
- <item msgid="1103601433382158155">"Số fax Cơ quan"</item>
- <item msgid="1735177144948329370">"Số fax Nhà riêng"</item>
+ <item msgid="1103601433382158155">"Số fax cơ quan"</item>
+ <item msgid="1735177144948329370">"Số fax nhà riêng"</item>
<item msgid="603878674477207394">"Số máy nhắn tin"</item>
<item msgid="1650824275177931637">"Khác"</item>
<item msgid="9192514806975898961">"Tùy chỉnh"</item>
@@ -573,8 +573,8 @@
<string name="phoneTypeHome" msgid="2570923463033985887">"Nhà riêng"</string>
<string name="phoneTypeMobile" msgid="6501463557754751037">"Di Động"</string>
<string name="phoneTypeWork" msgid="8863939667059911633">"Cơ quan"</string>
- <string name="phoneTypeFaxWork" msgid="3517792160008890912">"Số fax Cơ quan"</string>
- <string name="phoneTypeFaxHome" msgid="2067265972322971467">"Số fax Nhà riêng"</string>
+ <string name="phoneTypeFaxWork" msgid="3517792160008890912">"Số fax cơ quan"</string>
+ <string name="phoneTypeFaxHome" msgid="2067265972322971467">"Số fax nhà riêng"</string>
<string name="phoneTypePager" msgid="7582359955394921732">"Số máy nhắn tin"</string>
<string name="phoneTypeOther" msgid="1544425847868765990">"Khác"</string>
<string name="phoneTypeCallback" msgid="2712175203065678206">"Số gọi lại"</string>
@@ -586,8 +586,8 @@
<string name="phoneTypeRadio" msgid="4093738079908667513">"Radio"</string>
<string name="phoneTypeTelex" msgid="3367879952476250512">"Số telex"</string>
<string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string>
- <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Số điện thoại di động tại Cơ quan"</string>
- <string name="phoneTypeWorkPager" msgid="649938731231157056">"Số Máy nhắn tin tại Cơ quan"</string>
+ <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Di động tại cơ quan"</string>
+ <string name="phoneTypeWorkPager" msgid="649938731231157056">"Số máy nhắn tin cơ quan"</string>
<string name="phoneTypeAssistant" msgid="5596772636128562884">"Số điện thoại Hỗ trợ"</string>
<string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string>
<string name="eventTypeCustom" msgid="7837586198458073404">"Tùy chỉnh"</string>
diff --git a/core/res/res/values/colors_legacy.xml b/core/res/res/values/colors_legacy.xml
index 8b9d22c20836..a3ce6524bdcb 100644
--- a/core/res/res/values/colors_legacy.xml
+++ b/core/res/res/values/colors_legacy.xml
@@ -21,7 +21,7 @@
<color name="legacy_green">#ff90df25</color>
<!-- A bright orange suitable for use in the early 2000s -->
- <color name="legacy_orange">#ff90df25</color>
+ <color name="legacy_orange">#fffea50b</color>
<!-- Highlight colors for the legacy themes -->
<eat-comment />
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index 075ceaa66c16..b6b4f4fa96f9 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -48,6 +48,11 @@ public class LinkPropertiesTest extends TestCase {
private static LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
private static LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
+ // TODO: replace all calls to NetworkUtils.numericToInetAddress with calls to this method.
+ private InetAddress Address(String addrString) {
+ return NetworkUtils.numericToInetAddress(addrString);
+ }
+
public void assertLinkPropertiesEqual(LinkProperties source, LinkProperties target) {
// Check implementation of equals(), element by element.
assertTrue(source.isIdenticalInterfaceName(target));
@@ -647,5 +652,26 @@ public class LinkPropertiesTest extends TestCase {
assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope));
assertTrue(v6lp.isReachable(kOnLinkDns));
assertTrue(v6lp.isReachable(DNS6));
+
+ // Check isReachable on stacked links. This requires that the source IP address be assigned
+ // on the interface returned by the route lookup.
+ LinkProperties stacked = new LinkProperties();
+
+ // Can't add a stacked link without an interface name.
+ stacked.setInterfaceName("v4-test0");
+ v6lp.addStackedLink(stacked);
+
+ InetAddress stackedAddress = Address("192.0.0.4");
+ LinkAddress stackedLinkAddress = new LinkAddress(stackedAddress, 32);
+ assertFalse(v6lp.isReachable(stackedAddress));
+ stacked.addLinkAddress(stackedLinkAddress);
+ assertFalse(v6lp.isReachable(stackedAddress));
+ stacked.addRoute(new RouteInfo(stackedLinkAddress));
+ assertTrue(stacked.isReachable(stackedAddress));
+ assertTrue(v6lp.isReachable(stackedAddress));
+
+ assertFalse(v6lp.isReachable(DNS1));
+ stacked.addRoute(new RouteInfo((IpPrefix) null, stackedAddress));
+ assertTrue(v6lp.isReachable(DNS1));
}
}
diff --git a/core/tests/coretests/src/android/util/OrientationUtil.java b/core/tests/coretests/src/android/util/OrientationUtil.java
new file mode 100644
index 000000000000..ecdca5d608c4
--- /dev/null
+++ b/core/tests/coretests/src/android/util/OrientationUtil.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.util;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.pm.ActivityInfo;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Utilities for manipulating screen orientation.
+ */
+public final class OrientationUtil {
+
+ private final Activity mActivity;
+ private final Instrumentation mInstrumentation;
+
+ private final Runnable mSetToPortrait = new Runnable() {
+ @Override
+ public void run() {
+ mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+ };
+
+ private final Runnable mSetToLandscape = new Runnable() {
+ @Override
+ public void run() {
+ mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+ };
+
+ public static OrientationUtil initializeAndStartActivityIfNotStarted(
+ ActivityInstrumentationTestCase2 testCase) {
+ Preconditions.checkNotNull(testCase);
+ return new OrientationUtil(testCase.getActivity(), testCase.getInstrumentation());
+ }
+
+ private OrientationUtil(Activity activity, Instrumentation instrumentation) {
+ mActivity = activity;
+ mInstrumentation = instrumentation;
+ }
+
+ public void setPortraitOrientation() {
+ mInstrumentation.runOnMainSync(mSetToPortrait);
+ }
+
+ public void setLandscapeOrientation() {
+ mInstrumentation.runOnMainSync(mSetToLandscape);
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 4db1d9a56c47..6a76a27c0bd3 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -36,6 +36,7 @@ import com.android.frameworks.coretests.R;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.OrientationUtil;
import android.view.KeyEvent;
/**
@@ -43,14 +44,20 @@ import android.view.KeyEvent;
*/
public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextViewActivity>{
+ private OrientationUtil mOrientationUtil;
+
public TextViewActivityTest() {
super(TextViewActivity.class);
}
+ @Override
+ public void setUp() {
+ mOrientationUtil = OrientationUtil.initializeAndStartActivityIfNotStarted(this);
+ mOrientationUtil.setPortraitOrientation();
+ }
+
@SmallTest
public void testTypedTextIsOnScreen() throws Exception {
- getActivity();
-
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -60,8 +67,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testPositionCursorAtTextAtIndex() throws Exception {
- getActivity();
-
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -74,8 +79,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testLongPressToSelect() throws Exception {
- getActivity();
-
final String helloWorld = "Hello Kirk!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -87,8 +90,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testLongPressEmptySpace() throws Exception {
- getActivity();
-
final String helloWorld = "Hello big round sun!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -102,8 +103,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testLongPressAndDragToSelect() throws Exception {
- getActivity();
-
final String helloWorld = "Hello little handsome boy!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -115,8 +114,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testDoubleTapToSelect() throws Exception {
- getActivity();
-
final String helloWorld = "Hello SuetYi!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -128,8 +125,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testDoubleTapAndDragToSelect() throws Exception {
- getActivity();
-
final String helloWorld = "Hello young beautiful girl!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -141,8 +136,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testSelectBackwordsByTouch() throws Exception {
- getActivity();
-
final String helloWorld = "Hello king of the Jungle!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -154,8 +147,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testToolbarAppearsAfterSelection() throws Exception {
- getActivity();
-
// It'll be nice to check that the toolbar is not visible (or does not exist) here
// I can't currently find a way to do this. I'll get to it later.
diff --git a/data/keyboards/Vendor_18d1_Product_5018.kcm b/data/keyboards/Vendor_18d1_Product_5018.kcm
new file mode 100644
index 000000000000..0ca85a20d7f3
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_5018.kcm
@@ -0,0 +1,321 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Key character map for Google Pixel C Keyboard
+#
+
+type FULL
+
+### Basic QWERTY keys ###
+
+key A {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+}
+
+key B {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+}
+
+key C {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+ alt: '\u00e7'
+ shift+alt: '\u00c7'
+}
+
+key D {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+}
+
+key E {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+ alt: '\u0301'
+}
+
+key F {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+}
+
+key G {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+}
+
+key H {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+}
+
+key I {
+ label: 'I'
+ base: 'i'
+ shift, capslock: 'I'
+ alt: '\u0302'
+}
+
+key J {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+}
+
+key K {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+}
+
+key M {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+}
+
+key N {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+ alt: '\u0303'
+}
+
+key O {
+ label: 'O'
+ base: 'o'
+ shift, capslock: 'O'
+ ralt: '['
+ ralt+shift: '{'
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+ ralt: ']'
+ ralt+shift: '}'
+}
+
+key Q {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+}
+
+key R {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+}
+
+key S {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+ alt: '\u00df'
+}
+
+key T {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+}
+
+key U {
+ label: 'U'
+ base: 'u'
+ shift, capslock: 'U'
+ alt: '\u0308'
+}
+
+key V {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+}
+
+key W {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+}
+
+key X {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+}
+
+key Y {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+}
+
+key Z {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: ')'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+ ralt: replace ESCAPE
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '@'
+ ralt: '`'
+ ralt+shift: '~'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '#'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '^'
+ alt+shift: '\u0302'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '&'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '*'
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: '('
+}
+
+key SPACE {
+ label: ' '
+ base: ' '
+ alt, meta: fallback SEARCH
+ ctrl: fallback LANGUAGE_SWITCH
+}
+
+key ENTER {
+ label: '\n'
+ base: '\n'
+}
+
+key TAB {
+ label: '\t'
+ base: '\t'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift: '<'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift: '>'
+}
+
+key SLASH {
+ label: '/'
+ base: '/'
+ shift: '?'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '_'
+}
+
+key EQUALS {
+ label: '='
+ base: '='
+ shift: '+'
+ ralt: '\\'
+ ralt+shift: '|'
+}
+
+key SEMICOLON {
+ label: ';'
+ base: ';'
+ shift: ':'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base: '\''
+ shift: '"'
+}
+
+### Non-printing keys ###
+
+key ESCAPE {
+ base: fallback BACK
+ alt, meta: fallback HOME
+ ctrl: fallback MENU
+}
diff --git a/data/keyboards/Vendor_18d1_Product_5018.kl b/data/keyboards/Vendor_18d1_Product_5018.kl
new file mode 100644
index 000000000000..e95ccb5c472c
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_5018.kl
@@ -0,0 +1,84 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Key layout for Google Pixel C Keyboard
+#
+
+# Row 1
+key 2 1
+key 3 2
+key 4 3
+key 5 4
+key 6 5
+key 7 6
+key 8 7
+key 9 8
+key 10 9
+key 11 0
+key 12 MINUS
+key 14 DEL # Backspace
+
+# Row 2
+key 15 TAB
+key 16 Q
+key 17 W
+key 18 E
+key 19 R
+key 20 T
+key 21 Y
+key 22 U
+key 23 I
+key 24 O
+key 25 P
+key 13 EQUALS
+key 28 ENTER
+
+# Row 3
+key 125 META_LEFT # "Search key"
+key 30 A
+key 31 S
+key 32 D
+key 33 F
+key 34 G
+key 35 H
+key 36 J
+key 37 K
+key 38 L
+key 39 SEMICOLON
+key 40 APOSTROPHE
+
+# Row 4
+key 42 SHIFT_LEFT
+key 44 Z
+key 45 X
+key 46 C
+key 47 V
+key 48 B
+key 49 N
+key 50 M
+key 51 COMMA
+key 52 PERIOD
+key 53 SLASH
+key 54 SHIFT_RIGHT
+
+# Row 5
+key 29 CTRL_LEFT
+key 56 ALT_LEFT
+key 57 SPACE
+key 100 ALT_RIGHT
+key 103 DPAD_UP
+key 105 DPAD_LEFT
+key 106 DPAD_RIGHT
+key 108 DPAD_DOWN
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index cc0943f52fdb..eaade34a1db6 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -209,6 +209,7 @@ endif
LOCAL_SRC_FILES += \
tests/TestContext.cpp \
+ tests/TreeContentAnimation.cpp \
tests/main.cpp
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/tests/Benchmark.h b/libs/hwui/tests/Benchmark.h
new file mode 100644
index 000000000000..e16310e034be
--- /dev/null
+++ b/libs/hwui/tests/Benchmark.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef TESTS_BENCHMARK_H
+#define TESTS_BENCHMARK_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+struct BenchmarkOptions {
+ int count;
+};
+
+typedef void (*BenchmarkFunctor)(const BenchmarkOptions&);
+
+struct BenchmarkInfo {
+ std::string name;
+ std::string description;
+ BenchmarkFunctor functor;
+};
+
+class Benchmark {
+public:
+ Benchmark(const BenchmarkInfo& info) {
+ registerBenchmark(info);
+ }
+
+private:
+ Benchmark() = delete;
+ Benchmark(const Benchmark&) = delete;
+ Benchmark& operator=(const Benchmark&) = delete;
+
+ static void registerBenchmark(const BenchmarkInfo& info);
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TESTS_BENCHMARK_H */
diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp
index cebe7650dea2..ba763a8def62 100644
--- a/libs/hwui/tests/TestContext.cpp
+++ b/libs/hwui/tests/TestContext.cpp
@@ -22,16 +22,35 @@ namespace test {
static const int IDENT_DISPLAYEVENT = 1;
-static DisplayInfo getBuiltInDisplay() {
+static android::DisplayInfo DUMMY_DISPLAY {
+ 1080, //w
+ 1920, //h
+ 320.0, // xdpi
+ 320.0, // ydpi
+ 60.0, // fps
+ 2.0, // density
+ 0, // orientation
+ false, // secure?
+ 0, // appVsyncOffset
+ 0, // presentationDeadline
+ 0, // colorTransform
+};
+
+DisplayInfo getBuiltInDisplay() {
+#if !HWUI_NULL_GPU
DisplayInfo display;
sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display);
LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
return display;
+#else
+ return DUMMY_DISPLAY;
+#endif
}
-android::DisplayInfo gDisplay = getBuiltInDisplay();
+// Initialize to a dummy default
+android::DisplayInfo gDisplay = DUMMY_DISPLAY;
TestContext::TestContext() {
mLooper = new Looper(true);
diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h
index 7b30fc1dc7ce..2bbe5dffd9b8 100644
--- a/libs/hwui/tests/TestContext.h
+++ b/libs/hwui/tests/TestContext.h
@@ -32,6 +32,8 @@ namespace test {
extern DisplayInfo gDisplay;
#define dp(x) ((x) * android::uirenderer::test::gDisplay.density)
+DisplayInfo getBuiltInDisplay();
+
class TestContext {
public:
TestContext();
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
new file mode 100644
index 000000000000..a59261c14fc5
--- /dev/null
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/log.h>
+#include <gui/Surface.h>
+#include <ui/PixelFormat.h>
+
+#include <AnimationContext.h>
+#include <DisplayListCanvas.h>
+#include <RenderNode.h>
+#include <renderthread/RenderProxy.h>
+#include <renderthread/RenderTask.h>
+
+#include "Benchmark.h"
+#include "TestContext.h"
+
+#include "protos/hwui.pb.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+
+static DisplayListCanvas* startRecording(RenderNode* node) {
+ DisplayListCanvas* renderer = new DisplayListCanvas(
+ node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
+ return renderer;
+}
+
+static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
+ node->setStagingDisplayList(renderer->finishRecording());
+ delete renderer;
+}
+
+class TreeContentAnimation {
+public:
+ virtual ~TreeContentAnimation() {}
+ int frameCount = 150;
+ virtual int getFrameCount() { return frameCount; }
+ virtual void setFrameCount(int fc) {
+ if (fc > 0) {
+ frameCount = fc;
+ }
+ }
+ virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
+ virtual void doFrame(int frameNr) = 0;
+
+ template <class T>
+ static void run(const BenchmarkOptions& opts) {
+ // Switch to the real display
+ gDisplay = getBuiltInDisplay();
+
+ T animation;
+ animation.setFrameCount(opts.count);
+
+ TestContext testContext;
+
+ // create the native surface
+ const int width = gDisplay.w;
+ const int height = gDisplay.h;
+ sp<Surface> surface = testContext.surface();
+
+ RenderNode* rootNode = new RenderNode();
+ rootNode->incStrong(nullptr);
+ rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
+ rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ rootNode->mutateStagingProperties().setClipToBounds(false);
+ rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+
+ ContextFactory factory;
+ std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
+ proxy->loadSystemProperties();
+ proxy->initialize(surface);
+ float lightX = width / 2.0;
+ proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
+ proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
+
+ android::uirenderer::Rect DUMMY;
+
+ DisplayListCanvas* renderer = startRecording(rootNode);
+ animation.createContent(width, height, renderer);
+ endRecording(renderer, rootNode);
+
+ // Do a few cold runs then reset the stats so that the caches are all hot
+ for (int i = 0; i < 3; i++) {
+ testContext.waitForVsync();
+ proxy->syncAndDrawFrame();
+ }
+ proxy->resetProfileInfo();
+
+ for (int i = 0; i < animation.getFrameCount(); i++) {
+ testContext.waitForVsync();
+
+ ATRACE_NAME("UI-Draw Frame");
+ nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+ UiFrameInfoBuilder(proxy->frameInfo())
+ .setVsync(vsync, vsync);
+ animation.doFrame(i);
+ proxy->syncAndDrawFrame();
+ }
+
+ proxy->dumpProfileInfo(STDOUT_FILENO, 0);
+ rootNode->decStrong(nullptr);
+ }
+};
+
+class ShadowGridAnimation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _ShadowGrid(BenchmarkInfo{
+ "shadowgrid",
+ "A grid of rounded rects that cast a shadow. Simplified scenario of an "
+ "Android TV-style launcher interface. High CPU/GPU load.",
+ TreeContentAnimation::run<ShadowGridAnimation>
+});
+
+class ShadowGrid2Animation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
+ for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
+ sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _ShadowGrid2(BenchmarkInfo{
+ "shadowgrid2",
+ "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
+ "variant of shadowgrid. Very high CPU load, high GPU load.",
+ TreeContentAnimation::run<ShadowGrid2Animation>
+});
+
+class RectGridAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 200, 200);
+ renderer->drawRenderNode(card.get());
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+
+ SkRegion region;
+ for (int xOffset = 0; xOffset < width; xOffset+=2) {
+ for (int yOffset = 0; yOffset < height; yOffset+=2) {
+ region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+ }
+ }
+
+ SkPaint paint;
+ paint.setColor(0xff00ffff);
+ renderer->drawRegion(region, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _RectGrid(BenchmarkInfo{
+ "rectgrid",
+ "A dense grid of 1x1 rects that should visually look like a single rect. "
+ "Low CPU/GPU load.",
+ TreeContentAnimation::run<RectGridAnimation>
+});
+
+class OvalAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 400, 400);
+ renderer->drawRenderNode(card.get());
+
+ renderer->insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF000000);
+ renderer->drawOval(0, 0, width, height, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _Oval(BenchmarkInfo{
+ "oval",
+ "Draws 1 oval.",
+ TreeContentAnimation::run<OvalAnimation>
+});
+
+class PartialDamageTest : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ static SkColor COLORS[] = {
+ 0xFFF44336,
+ 0xFF9C27B0,
+ 0xFF2196F3,
+ 0xFF4CAF50,
+ };
+
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
+ COLORS[static_cast<int>((y / dp(116))) % 4]);
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ cards[0]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[0]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(cards[0].get());
+ renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
+ SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, cards[0].get());
+ }
+
+ static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
+ int startA = (start >> 24) & 0xff;
+ int startR = (start >> 16) & 0xff;
+ int startG = (start >> 8) & 0xff;
+ int startB = start & 0xff;
+
+ int endA = (end >> 24) & 0xff;
+ int endR = (end >> 16) & 0xff;
+ int endG = (end >> 8) & 0xff;
+ int endB = end & 0xff;
+
+ return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+ (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+ (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+ (int)((startB + (int)(fraction * (endB - startB))));
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _PartialDamage(BenchmarkInfo{
+ "partialdamage",
+ "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
+ "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
+ "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
+ TreeContentAnimation::run<PartialDamageTest>
+});
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index f7945074316c..aee84de3ae7b 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -14,361 +14,34 @@
* limitations under the License.
*/
-#include <cutils/log.h>
-#include <gui/Surface.h>
-#include <ui/PixelFormat.h>
-
-#include <AnimationContext.h>
-#include <DisplayListCanvas.h>
-#include <RenderNode.h>
-#include <renderthread/RenderProxy.h>
-#include <renderthread/RenderTask.h>
-
-#include "TestContext.h"
+#include "Benchmark.h"
#include "protos/hwui.pb.h"
+#include <getopt.h>
#include <stdio.h>
+#include <string>
#include <unistd.h>
-#include <getopt.h>
+#include <unordered_map>
#include <vector>
using namespace android;
using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-using namespace android::uirenderer::test;
-
-class ContextFactory : public IContextFactory {
-public:
- virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
- return new AnimationContext(clock);
- }
-};
-static DisplayListCanvas* startRecording(RenderNode* node) {
- DisplayListCanvas* renderer = new DisplayListCanvas(
- node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
- return renderer;
+// Not a static global because we need to force the map to be constructed
+// before we try to add things to it.
+std::unordered_map<std::string, BenchmarkInfo>& testMap() {
+ static std::unordered_map<std::string, BenchmarkInfo> testMap;
+ return testMap;
}
-static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
- node->setStagingDisplayList(renderer->finishRecording());
- delete renderer;
+void Benchmark::registerBenchmark(const BenchmarkInfo& info) {
+ testMap()[info.name] = info;
}
-class TreeContentAnimation {
-public:
- virtual ~TreeContentAnimation() {}
- int frameCount = 150;
- virtual int getFrameCount() { return frameCount; }
- virtual void setFrameCount(int fc) {
- if (fc > 0) {
- frameCount = fc;
- }
- }
- virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
- virtual void doFrame(int frameNr) = 0;
-
- template <class T>
- static void run(int frameCount) {
- T animation;
- animation.setFrameCount(frameCount);
-
- TestContext testContext;
-
- // create the native surface
- const int width = gDisplay.w;
- const int height = gDisplay.h;
- sp<Surface> surface = testContext.surface();
-
- RenderNode* rootNode = new RenderNode();
- rootNode->incStrong(nullptr);
- rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
- rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- rootNode->mutateStagingProperties().setClipToBounds(false);
- rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
-
- ContextFactory factory;
- std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
- proxy->loadSystemProperties();
- proxy->initialize(surface);
- float lightX = width / 2.0;
- proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
- proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
-
- android::uirenderer::Rect DUMMY;
-
- DisplayListCanvas* renderer = startRecording(rootNode);
- animation.createContent(width, height, renderer);
- endRecording(renderer, rootNode);
-
- // Do a few cold runs then reset the stats so that the caches are all hot
- for (int i = 0; i < 3; i++) {
- testContext.waitForVsync();
- proxy->syncAndDrawFrame();
- }
- proxy->resetProfileInfo();
-
- for (int i = 0; i < animation.getFrameCount(); i++) {
- testContext.waitForVsync();
-
- ATRACE_NAME("UI-Draw Frame");
- nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
- UiFrameInfoBuilder(proxy->frameInfo())
- .setVsync(vsync, vsync);
- animation.doFrame(i);
- proxy->syncAndDrawFrame();
- }
-
- proxy->dumpProfileInfo(STDOUT_FILENO, 0);
- rootNode->decStrong(nullptr);
- }
-};
-
-class ShadowGridAnimation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
- }
- }
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class ShadowGrid2Animation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
- for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
- sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
- }
- }
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class RectGridAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- card = createCard(40, 40, 200, 200);
- renderer->drawRenderNode(card.get());
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
-
- SkRegion region;
- for (int xOffset = 0; xOffset < width; xOffset+=2) {
- for (int yOffset = 0; yOffset < height; yOffset+=2) {
- region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
- }
- }
-
- SkPaint paint;
- paint.setColor(0xff00ffff);
- renderer->drawRegion(region, paint);
-
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class OvalAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- card = createCard(40, 40, 400, 400);
- renderer->drawRenderNode(card.get());
-
- renderer->insertReorderBarrier(false);
- }
-
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
-
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(0xFF000000);
- renderer->drawOval(0, 0, width, height, paint);
-
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class PartialInvalTest : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- static SkColor COLORS[] = {
- 0xFFF44336,
- 0xFF9C27B0,
- 0xFF2196F3,
- 0xFF4CAF50,
- };
-
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
- COLORS[static_cast<int>((y / dp(116))) % 4]);
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
- }
- }
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- cards[0]->mutateStagingProperties().setTranslationX(curFrame);
- cards[0]->mutateStagingProperties().setTranslationY(curFrame);
- cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(cards[0].get());
- renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
- SkXfermode::kSrcOver_Mode);
- endRecording(renderer, cards[0].get());
- }
-
- static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
- int startA = (start >> 24) & 0xff;
- int startR = (start >> 16) & 0xff;
- int startG = (start >> 8) & 0xff;
- int startB = start & 0xff;
-
- int endA = (end >> 24) & 0xff;
- int endR = (end >> 16) & 0xff;
- int endG = (end >> 8) & 0xff;
- int endB = end & 0xff;
-
- return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
- (int)((startR + (int)(fraction * (endR - startR))) << 16) |
- (int)((startG + (int)(fraction * (endG - startG))) << 8) |
- (int)((startB + (int)(fraction * (endB - startB))));
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-struct cstr_cmp {
- bool operator()(const char *a, const char *b) const {
- return std::strcmp(a, b) < 0;
- }
-};
-
-typedef void (*testProc)(int);
-
-std::map<const char*, testProc, cstr_cmp> gTestMap {
- {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
- {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>},
- {"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
- {"oval", TreeContentAnimation::run<OvalAnimation> },
- {"partialinval", TreeContentAnimation::run<PartialInvalTest> },
-};
-
static int gFrameCount = 150;
static int gRepeatCount = 1;
-static std::vector<testProc> gRunTests;
+static std::vector<BenchmarkInfo> gRunTests;
static void printHelp() {
printf("\
@@ -384,8 +57,31 @@ OPTIONS:\n\
static void listTests() {
printf("Tests: \n");
- for (auto&& test : gTestMap) {
- printf("%-20s <TODO DESCRIPTION>\n", test.first);
+ for (auto&& test : testMap()) {
+ auto&& info = test.second;
+ const char* col1 = info.name.c_str();
+ int dlen = info.description.length();
+ const char* col2 = info.description.c_str();
+ // World's best line breaking algorithm.
+ do {
+ int toPrint = dlen;
+ if (toPrint > 50) {
+ char* found = (char*) memrchr(col2, ' ', 50);
+ if (found) {
+ toPrint = found - col2;
+ } else {
+ toPrint = 50;
+ }
+ }
+ printf("%-20s %.*s\n", col1, toPrint, col2);
+ col1 = "";
+ col2 += toPrint;
+ dlen -= toPrint;
+ while (*col2 == ' ') {
+ col2++; dlen--;
+ }
+ } while (dlen > 0);
+ printf("\n");
}
}
@@ -470,8 +166,8 @@ void parseOptions(int argc, char* argv[]) {
if (optind < argc) {
do {
const char* test = argv[optind++];
- auto pos = gTestMap.find(test);
- if (pos == gTestMap.end()) {
+ auto pos = testMap().find(test);
+ if (pos == testMap().end()) {
fprintf(stderr, "Unknown test '%s'\n", test);
exit(EXIT_FAILURE);
} else {
@@ -479,16 +175,18 @@ void parseOptions(int argc, char* argv[]) {
}
} while (optind < argc);
} else {
- gRunTests.push_back(gTestMap["shadowgrid"]);
+ gRunTests.push_back(testMap()["shadowgrid"]);
}
}
int main(int argc, char* argv[]) {
parseOptions(argc, argv);
+ BenchmarkOptions opts;
+ opts.count = gFrameCount;
for (int i = 0; i < gRepeatCount; i++) {
for (auto&& test : gRunTests) {
- test(gFrameCount);
+ test.functor(opts);
}
}
printf("Success!\n");
diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java
index 2f4d136999f6..1fee58707613 100644
--- a/media/java/android/media/MediaActionSound.java
+++ b/media/java/android/media/MediaActionSound.java
@@ -52,7 +52,7 @@ public class MediaActionSound {
"/system/media/audio/ui/camera_click.ogg",
"/system/media/audio/ui/camera_focus.ogg",
"/system/media/audio/ui/VideoRecord.ogg",
- "/system/media/audio/ui/VideoRecord.ogg"
+ "/system/media/audio/ui/VideoStop.ogg"
};
private static final String TAG = "MediaActionSound";
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index bc1aedad1048..1c2c9406621d 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -479,11 +479,6 @@ import java.lang.ref.WeakReference;
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
* the object state. </p></td></tr>
- * <tr><td>setPlaybackRate</p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
* <tr><td>setPlaybackParams</p></td>
* <td>any </p></td>
* <td>{} </p></td>
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index ed2c4cbd248d..8ac86b01dcb8 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -807,6 +807,32 @@ public class MediaRecorder
public native void stop() throws IllegalStateException;
/**
+ * Pauses recording. Call this after start(). You may resume recording
+ * with resume() without reconfiguration, as opposed to stop(). It does
+ * nothing if the recording is already paused.
+ *
+ * When the recording is paused and resumed, the resulting output would
+ * be as if nothing happend during paused period, immediately switching
+ * to the resumed scene.
+ *
+ * @throws IllegalStateException if it is called before start() or after
+ * stop()
+ * {@hide}
+ */
+ public native void pause() throws IllegalStateException;
+
+ /**
+ * Resumes recording. Call this after start(). It does nothing if the
+ * recording is not paused.
+ *
+ * @throws IllegalStateException if it is called before start() or after
+ * stop()
+ * @see android.media.MediaRecorder#pause
+ * {@hide}
+ */
+ public native void resume() throws IllegalStateException;
+
+ /**
* Restarts the MediaRecorder to its idle state. After calling
* this method, you will have to configure it again as if it had just been
* constructed.
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index e05b3483aadc..701f7ac8d5bf 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -399,6 +399,22 @@ android_media_MediaRecorder_stop(JNIEnv *env, jobject thiz)
}
static void
+android_media_MediaRecorder_pause(JNIEnv *env, jobject thiz)
+{
+ ALOGV("pause");
+ sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+ process_media_recorder_call(env, mr->pause(), "java/lang/RuntimeException", "pause failed.");
+}
+
+static void
+android_media_MediaRecorder_resume(JNIEnv *env, jobject thiz)
+{
+ ALOGV("resume");
+ sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+ process_media_recorder_call(env, mr->resume(), "java/lang/RuntimeException", "resume failed.");
+}
+
+static void
android_media_MediaRecorder_native_reset(JNIEnv *env, jobject thiz)
{
ALOGV("native_reset");
@@ -528,6 +544,8 @@ static const JNINativeMethod gMethods[] = {
{"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude},
{"start", "()V", (void *)android_media_MediaRecorder_start},
{"stop", "()V", (void *)android_media_MediaRecorder_stop},
+ {"pause", "()V", (void *)android_media_MediaRecorder_pause},
+ {"resume", "()V", (void *)android_media_MediaRecorder_resume},
{"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset},
{"release", "()V", (void *)android_media_MediaRecorder_release},
{"native_init", "()V", (void *)android_media_MediaRecorder_native_init},
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 737444585296..f9e80275b10d 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -3,6 +3,7 @@
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.REMOVE_TASKS" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name=".DocumentsApplication"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index caaa2b9be0a3..9d2d171b05b6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -39,6 +39,7 @@ import android.provider.DocumentsContract.Root;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -465,6 +466,21 @@ abstract class BaseActivity extends Activity {
}
}
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (DEBUG) Log.d(mTag, "onKeyUp: keycode = " + keyCode);
+ DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MOVE_HOME:
+ dir.focusFirstFile();
+ return true;
+ case KeyEvent.KEYCODE_MOVE_END:
+ dir.focusLastFile();
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
public void onStackPicked(DocumentStack stack) {
try {
// Update the restored stack to ensure we have freshest data
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 66f8acd3fc50..047949f54ad8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -34,6 +34,7 @@ import android.net.Uri;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.DocumentsContract;
@@ -72,6 +73,8 @@ public class CopyService extends IntentService {
// TODO: Move it to a shared file when more operations are implemented.
public static final int FAILURE_COPY = 1;
+ private PowerManager mPowerManager;
+
private NotificationManager mNotificationManager;
private Notification.Builder mProgressBuilder;
@@ -140,12 +143,16 @@ public class CopyService extends IntentService {
return;
}
+ final PowerManager.WakeLock wakeLock = mPowerManager
+ .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
final ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST);
final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
// Copy by default.
final int transferMode = intent.getIntExtra(EXTRA_TRANSFER_MODE, TRANSFER_MODE_COPY);
try {
+ wakeLock.acquire();
+
// Acquire content providers.
mSrcClient = DocumentsApplication.acquireUnstableProviderOrThrow(getContentResolver(),
srcs.get(0).authority);
@@ -165,6 +172,8 @@ public class CopyService extends IntentService {
ContentProviderClient.releaseQuietly(mSrcClient);
ContentProviderClient.releaseQuietly(mDstClient);
+ wakeLock.release();
+
// Dismiss the ongoing copy notification when the copy is done.
mNotificationManager.cancel(mJobId, 0);
@@ -198,7 +207,8 @@ public class CopyService extends IntentService {
@Override
public void onCreate() {
super.onCreate();
- mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ mPowerManager = getSystemService(PowerManager.class);
+ mNotificationManager = getSystemService(NotificationManager.class);
}
/**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index a24c936e2a37..2fe829f266ba 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -1341,6 +1341,45 @@ public class DirectoryFragment extends Fragment {
}
}
+ /**
+ * Scrolls to the top of the file list and focuses the first file.
+ */
+ void focusFirstFile() {
+ focusFile(0);
+ }
+
+ /**
+ * Scrolls to the bottom of the file list and focuses the last file.
+ */
+ void focusLastFile() {
+ focusFile(mAdapter.getItemCount() - 1);
+ }
+
+ /**
+ * Scrolls to and then focuses on the file at the given position.
+ */
+ private void focusFile(final int pos) {
+ // Don't smooth scroll; that taxes the system unnecessarily and makes the scroll handling
+ // logic below more complicated.
+ mRecView.scrollToPosition(pos);
+
+ // If the item is already in view, focus it; otherwise, set a one-time listener to focus it
+ // when the scroll is completed.
+ RecyclerView.ViewHolder vh = mRecView.findViewHolderForAdapterPosition(pos);
+ if (vh != null) {
+ vh.itemView.requestFocus();
+ } else {
+ mRecView.addOnScrollListener(
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(RecyclerView view, int dx, int dy) {
+ view.findViewHolderForAdapterPosition(pos).itemView.requestFocus();
+ view.removeOnScrollListener(this);
+ }
+ });
+ }
+ }
+
private void setupDragAndDropOnDirectoryView(View view) {
// Listen for drops on non-directory items and empty space.
view.setOnDragListener(mOnDragListener);
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background.xml b/packages/SystemUI/res/drawable/qs_customizer_background.xml
new file mode 100644
index 000000000000..6bb27cc2a517
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customizer_background.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@color/qs_detail_transition" />
+ <item android:drawable="?android:attr/windowBackground" />
+</transition>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index dc928c77baaa..59fed5ba7d2a 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -20,7 +20,8 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/navigation_bar_size"
- android:background="?android:attr/windowBackground">
+ android:background="@drawable/qs_customizer_background"
+ android:gravity="center_horizontal">
<FrameLayout
android:layout_width="match_parent"
@@ -76,10 +77,10 @@
</FrameLayout>
<com.android.systemui.tuner.AutoScrollView
- android:layout_width="match_parent"
+ android:layout_width="@dimen/notification_panel_width"
android:layout_height="0dp"
android:layout_weight="1"
- android:paddingTop="8dp"
+ android:paddingTop="12dp"
android:paddingBottom="8dp"
android:elevation="2dp">
@@ -87,7 +88,9 @@
android:id="@+id/quick_settings_panel"
android:background="#0000"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/notification_side_padding"
+ android:layout_marginRight="@dimen/notification_side_padding" />
</com.android.systemui.tuner.AutoScrollView>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index c612600ac82e..32c906e46afb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -181,6 +181,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
public TilePage(Context context, AttributeSet attrs) {
super(context, attrs);
mAllowDual = false;
+ updateResources();
}
public void setMaxRows(int maxRows) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 880349e232fe..18af35ea61cb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -172,7 +172,8 @@ public class QSPanel extends FrameLayout implements Tunable {
mCustomizePanel.setHost(mHost);
} else {
if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
- mCustomizePanel.hide();
+ mCustomizePanel.hide(mCustomizePanel.getWidth() / 2,
+ mCustomizePanel.getHeight() / 2);
}
mCustomizePanel = null;
}
@@ -242,7 +243,7 @@ public class QSPanel extends FrameLayout implements Tunable {
public void onCollapse() {
if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
- mCustomizePanel.hide();
+ mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
}
}
@@ -382,7 +383,12 @@ public class QSPanel extends FrameLayout implements Tunable {
public boolean onLongClick(View v) {
if (mCustomizePanel != null) {
if (!mCustomizePanel.isCustomizing()) {
- mCustomizePanel.show();
+ int[] loc = new int[2];
+ getLocationInWindow(loc);
+ int x = r.tileView.getLeft() + r.tileView.getWidth() / 2 + loc[0];
+ int y = r.tileView.getTop() + mTileLayout.getOffsetTop(r)
+ + r.tileView.getHeight() / 2 + loc[1];
+ mCustomizePanel.show(x, y);
}
} else {
r.tile.longClick();
@@ -409,7 +415,7 @@ public class QSPanel extends FrameLayout implements Tunable {
public void closeDetail() {
if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
// Treat this as a detail panel for now, to make things easy.
- mCustomizePanel.hide();
+ mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
return;
}
showDetail(false, mDetailRecord);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 8bd05fad5699..b8342e2ada7c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -68,9 +68,10 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
final Resources res = mContext.getResources();
final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
mCellHeight = res.getDimensionPixelSize(R.dimen.qs_tile_height);
- mCellWidth = (int)(mCellHeight * TILE_ASPECT);
- mLargeCellHeight = res.getDimensionPixelSize(R.dimen.qs_dual_tile_height);
- mLargeCellWidth = (int)(mLargeCellHeight * TILE_ASPECT);
+ mCellWidth = (int) (mCellHeight * TILE_ASPECT);
+ mLargeCellHeight = mAllowDual ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_height)
+ : mCellHeight;
+ mLargeCellWidth = mAllowDual ? (int) (mLargeCellHeight * TILE_ASPECT) : mCellWidth;
mDualTileUnderlap = res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical);
if (mColumns != columns) {
mColumns = columns;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 7e7478545a07..601961b41a16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -15,23 +15,19 @@
*/
package com.android.systemui.qs.customize;
+import android.animation.Animator;
import android.content.ClipData;
import android.content.Context;
import android.util.AttributeSet;
import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.DragEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
+import android.view.*;
import android.view.View.OnClickListener;
-import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toolbar;
import android.widget.Toolbar.OnMenuItemClickListener;
-
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
+import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.qs.QSTile.Host.Callback;
import com.android.systemui.qs.customize.DropButton.OnDropListener;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -48,10 +44,11 @@ import java.util.ArrayList;
* *someday* do fancy animations to get into/out of it.
*/
public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener, Callback,
- OnDropListener, OnClickListener {
+ OnDropListener, OnClickListener, Animator.AnimatorListener {
private static final int MENU_SAVE = Menu.FIRST;
private static final int MENU_RESET = Menu.FIRST + 1;
+ private final QSDetailClipper mClipper;
private PhoneStatusBar mPhoneStatusBar;
@@ -69,6 +66,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
mPhoneStatusBar = ((SystemUIApplication) mContext.getApplicationContext())
.getComponent(PhoneStatusBar.class);
+ mClipper = new QSDetailClipper(this);
}
public void setHost(QSTileHost host) {
@@ -90,8 +88,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
mToolbar.setNavigationOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- // TODO: Is this all we want...?
- hide();
+ hide(0, 0);
}
});
mToolbar.setOnMenuItemClickListener(this);
@@ -115,17 +112,18 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
mFab.setOnClickListener(this);
}
- public void show() {
+ public void show(int x, int y) {
isShown = true;
mHost.setSavedTiles();
- // TODO: Fancy shmancy reveal.
mPhoneStatusBar.getStatusBarWindow().addView(this);
+ mQsPanel.setListening(true);
+ mClipper.animateCircularClip(x, y, true, this);
}
- public void hide() {
+ public void hide(int x, int y) {
isShown = false;
- // TODO: Similarly awesome or better hide.
- mPhoneStatusBar.getStatusBarWindow().removeView(this);
+ mQsPanel.setListening(false);
+ mClipper.animateCircularClip(x, y, false, this);
}
public boolean isCustomizing() {
@@ -146,7 +144,8 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
private void save() {
mHost.saveCurrentTiles();
- hide();
+ // TODO: At save button.
+ hide(0, 0);
}
@Override
@@ -197,4 +196,28 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
// TODO: Show list of tiles.
}
}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!isShown) {
+ mPhoneStatusBar.getStatusBarWindow().removeView(this);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (!isShown) {
+ mPhoneStatusBar.getStatusBarWindow().removeView(this);
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Don't care.
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ // Don't care.
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index e7a3c8a3fe72..aaed735990db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1539,6 +1539,7 @@ public abstract class BaseStatusBar extends SystemUI implements
if (viableAction != null) {
Notification stripped = n.clone();
Notification.Builder.stripForDelivery(stripped);
+ stripped.extras.putBoolean("android.rebuild", true);
stripped.actions = new Notification.Action[] { viableAction };
stripped.extras.putBoolean("android.rebuild.contentView", true);
stripped.contentView = null;
@@ -1547,6 +1548,13 @@ public abstract class BaseStatusBar extends SystemUI implements
stripped.extras.putBoolean("android.rebuild.hudView", true);
stripped.headsUpContentView = null;
+ stripped.extras.putParcelable(Notification.EXTRA_LARGE_ICON,
+ stripped.getLargeIcon());
+ if (SystemProperties.getBoolean("debug.strip_third_line", false)) {
+ stripped.extras.putCharSequence(Notification.EXTRA_INFO_TEXT, null);
+ stripped.extras.putCharSequence(Notification.EXTRA_SUMMARY_TEXT, null);
+ }
+
Notification rebuilt = Notification.Builder.rebuild(mContext, stripped);
n.actions = rebuilt.actions;
@@ -1580,27 +1588,35 @@ public abstract class BaseStatusBar extends SystemUI implements
if (remoteInput != null) {
View bigContentView = entry.getExpandedContentView();
if (bigContentView != null) {
- inflateRemoteInput(bigContentView, remoteInput, actions);
+ inflateRemoteInput(bigContentView, entry, remoteInput, actions);
}
View headsUpContentView = entry.getHeadsUpContentView();
if (headsUpContentView != null) {
- inflateRemoteInput(headsUpContentView, remoteInput, actions);
+ inflateRemoteInput(headsUpContentView, entry, remoteInput, actions);
}
}
}
- private void inflateRemoteInput(View view, RemoteInput remoteInput,
+ private void inflateRemoteInput(View view, Entry entry, RemoteInput remoteInput,
Notification.Action[] actions) {
View actionContainerCandidate = view.findViewById(com.android.internal.R.id.actions);
if (actionContainerCandidate instanceof ViewGroup) {
ViewGroup actionContainer = (ViewGroup) actionContainerCandidate;
- actionContainer.removeAllViews();
- actionContainer.addView(
- RemoteInputView.inflate(mContext, actionContainer, actions[0], remoteInput));
+ RemoteInputView riv = inflateRemoteInputView(actionContainer, entry,
+ actions[0], remoteInput);
+ if (riv != null) {
+ actionContainer.removeAllViews();
+ actionContainer.addView(riv);
+ }
}
}
+ protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry,
+ Notification.Action action, RemoteInput remoteInput) {
+ return null;
+ }
+
private final class NotificationClicker implements View.OnClickListener {
public void onClick(final View v) {
if (!(v instanceof ExpandableNotificationRow)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
new file mode 100644
index 000000000000..f243b00143a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.RemoteInputView;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Keeps track of the currently active {@link RemoteInputView}s.
+ */
+public class RemoteInputController {
+
+ private final ArrayList<WeakReference<NotificationData.Entry>> mRemoteInputs = new ArrayList<>();
+ private final StatusBarWindowManager mStatusBarWindowManager;
+ private final HeadsUpManager mHeadsUpManager;
+
+ public RemoteInputController(StatusBarWindowManager sbwm, HeadsUpManager headsUpManager) {
+ mStatusBarWindowManager = sbwm;
+ mHeadsUpManager = headsUpManager;
+ }
+
+ public void addRemoteInput(NotificationData.Entry entry) {
+ Preconditions.checkNotNull(entry);
+
+ boolean found = pruneWeakThenRemoveAndContains(
+ entry /* contains */, null /* remove */);
+ if (!found) {
+ mRemoteInputs.add(new WeakReference<>(entry));
+ }
+
+ apply(entry);
+ }
+
+ public void removeRemoteInput(NotificationData.Entry entry) {
+ Preconditions.checkNotNull(entry);
+
+ pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */);
+
+ apply(entry);
+ }
+
+ private void apply(NotificationData.Entry entry) {
+ mStatusBarWindowManager.setRemoteInputActive(isRemoteInputActive());
+ mHeadsUpManager.setRemoteInputActive(entry, isRemoteInputActive(entry));
+ }
+
+ /**
+ * @return true if {@param entry} has an active RemoteInput
+ */
+ public boolean isRemoteInputActive(NotificationData.Entry entry) {
+ return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */);
+ }
+
+ /**
+ * @return true if any entry has an active RemoteInput
+ */
+ public boolean isRemoteInputActive() {
+ pruneWeakThenRemoveAndContains(null /* contains */, null /* remove */);
+ return !mRemoteInputs.isEmpty();
+ }
+
+ /**
+ * Prunes dangling weak references, removes entries referring to {@param remove} and returns
+ * whether {@param contains} is part of the array in a single loop.
+ * @param remove if non-null, removes this entry from the active remote inputs
+ * @return true if {@param contains} is in the set of active remote inputs
+ */
+ private boolean pruneWeakThenRemoveAndContains(
+ NotificationData.Entry contains, NotificationData.Entry remove) {
+ boolean found = false;
+ for (int i = mRemoteInputs.size() - 1; i >= 0; i--) {
+ NotificationData.Entry item = mRemoteInputs.get(i).get();
+ if (item == null || item == remove) {
+ mRemoteInputs.remove(i);
+ } else if (item == contains) {
+ found = true;
+ }
+ }
+ return found;
+ }
+
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index f7c3c670ac4d..cc30882a7089 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -250,6 +250,7 @@ public class SignalClusterView
@Override
public void setNoSims(boolean show) {
mNoSimsVisible = show && !mBlockMobile;
+ apply();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java
new file mode 100644
index 000000000000..497f04494051
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.RelativeLayout;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+
+public abstract class BaseStatusBarHeader extends RelativeLayout implements
+ NetworkControllerImpl.EmergencyListener {
+
+ public BaseStatusBarHeader(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public abstract int getCollapsedHeight();
+ public abstract int getExpandedHeight();
+ public abstract void setExpanded(boolean b);
+ public abstract void setExpansion(float headerExpansionFraction);
+ public abstract void setListening(boolean listening);
+ public abstract void updateEverything();
+ public abstract void setActivityStarter(ActivityStarter activityStarter);
+ public abstract void setQSPanel(QSPanel qSPanel);
+ public abstract void setBatteryController(BatteryController batteryController);
+ public abstract void setNextAlarmController(NextAlarmController nextAlarmController);
+ public abstract void setUserInfoController(UserInfoController userInfoController);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 86f8f5a9a3e8..4ac2c3185b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -812,7 +812,7 @@ class NavigationBarApps extends LinearLayout
removeCallbacks(mShowMenuCallback);
break;
}
- return true;
+ return false;
}
}
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 08353cbb51c3..3453652404b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -92,7 +92,7 @@ public class NotificationPanelView extends PanelView implements
public static final long DOZE_ANIMATION_DURATION = 700;
private KeyguardAffordanceHelper mAfforanceHelper;
- private StatusBarHeaderView mHeader;
+ private BaseStatusBarHeader mHeader;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private KeyguardStatusBarView mKeyguardStatusBar;
private QSContainer mQsContainer;
@@ -232,7 +232,7 @@ public class NotificationPanelView extends PanelView implements
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mHeader = (StatusBarHeaderView) findViewById(R.id.header);
+ mHeader = (BaseStatusBarHeader) findViewById(R.id.header);
mHeader.setOnClickListener(this);
mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 37edc28b765d..05e84889334d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -25,6 +25,7 @@ import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
+import android.app.RemoteInput;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
@@ -82,6 +83,7 @@ import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewStub;
import android.view.WindowManager;
@@ -128,6 +130,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.NotificationOverflowContainer;
+import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.SpeedBumpView;
@@ -150,6 +153,7 @@ import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.PreviewInflater;
+import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -299,6 +303,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
StatusBarIconController mIconController;
+ private RemoteInputController mRemoteInputController;
+
// expanded notifications
NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
View mExpandedContents;
@@ -308,7 +314,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private QSPanel mQSPanel;
// top bar
- StatusBarHeaderView mHeader;
+ BaseStatusBarHeader mHeader;
KeyguardStatusBarView mKeyguardStatusBar;
View mKeyguardStatusView;
KeyguardBottomAreaView mKeyguardBottomArea;
@@ -800,7 +806,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mStatusBarView.setScrimController(mScrimController);
mDozeScrimController = new DozeScrimController(mScrimController, context);
- mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
+ mHeader = (BaseStatusBarHeader) mStatusBarWindow.findViewById(R.id.header);
mHeader.setActivityStarter(this);
mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
@@ -1084,6 +1090,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
return mStatusBarWindow;
}
+ @Override
+ protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry,
+ Notification.Action action, RemoteInput remoteInput) {
+ return RemoteInputView.inflate(mContext, root, entry, action, remoteInput,
+ mRemoteInputController);
+ }
+
public int getStatusBarHeight() {
if (mNaturalBarHeight < 0) {
final Resources res = mContext.getResources();
@@ -2840,6 +2853,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private void addStatusBarWindow() {
makeStatusBarView();
mStatusBarWindowManager = new StatusBarWindowManager(mContext);
+ mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
+ mHeadsUpManager);
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index e9c4e49d7b11..7f5ffafa3b44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -57,9 +57,9 @@ import java.text.NumberFormat;
/**
* The view to manage the header area in the expanded status bar.
*/
-public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener,
+public class StatusBarHeaderView extends BaseStatusBarHeader implements View.OnClickListener,
BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback,
- EmergencyListener {
+ EmergencyListener, TunerService.Tunable {
private boolean mExpanded;
private boolean mListening;
@@ -128,6 +128,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
private boolean mShowingDetail;
private boolean mDetailTransitioning;
+ private boolean mAllowExpand = true;
+
public StatusBarHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -232,6 +234,28 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
updateClockCollapsedMargin();
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ TunerService.get(mContext).addTunable(this, QSPanel.QS_PAGED_PANEL);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ TunerService.get(mContext).removeTunable(this);
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (QSPanel.QS_PAGED_PANEL.equals(key)) {
+ mAllowExpand = newValue == null || Integer.parseInt(newValue) == 0;
+ if (!mAllowExpand) {
+ setExpanded(false);
+ }
+ }
+ }
+
private void updateClockCollapsedMargin() {
Resources res = getResources();
int padding = res.getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin);
@@ -290,7 +314,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
}
public int getExpandedHeight() {
- return mExpandedHeight;
+ return mAllowExpand ? mExpandedHeight : mCollapsedHeight;
}
public void setListening(boolean listening) {
@@ -302,6 +326,9 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
}
public void setExpanded(boolean expanded) {
+ if (!mAllowExpand) {
+ expanded = false;
+ }
boolean changed = expanded != mExpanded;
mExpanded = expanded;
if (changed) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index dd62d9b46946..abe51ac3a5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -123,7 +123,7 @@ public class StatusBarWindowManager {
private void applyFocusableFlag(State state) {
boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
if (state.keyguardShowing && state.keyguardNeedsInput && state.bouncerShowing
- || BaseStatusBar.ENABLE_REMOTE_INPUT && panelFocusable) {
+ || BaseStatusBar.ENABLE_REMOTE_INPUT && state.remoteInputActive) {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
@@ -292,6 +292,11 @@ public class StatusBarWindowManager {
apply(mCurrentState);
}
+ public void setRemoteInputActive(boolean remoteInputActive) {
+ mCurrentState.remoteInputActive = remoteInputActive;
+ apply(mCurrentState);
+ }
+
/**
* Set whether the screen brightness is forced to the value we use for doze mode by the status
* bar window.
@@ -326,6 +331,8 @@ public class StatusBarWindowManager {
*/
int statusBarState;
+ boolean remoteInputActive;
+
private boolean isKeyguardShowingAndNotOccluded() {
return keyguardShowing && !keyguardOccluded;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index ed9b123f7431..4f7756e84fcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -102,6 +102,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
private boolean mHeadsUpGoingAway;
private boolean mWaitingOnCollapseWhenGoingAway;
private boolean mIsObserving;
+ private boolean mRemoteInputActive;
public HeadsUpManager(final Context context, View statusBarWindowView) {
mContext = context;
@@ -536,6 +537,18 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
return clicked != null && clicked;
}
+ public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) {
+ HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+ if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
+ headsUpEntry.remoteInputActive = remoteInputActive;
+ if (remoteInputActive) {
+ headsUpEntry.removeAutoRemovalCallbacks();
+ } else {
+ headsUpEntry.updateEntry(false /* updatePostTime */);
+ }
+ }
+ }
+
/**
* This represents a notification and how long it is in a heads up mode. It also manages its
* lifecycle automatically when created.
@@ -545,6 +558,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
public long postTime;
public long earliestRemovaltime;
private Runnable mRemoveHeadsUpRunnable;
+ public boolean remoteInputActive;
public void setEntry(final NotificationData.Entry entry) {
this.entry = entry;
@@ -565,12 +579,18 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
}
public void updateEntry() {
+ updateEntry(true);
+ }
+
+ public void updateEntry(boolean updatePostTime) {
mSortedEntries.remove(HeadsUpEntry.this);
long currentTime = mClock.currentTimeMillis();
earliestRemovaltime = currentTime + mMinimumDisplayTime;
- postTime = Math.max(postTime, currentTime);
+ if (updatePostTime) {
+ postTime = Math.max(postTime, currentTime);
+ }
removeAutoRemovalCallbacks();
- if (!hasFullScreenIntent(entry)) {
+ if (!hasFullScreenIntent(entry) && !mRemoteInputActive) {
long finishTime = postTime + mHeadsUpNotificationDecay;
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 7d721c22415b..2ad928717320 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.policy;
import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.RemoteInputController;
import android.annotation.NonNull;
import android.app.Notification;
@@ -33,12 +35,15 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
+import java.util.ArrayList;
+
/**
* Host for the remote input.
*/
@@ -51,6 +56,8 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
private PendingIntent mPendingIntent;
private RemoteInput mRemoteInput;
private Notification.Action mAction;
+ private RemoteInputController mController;
+ private NotificationData.Entry mEntry;
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -85,12 +92,13 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
});
mEditText.setOnClickListener(this);
mEditText.setInnerFocusable(false);
+ mEditText.mDefocusListener = this;
}
private void sendRemoteInput() {
Bundle results = new Bundle();
results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
- Intent fillInIntent = new Intent();
+ Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RemoteInput.addResultsToIntent(mAction.getRemoteInputs(), fillInIntent,
results);
@@ -105,7 +113,8 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
}
public static RemoteInputView inflate(Context context, ViewGroup root,
- Notification.Action action, RemoteInput remoteInput) {
+ NotificationData.Entry entry, Notification.Action action, RemoteInput remoteInput,
+ RemoteInputController controller) {
RemoteInputView v = (RemoteInputView)
LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
@@ -113,6 +122,8 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
v.mPendingIntent = action.actionIntent;
v.mRemoteInput = remoteInput;
v.mAction = action;
+ v.mController = controller;
+ v.mEntry = entry;
return v;
}
@@ -122,15 +133,22 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
if (v == mEditText) {
if (!mEditText.isFocusable()) {
mEditText.setInnerFocusable(true);
- InputMethodManager imm = InputMethodManager.getInstance();
- if (imm != null) {
- imm.viewClicked(mEditText);
- imm.showSoftInput(mEditText, 0);
- }
+ mController.addRemoteInput(mEntry);
+ mEditText.mShowImeOnInputConnection = true;
}
}
}
+ public void onDefocus() {
+ mController.removeRemoteInput(mEntry);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mController.removeRemoteInput(mEntry);
+ }
+
/**
* An EditText that changes appearance based on whether it's focusable and becomes
* un-focusable whenever the user navigates away from it or it becomes invisible.
@@ -138,6 +156,8 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
public static class RemoteEditText extends EditText {
private final Drawable mBackground;
+ private RemoteInputView mDefocusListener;
+ boolean mShowImeOnInputConnection;
public RemoteEditText(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -147,6 +167,10 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
private void defocusIfNeeded() {
if (isFocusable() && isEnabled()) {
setInnerFocusable(false);
+ if (mDefocusListener != null) {
+ mDefocusListener.onDefocus();
+ }
+ mShowImeOnInputConnection = false;
}
}
@@ -173,6 +197,28 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
return super.onKeyPreIme(keyCode, event);
}
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+
+ if (mShowImeOnInputConnection && inputConnection != null) {
+ final InputMethodManager imm = InputMethodManager.getInstance();
+ if (imm != null) {
+ // onCreateInputConnection is called by InputMethodManager in the middle of
+ // setting up the connection to the IME; wait with requesting the IME until that
+ // work has completed.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ imm.viewClicked(RemoteEditText.this);
+ imm.showSoftInput(RemoteEditText.this, 0);
+ }
+ });
+ }
+ }
+
+ return inputConnection;
+ }
void setInnerFocusable(boolean focusable) {
setFocusableInTouchMode(focusable);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
new file mode 100644
index 000000000000..7dff1a807211
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -0,0 +1,118 @@
+/*
+ ** Copyright 2015, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import android.content.Context;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+/**
+ * This class handles gesture detection for the Touch Explorer. It collects
+ * touch events, and sends events to mListener as gestures are recognized.
+ */
+class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListener {
+ private final GestureDetector mGestureDetector;
+ private final Listener mListener;
+ private boolean mFirstTapDetected;
+ private boolean mDoubleTapDetected;
+ private int mPolicyFlags;
+
+ AccessibilityGestureDetector(Context context, Listener listener) {
+ mListener = listener;
+
+ mGestureDetector = new GestureDetector(context, this);
+ mGestureDetector.setOnDoubleTapListener(this);
+ }
+
+ public void onMotionEvent(MotionEvent event, int policyFlags) {
+ mPolicyFlags = policyFlags;
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mDoubleTapDetected = false;
+ break;
+
+ case MotionEvent.ACTION_UP:
+ maybeFinishDoubleTap(event, policyFlags);
+ break;
+ }
+ mGestureDetector.onTouchEvent(event);
+ }
+
+ public void clear() {
+ mFirstTapDetected = false;
+ mDoubleTapDetected = false;
+ }
+
+ public boolean firstTapDetected() {
+ return mFirstTapDetected;
+ }
+
+ @Override
+ public boolean onDown(MotionEvent event) {
+ return true;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ maybeSendLongPress(e, mPolicyFlags);
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent event) {
+ mFirstTapDetected = true;
+ return false;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent event) {
+ clear();
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent event) {
+ // The processing of the double tap is deferred until the finger is
+ // lifted, so that we can detect a long press on the second tap.
+ mDoubleTapDetected = true;
+ return true;
+ }
+
+ private void maybeSendLongPress(MotionEvent event, int policyFlags) {
+ if (!mDoubleTapDetected) {
+ return;
+ }
+
+ clear();
+
+ mListener.onDoubleTapAndHold(event, policyFlags);
+ }
+
+ private void maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
+ if (!mDoubleTapDetected) {
+ return;
+ }
+
+ clear();
+
+ mListener.onDoubleTap(event, policyFlags);
+ }
+
+ public interface Listener {
+ public void onDoubleTapAndHold(MotionEvent event, int policyFlags);
+ public void onDoubleTap(MotionEvent event, int policyFlags);
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index f6b32f7a32ec..954536ff674a 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -25,11 +25,8 @@ import android.gesture.GestureStore;
import android.gesture.GestureStroke;
import android.gesture.Prediction;
import android.graphics.Point;
-import android.graphics.Rect;
import android.os.Handler;
-import android.os.SystemClock;
import android.util.Slog;
-import android.view.GestureDetector;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -67,7 +64,7 @@ import java.util.List;
*
* @hide
*/
-class TouchExplorer implements EventStreamTransformation {
+class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDetector.Listener {
private static final boolean DEBUG = false;
@@ -139,8 +136,8 @@ class TouchExplorer implements EventStreamTransformation {
// Command for exiting gesture detection mode after a timeout.
private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed;
- // Helper to detect and react to double tap in touch explore mode.
- private final DoubleTapDetector mDoubleTapDetector;
+ // Helper to detect gestures.
+ private final AccessibilityGestureDetector mGestureDetector;
// The scaled minimal distance before we take the middle of the distance between
// the two dragging pointers as opposed to use the location of the primary one.
@@ -230,7 +227,7 @@ class TouchExplorer implements EventStreamTransformation {
mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed(
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END,
mDetermineUserIntentTimeout);
- mDoubleTapDetector = new DoubleTapDetector(mContext);
+ mGestureDetector = new AccessibilityGestureDetector(context, this);
final float density = context.getResources().getDisplayMetrics().density;
mScaledMinPointerDistanceToUseMiddleLocation =
(int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
@@ -290,8 +287,8 @@ class TouchExplorer implements EventStreamTransformation {
// Reset the pointer trackers.
mReceivedPointerTracker.clear();
mInjectedPointerTracker.clear();
- // Clear the double tap detector
- mDoubleTapDetector.clear();
+ // Clear the gesture detector
+ mGestureDetector.clear();
// Go to initial state.
// Clear the long pressing pointer remap data.
mLongPressingPointerId = -1;
@@ -390,6 +387,77 @@ class TouchExplorer implements EventStreamTransformation {
}
}
+ @Override
+ public void onDoubleTapAndHold(MotionEvent event, int policyFlags) {
+ // Pointers should not be zero when running this command.
+ if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
+ return;
+ }
+
+ final int pointerIndex = event.getActionIndex();
+ final int pointerId = event.getPointerId(pointerIndex);
+
+ Point clickLocation = mTempPoint;
+ final int result = computeClickLocation(clickLocation);
+
+ if (result == CLICK_LOCATION_NONE) {
+ return;
+ }
+
+ mLongPressingPointerId = pointerId;
+ mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
+ mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
+
+ sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
+
+ mCurrentState = STATE_DELEGATING;
+ sendDownForAllNotInjectedPointers(event, policyFlags);
+ }
+
+ @Override
+ public void onDoubleTap(MotionEvent event, int policyFlags) {
+ // This should never be called when more than two pointers are down.
+ if (event.getPointerCount() > 2) {
+ return;
+ }
+
+ // Remove pending event deliveries.
+ mSendHoverEnterAndMoveDelayed.cancel();
+ mSendHoverExitDelayed.cancel();
+
+ if (mSendTouchExplorationEndDelayed.isPending()) {
+ mSendTouchExplorationEndDelayed.forceSendAndRemove();
+ }
+ if (mSendTouchInteractionEndDelayed.isPending()) {
+ mSendTouchInteractionEndDelayed.forceSendAndRemove();
+ }
+
+ final int pointerIndex = event.getActionIndex();
+ final int pointerId = event.getPointerId(pointerIndex);
+
+ Point clickLocation = mTempPoint;
+ final int result = computeClickLocation(clickLocation);
+ if (result == CLICK_LOCATION_NONE) {
+ return;
+ }
+
+ // Do the click.
+ PointerProperties[] properties = new PointerProperties[1];
+ properties[0] = new PointerProperties();
+ event.getPointerProperties(pointerIndex, properties[0]);
+ PointerCoords[] coords = new PointerCoords[1];
+ coords[0] = new PointerCoords();
+ coords[0].x = clickLocation.x;
+ coords[0].y = clickLocation.y;
+ MotionEvent click_event = MotionEvent.obtain(event.getDownTime(),
+ event.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
+ coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0,
+ event.getSource(), event.getFlags());
+ final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
+ sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus);
+ click_event.recycle();
+ }
+
/**
* Handles a motion event in touch exploring state.
*
@@ -403,7 +471,7 @@ class TouchExplorer implements EventStreamTransformation {
mVelocityTracker.addMovement(rawEvent);
- mDoubleTapDetector.onMotionEvent(event, policyFlags);
+ mGestureDetector.onMotionEvent(event, policyFlags);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
@@ -430,7 +498,7 @@ class TouchExplorer implements EventStreamTransformation {
mSendTouchInteractionEndDelayed.forceSendAndRemove();
}
- if (!mDoubleTapDetector.firstTapDetected() && !mTouchExplorationInProgress) {
+ if (!mGestureDetector.firstTapDetected() && !mTouchExplorationInProgress) {
if (!mSendHoverEnterAndMoveDelayed.isPending()) {
// Deliver hover enter with a delay to have a chance
// to detect what the user is trying to do.
@@ -1055,153 +1123,6 @@ class TouchExplorer implements EventStreamTransformation {
}
}
- private class DoubleTapDetector extends GestureDetector.SimpleOnGestureListener {
- private final GestureDetector mGestureDetector;
- private boolean mFirstTapDetected;
- private boolean mDoubleTapDetected;
- private int mPolicyFlags;
-
- DoubleTapDetector(Context context) {
- mGestureDetector = new GestureDetector(context, this);
- mGestureDetector.setOnDoubleTapListener(this);
- }
-
- public void onMotionEvent(MotionEvent event, int policyFlags) {
- mPolicyFlags = policyFlags;
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mDoubleTapDetected = false;
- break;
-
- case MotionEvent.ACTION_UP:
- maybeFinishDoubleTap(event, policyFlags);
- break;
- }
- mGestureDetector.onTouchEvent(event);
- }
-
- @Override
- public boolean onDown(MotionEvent event) {
- return true;
- }
-
- @Override
- public void onLongPress(MotionEvent e) {
- maybeSendLongPress(e, mPolicyFlags);
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent event) {
- mFirstTapDetected = true;
- return false;
- }
-
- @Override
- public boolean onSingleTapConfirmed(MotionEvent event) {
- clear();
- return false;
- }
-
- @Override
- public boolean onDoubleTap(MotionEvent event) {
- // The processing of the double tap is deferred until the finger is
- // lifted, so that we can detect a long press on the second tap.
- mDoubleTapDetected = true;
- return true;
- }
-
- private void maybeSendLongPress(MotionEvent event, int policyFlags) {
- if (!mDoubleTapDetected) {
- return;
- }
-
- clear();
-
- // Pointers should not be zero when running this command.
- if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
- return;
- }
-
- final int pointerIndex = event.getActionIndex();
- final int pointerId = event.getPointerId(pointerIndex);
-
- Point clickLocation = mTempPoint;
- final int result = computeClickLocation(clickLocation);
-
- if (result == CLICK_LOCATION_NONE) {
- return;
- }
-
- mLongPressingPointerId = pointerId;
- mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
- mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
-
- sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
-
- mCurrentState = STATE_DELEGATING;
- sendDownForAllNotInjectedPointers(event, policyFlags);
- }
-
- private void maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
- if (!mDoubleTapDetected) {
- return;
- }
-
- clear();
-
- // This should never be called when more than two pointers are down.
- if (event.getPointerCount() > 2) {
- return;
- }
-
- // Remove pending event deliveries.
- mSendHoverEnterAndMoveDelayed.cancel();
- mSendHoverExitDelayed.cancel();
-
- if (mSendTouchExplorationEndDelayed.isPending()) {
- mSendTouchExplorationEndDelayed.forceSendAndRemove();
- }
- if (mSendTouchInteractionEndDelayed.isPending()) {
- mSendTouchInteractionEndDelayed.forceSendAndRemove();
- }
-
- final int pointerIndex = event.getActionIndex();
- final int pointerId = event.getPointerId(pointerIndex);
-
- Point clickLocation = mTempPoint;
- final int result = computeClickLocation(clickLocation);
- if (result == CLICK_LOCATION_NONE) {
- return;
- }
-
- // Do the click.
- PointerProperties[] properties = new PointerProperties[1];
- properties[0] = new PointerProperties();
- event.getPointerProperties(pointerIndex, properties[0]);
- PointerCoords[] coords = new PointerCoords[1];
- coords[0] = new PointerCoords();
- coords[0].x = clickLocation.x;
- coords[0].y = clickLocation.y;
- MotionEvent click_event = MotionEvent.obtain(event.getDownTime(),
- event.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
- coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0,
- event.getSource(), event.getFlags());
- final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
- sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus);
- click_event.recycle();
- return;
- }
-
- public void clear() {
- mFirstTapDetected = false;
- mDoubleTapDetected = false;
- }
-
- public boolean firstTapDetected() {
- return mFirstTapDetected;
- }
- }
-
/**
* Determines whether a two pointer gesture is a dragging one.
*
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index a7e6f1187719..75dda9c0ae20 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1366,7 +1366,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway(), iface);
}
}
- if (DBG) log("Adding " + bestRoute + " for interface " + bestRoute.getInterface());
+ if (DBG) log("Adding legacy route " + bestRoute +
+ " for UID/PID " + uid + "/" + Binder.getCallingPid());
try {
mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 91eaf934cd7e..759a4f380ba4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -419,6 +419,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// Lower delay than APP_BOOST_MESSAGE_DELAY to disable the boost
static final int APP_BOOST_TIMEOUT = 2500;
+ // Used to indicate that a task is removed it should also be removed from recents.
+ private static final boolean REMOVE_FROM_RECENTS = true;
+
private static native int nativeMigrateToBoost();
private static native int nativeMigrateFromBoost();
private boolean mIsBoosted = false;
@@ -4382,12 +4385,16 @@ public final class ActivityManagerService extends ActivityManagerNative
final long origId = Binder.clearCallingIdentity();
try {
boolean res;
+ final boolean finishWithRootActivity =
+ finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
- || (finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY && r == rootR)) {
+ || (finishWithRootActivity && r == rootR)) {
// If requested, remove the task that is associated to this activity only if it
// was the root activity in the task. The result code and data is ignored
- // because we don't support returning them across task boundaries.
- res = removeTaskByIdLocked(tr.taskId, false);
+ // because we don't support returning them across task boundaries. Also, to
+ // keep backwards compatibility we remove the task from recents when finishing
+ // task with root activity.
+ res = removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
if (!res) {
Slog.i(TAG, "Removing task failed to finish activity");
}
@@ -5206,7 +5213,7 @@ public final class ActivityManagerService extends ActivityManagerNative
tr.getBaseIntent().getComponent().getPackageName();
if (tr.userId != userId) continue;
if (!taskPackageName.equals(packageName)) continue;
- removeTaskByIdLocked(tr.taskId, false);
+ removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
}
}
@@ -8779,9 +8786,12 @@ public final class ActivityManagerService extends ActivityManagerNative
mWindowManager.executeAppTransition();
}
- private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess) {
- mRecentTasks.remove(tr);
- tr.removedFromRecents();
+ private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess,
+ boolean removeFromRecents) {
+ if (removeFromRecents) {
+ mRecentTasks.remove(tr);
+ tr.removedFromRecents();
+ }
ComponentName component = tr.getBaseIntent().getComponent();
if (component == null) {
Slog.w(TAG, "No component for base intent of task: " + tr);
@@ -8858,7 +8868,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ComponentName cn = tr.intent.getComponent();
if (cn != null && cn.getPackageName().equals(packageName)) {
// If the package name matches, remove the task.
- removeTaskByIdLocked(tr.taskId, true);
+ removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
}
}
}
@@ -8876,7 +8886,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
&& (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
if (sameComponent) {
- removeTaskByIdLocked(tr.taskId, false);
+ removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
}
}
}
@@ -8886,14 +8896,16 @@ public final class ActivityManagerService extends ActivityManagerNative
*
* @param taskId Identifier of the task to be removed.
* @param killProcess Kill any process associated with the task if possible.
+ * @param removeFromRecents Whether to also remove the task from recents.
* @return Returns true if the given task was found and removed.
*/
- private boolean removeTaskByIdLocked(int taskId, boolean killProcess) {
+ private boolean removeTaskByIdLocked(int taskId, boolean killProcess,
+ boolean removeFromRecents) {
final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
if (tr != null) {
tr.removeTaskActivitiesLocked();
- cleanUpRemovedTaskLocked(tr, killProcess);
+ cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
if (tr.isPersistable) {
notifyTaskPersisterLocked(null, true);
}
@@ -8910,7 +8922,7 @@ public final class ActivityManagerService extends ActivityManagerNative
"removeTask()");
long ident = Binder.clearCallingIdentity();
try {
- return removeTaskByIdLocked(taskId, true);
+ return removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -17583,7 +17595,8 @@ public final class ActivityManagerService extends ActivityManagerNative
if (stack != null) {
ArrayList<TaskRecord> tasks = stack.getAllTasks();
for (int i = tasks.size() - 1; i >= 0; i--) {
- removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */);
+ removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+ !REMOVE_FROM_RECENTS);
}
}
}
@@ -21075,7 +21088,8 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (ActivityManagerService.this) {
long origId = Binder.clearCallingIdentity();
try {
- if (!removeTaskByIdLocked(mTaskId, false)) {
+ // We remove the task from recents to preserve backwards
+ if (!removeTaskByIdLocked(mTaskId, false, REMOVE_FROM_RECENTS)) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
} finally {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index cda9a5dc22bf..8bec7f7cad97 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1863,17 +1863,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
boolean overrideBounds = false;
Rect newBounds = null;
- if (r.info.resizeable || (inTask != null && inTask.mResizeable)) {
- if (intent.hasExtra(ActivityOptions.KEY_BOUNDS)) {
+ if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) {
+ ActivityOptions opts = new ActivityOptions(options);
+ if (opts.hasBounds()) {
overrideBounds = true;
- newBounds = Rect.unflattenFromString(
- intent.getStringExtra(ActivityOptions.KEY_BOUNDS));
- } else if (options != null) {
- ActivityOptions opts = new ActivityOptions(options);
- if (opts.hasBounds()) {
- overrideBounds = true;
- newBounds = opts.getBounds();
- }
+ newBounds = opts.getBounds();
}
}
@@ -3018,7 +3012,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
// In this case we make all other static stacks fullscreen and move all
// docked stack tasks to the fullscreen stack.
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (i != DOCKED_STACK_ID) {
+ if (i != DOCKED_STACK_ID && getStack(i) != null) {
resizeStackLocked(i, null, preserveWindows);
}
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index ecda36a0d4c6..06bd583fc7b3 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -443,13 +443,16 @@ public class JobSchedulerService extends com.android.server.SystemService
}
/**
- * A job is rescheduled with exponential back-off if the client requests this from their
- * execution logic.
- * A caveat is for idle-mode jobs, for which the idle-mode constraint will usurp the
- * timeliness of the reschedule. For an idle-mode job, no deadline is given.
+ * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
+ * specify an override deadline on a failed job (the failed job will run even though it's not
+ * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
+ * ready job with {@link JobStatus#numFailures} > 0 will be executed.
+ *
* @param failureToReschedule Provided job status that we will reschedule.
* @return A newly instantiated JobStatus with the same constraints as the last job except
* with adjusted timing constraints.
+ *
+ * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
*/
private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
final long elapsedNowMillis = SystemClock.elapsedRealtime();
@@ -479,8 +482,9 @@ public class JobSchedulerService extends com.android.server.SystemService
}
/**
- * Called after a periodic has executed so we can to re-add it. We take the last execution time
- * of the job to be the time of completion (i.e. the time at which this function is called).
+ * Called after a periodic has executed so we can reschedule it. We take the last execution
+ * time of the job to be the time of completion (i.e. the time at which this function is
+ * called).
* This could be inaccurate b/c the job can run for as long as
* {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
* to underscheduling at least, rather than if we had taken the last execution time to be the
@@ -491,7 +495,12 @@ public class JobSchedulerService extends com.android.server.SystemService
private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
final long elapsedNow = SystemClock.elapsedRealtime();
// Compute how much of the period is remaining.
- long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0);
+ long runEarly = 0L;
+
+ // If this periodic was rescheduled it won't have a deadline.
+ if (periodicToReschedule.hasDeadlineConstraint()) {
+ runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
+ }
long newEarliestRunTimeElapsed = elapsedNow + runEarly;
long period = periodicToReschedule.getJob().getIntervalMillis();
long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index d26319b5b0b9..d7fafe3e570d 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -68,7 +68,7 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne
private static final int defaultMaxActiveJobsPerService =
ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
/** Amount of time a job is allowed to execute for before being considered timed-out. */
- private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000;
+ private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
/** Amount of time the JobScheduler will wait for a response from an app for a message. */
private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7b15aad7b6ca..e8e46efd55df 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1602,41 +1602,45 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public AutomaticZenRule getAutomaticZenRule(String name) throws RemoteException {
+ public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
+ Preconditions.checkNotNull(id, "Id is null");
enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
- return mZenModeHelper.getAutomaticZenRule(name);
+ return mZenModeHelper.getAutomaticZenRule(id);
}
@Override
- public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule)
+ public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule)
throws RemoteException {
Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
- enforcePolicyAccess(Binder.getCallingUid(), "addOrUpdateZenModeRule");
+ enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
- return mZenModeHelper.addOrUpdateAutomaticZenRule(automaticZenRule,
- "addOrUpdateAutomaticZenRule");
+ return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
+ "addAutomaticZenRule");
}
@Override
- public boolean renameAutomaticZenRule(String oldName, String newName) {
- Preconditions.checkNotNull(oldName, "oldName is null");
- Preconditions.checkNotNull(newName, "newName is null");
- enforcePolicyAccess(Binder.getCallingUid(), "renameAutomaticZenRule");
+ public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule)
+ throws RemoteException {
+ Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
+ Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
+ Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
+ Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
+ enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
- return mZenModeHelper.renameAutomaticZenRule(
- oldName, newName, "renameAutomaticZenRule");
+ return mZenModeHelper.updateAutomaticZenRule(automaticZenRule,
+ "updateAutomaticZenRule");
}
@Override
- public boolean removeAutomaticZenRule(String name) throws RemoteException {
- Preconditions.checkNotNull(name, "Name is null");
+ public boolean removeAutomaticZenRule(String id) throws RemoteException {
+ Preconditions.checkNotNull(id, "Id is null");
// Verify that they can modify zen rules.
enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
- return mZenModeHelper.removeAutomaticZenRule(name, "removeAutomaticZenRule");
+ return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
}
@Override
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 07c4323c30a7..76c64432e54b 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -49,6 +49,7 @@ import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.EventInfo;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
@@ -211,84 +212,69 @@ public class ZenModeHelper {
return rules;
}
- public AutomaticZenRule getAutomaticZenRule(String name) {
+ public AutomaticZenRule getAutomaticZenRule(String id) {
if (mConfig == null) return null;
- for(ZenRule rule : mConfig.automaticRules.values()) {
- if (canManageAutomaticZenRule(rule) && rule.name.equals(name)) {
- return createAutomaticZenRule(rule);
- }
+ ZenRule rule = mConfig.automaticRules.get(id);
+ if (rule == null) return null;
+ if (canManageAutomaticZenRule(rule)) {
+ return createAutomaticZenRule(rule);
}
return null;
}
- public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
- if (mConfig == null) return false;
+ public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
+ if (mConfig == null) return null;
if (DEBUG) {
- Log.d(TAG, "addOrUpdateAutomaticZenRule zenRule=" + automaticZenRule
- + " reason=" + reason);
+ Log.d(TAG, "addAutomaticZenRule zenRule= " + automaticZenRule + " reason=" +reason);
+ }
+ if (!TextUtils.isEmpty(automaticZenRule.getId())) {
+ throw new IllegalArgumentException("Rule already exists");
}
final ZenModeConfig newConfig = mConfig.copy();
- String ruleId = findMatchingRuleId(newConfig, automaticZenRule.getName());
- ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
- if (ruleId == null) {
- ruleId = newConfig.newRuleId();
- rule.id = ruleId;
- rule.creationTime = System.currentTimeMillis();
- rule.name = automaticZenRule.getName();
- rule.component = automaticZenRule.getOwner();
+ ZenRule rule = new ZenRule();
+ populateZenRule(automaticZenRule, rule, true);
+ newConfig.automaticRules.put(rule.id, rule);
+ if (setConfig(newConfig, reason, true)) {
+ return createAutomaticZenRule(rule);
} else {
- rule = newConfig.automaticRules.get(ruleId);
- if (!canManageAutomaticZenRule(rule)) {
- throw new SecurityException(
- "Cannot update rules not owned by your condition provider");
- }
+ return null;
}
- if (rule.enabled != automaticZenRule.isEnabled()) {
- rule.snoozing = false;
- }
- rule.condition = null;
- rule.conditionId = automaticZenRule.getConditionId();
- rule.enabled = automaticZenRule.isEnabled();
- rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
- automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
- newConfig.automaticRules.put(ruleId, rule);
- return setConfig(newConfig, reason, true);
}
- public boolean renameAutomaticZenRule(String oldName, String newName, String reason) {
+ public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
if (mConfig == null) return false;
if (DEBUG) {
- Log.d(TAG, "renameAutomaticZenRule oldName=" + oldName + " newName=" + newName
+ Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
+ " reason=" + reason);
}
final ZenModeConfig newConfig = mConfig.copy();
- String ruleId = findMatchingRuleId(newConfig, oldName);
+ final String ruleId = automaticZenRule.getId();
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
if (ruleId == null) {
- return false;
+ throw new IllegalArgumentException("Rule doesn't exist");
} else {
- ZenRule rule = newConfig.automaticRules.get(ruleId);
- if (!canManageAutomaticZenRule(rule)) {
+ rule = newConfig.automaticRules.get(ruleId);
+ if (rule == null || !canManageAutomaticZenRule(rule)) {
throw new SecurityException(
"Cannot update rules not owned by your condition provider");
}
- rule.name = newName;
- return setConfig(newConfig, reason, true);
}
+ populateZenRule(automaticZenRule, rule, false);
+ newConfig.automaticRules.put(ruleId, rule);
+ return setConfig(newConfig, reason, true);
}
- public boolean removeAutomaticZenRule(String name, String reason) {
+ public boolean removeAutomaticZenRule(String id, String reason) {
if (mConfig == null) return false;
final ZenModeConfig newConfig = mConfig.copy();
- String ruleId = findMatchingRuleId(newConfig, name);
- if (ruleId != null) {
- ZenRule rule = newConfig.automaticRules.get(ruleId);
- if (canManageAutomaticZenRule(rule)) {
- newConfig.automaticRules.remove(ruleId);
- if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + name + " reason=" + reason);
- } else {
- throw new SecurityException(
- "Cannot delete rules not owned by your condition provider");
- }
+ ZenRule rule = newConfig.automaticRules.get(id);
+ if (rule == null) return false;
+ if (canManageAutomaticZenRule(rule)) {
+ newConfig.automaticRules.remove(id);
+ if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + id + " reason=" + reason);
+ } else {
+ throw new SecurityException(
+ "Cannot delete rules not owned by your condition provider");
}
return setConfig(newConfig, reason, true);
}
@@ -312,6 +298,24 @@ public class ZenModeHelper {
}
}
+ private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
+ if (isNew) {
+ rule.id = ZenModeConfig.newRuleId();
+ rule.creationTime = System.currentTimeMillis();
+ rule.component = automaticZenRule.getOwner();
+ }
+
+ if (rule.enabled != automaticZenRule.isEnabled()) {
+ rule.snoozing = false;
+ }
+ rule.name = automaticZenRule.getName();
+ rule.condition = null;
+ rule.conditionId = automaticZenRule.getConditionId();
+ rule.enabled = automaticZenRule.isEnabled();
+ rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
+ automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+ }
+
private AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
return new AutomaticZenRule(rule.name, rule.component, rule.conditionId,
NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
@@ -347,15 +351,6 @@ public class ZenModeHelper {
setConfig(newConfig, reason, setRingerMode);
}
- private String findMatchingRuleId(ZenModeConfig config, String ruleName) {
- for (String ruleId : config.automaticRules.keySet()) {
- if (config.automaticRules.get(ruleId).name.equals(ruleName)) {
- return ruleId;
- }
- }
- return null;
- }
-
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mZenMode=");
pw.println(Global.zenModeToString(mZenMode));
@@ -647,7 +642,7 @@ public class ZenModeHelper {
rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
rule1.zenMode = Global.ZEN_MODE_ALARMS;
rule1.component = ScheduleConditionProvider.COMPONENT;
- rule1.id = config.newRuleId();
+ rule1.id = ZenModeConfig.newRuleId();
rule1.creationTime = System.currentTimeMillis();
config.automaticRules.put(rule1.id, rule1);
@@ -663,7 +658,7 @@ public class ZenModeHelper {
rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends);
rule2.zenMode = Global.ZEN_MODE_ALARMS;
rule2.component = ScheduleConditionProvider.COMPONENT;
- rule2.id = config.newRuleId();
+ rule2.id = ZenModeConfig.newRuleId();
rule2.creationTime = System.currentTimeMillis();
config.automaticRules.put(rule2.id, rule2);
}
@@ -680,7 +675,7 @@ public class ZenModeHelper {
rule.conditionId = ZenModeConfig.toEventConditionId(events);
rule.zenMode = Global.ZEN_MODE_ALARMS;
rule.component = EventConditionProvider.COMPONENT;
- rule.id = config.newRuleId();
+ rule.id = ZenModeConfig.newRuleId();
rule.creationTime = System.currentTimeMillis();
config.automaticRules.put(rule.id, rule);
}
@@ -723,7 +718,7 @@ public class ZenModeHelper {
rule.zenMode = v1.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS
: Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
rule.component = ScheduleConditionProvider.COMPONENT;
- rt.automaticRules.put(rt.newRuleId(), rule);
+ rt.automaticRules.put(ZenModeConfig.newRuleId(), rule);
} else {
Log.i(TAG, "No existing V1 downtime found, generating default schedules");
appendDefaultScheduleRules(rt);
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index b5046056e913..8176aff89fc0 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -571,6 +571,24 @@ final class DefaultPermissionGrantPolicy {
grantRuntimePermissionsLPw(musicPackage, STORAGE_PERMISSIONS, userId);
}
+ // Android Wear Home
+ if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
+
+ PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackageLPr(
+ homeIntent, userId);
+
+ if (wearHomePackage != null
+ && doesPackageSupportRuntimePermissions(wearHomePackage)) {
+ grantRuntimePermissionsLPw(wearHomePackage, CONTACTS_PERMISSIONS, false,
+ userId);
+ grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId);
+ grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false,
+ userId);
+ }
+ }
+
mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 99562f3df59e..3e27c954ac42 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12860,9 +12860,11 @@ public class PackageManagerService extends IPackageManager.Stub {
ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
try {
if (dpm != null) {
- if (dpm.isDeviceOwner(packageName)) {
+ // Does the package contains the device owner?
+ if (dpm.isDeviceOwnerPackage(packageName)) {
return true;
}
+ // Does it contain a device admin for any user?
int[] users;
if (userId == UserHandle.USER_ALL) {
users = sUserManager.getUserIds();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4f30a1531701..16add371cbbb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -16,6 +16,16 @@
package com.android.server.policy;
+import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.view.WindowManager.LayoutParams.*;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
+
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.SleepToken;
@@ -40,7 +50,6 @@ import android.content.pm.ResolveInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -130,14 +139,6 @@ import java.io.PrintWriter;
import java.util.HashSet;
import java.util.List;
-import static android.view.WindowManager.LayoutParams.*;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
-
/**
* WindowManagerPolicy implementation for the Android phone UI. This
* introduces a new method suffix, Lp, for an internal lock of the
@@ -4627,7 +4628,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mStatusBarController.setBarShowingLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
- } else if (topIsFullscreen) {
+ } else if (topIsFullscreen
+ && !mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)
+ && !mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
if (mStatusBarController.setBarShowingLw(false)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
@@ -6714,6 +6717,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
+ final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
+ final boolean freeformStackVisible =
+ mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID);
+ final boolean forceShowSystemBars = dockedStackVisible || freeformStackVisible;
+ // TODO(multi-window): Update to force opaque independently for status bar and nav bar.
+ // This will require refactoring the code to have separate vis flag for each bar so it can
+ // be adjusted independently.
+ final boolean forceOpaqueSystemBars = forceShowSystemBars;
+
// apply translucent bar vis flags
WindowState transWin = isStatusBarKeyguard() && !mHideLockScreen
? mStatusBar
@@ -6736,7 +6748,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
vis = (vis & ~flags) | (oldVis & flags);
}
- if (!areTranslucentBarsAllowed() && transWin != mStatusBar) {
+ if ((!areTranslucentBarsAllowed() && transWin != mStatusBar)
+ || forceOpaqueSystemBars) {
vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT
| View.SYSTEM_UI_TRANSPARENT);
}
@@ -6744,24 +6757,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// update status bar
boolean immersiveSticky =
(vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
- boolean hideStatusBarWM =
- mTopFullscreenOpaqueWindowState != null &&
- (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
+ final boolean hideStatusBarWM =
+ mTopFullscreenOpaqueWindowState != null
+ && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
& WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
- boolean hideStatusBarSysui =
+ final boolean hideStatusBarSysui =
(vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
- boolean hideNavBarSysui =
+ final boolean hideNavBarSysui =
(vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
- boolean transientStatusBarAllowed =
- mStatusBar != null && (
- hideStatusBarWM
- || (hideStatusBarSysui && immersiveSticky)
- || statusBarHasFocus);
+ final boolean transientStatusBarAllowed = mStatusBar != null
+ && (statusBarHasFocus || (!forceShowSystemBars
+ && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
- boolean transientNavBarAllowed =
- mNavigationBar != null &&
- hideNavBarSysui && immersiveSticky;
+ final boolean transientNavBarAllowed = mNavigationBar != null
+ && !forceShowSystemBars && hideNavBarSysui && immersiveSticky;
final long now = SystemClock.uptimeMillis();
final boolean pendingPanic = mPendingPanicGestureUptime != 0
@@ -6774,11 +6784,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mNavigationBarController.showTransient();
}
- boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
+ final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
&& !transientStatusBarAllowed && hideStatusBarSysui;
- boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
+ final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
&& !transientNavBarAllowed;
- if (denyTransientStatus || denyTransientNav) {
+ if (denyTransientStatus || denyTransientNav || forceShowSystemBars) {
// clear the clearable flags instead
clearClearableFlagsLw();
vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 438658ee40e4..39479c1a9681 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -191,6 +191,7 @@ class DisplayContent {
void updateDisplayInfo() {
mDisplay.getDisplayInfo(mDisplayInfo);
mDisplay.getMetrics(mDisplayMetrics);
+ mDividerControllerLocked.updateDisplayInfo();
for (int i = mStacks.size() - 1; i >= 0; --i) {
mStacks.get(i).updateDisplayInfo(null);
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 120b077dc0e8..3c2864853fab 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -57,6 +57,8 @@ public class DockedStackDividerController implements View.OnTouchListener, DimLa
private final DisplayContent mDisplayContent;
private final int mSideMargin;
private final DimLayer mDimLayer;
+ private int mDisplayWidth;
+ private int mDisplayHeight;
private View mView;
private Rect mTmpRect = new Rect();
private Rect mLastResizeRect = new Rect();
@@ -70,6 +72,7 @@ public class DockedStackDividerController implements View.OnTouchListener, DimLa
DockedStackDividerController(Context context, DisplayContent displayContent) {
mContext = context;
mDisplayContent = displayContent;
+ updateDisplayInfo();
mDividerWidth = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayContent.getDisplayMetrics());
@@ -105,6 +108,12 @@ public class DockedStackDividerController implements View.OnTouchListener, DimLa
return mView != null;
}
+ void updateDisplayInfo() {
+ final DisplayInfo info = mDisplayContent.getDisplayInfo();
+ mDisplayWidth = info.logicalWidth;
+ mDisplayHeight = info.logicalHeight;
+ }
+
void update(Configuration configuration, boolean forceUpdate) {
if (forceUpdate && mView != null) {
removeDivider();
@@ -159,8 +168,10 @@ public class DockedStackDividerController implements View.OnTouchListener, DimLa
mStartY = (int) event.getRawY();
synchronized (mDisplayContent.mService.mWindowMap) {
mTaskStack = mDisplayContent.getDockedStackLocked();
- mTaskStack.getBounds(mOriginalRect);
- mDockSide = mTaskStack.getDockSide();
+ if (mTaskStack != null) {
+ mTaskStack.getBounds(mOriginalRect);
+ mDockSide = mTaskStack.getDockSide();
+ }
}
break;
case MotionEvent.ACTION_MOVE:
@@ -173,16 +184,35 @@ public class DockedStackDividerController implements View.OnTouchListener, DimLa
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mTaskStack != null) {
- maybeDismissTaskStack((int) event.getRawX(), (int) event.getRawY());
+ final int x = (int) event.getRawX();
+ final int y = (int) event.getRawY();
+ // At most one of these will be executed, the other one will exit early.
+ maybeDismissTaskStack(x, y);
+ maybeMaximizeTaskStack(x, y);
+ mTaskStack = null;
}
- setDimLayerVisible(false, -1, -1);
- mTaskStack = null;
+ setDimLayerVisible(false);
mDockSide = TaskStack.DOCKED_INVALID;
break;
}
return true;
}
+ private void maybeMaximizeTaskStack(int x, int y) {
+ final int distance = distanceFromFullScreen(mDockSide, x, y);
+ if (distance == -1) {
+ Slog.wtf(TAG, "maybeMaximizeTaskStack: Unknown dock side=" + mDockSide);
+ return;
+ }
+ if (distance <= mSideMargin) {
+ try {
+ mDisplayContent.mService.mActivityManager.resizeStack(mTaskStack.mStackId, null);
+ } catch (RemoteException e) {
+ // This can't happen because we are in the same process.
+ }
+ }
+ }
+
private void maybeDismissTaskStack(int x, int y) {
final int distance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
if (distance == -1) {
@@ -199,12 +229,23 @@ public class DockedStackDividerController implements View.OnTouchListener, DimLa
}
private void updateDimLayer(int x, int y) {
- final int distance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
- if (distance == -1) {
+ final int dismissDistance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
+ final int maximizeDistance = distanceFromFullScreen(mDockSide, x, y);
+ if (dismissDistance == -1 || maximizeDistance == -1) {
Slog.wtf(TAG, "updateDimLayer: Unknown dock side=" + mDockSide);
return;
}
- setDimLayerVisible(distance <= mSideMargin, x, y);
+ if (dismissDistance <= mSideMargin && maximizeDistance <= mSideMargin) {
+ Slog.wtf(TAG, "Both dismiss and maximize distances would trigger dim layer.");
+ return;
+ }
+ if (dismissDistance <= mSideMargin) {
+ setDismissDimLayerVisible(x, y);
+ } else if (maximizeDistance <= mSideMargin) {
+ setMaximizeDimLayerVisible(x, y);
+ } else {
+ setDimLayerVisible(false);
+ }
}
/**
@@ -226,29 +267,68 @@ public class DockedStackDividerController implements View.OnTouchListener, DimLa
return -1;
}
- private void setDimLayerVisible(boolean visible, int x, int y) {
- if (visible) {
- mTmpRect.set(mOriginalRect);
- switch (mDockSide) {
- case DOCKED_LEFT:
- mTmpRect.right = x;
- break;
- case DOCKED_TOP:
- mTmpRect.bottom = y;
- break;
- case DOCKED_RIGHT:
- mTmpRect.left = x;
- break;
- case DOCKED_BOTTOM:
- mTmpRect.top = y;
- break;
- default:
- Slog.wtf(TAG, "setDimLayerVisible: Unknown dock side when setting dim layer="
- + mDockSide);
- return;
- }
- mDimLayer.setBounds(mTmpRect);
+ private int distanceFromFullScreen(int dockSide, int x, int y) {
+ switch (dockSide) {
+ case DOCKED_LEFT:
+ return mDisplayWidth - x;
+ case DOCKED_TOP:
+ return mDisplayHeight - y;
+ case DOCKED_RIGHT:
+ return x;
+ case DOCKED_BOTTOM:
+ return y;
}
+ return -1;
+ }
+
+ private void setDismissDimLayerVisible(int x, int y) {
+ mTmpRect.set(mOriginalRect);
+ switch (mDockSide) {
+ case DOCKED_LEFT:
+ mTmpRect.right = x;
+ break;
+ case DOCKED_TOP:
+ mTmpRect.bottom = y;
+ break;
+ case DOCKED_RIGHT:
+ mTmpRect.left = x;
+ break;
+ case DOCKED_BOTTOM:
+ mTmpRect.top = y;
+ break;
+ default:
+ Slog.wtf(TAG, "setDismissDimLayerVisible: Unknown dock side when setting dim "
+ + "layer=" + mDockSide);
+ return;
+ }
+ mDimLayer.setBounds(mTmpRect);
+ setDimLayerVisible(true);
+ }
+
+ private void setMaximizeDimLayerVisible(int x, int y) {
+ mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ switch (mDockSide) {
+ case DOCKED_LEFT:
+ mTmpRect.left = x;
+ break;
+ case DOCKED_TOP:
+ mTmpRect.top = y;
+ break;
+ case DOCKED_RIGHT:
+ mTmpRect.right = x;
+ break;
+ case DOCKED_BOTTOM:
+ mTmpRect.top = y;
+ break;
+ default:
+ Slog.wtf(TAG, "setMaximizeDimLayerVisible: Unknown dock side when setting dim "
+ + "layer=" + mDockSide);
+ }
+ mDimLayer.setBounds(mTmpRect);
+ setDimLayerVisible(true);
+ }
+
+ private void setDimLayerVisible(boolean visible) {
if (mDimLayerVisible == visible) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 7dd716ee0d4b..283d49815049 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -18,8 +18,9 @@ package com.android.server.wm;
import java.io.PrintWriter;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowStateAnimator.SurfaceTrace;
-
+import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
@@ -42,7 +43,15 @@ class ScreenRotationAnimation {
static final boolean TWO_PHASE_ANIMATION = false;
static final boolean USE_CUSTOM_BLACK_FRAME = false;
- static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200;
+ /*
+ * Layers for screen rotation animation. We put these layers above
+ * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
+ */
+ static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
+ static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE;
+ static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1;
+ static final int SCREEN_FREEZE_LAYER_EXIT = SCREEN_FREEZE_LAYER_BASE + 2;
+ static final int SCREEN_FREEZE_LAYER_CUSTOM = SCREEN_FREEZE_LAYER_BASE + 3;
final Context mContext;
final DisplayContent mDisplayContent;
@@ -265,7 +274,7 @@ class ScreenRotationAnimation {
SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
mSurfaceControl.setLayerStack(display.getLayerStack());
- mSurfaceControl.setLayer(FREEZE_LAYER + 1);
+ mSurfaceControl.setLayer(SCREEN_FREEZE_LAYER_SCREENSHOT);
mSurfaceControl.setAlpha(0);
mSurfaceControl.show();
sur.destroy();
@@ -545,8 +554,8 @@ class ScreenRotationAnimation {
Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
mOriginalWidth*2, mOriginalHeight*2);
Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
- mCustomBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 3,
- layerStack, false);
+ mCustomBlackFrame = new BlackFrame(session, outer, inner,
+ SCREEN_FREEZE_LAYER_CUSTOM, layerStack, false);
mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
@@ -585,8 +594,8 @@ class ScreenRotationAnimation {
mOriginalWidth*2, mOriginalHeight*2);
inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
}
- mExitingBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 2,
- layerStack, mForceDefaultOrientation);
+ mExitingBlackFrame = new BlackFrame(session, outer, inner,
+ SCREEN_FREEZE_LAYER_EXIT, layerStack, mForceDefaultOrientation);
mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
@@ -608,8 +617,8 @@ class ScreenRotationAnimation {
Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
finalWidth*2, finalHeight*2);
Rect inner = new Rect(0, 0, finalWidth, finalHeight);
- mEnteringBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER,
- layerStack, false);
+ mEnteringBlackFrame = new BlackFrame(session, outer, inner,
+ SCREEN_FREEZE_LAYER_ENTER, layerStack, false);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 513826995e5e..928a1170625c 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -734,6 +734,9 @@ public class WindowAnimator {
if (!mAnimating && wasAnimating) {
mWindowPlacerLocked.requestTraversal();
}
+
+ mService.destroyPreservedSurfaceLocked();
+
if (WindowManagerService.DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index de3d83636bdb..0170bb965540 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -400,6 +400,13 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
/**
+ * Windows with a preserved surface waiting to be destroyed. These windows
+ * are going through a surface change. We keep the old surface around until
+ * the first frame on the new surface finishes drawing.
+ */
+ final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
+
+ /**
* Windows that have lost input focus and are waiting for the new
* focus window to be displayed before they are told about this.
*/
@@ -1163,7 +1170,9 @@ public class WindowManagerService extends IWindowManager.Stub
for (i = windows.size() - 1; i >= 0; --i) {
WindowState w = windows.get(i);
// Dock divider shares the base layer with application windows, but we want to always
- // keep it above the application windows.
+ // keep it above the application windows. The sharing of the base layer is intended
+ // for window animations, which need to be above the dock divider for the duration
+ // of the animation.
if (w.mBaseLayer <= myLayer && w.mAttrs.type != TYPE_DOCK_DIVIDER) {
break;
}
@@ -1326,13 +1335,18 @@ public class WindowManagerService extends IWindowManager.Stub
static boolean canBeImeTarget(WindowState w) {
final int fl = w.mAttrs.flags
& (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM);
+ final int type = w.mAttrs.type;
+ // The dock divider has to sit above the application windows and so does the IME. IME also
+ // needs to sit above the dock divider, so it doesn't get cut in half. We make the dock
+ // divider be a target for IME, so this relationship can occur naturally.
if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)
- || w.mAttrs.type == TYPE_APPLICATION_STARTING) {
+ || type == TYPE_APPLICATION_STARTING || type == TYPE_DOCK_DIVIDER) {
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
if (!w.isVisibleOrAdding()) {
Slog.i(TAG, " mSurface=" + w.mWinAnimator.mSurfaceControl
- + " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility
+ + " relayoutCalled=" + w.mRelayoutCalled
+ + " viewVis=" + w.mViewVisibility
+ " policyVis=" + w.mPolicyVisibility
+ " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim
+ " attachHid=" + w.mAttachedHidden
@@ -2610,9 +2624,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.isDragResizeChanged()) {
win.setDragResizing();
if (win.mHasSurface) {
- winAnimator.mDestroyPendingSurfaceUponRedraw = true;
- winAnimator.mSurfaceDestroyDeferred = true;
- winAnimator.destroySurfaceLocked();
+ winAnimator.preserveSurfaceLocked();
toBeDisplayed = true;
}
}
@@ -5584,10 +5596,13 @@ public class WindowManagerService extends IWindowManager.Stub
int retryCount = 0;
WindowState appWin = null;
- final boolean appIsImTarget = mInputMethodTarget != null
- && mInputMethodTarget.mAppToken != null
- && mInputMethodTarget.mAppToken.appToken != null
- && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+ boolean appIsImTarget;
+ synchronized(mWindowMap) {
+ appIsImTarget = mInputMethodTarget != null
+ && mInputMethodTarget.mAppToken != null
+ && mInputMethodTarget.mAppToken.appToken != null
+ && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+ }
final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
@@ -7775,6 +7790,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ void destroyPreservedSurfaceLocked() {
+ for (int i = mDestroyPreservedSurface.size() - 1; i >= 0 ; i--) {
+ final WindowState w = mDestroyPreservedSurface.get(i);
+ w.mWinAnimator.destroyPreservedSurfaceLocked();
+ }
+ mDestroyPreservedSurface.clear();
+ }
// -------------------------------------------------------------
// IWindowManager API
// -------------------------------------------------------------
@@ -9118,9 +9140,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
// TOOD(multidisplay): StatusBar on multiple screens?
- void updateStatusBarVisibilityLocked(int visibility) {
+ boolean updateStatusBarVisibilityLocked(int visibility) {
if (mLastDispatchedSystemUiVisibility == visibility) {
- return;
+ return false;
}
final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility)
// We are only interested in differences of one of the
@@ -9151,14 +9173,16 @@ public class WindowManagerService extends IWindowManager.Stub
// so sorry
}
}
+ return true;
}
@Override
public void reevaluateStatusBarVisibility() {
synchronized (mWindowMap) {
int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
- updateStatusBarVisibilityLocked(visibility);
- mWindowPlacerLocked.performSurfacePlacement();
+ if (updateStatusBarVisibilityLocked(visibility)) {
+ mWindowPlacerLocked.requestTraversal();
+ }
}
}
@@ -10126,5 +10150,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
+ public boolean isStackVisible(int stackId) {
+ synchronized (mWindowMap) {
+ final TaskStack stack = mStackIdToStack.get(stackId);
+ return (stack != null && stack.isVisibleLocked());
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8ace99053cf4..383ad8cb4b07 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -583,12 +583,6 @@ final class WindowState implements WindowManagerPolicy.WindowState {
if (mContainingFrame.isEmpty()) {
mContainingFrame.set(cf);
}
- } else {
- // Make sure the containing frame is within the content frame so we don't layout
- // resized window under screen decorations.
- if (!mContainingFrame.intersect(cf)) {
- mContainingFrame.set(cf);
- }
}
mDisplayFrame.set(mContainingFrame);
} else {
@@ -713,6 +707,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mStableFrame.set(mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
mDisplayContent.mDividerControllerLocked.positionDockedStackedDivider(mFrame);
+ mContentFrame.set(mFrame);
} else {
mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
Math.max(mContentFrame.top, mFrame.top),
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 722ef9f0d302..27339333b53e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,11 +23,12 @@ import static com.android.server.wm.WindowManagerService.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerService.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerService.DEBUG_SURFACE_TRACE;
-import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerService.localLOGV;
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -66,6 +67,7 @@ import java.util.ArrayList;
**/
class WindowStateAnimator {
static final String TAG = "WindowStateAnimator";
+ static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
// Unchanging local convenience fields.
final WindowManagerService mService;
@@ -108,7 +110,7 @@ class WindowStateAnimator {
*/
boolean mSurfaceDestroyDeferred;
- boolean mDestroyPendingSurfaceUponRedraw;
+ boolean mDestroyPreservedSurfaceUponRedraw;
float mShownAlpha = 0;
float mAlpha = 0;
float mLastAlpha = 0;
@@ -556,12 +558,10 @@ class WindowStateAnimator {
if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = performShowLocked();
}
- if (mDestroyPendingSurfaceUponRedraw) {
- mDestroyPendingSurfaceUponRedraw = false;
- destroyDeferredSurfaceLocked();
- mService.stopFreezingDisplayLocked();
+ if (mDestroyPreservedSurfaceUponRedraw && result) {
+ mService.mDestroyPreservedSurface.add(mWin);
}
- return false;
+ return result;
}
static class SurfaceTrace extends SurfaceControl {
@@ -781,6 +781,31 @@ class WindowStateAnimator {
}
}
+ void preserveSurfaceLocked() {
+ if (mDestroyPreservedSurfaceUponRedraw) {
+ return;
+ }
+ if (mSurfaceControl != null) {
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControl.setLayer(WINDOW_FREEZE_LAYER);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ }
+ mDestroyPreservedSurfaceUponRedraw = true;
+ mSurfaceDestroyDeferred = true;
+ destroySurfaceLocked();
+ }
+
+ void destroyPreservedSurfaceLocked() {
+ if (!mDestroyPreservedSurfaceUponRedraw) {
+ return;
+ }
+ destroyDeferredSurfaceLocked();
+ mDestroyPreservedSurfaceUponRedraw = false;
+ }
+
SurfaceControl createSurfaceLocked() {
final WindowState w = mWin;
if (mSurfaceControl == null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index cb5ab1bc8569..0860f025c888 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1326,10 +1326,59 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
void loadOwners() {
synchronized (this) {
mOwners.load();
+ findOwnerComponentIfNecessaryLocked();
+
+ // TODO PO may not have a class name either due to b/17652534. Address that too.
+
updateDeviceOwnerLocked();
}
}
+ private void findOwnerComponentIfNecessaryLocked() {
+ if (!mOwners.hasDeviceOwner()) {
+ return;
+ }
+ final ComponentName doComponentName = mOwners.getDeviceOwnerComponent();
+
+ if (!TextUtils.isEmpty(doComponentName.getClassName())) {
+ return; // Already a full component name.
+ }
+
+ final ComponentName doComponent = findAdminComponentWithPackageLocked(
+ doComponentName.getPackageName(),
+ mOwners.getDeviceOwnerUserId());
+ if (doComponent == null) {
+ Slog.e(LOG_TAG, "Device-owner isn't registered as device-admin");
+ } else {
+ mOwners.setDeviceOwner(
+ doComponent,
+ mOwners.getDeviceOwnerName(),
+ mOwners.getDeviceOwnerUserId());
+ mOwners.writeDeviceOwner();
+ }
+ }
+
+ private ComponentName findAdminComponentWithPackageLocked(String packageName, int userId) {
+ final DevicePolicyData policy = getUserData(userId);
+ final int n = policy.mAdminList.size();
+ ComponentName found = null;
+ int nFound = 0;
+ for (int i = 0; i < n; i++) {
+ final ActiveAdmin admin = policy.mAdminList.get(i);
+ if (packageName.equals(admin.info.getPackageName())) {
+ // Found!
+ if (nFound == 0) {
+ found = admin.info.getComponent();
+ }
+ nFound++;
+ }
+ }
+ if (nFound > 0) {
+ Slog.w(LOG_TAG, "Multiple DA found; assume the first one is DO.");
+ }
+ return found;
+ }
+
/**
* Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
* reminders. Clears alarm if no expirations are configured.
@@ -1440,9 +1489,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return null;
}
- private boolean isActiveAdminWithPolicyForUserLocked(ActiveAdmin admin, int reqPolicy,
+ @VisibleForTesting
+ boolean isActiveAdminWithPolicyForUserLocked(ActiveAdmin admin, int reqPolicy,
int userId) {
- boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
+ boolean ownsDevice = isDeviceOwner(admin.info.getComponent());
boolean ownsProfile = (getProfileOwner(userId) != null
&& getProfileOwner(userId).getPackageName()
.equals(admin.info.getPackageName()));
@@ -1880,8 +1930,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private void updateDeviceOwnerLocked() {
long ident = mInjector.binderClearCallingIdentity();
try {
- mInjector.getIActivityManager()
- .updateDeviceOwner(getDeviceOwner());
+ if (getDeviceOwner() != null) {
+ mInjector.getIActivityManager()
+ .updateDeviceOwner(getDeviceOwner().getPackageName());
+ }
} catch (RemoteException e) {
// Not gonna happen.
} finally {
@@ -1945,7 +1997,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void systemReady(int phase) {
+ @VisibleForTesting
+ void systemReady(int phase) {
if (!mHasFeature) {
return;
}
@@ -2284,7 +2337,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
if (admin.getUid() != mInjector.binderGetCallingUid()) {
// Active device owners must remain active admins.
- if (isDeviceOwner(adminReceiver.getPackageName())) {
+ if (isDeviceOwner(adminReceiver)) {
return;
}
mContext.enforceCallingOrSelfPermission(
@@ -3544,7 +3597,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
try {
if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
if (userHandle != UserHandle.USER_SYSTEM
- || !isDeviceOwner(admin.info.getPackageName())) {
+ || !isDeviceOwner(admin.info.getComponent())) {
throw new SecurityException(
"Only device owner admins can set WIPE_RESET_PROTECTION_DATA");
}
@@ -4293,13 +4346,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
- public boolean setDeviceOwner(String packageName, String ownerName, int userId) {
+ public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId) {
if (!mHasFeature) {
return false;
}
- if (packageName == null
- || !isPackageInstalledForUser(packageName, userId)) {
- throw new IllegalArgumentException("Invalid package name " + packageName
+ if (admin == null
+ || !isPackageInstalledForUser(admin.getPackageName(), userId)) {
+ throw new IllegalArgumentException("Invalid component " + admin
+ " for device owner");
}
synchronized (this) {
@@ -4315,7 +4368,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mInjector.binderRestoreCallingIdentity(ident);
}
- mOwners.setDeviceOwner(packageName, ownerName, userId);
+ mOwners.setDeviceOwner(admin, ownerName, userId);
mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
@@ -4331,24 +4384,33 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ public boolean isDeviceOwner(ComponentName who) {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ return mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerComponent().equals(who);
+ }
+ }
+
@Override
- public boolean isDeviceOwner(String packageName) {
+ public boolean isDeviceOwnerPackage(String packageName) {
if (!mHasFeature) {
return false;
}
synchronized (this) {
return mOwners.hasDeviceOwner()
- && mOwners.getDeviceOwnerPackageName().equals(packageName);
+ && mOwners.getDeviceOwnerComponent().getPackageName().equals(packageName);
}
}
@Override
- public String getDeviceOwner() {
+ public ComponentName getDeviceOwner() {
if (!mHasFeature) {
return null;
}
synchronized (this) {
- return mOwners.getDeviceOwnerPackageName();
+ return mOwners.getDeviceOwnerComponent();
}
}
@@ -4373,16 +4435,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Returns the active device owner or null if there is no device owner.
@VisibleForTesting
ActiveAdmin getDeviceOwnerAdminLocked() {
- String deviceOwnerPackageName = getDeviceOwner();
- if (deviceOwnerPackageName == null) {
+ ComponentName component = getDeviceOwner();
+ if (component == null) {
return null;
}
- DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
+ DevicePolicyData policy = getUserData(mOwners.getDeviceOwnerUserId());
final int n = policy.mAdminList.size();
for (int i = 0; i < n; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
- if (deviceOwnerPackageName.equals(admin.info.getPackageName())) {
+ if (component.equals(admin.info.getComponent())) {
return admin;
}
}
@@ -4392,15 +4454,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void clearDeviceOwner(String packageName) {
Preconditions.checkNotNull(packageName, "packageName is null");
+ final int callingUid = mInjector.binderGetCallingUid();
try {
int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
- if (uid != mInjector.binderGetCallingUid()) {
+ if (uid != callingUid) {
throw new SecurityException("Invalid packageName");
}
} catch (NameNotFoundException e) {
throw new SecurityException(e);
}
- if (!isDeviceOwner(packageName)) {
+ if (!mOwners.hasDeviceOwner() || !getDeviceOwner().getPackageName().equals(packageName)
+ || (mOwners.getDeviceOwnerUserId() != UserHandle.getUserId(callingUid))) {
throw new SecurityException("clearDeviceOwner can only be called by the device owner");
}
synchronized (this) {
@@ -5443,7 +5507,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
ActiveAdmin activeAdmin =
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- boolean isDeviceOwner = isDeviceOwner(activeAdmin.info.getPackageName());
+ boolean isDeviceOwner = isDeviceOwner(who);
if (!isDeviceOwner && userHandle != UserHandle.USER_SYSTEM
&& DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
throw new SecurityException("Profile owners cannot set user restriction " + key);
@@ -5976,7 +6040,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Bundle adminExtras = new Bundle();
adminExtras.putString(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE, pkg);
for (ActiveAdmin admin : policy.mAdminList) {
- boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
+ boolean ownsDevice = isDeviceOwner(admin.info.getComponent());
boolean ownsProfile = (getProfileOwner(userHandle) != null
&& getProfileOwner(userHandle).equals(admin.info.getPackageName()));
if (ownsDevice || ownsProfile) {
@@ -6034,10 +6098,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final ContentResolver contentResolver = mContext.getContentResolver();
synchronized (this) {
- ActiveAdmin activeAdmin =
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (isDeviceOwner(activeAdmin.info.getPackageName())) {
+ if (isDeviceOwner(who)) {
if (!SECURE_SETTINGS_DEVICEOWNER_WHITELIST.contains(setting)) {
throw new SecurityException(String.format(
"Permission denial: Device owners cannot update %1$s", setting));
@@ -6320,7 +6383,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private boolean isCallerDeviceOwner(int callerUid) {
String[] pkgs = mContext.getPackageManager().getPackagesForUid(callerUid);
for (String pkg : pkgs) {
- if (isDeviceOwner(pkg)) {
+ if (isDeviceOwnerPackage(pkg)) {
return true;
}
}
@@ -6342,7 +6405,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
updateReceivedTime);
synchronized (this) {
- String deviceOwnerPackage = getDeviceOwner();
+ final String deviceOwnerPackage = getDeviceOwner() == null ? null :
+ getDeviceOwner().getPackageName();
if (deviceOwnerPackage == null) {
return;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index f9543c22e086..799267d9bf44 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -30,6 +30,7 @@ import android.util.Slog;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -145,12 +146,16 @@ class Owners {
return mDeviceOwner != null ? mDeviceOwner.name : null;
}
- void setDeviceOwner(String packageName, String ownerName, int userId) {
+ ComponentName getDeviceOwnerComponent() {
+ return mDeviceOwner != null ? mDeviceOwner.admin : null;
+ }
+
+ void setDeviceOwner(ComponentName admin, String ownerName, int userId) {
if (userId < 0) {
Slog.e(TAG, "Invalid user id for device owner user: " + userId);
return;
}
- mDeviceOwner = new OwnerInfo(ownerName, packageName);
+ mDeviceOwner = new OwnerInfo(ownerName, admin);
mDeviceOwnerUserId = userId;
}
@@ -541,7 +546,7 @@ class Owners {
} else {
// This shouldn't happen but switch from package name -> component name
// might have written bad device owner files. b/17652534
- Slog.e(TAG, "Error parsing device-owner file. Bad component name " +
+ Slog.e(TAG, "Error parsing owner file. Bad component name " +
componentName);
}
}
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index e0d2ac12be21..1a728a0ca261 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -299,6 +299,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
Os.setsockoptIfreq(mUdpSock, SOL_SOCKET, SO_BINDTODEVICE, mIfaceName);
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
+ Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT);
NetworkUtils.protectFromVpn(mUdpSock);
} catch(SocketException|ErrnoException e) {
@@ -308,6 +309,16 @@ public class DhcpClient extends BaseDhcpStateMachine {
return true;
}
+ private boolean connectUdpSock(Inet4Address to) {
+ try {
+ Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
+ return true;
+ } catch (SocketException|ErrnoException e) {
+ Log.e(TAG, "Error connecting UDP socket", e);
+ return false;
+ }
+ }
+
private static void closeQuietly(FileDescriptor fd) {
try {
IoBridge.closeAndSignalBlockedThreads(fd);
@@ -325,7 +336,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
try {
mNMService.setInterfaceConfig(mIfaceName, ifcg);
} catch (RemoteException|IllegalStateException e) {
- Log.e(TAG, "Error configuring IP address : " + e);
+ Log.e(TAG, "Error configuring IP address " + address + ": ", e);
return false;
}
return true;
@@ -376,8 +387,10 @@ public class DhcpClient extends BaseDhcpStateMachine {
maybeLog("Broadcasting " + description);
Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
} else {
- maybeLog("Unicasting " + description + " to " + to.getHostAddress());
- Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
+ // It's safe to call getpeername here, because we only send unicast packets if we
+ // have an IP address, and we connect the UDP socket in DhcpHaveAddressState#enter.
+ maybeLog("Unicasting " + description + " to " + Os.getpeername(mUdpSock));
+ Os.write(mUdpSock, buf);
}
} catch(ErrnoException|IOException e) {
Log.e(TAG, "Can't send packet: ", e);
@@ -789,6 +802,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
transitionTo(mDhcpBoundState);
}
} else if (packet instanceof DhcpNakPacket) {
+ // TODO: Wait a while before returning into INIT state.
Log.d(TAG, "Received NAK, returning to INIT");
mOffer = null;
transitionTo(mDhcpInitState);
@@ -806,10 +820,8 @@ public class DhcpClient extends BaseDhcpStateMachine {
@Override
public void enter() {
super.enter();
- if (setIpAddress(mDhcpLease.ipAddress)) {
- maybeLog("Configured IP address " + mDhcpLease.ipAddress);
- } else {
- Log.e(TAG, "Failed to configure IP address " + mDhcpLease.ipAddress);
+ if (!setIpAddress(mDhcpLease.ipAddress) ||
+ !connectUdpSock((mDhcpLease.serverAddress))) {
notifyFailure();
// There's likely no point in going into DhcpInitState here, we'll probably just
// repeat the transaction, get the same IP address as before, and fail.
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 03b892e55c41..d6a60c7f3e65 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import android.Manifest.permission;
import android.app.Activity;
@@ -44,6 +45,7 @@ import java.util.List;
import java.util.Map;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
@@ -81,9 +83,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
.thenReturn(true);
- LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
- dpms = new DevicePolicyManagerServiceTestable(mContext, dataDir);
- dpm = new DevicePolicyManagerTestable(mContext, dpms);
+ initializeDpms();
admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
@@ -93,19 +93,36 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
- setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
- DpmMockContext.CALLER_UID);
-
- setUpPackageInfo();
setUpUserManager();
}
- /**
- * Set up a mock result for {@link PackageManager#queryBroadcastReceivers}. We'll return
- * the actual ResolveInfo for the admin component, but we need to mock PM so it'll return
- * it for user {@link DpmMockContext#CALLER_USER_HANDLE}.
- */
- private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid) {
+ private void initializeDpms() {
+ // Need clearCallingIdentity() to pass permission checks.
+ final long ident = mContext.binder.clearCallingIdentity();
+ try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+ dpms = new DevicePolicyManagerServiceTestable(mContext, dataDir);
+
+ dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
+ dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
+
+ dpm = new DevicePolicyManagerTestable(mContext, dpms);
+ } finally {
+ mContext.binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid) throws Exception {
+ setUpPackageManagerForAdmin(admin, packageUid,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+ }
+
+ private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
+ int enabledSetting) throws Exception {
+
+ // Set up queryBroadcastReceivers().
+
final Intent resolveIntent = new Intent();
resolveIntent.setComponent(admin);
final List<ResolveInfo> realResolveInfo =
@@ -126,13 +143,9 @@ public class DevicePolicyManagerTest extends DpmTestBase {
eq(PackageManager.GET_META_DATA
| PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
eq(UserHandle.getUserId(packageUid)));
- }
- /**
- * Set up a mock result for {@link IPackageManager#getApplicationInfo} for user
- * {@link DpmMockContext#CALLER_USER_HANDLE}.
- */
- private void setUpApplicationInfo(int enabledSetting, int packageUid) throws Exception {
+ // Set up getApplicationInfo().
+
final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
mRealTestContext.getPackageManager().getApplicationInfo(
admin1.getPackageName(),
@@ -145,25 +158,20 @@ public class DevicePolicyManagerTest extends DpmTestBase {
eq(admin1.getPackageName()),
eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
eq(UserHandle.getUserId(packageUid)));
- }
- /**
- * Set up a mock result for {@link IPackageManager#getPackageInfo(String, int, int)} for user
- * {@link DpmMockContext#CALLER_USER_HANDLE} as well as the system user.
- */
- private void setUpPackageInfo() throws Exception {
- final PackageInfo pi = mRealTestContext.getPackageManager().getPackageInfo(
- admin1.getPackageName(), 0);
+ // Set up getPackageInfo().
+
+ final PackageInfo pi = DpmTestUtils.cloneParcelable(
+ mRealTestContext.getPackageManager().getPackageInfo(
+ admin1.getPackageName(), 0));
assertTrue(pi.applicationInfo.flags != 0);
+ pi.applicationInfo.uid = packageUid;
+
doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
eq(admin1.getPackageName()),
eq(0),
- eq(DpmMockContext.CALLER_USER_HANDLE));
- doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
- eq(admin1.getPackageName()),
- eq(0),
- eq(UserHandle.USER_SYSTEM));
+ eq(UserHandle.getUserId(packageUid)));
}
private void setUpUserManager() {
@@ -303,8 +311,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Next, add one more admin.
// Before doing so, update the application info, now it's enabled.
- setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
- DpmMockContext.CALLER_UID);
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
dpm.setActiveAdmin(admin2, /* replace =*/ false);
@@ -358,8 +366,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Set up pacakge manager for the other user.
setUpPackageManagerForAdmin(admin2, ANOTHER_ADMIN_UID);
- setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
- ANOTHER_ADMIN_UID);
mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
@@ -532,14 +538,12 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Make sure admin1 is installed on system user.
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
- setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
- DpmMockContext.CALLER_SYSTEM_USER_UID);
// DO needs to be an DA.
dpm.setActiveAdmin(admin1, /* replace =*/ false);
// Fire!
- assertTrue(dpm.setDeviceOwner(admin1.getPackageName(), "owner-name"));
+ assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
// Verify internal calls.
verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
@@ -571,7 +575,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
try {
- dpm.setDeviceOwner("a.b.c");
+ dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"));
fail("Didn't throw IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
@@ -581,6 +585,85 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// TODO Test more failure cases. Basically test all chacks in enforceCanSetDeviceOwner().
}
+ public void testClearDeviceOwner() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // Set admin1 as a DA to the secondary user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ // Set admin 1 as the DO to the system user.
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
+
+ // Verify internal calls.
+ verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+ eq(admin1.getPackageName()));
+
+ assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+
+ // Set up other mocks.
+ when(mContext.userManager.getUserRestrictions()).thenReturn(new Bundle());
+
+ // Now call clear.
+ doReturn(DpmMockContext.CALLER_SYSTEM_USER_UID).when(mContext.packageManager).getPackageUid(
+ eq(admin1.getPackageName()),
+ anyInt());
+ dpm.clearDeviceOwnerApp(admin1.getPackageName());
+
+ // Now DO shouldn't be set.
+ assertNull(dpm.getDeviceOwner());
+
+ // TODO Check other calls.
+ }
+
+ public void testClearDeviceOwner_fromDifferentUser() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // Set admin1 as a DA to the secondary user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ // Set admin 1 as the DO to the system user.
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
+
+ // Verify internal calls.
+ verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+ eq(admin1.getPackageName()));
+
+ assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+
+ // Now call clear from the secondary user, which should throw.
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ // Now call clear.
+ doReturn(DpmMockContext.CALLER_UID).when(mContext.packageManager).getPackageUid(
+ eq(admin1.getPackageName()),
+ anyInt());
+ try {
+ dpm.clearDeviceOwnerApp(admin1.getPackageName());
+ fail("Didn't throw");
+ } catch (SecurityException e) {
+ assertEquals("clearDeviceOwner can only be called by the device owner", e.getMessage());
+ }
+
+ // Now DO shouldn't be set.
+ assertNotNull(dpm.getDeviceOwner());
+ }
+
public void testSetProfileOwner() throws Exception {
setAsProfileOwner(admin1);
}
@@ -589,6 +672,85 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// TODO Test more failure cases. Basically test all chacks in enforceCanSetProfileOwner().
}
+ public void testGetDeviceOwnerAdminLocked() throws Exception {
+ checkDeviceOwnerWithMultipleDeviceAdmins();
+ }
+
+ private void checkDeviceOwnerWithMultipleDeviceAdmins() throws Exception {
+ // In ths test, we use 3 users (system + 2 secondary users), set some device admins to them,
+ // set admin2 on CALLER_USER_HANDLE as DO, then call getDeviceOwnerAdminLocked() to
+ // make sure it gets the right component from the right user.
+
+ final int ANOTHER_USER_ID = 100;
+ final int ANOTHER_ADMIN_UID = UserHandle.getUid(ANOTHER_USER_ID, 456);
+
+ mMockContext.addUser(ANOTHER_USER_ID, 0); // Add one more user.
+
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // Make sure the admin packge is installed to each user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
+
+ setUpPackageManagerForAdmin(admin2, ANOTHER_ADMIN_UID);
+
+
+ // Set active admins to the users.
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ dpm.setActiveAdmin(admin3, /* replace =*/ false);
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false, DpmMockContext.CALLER_USER_HANDLE);
+ dpm.setActiveAdmin(admin2, /* replace =*/ false, DpmMockContext.CALLER_USER_HANDLE);
+
+ dpm.setActiveAdmin(admin2, /* replace =*/ false, ANOTHER_USER_ID);
+
+ // Set DO on the first non-system user.
+ mContext.setUserRunning(DpmMockContext.CALLER_USER_HANDLE, true);
+ assertTrue(dpm.setDeviceOwner(admin2, "owner-name", DpmMockContext.CALLER_USER_HANDLE));
+
+ // Make sure it's set.
+ assertEquals(admin2, dpm.getDeviceOwnerComponent());
+
+ // Then check getDeviceOwnerAdminLocked().
+ assertEquals(admin2, dpms.getDeviceOwnerAdminLocked().info.getComponent());
+ assertEquals(DpmMockContext.CALLER_UID, dpms.getDeviceOwnerAdminLocked().getUid());
+ }
+
+ /**
+ * This essentially tests
+ * {@code DevicePolicyManagerService.findOwnerComponentIfNecessaryLocked()}. (which is private.)
+ *
+ * We didn't use to persist the DO component class name, but now we do, and the above method
+ * finds the right component from a package name upon migration.
+ */
+ public void testDeviceOwnerMigration() throws Exception {
+ checkDeviceOwnerWithMultipleDeviceAdmins();
+
+ // Overwrite the device owner setting and clears the clas name.
+ dpms.mOwners.setDeviceOwner(
+ new ComponentName(admin2.getPackageName(), ""),
+ "owner-name", DpmMockContext.CALLER_USER_HANDLE);
+ dpms.mOwners.writeDeviceOwner();
+
+ // Make sure the DO component name doesn't have a class name.
+ assertEquals("", dpms.getDeviceOwner().getClassName());
+
+ // Then create a new DPMS to have it load the settings from files.
+ initializeDpms();
+
+ // Now the DO component name is a full name.
+ // *BUT* because both admin1 and admin2 belong to the same package, we think admin1 is the
+ // DO.
+ assertEquals(admin1, dpms.getDeviceOwner());
+ }
+
public void testSetGetApplicationRestriction() {
setAsProfileOwner(admin1);
@@ -634,12 +796,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Make sure admin1 is installed on system user.
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
- setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
- DpmMockContext.CALLER_SYSTEM_USER_UID);
// Call.
dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
- assertTrue(dpm.setDeviceOwner(admin1.getPackageName(), "owner-name",
+ assertTrue(dpm.setDeviceOwner(admin1, "owner-name",
UserHandle.USER_SYSTEM));
assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 73d63ea64026..d1b483551809 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -22,6 +22,7 @@ import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.backup.IBackupManager;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -35,6 +36,7 @@ import android.os.PowerManager.WakeLock;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.UserManager;
+import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.view.IWindowManager;
@@ -212,6 +214,7 @@ public class DpmMockContext extends MockContext {
public final IAudioService iaudioService;
public final LockPatternUtils lockPatternUtils;
public final SettingsForMock settings;
+ public final MockContentResolver contentResolver;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
@@ -247,12 +250,13 @@ public class DpmMockContext extends MockContext {
spiedContext = mock(Context.class);
+ contentResolver = new MockContentResolver();
+
// Add the system user
systemUserDataDir = addUser(UserHandle.USER_SYSTEM, UserInfo.FLAG_PRIMARY);
// System user is always running.
- when(userManager.isUserRunning(MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)))
- .thenReturn(true);
+ setUserRunning(UserHandle.USER_SYSTEM, true);
}
public File addUser(int userId, int flags) {
@@ -281,6 +285,11 @@ public class DpmMockContext extends MockContext {
}
}
+ public void setUserRunning(int userId, boolean isRunning) {
+ when(userManager.isUserRunning(MockUtils.checkUserHandle(userId)))
+ .thenReturn(isRunning);
+ }
+
@Override
public Object getSystemService(String name) {
switch (name) {
@@ -473,4 +482,9 @@ public class DpmMockContext extends MockContext {
public void unregisterReceiver(BroadcastReceiver receiver) {
spiedContext.unregisterReceiver(receiver);
}
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return contentResolver;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index a210d4652fe3..79845d21281f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -306,7 +306,8 @@ public class OwnersTest extends DpmTestBase {
// The legacy file should be removed.
assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
- assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists());
+ // Note device initializer is no longer supported. No need to write the DO file.
+ assertFalse(owners.getDeviceOwnerFileWithTestOverride().exists());
assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index cdb0bf257baa..5c6416884aa5 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -28,6 +28,7 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -160,6 +161,7 @@ public final class PhoneAccount implements Parcelable {
private final CharSequence mShortDescription;
private final List<String> mSupportedUriSchemes;
private final Icon mIcon;
+ private final Bundle mExtras;
private boolean mIsEnabled;
/**
@@ -175,6 +177,7 @@ public final class PhoneAccount implements Parcelable {
private CharSequence mShortDescription;
private List<String> mSupportedUriSchemes = new ArrayList<String>();
private Icon mIcon;
+ private Bundle mExtras;
private boolean mIsEnabled = false;
/**
@@ -300,6 +303,20 @@ public final class PhoneAccount implements Parcelable {
}
/**
+ * Specifies the extras associated with the {@link PhoneAccount}.
+ * <p>
+ * {@code PhoneAccount}s only support extra values of type: {@link String}, {@link Integer},
+ * and {@link Boolean}. Extras which are not of these types are ignored.
+ *
+ * @param extras
+ * @return
+ */
+ public Builder setExtras(Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Sets the enabled state of the phone account.
*
* @param isEnabled The enabled state.
@@ -332,6 +349,7 @@ public final class PhoneAccount implements Parcelable {
mLabel,
mShortDescription,
mSupportedUriSchemes,
+ mExtras,
mIsEnabled);
}
}
@@ -346,6 +364,7 @@ public final class PhoneAccount implements Parcelable {
CharSequence label,
CharSequence shortDescription,
List<String> supportedUriSchemes,
+ Bundle extras,
boolean isEnabled) {
mAccountHandle = account;
mAddress = address;
@@ -356,6 +375,7 @@ public final class PhoneAccount implements Parcelable {
mLabel = label;
mShortDescription = shortDescription;
mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
+ mExtras = extras;
mIsEnabled = isEnabled;
}
@@ -455,6 +475,18 @@ public final class PhoneAccount implements Parcelable {
}
/**
+ * The extras associated with this {@code PhoneAccount}.
+ * <p>
+ * A {@link ConnectionService} may provide implementation specific information about the
+ * {@link PhoneAccount} via the extras.
+ *
+ * @return The extras.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
* The icon to represent this {@code PhoneAccount}.
*
* @return The icon.
@@ -552,6 +584,8 @@ public final class PhoneAccount implements Parcelable {
out.writeInt(1);
mIcon.writeToParcel(out, flags);
}
+
+ out.writeBundle(mExtras);
out.writeByte((byte) (mIsEnabled ? 1 : 0));
}
@@ -594,6 +628,7 @@ public final class PhoneAccount implements Parcelable {
} else {
mIcon = null;
}
+ mExtras = in.readBundle();
mIsEnabled = in.readByte() == 1;
}
@@ -610,6 +645,8 @@ public final class PhoneAccount implements Parcelable {
sb.append(scheme)
.append(" ");
}
+ sb.append(" Extras : ");
+ sb.append(mExtras);
sb.append("]");
return sb.toString();
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index f8b3739b6a89..64cd5031346e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -35,6 +35,8 @@ import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
@@ -707,6 +709,12 @@ public final class Canvas_Delegate {
@Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
Shape shape = pathDelegate.getJavaShape();
+ Rectangle2D bounds = shape.getBounds2D();
+ if (bounds.isEmpty()) {
+ // Apple JRE 1.6 doesn't like drawing empty shapes.
+ // http://b.android.com/178278
+ return;
+ }
int style = paintDelegate.getStyle();
if (style == Paint.Style.FILL.nativeInt ||
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java
new file mode 100644
index 000000000000..a3ad2aac7d3d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.Path;
+import android.graphics.drawable.GradientDrawable.GradientState;
+
+import java.lang.reflect.Field;
+
+/**
+ * Delegate implementing the native methods of {@link GradientDrawable}
+ *
+ * Through the layoutlib_create tool, the original native methods of GradientDrawable have been
+ * replaced by calls to methods of the same name in this delegate class.
+ */
+public class GradientDrawable_Delegate {
+
+ /**
+ * The ring can be built either by drawing full circles, or by drawing arcs in case the
+ * circle isn't complete. LayoutLib cannot handle drawing full circles (requires path
+ * subtraction). So, if we need to draw full circles, we switch to drawing 99% circle.
+ */
+ @LayoutlibDelegate
+ /*package*/ static Path buildRing(GradientDrawable thisDrawable, GradientState st) {
+ boolean useLevel = st.mUseLevelForShape;
+ int level = thisDrawable.getLevel();
+ // 10000 is the max level. See android.graphics.drawable.Drawable#getLevel()
+ float sweep = useLevel ? (360.0f * level / 10000.0f) : 360f;
+ Field mLevel = null;
+ if (sweep >= 360 || sweep <= -360) {
+ st.mUseLevelForShape = true;
+ // Use reflection to set the value of the field to prevent setting the drawable to
+ // dirty again.
+ try {
+ mLevel = Drawable.class.getDeclaredField("mLevel");
+ mLevel.setAccessible(true);
+ mLevel.setInt(thisDrawable, 9999); // set to one less than max.
+ } catch (NoSuchFieldException e) {
+ // The field has been removed in a recent framework change. Fall back to old
+ // buggy behaviour.
+ } catch (IllegalAccessException e) {
+ // We've already set the field to be accessible.
+ assert false;
+ }
+ }
+ Path path = thisDrawable.buildRing_Original(st);
+ st.mUseLevelForShape = useLevel;
+ if (mLevel != null) {
+ try {
+ mLevel.setInt(thisDrawable, level);
+ } catch (IllegalAccessException e) {
+ assert false;
+ }
+ }
+ return path;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java b/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java
index 49ee6426acae..2e44a7770aae 100644
--- a/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java
@@ -29,9 +29,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
-import java.util.HashMap;
-import java.util.Map;
-
/**
* Delegate that provides implementation for native methods in {@link Preference}
* <p/>
@@ -59,9 +56,9 @@ public class Preference_Delegate {
*/
public static View inflatePreference(Context context, XmlPullParser parser, ViewGroup root) {
PreferenceManager pm = new PreferenceManager(context);
- PreferenceScreen ps = pm.getPreferenceScreen();
PreferenceInflater inflater = new BridgePreferenceInflater(context, pm);
- ps = (PreferenceScreen) inflater.inflate(parser, ps, true);
+ PreferenceScreen ps = (PreferenceScreen) inflater.inflate(parser, null, true);
+ pm.setPreferences(ps);
ListView preferenceView = createContainerView(context, root);
ps.bind(preferenceView);
return preferenceView;
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index c92fdb4ac74b..e480eadc0cf6 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -161,6 +161,7 @@ public final class CreateInfo implements ICreateInfo {
"android.content.res.TypedArray#getValueAt",
"android.content.res.TypedArray#obtain",
"android.graphics.BitmapFactory#finishDecode",
+ "android.graphics.drawable.GradientDrawable#buildRing",
"android.graphics.Typeface#getSystemFontConfigLocation",
"android.os.Handler#sendMessageAtTime",
"android.os.HandlerThread#run",