OSDN Git Service

Merge "Remove unused lines in MtpDocumentsProvider."
authorDaichi Hirono <hirono@google.com>
Tue, 12 Jan 2016 04:23:07 +0000 (04:23 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Tue, 12 Jan 2016 04:23:07 +0000 (04:23 +0000)
284 files changed:
api/current.txt
api/system-current.txt
api/test-current.txt
core/java/android/accessibilityservice/AccessibilityService.java
core/java/android/accessibilityservice/AccessibilityServiceInfo.java
core/java/android/accessibilityservice/GestureDescription.java [new file with mode: 0644]
core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
core/java/android/animation/PropertyValuesHolder.java
core/java/android/app/Activity.java
core/java/android/app/UiAutomation.java
core/java/android/app/job/JobInfo.java
core/java/android/app/usage/NetworkStats.java
core/java/android/content/pm/PackageManager.java
core/java/android/content/pm/PackageParser.java
core/java/android/content/pm/PackageUserState.java
core/java/android/net/ConnectivityManager.java
core/java/android/provider/Settings.java
core/java/android/service/notification/NotificationAssistantService.java
core/java/android/util/LocaleList.java
core/java/android/view/WindowManagerPolicy.java
core/java/android/view/inputmethod/BaseInputConnection.java
core/java/android/view/inputmethod/InputConnection.java
core/java/android/webkit/WebViewFactory.java
core/java/android/webkit/WebViewProviderInfo.java
core/java/com/android/internal/logging/MetricsLogger.java
core/java/com/android/internal/policy/BackdropFrameRenderer.java
core/java/com/android/internal/policy/DecorView.java
core/java/com/android/internal/policy/DividerSnapAlgorithm.java [moved from packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java with 91% similarity]
core/java/com/android/internal/policy/DockedDividerUtils.java [new file with mode: 0644]
core/java/com/android/internal/util/ArrayUtils.java
core/res/AndroidManifest.xml
core/res/res/values/attrs.xml
core/res/res/values/public.xml
core/res/res/values/strings.xml
core/res/res/values/symbols.xml
core/res/res/xml/config_webview_packages.xml
core/tests/ConnectivityManagerTest/AndroidManifest.xml
core/tests/coretests/src/android/util/LocaleListTest.java [new file with mode: 0644]
libs/hwui/Android.mk
libs/hwui/BakedOpDispatcher.cpp
libs/hwui/BakedOpRenderer.cpp
libs/hwui/BakedOpState.cpp
libs/hwui/BakedOpState.h
libs/hwui/FrameReorderer.cpp [moved from libs/hwui/OpReorderer.cpp with 64% similarity]
libs/hwui/FrameReorderer.h [moved from libs/hwui/OpReorderer.h with 65% similarity]
libs/hwui/LayerReorderer.cpp [new file with mode: 0644]
libs/hwui/LayerReorderer.h [new file with mode: 0644]
libs/hwui/RenderNode.h
libs/hwui/renderthread/CanvasContext.cpp
libs/hwui/tests/microbench/FrameReordererBench.cpp [moved from libs/hwui/tests/microbench/OpReordererBench.cpp with 86% similarity]
libs/hwui/tests/unit/BakedOpStateTests.cpp
libs/hwui/tests/unit/FrameReordererTests.cpp [moved from libs/hwui/tests/unit/OpReordererTests.cpp with 93% similarity]
media/java/android/mtp/MtpEvent.java
media/jni/android_mtp_MtpDevice.cpp
opengl/java/android/opengl/GLSurfaceView.java
packages/DocumentsUI/res/menu/activity.xml
packages/DocumentsUI/res/values/colors.xml
packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
packages/PrintSpooler/res/values-af/strings.xml
packages/PrintSpooler/res/values-am/strings.xml
packages/PrintSpooler/res/values-ar/strings.xml
packages/PrintSpooler/res/values-az-rAZ/strings.xml
packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
packages/PrintSpooler/res/values-bg/strings.xml
packages/PrintSpooler/res/values-bn-rBD/strings.xml
packages/PrintSpooler/res/values-ca/strings.xml
packages/PrintSpooler/res/values-cs/strings.xml
packages/PrintSpooler/res/values-da/strings.xml
packages/PrintSpooler/res/values-de/strings.xml
packages/PrintSpooler/res/values-el/strings.xml
packages/PrintSpooler/res/values-en-rAU/strings.xml
packages/PrintSpooler/res/values-en-rGB/strings.xml
packages/PrintSpooler/res/values-en-rIN/strings.xml
packages/PrintSpooler/res/values-es-rUS/strings.xml
packages/PrintSpooler/res/values-es/strings.xml
packages/PrintSpooler/res/values-et-rEE/strings.xml
packages/PrintSpooler/res/values-eu-rES/strings.xml
packages/PrintSpooler/res/values-fa/strings.xml
packages/PrintSpooler/res/values-fi/strings.xml
packages/PrintSpooler/res/values-fr-rCA/strings.xml
packages/PrintSpooler/res/values-fr/strings.xml
packages/PrintSpooler/res/values-gl-rES/strings.xml
packages/PrintSpooler/res/values-gu-rIN/strings.xml
packages/PrintSpooler/res/values-hi/strings.xml
packages/PrintSpooler/res/values-hr/strings.xml
packages/PrintSpooler/res/values-hu/strings.xml
packages/PrintSpooler/res/values-hy-rAM/strings.xml
packages/PrintSpooler/res/values-in/strings.xml
packages/PrintSpooler/res/values-is-rIS/strings.xml
packages/PrintSpooler/res/values-it/strings.xml
packages/PrintSpooler/res/values-iw/strings.xml
packages/PrintSpooler/res/values-ja/strings.xml
packages/PrintSpooler/res/values-ka-rGE/strings.xml
packages/PrintSpooler/res/values-kk-rKZ/strings.xml
packages/PrintSpooler/res/values-km-rKH/strings.xml
packages/PrintSpooler/res/values-kn-rIN/strings.xml
packages/PrintSpooler/res/values-ko/strings.xml
packages/PrintSpooler/res/values-ky-rKG/strings.xml
packages/PrintSpooler/res/values-lo-rLA/strings.xml
packages/PrintSpooler/res/values-lt/strings.xml
packages/PrintSpooler/res/values-lv/strings.xml
packages/PrintSpooler/res/values-mk-rMK/strings.xml
packages/PrintSpooler/res/values-ml-rIN/strings.xml
packages/PrintSpooler/res/values-mn-rMN/strings.xml
packages/PrintSpooler/res/values-mr-rIN/strings.xml
packages/PrintSpooler/res/values-ms-rMY/strings.xml
packages/PrintSpooler/res/values-my-rMM/strings.xml
packages/PrintSpooler/res/values-nb/strings.xml
packages/PrintSpooler/res/values-ne-rNP/strings.xml
packages/PrintSpooler/res/values-nl/strings.xml
packages/PrintSpooler/res/values-pa-rIN/strings.xml
packages/PrintSpooler/res/values-pl/strings.xml
packages/PrintSpooler/res/values-pt-rBR/strings.xml
packages/PrintSpooler/res/values-pt-rPT/strings.xml
packages/PrintSpooler/res/values-pt/strings.xml
packages/PrintSpooler/res/values-ro/strings.xml
packages/PrintSpooler/res/values-ru/strings.xml
packages/PrintSpooler/res/values-si-rLK/strings.xml
packages/PrintSpooler/res/values-sk/strings.xml
packages/PrintSpooler/res/values-sl/strings.xml
packages/PrintSpooler/res/values-sq-rAL/strings.xml
packages/PrintSpooler/res/values-sr/strings.xml
packages/PrintSpooler/res/values-sv/strings.xml
packages/PrintSpooler/res/values-sw/strings.xml
packages/PrintSpooler/res/values-ta-rIN/strings.xml
packages/PrintSpooler/res/values-te-rIN/strings.xml
packages/PrintSpooler/res/values-th/strings.xml
packages/PrintSpooler/res/values-tl/strings.xml
packages/PrintSpooler/res/values-tr/strings.xml
packages/PrintSpooler/res/values-uk/strings.xml
packages/PrintSpooler/res/values-ur-rPK/strings.xml
packages/PrintSpooler/res/values-uz-rUZ/strings.xml
packages/PrintSpooler/res/values-vi/strings.xml
packages/PrintSpooler/res/values-zh-rCN/strings.xml
packages/PrintSpooler/res/values-zh-rHK/strings.xml
packages/PrintSpooler/res/values-zh-rTW/strings.xml
packages/PrintSpooler/res/values-zu/strings.xml
packages/PrintSpooler/res/values/strings.xml
packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
packages/SettingsLib/res/values-b+sr+Latn/strings.xml
packages/SettingsLib/res/values-bg/strings.xml
packages/SettingsLib/res/values-bn-rBD/strings.xml
packages/SettingsLib/res/values-ca/strings.xml
packages/SettingsLib/res/values-de/strings.xml
packages/SettingsLib/res/values-en-rAU/strings.xml
packages/SettingsLib/res/values-en-rGB/strings.xml
packages/SettingsLib/res/values-en-rIN/strings.xml
packages/SettingsLib/res/values-es-rUS/strings.xml
packages/SettingsLib/res/values-eu-rES/strings.xml
packages/SettingsLib/res/values-fa/strings.xml
packages/SettingsLib/res/values-fi/strings.xml
packages/SettingsLib/res/values-fr-rCA/strings.xml
packages/SettingsLib/res/values-fr/strings.xml
packages/SettingsLib/res/values-gu-rIN/strings.xml
packages/SettingsLib/res/values-hr/strings.xml
packages/SettingsLib/res/values-hy-rAM/strings.xml
packages/SettingsLib/res/values-in/strings.xml
packages/SettingsLib/res/values-is-rIS/strings.xml
packages/SettingsLib/res/values-iw/strings.xml
packages/SettingsLib/res/values-ja/strings.xml
packages/SettingsLib/res/values-ka-rGE/strings.xml
packages/SettingsLib/res/values-kk-rKZ/strings.xml
packages/SettingsLib/res/values-km-rKH/strings.xml
packages/SettingsLib/res/values-ko/strings.xml
packages/SettingsLib/res/values-ky-rKG/strings.xml
packages/SettingsLib/res/values-lo-rLA/strings.xml
packages/SettingsLib/res/values-lt/strings.xml
packages/SettingsLib/res/values-lv/strings.xml
packages/SettingsLib/res/values-ml-rIN/strings.xml
packages/SettingsLib/res/values-mn-rMN/strings.xml
packages/SettingsLib/res/values-ms-rMY/strings.xml
packages/SettingsLib/res/values-my-rMM/strings.xml
packages/SettingsLib/res/values-nb/strings.xml
packages/SettingsLib/res/values-ne-rNP/strings.xml
packages/SettingsLib/res/values-pt-rBR/strings.xml
packages/SettingsLib/res/values-pt/strings.xml
packages/SettingsLib/res/values-ro/strings.xml
packages/SettingsLib/res/values-ru/strings.xml
packages/SettingsLib/res/values-si-rLK/strings.xml
packages/SettingsLib/res/values-sk/strings.xml
packages/SettingsLib/res/values-sl/strings.xml
packages/SettingsLib/res/values-sr/strings.xml
packages/SettingsLib/res/values-sv/strings.xml
packages/SettingsLib/res/values-sw/strings.xml
packages/SettingsLib/res/values-ta-rIN/strings.xml
packages/SettingsLib/res/values-th/strings.xml
packages/SettingsLib/res/values-tl/strings.xml
packages/SettingsLib/res/values-tr/strings.xml
packages/SettingsLib/res/values-ur-rPK/strings.xml
packages/SettingsLib/res/values-uz-rUZ/strings.xml
packages/SettingsLib/res/values-vi/strings.xml
packages/SettingsLib/res/values-zh-rCN/strings.xml
packages/SettingsLib/res/values-zh-rHK/strings.xml
packages/SettingsLib/res/values-zh-rTW/strings.xml
packages/SettingsLib/res/values/strings.xml
packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java [new file with mode: 0644]
packages/SettingsLib/src/com/android/settingslib/Utils.java
packages/SystemUI/AndroidManifest.xml
packages/SystemUI/res/layout/battery_detail.xml [new file with mode: 0644]
packages/SystemUI/res/layout/super_status_bar.xml
packages/SystemUI/res/values-zh-rCN/strings.xml
packages/SystemUI/res/values/arrays.xml
packages/SystemUI/res/values/colors.xml
packages/SystemUI/res/values/dimens.xml
packages/SystemUI/res/values/strings.xml
packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
packages/SystemUI/src/com/android/systemui/RecentsComponent.java
packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
packages/SystemUI/src/com/android/systemui/power/PowerUI.java
packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
packages/SystemUI/src/com/android/systemui/qs/QSTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
packages/SystemUI/src/com/android/systemui/recents/Recents.java
packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java [deleted file]
packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java [new file with mode: 0644]
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/ActivityStack.java
services/core/java/com/android/server/am/ActivityStarter.java
services/core/java/com/android/server/job/JobSchedulerService.java
services/core/java/com/android/server/job/JobStore.java
services/core/java/com/android/server/job/controllers/JobStatus.java
services/core/java/com/android/server/notification/NotificationManagerService.java
services/core/java/com/android/server/notification/RankingHelper.java
services/core/java/com/android/server/pm/PackageManagerService.java
services/core/java/com/android/server/pm/Settings.java
services/core/java/com/android/server/policy/PhoneWindowManager.java
services/core/java/com/android/server/webkit/WebViewUpdateService.java
services/core/java/com/android/server/wm/DisplayContent.java
services/core/java/com/android/server/wm/Task.java
services/core/java/com/android/server/wm/TaskStack.java
services/core/java/com/android/server/wm/WindowManagerService.java
services/core/java/com/android/server/wm/WindowState.java
telecomm/java/android/telecom/PhoneAccount.java
telecomm/java/android/telecom/VideoProfile.java
telephony/java/android/telephony/CarrierConfigManager.java
test-runner/src/android/test/mock/MockPackageManager.java
tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
tools/aapt2/ResourceParser.cpp
tools/aapt2/ResourceParser.h
tools/aapt2/compile/Compile.cpp
tools/aapt2/link/Link.cpp
tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png [new file with mode: 0644]
tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java

index 6c49041..4189409 100644 (file)
@@ -334,6 +334,7 @@ package android {
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
     field public static final int canControlMagnification = 16844040; // 0x1010508
+    field public static final int canPerformGestures = 16844046; // 0x101050e
     field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
     field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
     field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -2610,6 +2611,7 @@ package android.accessibilityservice {
 
   public abstract class AccessibilityService extends android.app.Service {
     ctor public AccessibilityService();
+    method public final boolean dispatchGesture(android.accessibilityservice.GestureDescription, android.accessibilityservice.AccessibilityService.GestureResultCallback, android.os.Handler);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
@@ -2649,6 +2651,12 @@ package android.accessibilityservice {
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
   }
 
+  public static abstract class AccessibilityService.GestureResultCallback {
+    ctor public AccessibilityService.GestureResultCallback();
+    method public void onCancelled(android.accessibilityservice.GestureDescription);
+    method public void onCompleted(android.accessibilityservice.GestureDescription);
+  }
+
   public static final class AccessibilityService.MagnificationController {
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
@@ -2681,6 +2689,7 @@ package android.accessibilityservice {
     method public java.lang.String loadDescription(android.content.pm.PackageManager);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
+    field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
     field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
     field public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8; // 0x8
     field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
@@ -2707,6 +2716,30 @@ package android.accessibilityservice {
     field public java.lang.String[] packageNames;
   }
 
+  public final class GestureDescription {
+    method public static android.accessibilityservice.GestureDescription createClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
+    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
+    method public int getStrokeCount();
+    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
+    field public static final int MAX_STROKE_COUNT = 10; // 0xa
+  }
+
+  public static class GestureDescription.Builder {
+    ctor public GestureDescription.Builder();
+    method public android.accessibilityservice.GestureDescription.Builder addStroke(android.accessibilityservice.GestureDescription.StrokeDescription);
+    method public android.accessibilityservice.GestureDescription build();
+  }
+
+  public static class GestureDescription.StrokeDescription {
+    ctor public GestureDescription.StrokeDescription(android.graphics.Path, long, long);
+    method public long getDuration();
+    method public android.graphics.Path getPath();
+    method public long getStartTime();
+  }
+
 }
 
 package android.accounts {
@@ -6133,6 +6166,7 @@ package android.app.job {
     method public int describeContents();
     method public int getBackoffPolicy();
     method public android.os.PersistableBundle getExtras();
+    method public long getFlexMillis();
     method public int getId();
     method public long getInitialBackoffMillis();
     method public long getIntervalMillis();
@@ -6150,6 +6184,8 @@ package android.app.job {
     field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
     field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
     field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
+    field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
+    field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
     field public static final int NETWORK_TYPE_ANY = 1; // 0x1
     field public static final int NETWORK_TYPE_NONE = 0; // 0x0
     field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6163,6 +6199,7 @@ package android.app.job {
     method public android.app.job.JobInfo.Builder setMinimumLatency(long);
     method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long);
+    method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -6223,6 +6260,8 @@ package android.app.usage {
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
     method public long getEndTimeStamp();
+    method public int getMetering();
+    method public int getRoaming();
     method public long getRxBytes();
     method public long getRxPackets();
     method public long getStartTimeStamp();
@@ -6230,6 +6269,12 @@ package android.app.usage {
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
+    field public static final int METERING_ALL = -1; // 0xffffffff
+    field public static final int METERING_DEFAULT = 1; // 0x1
+    field public static final int METERING_METERED = 2; // 0x2
+    field public static final int ROAMING_ALL = -1; // 0xffffffff
+    field public static final int ROAMING_DEFAULT = 1; // 0x1
+    field public static final int ROAMING_ROAMING = 2; // 0x2
     field public static final int STATE_ALL = -1; // 0xffffffff
     field public static final int STATE_DEFAULT = 1; // 0x1
     field public static final int STATE_FOREGROUND = 2; // 0x2
@@ -9443,8 +9488,6 @@ package android.content.pm {
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public abstract byte[] getEphemeralCookie();
-    method public abstract int getEphemeralCookieMaxSizeBytes();
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9478,7 +9521,6 @@ package android.content.pm {
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
-    method public abstract boolean isEphemeralApplication();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9496,7 +9538,6 @@ package android.content.pm {
     method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public abstract boolean setEphemeralCookie(byte[]);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void verifyPendingInstall(int, int);
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -22538,7 +22579,16 @@ package android.mtp {
 
   public class MtpEvent {
     ctor public MtpEvent();
+    method public int getDevicePropCode();
     method public int getEventCode();
+    method public int getObjectFormatCode();
+    method public int getObjectHandle();
+    method public int getObjectPropCode();
+    method public int getParameter1();
+    method public int getParameter2();
+    method public int getParameter3();
+    method public int getStorageId();
+    method public int getTransactionId();
   }
 
   public final class MtpObjectInfo {
@@ -27189,7 +27239,7 @@ package android.opengl {
     ctor public GLException(int, java.lang.String);
   }
 
-  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback {
+  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback2 {
     ctor public GLSurfaceView(android.content.Context);
     ctor public GLSurfaceView(android.content.Context, android.util.AttributeSet);
     method public int getDebugFlags();
@@ -27213,6 +27263,7 @@ package android.opengl {
     method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
     method public void surfaceCreated(android.view.SurfaceHolder);
     method public void surfaceDestroyed(android.view.SurfaceHolder);
+    method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
     field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
     field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
     field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -31327,6 +31378,7 @@ package android.provider {
     field public static final java.lang.String AUTO_TIME = "auto_time";
     field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
     field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+    field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
     field public static final java.lang.String DEBUG_APP = "debug_app";
@@ -33569,6 +33621,7 @@ package android.service.notification {
     field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
     field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
     field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+    field public static final int REASON_TOPIC_BANNED = 14; // 0xe
     field public static final int REASON_USER_STOPPED = 6; // 0x6
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
@@ -36793,8 +36846,6 @@ package android.test.mock {
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public byte[] getEphemeralCookie();
-    method public int getEphemeralCookieMaxSizeBytes();
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -36827,7 +36878,6 @@ package android.test.mock {
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
-    method public boolean isEphemeralApplication();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -36845,7 +36895,6 @@ package android.test.mock {
     method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public boolean setEphemeralCookie(byte[]);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void verifyPendingInstall(int, int);
   }
@@ -39076,7 +39125,9 @@ package android.util {
     method public static android.util.LocaleList getEmptyLocaleList();
     method public java.util.Locale getFirstMatch(java.lang.String[]);
     method public java.util.Locale getPrimary();
+    method public int indexOf(java.util.Locale);
     method public boolean isEmpty();
+    method public static void setDefault(android.util.LocaleList);
     method public int size();
     method public java.lang.String toLanguageTags();
     method public void writeToParcel(android.os.Parcel, int);
index 3ede80a..a63a073 100644 (file)
@@ -214,6 +214,7 @@ package android {
     field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
     field public static final java.lang.String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final java.lang.String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
+    field public static final java.lang.String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
     field public static final java.lang.String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
     field public static final java.lang.String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
     field public static final java.lang.String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
@@ -428,6 +429,7 @@ package android {
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
     field public static final int canControlMagnification = 16844040; // 0x1010508
+    field public static final int canPerformGestures = 16844046; // 0x101050e
     field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
     field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
     field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -2711,6 +2713,7 @@ package android.accessibilityservice {
 
   public abstract class AccessibilityService extends android.app.Service {
     ctor public AccessibilityService();
+    method public final boolean dispatchGesture(android.accessibilityservice.GestureDescription, android.accessibilityservice.AccessibilityService.GestureResultCallback, android.os.Handler);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
@@ -2750,6 +2753,12 @@ package android.accessibilityservice {
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
   }
 
+  public static abstract class AccessibilityService.GestureResultCallback {
+    ctor public AccessibilityService.GestureResultCallback();
+    method public void onCancelled(android.accessibilityservice.GestureDescription);
+    method public void onCompleted(android.accessibilityservice.GestureDescription);
+  }
+
   public static final class AccessibilityService.MagnificationController {
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
@@ -2782,6 +2791,7 @@ package android.accessibilityservice {
     method public java.lang.String loadDescription(android.content.pm.PackageManager);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
+    field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
     field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
     field public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8; // 0x8
     field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
@@ -2808,6 +2818,30 @@ package android.accessibilityservice {
     field public java.lang.String[] packageNames;
   }
 
+  public final class GestureDescription {
+    method public static android.accessibilityservice.GestureDescription createClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
+    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
+    method public int getStrokeCount();
+    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
+    field public static final int MAX_STROKE_COUNT = 10; // 0xa
+  }
+
+  public static class GestureDescription.Builder {
+    ctor public GestureDescription.Builder();
+    method public android.accessibilityservice.GestureDescription.Builder addStroke(android.accessibilityservice.GestureDescription.StrokeDescription);
+    method public android.accessibilityservice.GestureDescription build();
+  }
+
+  public static class GestureDescription.StrokeDescription {
+    ctor public GestureDescription.StrokeDescription(android.graphics.Path, long, long);
+    method public long getDuration();
+    method public android.graphics.Path getPath();
+    method public long getStartTime();
+  }
+
 }
 
 package android.accounts {
@@ -6347,6 +6381,7 @@ package android.app.job {
     method public int describeContents();
     method public int getBackoffPolicy();
     method public android.os.PersistableBundle getExtras();
+    method public long getFlexMillis();
     method public int getId();
     method public long getInitialBackoffMillis();
     method public long getIntervalMillis();
@@ -6364,6 +6399,8 @@ package android.app.job {
     field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
     field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
     field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
+    field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
+    field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
     field public static final int NETWORK_TYPE_ANY = 1; // 0x1
     field public static final int NETWORK_TYPE_NONE = 0; // 0x0
     field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6377,6 +6414,7 @@ package android.app.job {
     method public android.app.job.JobInfo.Builder setMinimumLatency(long);
     method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long);
+    method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -6437,6 +6475,8 @@ package android.app.usage {
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
     method public long getEndTimeStamp();
+    method public int getMetering();
+    method public int getRoaming();
     method public long getRxBytes();
     method public long getRxPackets();
     method public long getStartTimeStamp();
@@ -6444,6 +6484,12 @@ package android.app.usage {
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
+    field public static final int METERING_ALL = -1; // 0xffffffff
+    field public static final int METERING_DEFAULT = 1; // 0x1
+    field public static final int METERING_METERED = 2; // 0x2
+    field public static final int ROAMING_ALL = -1; // 0xffffffff
+    field public static final int ROAMING_DEFAULT = 1; // 0x1
+    field public static final int ROAMING_ROAMING = 2; // 0x2
     field public static final int STATE_ALL = -1; // 0xffffffff
     field public static final int STATE_DEFAULT = 1; // 0x1
     field public static final int STATE_FOREGROUND = 2; // 0x2
@@ -9744,8 +9790,6 @@ package android.content.pm {
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public abstract byte[] getEphemeralCookie();
-    method public abstract int getEphemeralCookieMaxSizeBytes();
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9781,7 +9825,6 @@ package android.content.pm {
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean hasSystemFeature(java.lang.String);
-    method public abstract boolean isEphemeralApplication();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9801,7 +9844,6 @@ package android.content.pm {
     method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public abstract boolean setEphemeralCookie(byte[]);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public abstract void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -24084,7 +24126,16 @@ package android.mtp {
 
   public class MtpEvent {
     ctor public MtpEvent();
+    method public int getDevicePropCode();
     method public int getEventCode();
+    method public int getObjectFormatCode();
+    method public int getObjectHandle();
+    method public int getObjectPropCode();
+    method public int getParameter1();
+    method public int getParameter2();
+    method public int getParameter3();
+    method public int getStorageId();
+    method public int getTransactionId();
   }
 
   public final class MtpObjectInfo {
@@ -29179,7 +29230,7 @@ package android.opengl {
     ctor public GLException(int, java.lang.String);
   }
 
-  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback {
+  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback2 {
     ctor public GLSurfaceView(android.content.Context);
     ctor public GLSurfaceView(android.content.Context, android.util.AttributeSet);
     method public int getDebugFlags();
@@ -29203,6 +29254,7 @@ package android.opengl {
     method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
     method public void surfaceCreated(android.view.SurfaceHolder);
     method public void surfaceDestroyed(android.view.SurfaceHolder);
+    method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
     field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
     field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
     field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -33462,6 +33514,7 @@ package android.provider {
     field public static final java.lang.String AUTO_TIME = "auto_time";
     field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
     field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+    field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
     field public static final java.lang.String DEBUG_APP = "debug_app";
@@ -35705,6 +35758,7 @@ package android.service.notification {
     field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
     field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
     field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+    field public static final int REASON_TOPIC_BANNED = 14; // 0xe
     field public static final int REASON_USER_STOPPED = 6; // 0x6
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
@@ -37868,6 +37922,7 @@ package android.telephony {
     field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
     field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
     field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
+    field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
     field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
     field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
@@ -39135,8 +39190,6 @@ package android.test.mock {
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public byte[] getEphemeralCookie();
-    method public int getEphemeralCookieMaxSizeBytes();
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -39171,7 +39224,6 @@ package android.test.mock {
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean hasSystemFeature(java.lang.String);
-    method public boolean isEphemeralApplication();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -39191,7 +39243,6 @@ package android.test.mock {
     method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public boolean setEphemeralCookie(byte[]);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -41424,7 +41475,9 @@ package android.util {
     method public static android.util.LocaleList getEmptyLocaleList();
     method public java.util.Locale getFirstMatch(java.lang.String[]);
     method public java.util.Locale getPrimary();
+    method public int indexOf(java.util.Locale);
     method public boolean isEmpty();
+    method public static void setDefault(android.util.LocaleList);
     method public int size();
     method public java.lang.String toLanguageTags();
     method public void writeToParcel(android.os.Parcel, int);
index 306cf73..f15ab58 100644 (file)
@@ -334,6 +334,7 @@ package android {
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
     field public static final int canControlMagnification = 16844040; // 0x1010508
+    field public static final int canPerformGestures = 16844046; // 0x101050e
     field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
     field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
     field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -2610,6 +2611,7 @@ package android.accessibilityservice {
 
   public abstract class AccessibilityService extends android.app.Service {
     ctor public AccessibilityService();
+    method public final boolean dispatchGesture(android.accessibilityservice.GestureDescription, android.accessibilityservice.AccessibilityService.GestureResultCallback, android.os.Handler);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
@@ -2649,6 +2651,12 @@ package android.accessibilityservice {
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
   }
 
+  public static abstract class AccessibilityService.GestureResultCallback {
+    ctor public AccessibilityService.GestureResultCallback();
+    method public void onCancelled(android.accessibilityservice.GestureDescription);
+    method public void onCompleted(android.accessibilityservice.GestureDescription);
+  }
+
   public static final class AccessibilityService.MagnificationController {
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
@@ -2681,6 +2689,7 @@ package android.accessibilityservice {
     method public java.lang.String loadDescription(android.content.pm.PackageManager);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
+    field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
     field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
     field public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8; // 0x8
     field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
@@ -2707,6 +2716,30 @@ package android.accessibilityservice {
     field public java.lang.String[] packageNames;
   }
 
+  public final class GestureDescription {
+    method public static android.accessibilityservice.GestureDescription createClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
+    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
+    method public int getStrokeCount();
+    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
+    field public static final int MAX_STROKE_COUNT = 10; // 0xa
+  }
+
+  public static class GestureDescription.Builder {
+    ctor public GestureDescription.Builder();
+    method public android.accessibilityservice.GestureDescription.Builder addStroke(android.accessibilityservice.GestureDescription.StrokeDescription);
+    method public android.accessibilityservice.GestureDescription build();
+  }
+
+  public static class GestureDescription.StrokeDescription {
+    ctor public GestureDescription.StrokeDescription(android.graphics.Path, long, long);
+    method public long getDuration();
+    method public android.graphics.Path getPath();
+    method public long getStartTime();
+  }
+
 }
 
 package android.accounts {
@@ -6135,6 +6168,7 @@ package android.app.job {
     method public int describeContents();
     method public int getBackoffPolicy();
     method public android.os.PersistableBundle getExtras();
+    method public long getFlexMillis();
     method public int getId();
     method public long getInitialBackoffMillis();
     method public long getIntervalMillis();
@@ -6152,6 +6186,8 @@ package android.app.job {
     field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
     field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
     field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
+    field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
+    field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
     field public static final int NETWORK_TYPE_ANY = 1; // 0x1
     field public static final int NETWORK_TYPE_NONE = 0; // 0x0
     field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6165,6 +6201,7 @@ package android.app.job {
     method public android.app.job.JobInfo.Builder setMinimumLatency(long);
     method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long);
+    method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -6225,6 +6262,8 @@ package android.app.usage {
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
     method public long getEndTimeStamp();
+    method public int getMetering();
+    method public int getRoaming();
     method public long getRxBytes();
     method public long getRxPackets();
     method public long getStartTimeStamp();
@@ -6232,6 +6271,12 @@ package android.app.usage {
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
+    field public static final int METERING_ALL = -1; // 0xffffffff
+    field public static final int METERING_DEFAULT = 1; // 0x1
+    field public static final int METERING_METERED = 2; // 0x2
+    field public static final int ROAMING_ALL = -1; // 0xffffffff
+    field public static final int ROAMING_DEFAULT = 1; // 0x1
+    field public static final int ROAMING_ROAMING = 2; // 0x2
     field public static final int STATE_ALL = -1; // 0xffffffff
     field public static final int STATE_DEFAULT = 1; // 0x1
     field public static final int STATE_FOREGROUND = 2; // 0x2
@@ -9451,8 +9496,6 @@ package android.content.pm {
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public abstract byte[] getEphemeralCookie();
-    method public abstract int getEphemeralCookieMaxSizeBytes();
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9486,7 +9529,6 @@ package android.content.pm {
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
-    method public abstract boolean isEphemeralApplication();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9504,7 +9546,6 @@ package android.content.pm {
     method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public abstract boolean setEphemeralCookie(byte[]);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void verifyPendingInstall(int, int);
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -22546,7 +22587,16 @@ package android.mtp {
 
   public class MtpEvent {
     ctor public MtpEvent();
+    method public int getDevicePropCode();
     method public int getEventCode();
+    method public int getObjectFormatCode();
+    method public int getObjectHandle();
+    method public int getObjectPropCode();
+    method public int getParameter1();
+    method public int getParameter2();
+    method public int getParameter3();
+    method public int getStorageId();
+    method public int getTransactionId();
   }
 
   public final class MtpObjectInfo {
@@ -27197,7 +27247,7 @@ package android.opengl {
     ctor public GLException(int, java.lang.String);
   }
 
-  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback {
+  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback2 {
     ctor public GLSurfaceView(android.content.Context);
     ctor public GLSurfaceView(android.content.Context, android.util.AttributeSet);
     method public int getDebugFlags();
@@ -27221,6 +27271,7 @@ package android.opengl {
     method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
     method public void surfaceCreated(android.view.SurfaceHolder);
     method public void surfaceDestroyed(android.view.SurfaceHolder);
+    method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
     field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
     field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
     field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -31339,6 +31390,7 @@ package android.provider {
     field public static final java.lang.String AUTO_TIME = "auto_time";
     field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
     field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+    field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
     field public static final java.lang.String DEBUG_APP = "debug_app";
@@ -33583,6 +33635,7 @@ package android.service.notification {
     field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
     field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
     field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+    field public static final int REASON_TOPIC_BANNED = 14; // 0xe
     field public static final int REASON_USER_STOPPED = 6; // 0x6
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
@@ -36809,8 +36862,6 @@ package android.test.mock {
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public byte[] getEphemeralCookie();
-    method public int getEphemeralCookieMaxSizeBytes();
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -36843,7 +36894,6 @@ package android.test.mock {
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
-    method public boolean isEphemeralApplication();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -36861,7 +36911,6 @@ package android.test.mock {
     method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public boolean setEphemeralCookie(byte[]);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void verifyPendingInstall(int, int);
   }
@@ -39092,7 +39141,9 @@ package android.util {
     method public static android.util.LocaleList getEmptyLocaleList();
     method public java.util.Locale getFirstMatch(java.lang.String[]);
     method public java.util.Locale getPrimary();
+    method public int indexOf(java.util.Locale);
     method public boolean isEmpty();
+    method public static void setDefault(android.util.LocaleList);
     method public int size();
     method public java.lang.String toLanguageTags();
     method public void writeToParcel(android.os.Parcel, int);
index 468c145..3293c26 100644 (file)
 
 package android.accessibilityservice;
 
+import android.accessibilityservice.GestureDescription.MotionEventGenerator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ParceledListSlice;
 import android.graphics.Region;
 import android.os.Handler;
 import android.os.IBinder;
@@ -29,8 +31,11 @@ import android.os.Message;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 import android.view.accessibility.AccessibilityEvent;
@@ -41,10 +46,7 @@ import android.view.accessibility.AccessibilityWindowInfo;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
 
 /**
  * An accessibility service runs in the background and receives callbacks by the system
@@ -376,6 +378,7 @@ public abstract class AccessibilityService extends Service {
         public boolean onKeyEvent(KeyEvent event);
         public void onMagnificationChanged(@NonNull Region region,
                 float scale, float centerX, float centerY);
+        public void onPerformGestureResult(int sequence, boolean completedSuccessfully);
     }
 
     private int mConnectionId;
@@ -388,6 +391,12 @@ public abstract class AccessibilityService extends Service {
 
     private MagnificationController mMagnificationController;
 
+    private int mGestureStatusCallbackSequence;
+
+    private SparseArray<GestureResultCallbackInfo> mGestureStatusCallbackInfos;
+
+    private final Object mLock = new Object();
+
     /**
      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
      *
@@ -551,6 +560,88 @@ public abstract class AccessibilityService extends Service {
         return mMagnificationController;
     }
 
+    /**
+     * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
+     * the user, this service, or another service, will be cancelled.
+     * <p>
+     * <strong>Note:</strong> In order to dispatch gestures, your service
+     * must declare the capability by setting the
+     * {@link android.R.styleable#AccessibilityService_canPerformGestures}
+     * property in its meta-data. For more information, see
+     * {@link #SERVICE_META_DATA}.
+     *
+     * @param gesture The gesture to dispatch
+     * @param callback The object to call back when the status of the gesture is known. If
+     * {@code null}, no status is reported.
+     * @param handler The handler on which to call back the {@code callback} object. If
+     * {@code null}, the object is called back on the service's main thread.
+     *
+     * @return {@code true} if the gesture is dispatched, {@code false} if not.
+     */
+    public final boolean dispatchGesture(@NonNull GestureDescription gesture,
+            @Nullable GestureResultCallback callback,
+            @Nullable Handler handler) {
+        final IAccessibilityServiceConnection connection =
+                AccessibilityInteractionClient.getInstance().getConnection(
+                        mConnectionId);
+        if (connection == null) {
+            return false;
+        }
+        List<MotionEvent> events = MotionEventGenerator.getMotionEventsFromGestureDescription(
+                gesture, 100);
+        try {
+            synchronized (mLock) {
+                connection.sendMotionEvents(++mGestureStatusCallbackSequence,
+                        new ParceledListSlice<>(events));
+                if (callback != null) {
+                    if (mGestureStatusCallbackInfos == null) {
+                        mGestureStatusCallbackInfos = new SparseArray<>();
+                    }
+                    GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture,
+                            callback, handler);
+                    mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
+                }
+            }
+        } catch (RemoteException re) {
+            throw new RuntimeException(re);
+        }
+        return true;
+    }
+
+    void onPerformGestureResult(int sequence, final boolean completedSuccessfully) {
+        if (mGestureStatusCallbackInfos == null) {
+            return;
+        }
+        GestureResultCallbackInfo callbackInfo;
+        synchronized (mLock) {
+            callbackInfo = mGestureStatusCallbackInfos.get(sequence);
+        }
+        final GestureResultCallbackInfo finalCallbackInfo = callbackInfo;
+        if ((callbackInfo != null) && (callbackInfo.gestureDescription != null)
+                && (callbackInfo.callback != null)) {
+            if (callbackInfo.handler != null) {
+                callbackInfo.handler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (completedSuccessfully) {
+                            finalCallbackInfo.callback
+                                    .onCompleted(finalCallbackInfo.gestureDescription);
+                        } else {
+                            finalCallbackInfo.callback
+                                    .onCancelled(finalCallbackInfo.gestureDescription);
+                        }
+                    }
+                });
+                return;
+            }
+            if (completedSuccessfully) {
+                callbackInfo.callback.onCompleted(callbackInfo.gestureDescription);
+            } else {
+                callbackInfo.callback.onCancelled(callbackInfo.gestureDescription);
+            }
+        }
+    }
+
     private void onMagnificationChanged(@NonNull Region region, float scale,
             float centerX, float centerY) {
         if (mMagnificationController != null) {
@@ -1082,6 +1173,11 @@ public abstract class AccessibilityService extends Service {
                     float scale, float centerX, float centerY) {
                 AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
             }
+
+            @Override
+            public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
+                AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
+            }
         });
     }
 
@@ -1100,6 +1196,7 @@ public abstract class AccessibilityService extends Service {
         private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
         private static final int DO_ON_KEY_EVENT = 6;
         private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
+        private static final int DO_GESTURE_COMPLETE = 8;
 
         private final HandlerCaller mCaller;
 
@@ -1158,6 +1255,12 @@ public abstract class AccessibilityService extends Service {
             mCaller.sendMessage(message);
         }
 
+        public void onPerformGestureResult(int sequence, boolean successfully) {
+            Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
+                    successfully ? 1 : 0);
+            mCaller.sendMessage(message);
+        }
+
         @Override
         public void executeMessage(Message message) {
             switch (message.what) {
@@ -1242,9 +1345,47 @@ public abstract class AccessibilityService extends Service {
                     mCallback.onMagnificationChanged(region, scale, centerX, centerY);
                 } return;
 
+                case DO_GESTURE_COMPLETE: {
+                    final boolean successfully = message.arg2 == 1;
+                    mCallback.onPerformGestureResult(message.arg1, successfully);
+                } return;
+
                 default :
                     Log.w(LOG_TAG, "Unknown message type " + message.what);
             }
         }
     }
+
+    /**
+     * Class used to report status of dispatched gestures
+     */
+    public static abstract class GestureResultCallback {
+        /** Called when the gesture has completed successfully
+         *
+         * @param gestureDescription The description of the gesture that completed.
+         */
+        public void onCompleted(GestureDescription gestureDescription) {
+        }
+
+        /** Called when the gesture was cancelled
+         *
+         * @param gestureDescription The description of the gesture that was cancelled.
+         */
+        public void onCancelled(GestureDescription gestureDescription) {
+        }
+    }
+
+    /* Object to keep track of gesture result callbacks */
+    private static class GestureResultCallbackInfo {
+        GestureDescription gestureDescription;
+        GestureResultCallback callback;
+        Handler handler;
+
+        GestureResultCallbackInfo(GestureDescription gestureDescription,
+                GestureResultCallback callback, Handler handler) {
+            this.gestureDescription = gestureDescription;
+            this.callback = callback;
+            this.handler = handler;
+        }
+    }
 }
index 2c98fef..079bdfc 100644 (file)
@@ -110,6 +110,12 @@ public class AccessibilityServiceInfo implements Parcelable {
      */
     public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 0x00000010;
 
+    /**
+     * Capability: This accessibility service can perform gestures.
+     * @see android.R.styleable#AccessibilityService_canPerformGestures
+     */
+    public static final int CAPABILITY_CAN_PERFORM_GESTURES = 0x00000020;
+
     private static final SparseArray<CapabilityInfo> sAvailableCapabilityInfos =
             new SparseArray<CapabilityInfo>();
     static {
@@ -133,6 +139,10 @@ public class AccessibilityServiceInfo implements Parcelable {
                 new CapabilityInfo(CAPABILITY_CAN_CONTROL_MAGNIFICATION,
                         R.string.capability_title_canControlMagnification,
                         R.string.capability_desc_canControlMagnification));
+        sAvailableCapabilityInfos.put(CAPABILITY_CAN_PERFORM_GESTURES,
+                new CapabilityInfo(CAPABILITY_CAN_PERFORM_GESTURES,
+                        R.string.capability_title_canPerformGestures,
+                        R.string.capability_desc_canPerformGestures));
     }
 
     /**
@@ -276,12 +286,7 @@ public class AccessibilityServiceInfo implements Parcelable {
     /**
      * This flag requests from the system to filter key events. If this flag
      * is set the accessibility service will receive the key events before
-     * applications allowing it implement global shortcuts. Setting this flag
-     * does not guarantee that this service will filter key events since only
-     * one service can do so at any given time. This avoids user confusion due
-     * to behavior change in case different key filtering services are enabled.
-     * If there is already another key filtering service enabled, this one will
-     * not receive key events.
+     * applications allowing it implement global shortcuts.
      * <p>
      * Services that want to set this flag have to declare this capability
      * in their meta-data by setting the attribute {@link android.R.attr
@@ -516,6 +521,10 @@ public class AccessibilityServiceInfo implements Parcelable {
                     .AccessibilityService_canControlMagnification, false)) {
                 mCapabilities |= CAPABILITY_CAN_CONTROL_MAGNIFICATION;
             }
+            if (asAttributes.getBoolean(com.android.internal.R.styleable
+                    .AccessibilityService_canPerformGestures, false)) {
+                mCapabilities |= CAPABILITY_CAN_PERFORM_GESTURES;
+            }
             TypedValue peekedValue = asAttributes.peekValue(
                     com.android.internal.R.styleable.AccessibilityService_description);
             if (peekedValue != null) {
@@ -616,6 +625,8 @@ public class AccessibilityServiceInfo implements Parcelable {
      * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
      * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
      * @see #CAPABILITY_FILTER_KEY_EVENTS
+     * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION
+     * @see #CAPABILITY_CAN_PERFORM_GESTURES
      */
     public int getCapabilities() {
         return mCapabilities;
@@ -631,6 +642,8 @@ public class AccessibilityServiceInfo implements Parcelable {
      * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
      * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
      * @see #CAPABILITY_FILTER_KEY_EVENTS
+     * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION
+     * @see #CAPABILITY_CAN_PERFORM_GESTURES
      *
      * @hide
      */
@@ -933,6 +946,8 @@ public class AccessibilityServiceInfo implements Parcelable {
                 return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
             case CAPABILITY_CAN_CONTROL_MAGNIFICATION:
                 return "CAPABILITY_CAN_CONTROL_MAGNIFICATION";
+            case CAPABILITY_CAN_PERFORM_GESTURES:
+                return "CAPABILITY_CAN_PERFORM_GESTURES";
             default:
                 return "UNKNOWN";
         }
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
new file mode 100644 (file)
index 0000000..14aabcf
--- /dev/null
@@ -0,0 +1,612 @@
+/*
+ * 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.accessibilityservice;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.RectF;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+import android.view.ViewConfiguration;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Accessibility services with the
+ * {@link android.R.styleable#AccessibilityService_canPerformGestures} property can dispatch
+ * gestures. This class describes those gestures. Gestures are made up of one or more strokes.
+ * Gestures are immutable; use the {@code create} methods to get common gesture, or a
+ * {@code Builder} to create a new one.
+ * <p>
+ * Spatial dimensions throughout are in screen pixels. Time is measured in milliseconds.
+ */
+public final class GestureDescription {
+    /** Gestures may contain no more than this many strokes */
+    public static final int MAX_STROKE_COUNT = 10;
+
+    /**
+     * Upper bound on total gesture duration. Nearly all gestures will be much shorter.
+     */
+    public static final long MAX_GESTURE_DURATION_MS = 60 * 1000;
+
+    private final List<StrokeDescription> mStrokes = new ArrayList<>();
+    private final float[] mTempPos = new float[2];
+
+    /**
+     * Create a description of a click gesture
+     *
+     * @param x The x coordinate to click. Must not be negative.
+     * @param y The y coordinate to click. Must not be negative.
+     *
+     * @return A description of a click at (x, y)
+     */
+    public static GestureDescription createClick(@IntRange(from = 0) int x,
+            @IntRange(from = 0) int y) {
+        Path clickPath = new Path();
+        clickPath.moveTo(x, y);
+        clickPath.lineTo(x + 1, y);
+        return new GestureDescription(
+                new StrokeDescription(clickPath, 0, ViewConfiguration.getTapTimeout()));
+    }
+
+    /**
+     * Create a description of a long click gesture
+     *
+     * @param x The x coordinate to click. Must not be negative.
+     * @param y The y coordinate to click. Must not be negative.
+     *
+     * @return A description of a click at (x, y)
+     */
+    public static GestureDescription createLongClick(@IntRange(from = 0) int x,
+            @IntRange(from = 0) int y) {
+        Path clickPath = new Path();
+        clickPath.moveTo(x, y);
+        clickPath.lineTo(x + 1, y);
+        int longPressTime = ViewConfiguration.getLongPressTimeout();
+        return new GestureDescription(
+                new StrokeDescription(clickPath, 0, longPressTime + (longPressTime / 2)));
+    }
+
+    /**
+     * Create a description of a swipe gesture
+     *
+     * @param startX The x coordinate of the starting point. Must not be negative.
+     * @param startY The y coordinate of the starting point. Must not be negative.
+     * @param endX The x coordinate of the ending point. Must not be negative.
+     * @param endY The y coordinate of the ending point. Must not be negative.
+     * @param duration The time, in milliseconds, to complete the gesture. Must not be negative.
+     *
+     * @return A description of a swipe from ({@code startX}, {@code startY}) to
+     * ({@code endX}, {@code endY}) that takes {@code duration} milliseconds. Returns {@code null}
+     * if the path specified for the swipe is invalid.
+     */
+    public static GestureDescription createSwipe(@IntRange(from = 0) int startX,
+            @IntRange(from = 0) int startY,
+            @IntRange(from = 0) int endX,
+            @IntRange(from = 0) int endY,
+            @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
+        Path swipePath = new Path();
+        swipePath.moveTo(startX, startY);
+        swipePath.lineTo(endX, endY);
+        return new GestureDescription(new StrokeDescription(swipePath, 0, duration));
+    }
+
+    /**
+     * Create a description for a pinch (or zoom) gesture.
+     *
+     * @param centerX The x coordinate of the center of the pinch. Must not be negative.
+     * @param centerY The y coordinate of the center of the pinch. Must not be negative.
+     * @param startSpacing The spacing of the touch points at the beginning of the gesture. Must not
+     * be negative.
+     * @param endSpacing The spacing of the touch points at the end of the gesture. Must not be
+     * negative.
+     * @param orientation The angle, in degrees, of the gesture. 0 represents a horizontal pinch
+     * @param duration The time, in milliseconds, to complete the gesture. Must not be negative.
+     *
+     * @return A description of a pinch centered at ({code centerX}, {@code centerY}) that starts
+     * with the touch points spaced by {@code startSpacing} and ends with them spaced by
+     * {@code endSpacing} that lasts {@code duration} ms. Returns {@code null} if either path
+     * specified for the pinch is invalid.
+     */
+    public static GestureDescription createPinch(@IntRange(from = 0) int centerX,
+            @IntRange(from = 0) int centerY,
+            @IntRange(from = 0) int startSpacing,
+            @IntRange(from = 0) int endSpacing,
+            float orientation,
+            @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
+        if ((startSpacing < 0) || (endSpacing < 0)) {
+            throw new IllegalArgumentException("Pinch spacing cannot be negative");
+        }
+        float[] startPoint1 = new float[2];
+        float[] endPoint1 = new float[2];
+        float[] startPoint2 = new float[2];
+        float[] endPoint2 = new float[2];
+
+        /* Build points for a horizontal gesture centered at the origin */
+        startPoint1[0] = startSpacing / 2;
+        startPoint1[1] = 0;
+        endPoint1[0] = endSpacing / 2;
+        endPoint1[1] = 0;
+        startPoint2[0] = -startSpacing / 2;
+        startPoint2[1] = 0;
+        endPoint2[0] = -endSpacing / 2;
+        endPoint2[1] = 0;
+
+        /* Rotate and translate the points */
+        Matrix matrix = new Matrix();
+        matrix.setRotate(orientation);
+        matrix.postTranslate(centerX, centerY);
+        matrix.mapPoints(startPoint1);
+        matrix.mapPoints(endPoint1);
+        matrix.mapPoints(startPoint2);
+        matrix.mapPoints(endPoint2);
+
+        Path path1 = new Path();
+        path1.moveTo(startPoint1[0], startPoint1[1]);
+        path1.lineTo(endPoint1[0], endPoint1[1]);
+        Path path2 = new Path();
+        path2.moveTo(startPoint2[0], startPoint2[1]);
+        path2.lineTo(endPoint2[0], endPoint2[1]);
+
+        return new GestureDescription(Arrays.asList(
+                new StrokeDescription(path1, 0, duration),
+                new StrokeDescription(path2, 0, duration)));
+    }
+
+    private GestureDescription() {}
+
+    private GestureDescription(List<StrokeDescription> strokes) {
+        mStrokes.addAll(strokes);
+    }
+
+    private GestureDescription(StrokeDescription stroke) {
+        mStrokes.add(stroke);
+    }
+
+    /**
+     * Get the number of stroke in the gesture.
+     *
+     * @return the number of strokes in this gesture
+     */
+    public int getStrokeCount() {
+        return mStrokes.size();
+    }
+
+    /**
+     * Read a stroke from the gesture
+     *
+     * @param index the index of the stroke
+     *
+     * @return A description of the stroke.
+     */
+    public StrokeDescription getStroke(@IntRange(from = 0) int index) {
+        return mStrokes.get(index);
+    }
+
+    /**
+     * Return the smallest key point (where a path starts or ends) that is at least a specified
+     * offset
+     * @param offset the minimum start time
+     * @return The next key time that is at least the offset or -1 if one can't be found
+     */
+    private long getNextKeyPointAtLeast(long offset) {
+        long nextKeyPoint = Long.MAX_VALUE;
+        for (int i = 0; i < mStrokes.size(); i++) {
+            long thisStartTime = mStrokes.get(i).mStartTime;
+            if ((thisStartTime < nextKeyPoint) && (thisStartTime >= offset)) {
+                nextKeyPoint = thisStartTime;
+            }
+            long thisEndTime = mStrokes.get(i).mEndTime;
+            if ((thisEndTime < nextKeyPoint) && (thisEndTime >= offset)) {
+                nextKeyPoint = thisEndTime;
+            }
+        }
+        return (nextKeyPoint == Long.MAX_VALUE) ? -1L : nextKeyPoint;
+    }
+
+    /**
+     * Get the points that correspond to a particular moment in time.
+     * @param time The time of interest
+     * @param touchPoints An array to hold the current touch points. Must be preallocated to at
+     * least the number of paths in the gesture to prevent going out of bounds
+     * @return The number of points found, and thus the number of elements set in each array
+     */
+    private int getPointsForTime(long time, TouchPoint[] touchPoints) {
+        int numPointsFound = 0;
+        for (int i = 0; i < mStrokes.size(); i++) {
+            StrokeDescription strokeDescription = mStrokes.get(i);
+            if (strokeDescription.hasPointForTime(time)) {
+                touchPoints[numPointsFound].mPathIndex = i;
+                touchPoints[numPointsFound].mIsStartOfPath = (time == strokeDescription.mStartTime);
+                touchPoints[numPointsFound].mIsEndOfPath = (time == strokeDescription.mEndTime);
+                strokeDescription.getPosForTime(time, mTempPos);
+                touchPoints[numPointsFound].mX = Math.round(mTempPos[0]);
+                touchPoints[numPointsFound].mY = Math.round(mTempPos[1]);
+                numPointsFound++;
+            }
+        }
+        return numPointsFound;
+    }
+
+    // Total duration assumes that the gesture starts at 0; waiting around to start a gesture
+    // counts against total duration
+    private static long getTotalDuration(List<StrokeDescription> paths) {
+        long latestEnd = Long.MIN_VALUE;
+        for (int i = 0; i < paths.size(); i++) {
+            StrokeDescription path = paths.get(i);
+            latestEnd = Math.max(latestEnd, path.mEndTime);
+        }
+        return Math.max(latestEnd, 0);
+    }
+
+    /**
+     * Builder for a {@code GestureDescription}
+     */
+    public static class Builder {
+
+        private final List<StrokeDescription> mStrokes = new ArrayList<>();
+
+        /**
+         * Add a stroke to the gesture description. Up to {@code MAX_STROKE_COUNT} paths may be
+         * added to a gesture, and the total gesture duration (earliest path start time to latest path
+         * end time) may not exceed {@code MAX_GESTURE_DURATION_MS}.
+         *
+         * @param strokeDescription the stroke to add.
+         *
+         * @return this
+         */
+        public Builder addStroke(@NonNull StrokeDescription strokeDescription) {
+            if (mStrokes.size() >= MAX_STROKE_COUNT) {
+                throw new RuntimeException("Attempting to add too many strokes to a gesture");
+            }
+
+            mStrokes.add(strokeDescription);
+
+            if (getTotalDuration(mStrokes) > MAX_GESTURE_DURATION_MS) {
+                mStrokes.remove(strokeDescription);
+                throw new RuntimeException("Gesture would exceed maximum duration with new stroke");
+            }
+            return this;
+        }
+
+        public GestureDescription build() {
+            if (mStrokes.size() == 0) {
+                throw new RuntimeException("Gestures must have at least one stroke");
+            }
+            return new GestureDescription(mStrokes);
+        }
+    }
+
+    /**
+     * Immutable description of stroke that can be part of a gesture.
+     */
+    public static class StrokeDescription {
+        Path mPath;
+        long mStartTime;
+        long mEndTime;
+        private float mTimeToLengthConversion;
+        private PathMeasure mPathMeasure;
+
+        /**
+         * @param path The path to follow. Must have exactly one contour, and that contour must
+         * have nonzero length. The bounds of the path must not be negative.
+         * @param startTime The time, in milliseconds, from the time the gesture starts to the
+         * time the stroke should start. Must not be negative.
+         * @param duration The duration, in milliseconds, the stroke takes to traverse the path.
+         * Must not be negative.
+         */
+        public StrokeDescription(@NonNull Path path,
+                @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long startTime,
+                @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
+            if (duration <= 0) {
+                throw new IllegalArgumentException("Duration must be positive");
+            }
+            if (startTime < 0) {
+                throw new IllegalArgumentException("Start time must not be negative");
+            }
+            RectF bounds = new RectF();
+            path.computeBounds(bounds, false /* unused */);
+            if ((bounds.bottom < 0) || (bounds.top < 0) || (bounds.right < 0)
+                    || (bounds.left < 0)) {
+                throw new IllegalArgumentException("Path bounds must not be negative");
+            }
+            mPath = new Path(path);
+            mPathMeasure = new PathMeasure(path, false);
+            if (mPathMeasure.getLength() == 0) {
+                throw new IllegalArgumentException("Path has zero length");
+            }
+            if (mPathMeasure.nextContour()) {
+                throw new IllegalArgumentException("Path has more than one contour");
+            }
+            /*
+             * Calling nextContour has moved mPathMeasure off the first contour, which is the only
+             * one we care about. Set the path again to go back to the first contour.
+             */
+            mPathMeasure.setPath(path, false);
+            mStartTime = startTime;
+            mEndTime = startTime + duration;
+            if (duration > 0) {
+                mTimeToLengthConversion = getLength() / duration;
+            }
+        }
+
+        /**
+         * Retrieve a copy of the path for this stroke
+         *
+         * @return A copy of the path
+         */
+        public Path getPath() {
+            return new Path(mPath);
+        }
+
+        /**
+         * Get the stroke's start time
+         *
+         * @return the start time for this stroke.
+         */
+        public long getStartTime() {
+            return mStartTime;
+        }
+
+        /**
+         * Get the stroke's duration
+         *
+         * @return the duration for this stroke
+         */
+        public long getDuration() {
+            return mEndTime - mStartTime;
+        }
+
+        float getLength() {
+            return mPathMeasure.getLength();
+        }
+
+        /* Assumes hasPointForTime returns true */
+        boolean getPosForTime(long time, float[] pos) {
+            if (time == mEndTime) {
+                // Close to the end time, roundoff can be a problem
+                return mPathMeasure.getPosTan(getLength(), pos, null);
+            }
+            float length = mTimeToLengthConversion * ((float) (time - mStartTime));
+            return mPathMeasure.getPosTan(length, pos, null);
+        }
+
+        boolean hasPointForTime(long time) {
+            return ((time >= mStartTime) && (time <= mEndTime));
+        }
+    }
+
+    private static class TouchPoint {
+        int mPathIndex;
+        boolean mIsStartOfPath;
+        boolean mIsEndOfPath;
+        float mX;
+        float mY;
+
+        void copyFrom(TouchPoint other) {
+            mPathIndex = other.mPathIndex;
+            mIsStartOfPath = other.mIsStartOfPath;
+            mIsEndOfPath = other.mIsEndOfPath;
+            mX = other.mX;
+            mY = other.mY;
+        }
+    }
+
+    /**
+     * Class to convert a GestureDescription to a series of MotionEvents.
+     */
+    static class MotionEventGenerator {
+        /**
+         * Constants used to initialize all MotionEvents
+         */
+        private static final int EVENT_META_STATE = 0;
+        private static final int EVENT_BUTTON_STATE = 0;
+        private static final int EVENT_DEVICE_ID = 0;
+        private static final int EVENT_EDGE_FLAGS = 0;
+        private static final int EVENT_SOURCE = InputDevice.SOURCE_TOUCHSCREEN;
+        private static final int EVENT_FLAGS = 0;
+        private static final float EVENT_X_PRECISION = 1;
+        private static final float EVENT_Y_PRECISION = 1;
+
+        /* Lazily-created scratch memory for processing touches */
+        private static TouchPoint[] sCurrentTouchPoints;
+        private static TouchPoint[] sLastTouchPoints;
+        private static PointerCoords[] sPointerCoords;
+        private static PointerProperties[] sPointerProps;
+
+        static List<MotionEvent> getMotionEventsFromGestureDescription(
+                GestureDescription description, int sampleTimeMs) {
+            final List<MotionEvent> motionEvents = new ArrayList<>();
+
+            // Point data at each time we generate an event for
+            final TouchPoint[] currentTouchPoints =
+                    getCurrentTouchPoints(description.getStrokeCount());
+            // Point data sent in last touch event
+            int lastTouchPointSize = 0;
+            final TouchPoint[] lastTouchPoints =
+                    getLastTouchPoints(description.getStrokeCount());
+
+            /* Loop through each time slice where there are touch points */
+            long timeSinceGestureStart = 0;
+            long nextKeyPointTime = description.getNextKeyPointAtLeast(timeSinceGestureStart);
+            while (nextKeyPointTime >= 0) {
+                timeSinceGestureStart = (lastTouchPointSize == 0) ? nextKeyPointTime
+                        : Math.min(nextKeyPointTime, timeSinceGestureStart + sampleTimeMs);
+                int currentTouchPointSize = description.getPointsForTime(timeSinceGestureStart,
+                        currentTouchPoints);
+
+                appendMoveEventIfNeeded(motionEvents, lastTouchPoints, lastTouchPointSize,
+                        currentTouchPoints, currentTouchPointSize, timeSinceGestureStart);
+                lastTouchPointSize = appendUpEvents(motionEvents, lastTouchPoints,
+                        lastTouchPointSize, currentTouchPoints, currentTouchPointSize,
+                        timeSinceGestureStart);
+                lastTouchPointSize = appendDownEvents(motionEvents, lastTouchPoints,
+                        lastTouchPointSize, currentTouchPoints, currentTouchPointSize,
+                        timeSinceGestureStart);
+
+                /* Move to next time slice */
+                nextKeyPointTime = description.getNextKeyPointAtLeast(timeSinceGestureStart + 1);
+            }
+            return motionEvents;
+        }
+
+        private static TouchPoint[] getCurrentTouchPoints(int requiredCapacity) {
+            if ((sCurrentTouchPoints == null) || (sCurrentTouchPoints.length < requiredCapacity)) {
+                sCurrentTouchPoints = new TouchPoint[requiredCapacity];
+                for (int i = 0; i < requiredCapacity; i++) {
+                    sCurrentTouchPoints[i] = new TouchPoint();
+                }
+            }
+            return sCurrentTouchPoints;
+        }
+
+        private static TouchPoint[] getLastTouchPoints(int requiredCapacity) {
+            if ((sLastTouchPoints == null) || (sLastTouchPoints.length < requiredCapacity)) {
+                sLastTouchPoints = new TouchPoint[requiredCapacity];
+                for (int i = 0; i < requiredCapacity; i++) {
+                    sLastTouchPoints[i] = new TouchPoint();
+                }
+            }
+            return sLastTouchPoints;
+        }
+
+        private static PointerCoords[] getPointerCoords(int requiredCapacity) {
+            if ((sPointerCoords == null) || (sPointerCoords.length < requiredCapacity)) {
+                sPointerCoords = new PointerCoords[requiredCapacity];
+                for (int i = 0; i < requiredCapacity; i++) {
+                    sPointerCoords[i] = new PointerCoords();
+                }
+            }
+            return sPointerCoords;
+        }
+
+        private static PointerProperties[] getPointerProps(int requiredCapacity) {
+            if ((sPointerProps == null) || (sPointerProps.length < requiredCapacity)) {
+                sPointerProps = new PointerProperties[requiredCapacity];
+                for (int i = 0; i < requiredCapacity; i++) {
+                    sPointerProps[i] = new PointerProperties();
+                }
+            }
+            return sPointerProps;
+        }
+
+        private static void appendMoveEventIfNeeded(List<MotionEvent> motionEvents,
+                TouchPoint[] lastTouchPoints, int lastTouchPointsSize,
+                TouchPoint[] currentTouchPoints, int currentTouchPointsSize, long currentTime) {
+            /* Look for pointers that have moved */
+            boolean moveFound = false;
+            for (int i = 0; i < currentTouchPointsSize; i++) {
+                int lastPointsIndex = findPointByPathIndex(lastTouchPoints, lastTouchPointsSize,
+                        currentTouchPoints[i].mPathIndex);
+                if (lastPointsIndex >= 0) {
+                    moveFound |= (lastTouchPoints[lastPointsIndex].mX != currentTouchPoints[i].mX)
+                            || (lastTouchPoints[lastPointsIndex].mY != currentTouchPoints[i].mY);
+                    lastTouchPoints[lastPointsIndex].copyFrom(currentTouchPoints[i]);
+                }
+            }
+
+            if (moveFound) {
+                long downTime = motionEvents.get(motionEvents.size() - 1).getDownTime();
+                motionEvents.add(obtainMotionEvent(downTime, currentTime, MotionEvent.ACTION_MOVE,
+                        lastTouchPoints, lastTouchPointsSize));
+            }
+        }
+
+        private static int appendUpEvents(List<MotionEvent> motionEvents,
+                TouchPoint[] lastTouchPoints, int lastTouchPointsSize,
+                TouchPoint[] currentTouchPoints, int currentTouchPointsSize, long currentTime) {
+            /* Look for a pointer at the end of its path */
+            for (int i = 0; i < currentTouchPointsSize; i++) {
+                if (currentTouchPoints[i].mIsEndOfPath) {
+                    int indexOfUpEvent = findPointByPathIndex(lastTouchPoints, lastTouchPointsSize,
+                            currentTouchPoints[i].mPathIndex);
+                    if (indexOfUpEvent < 0) {
+                        continue; // Should not happen
+                    }
+                    long downTime = motionEvents.get(motionEvents.size() - 1).getDownTime();
+                    int action = (lastTouchPointsSize == 1) ? MotionEvent.ACTION_UP
+                            : MotionEvent.ACTION_POINTER_UP;
+                    action |= indexOfUpEvent << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+                    motionEvents.add(obtainMotionEvent(downTime, currentTime, action,
+                            lastTouchPoints, lastTouchPointsSize));
+                    /* Remove this point from lastTouchPoints */
+                    for (int j = indexOfUpEvent; j < lastTouchPointsSize - 1; j++) {
+                        lastTouchPoints[j].copyFrom(lastTouchPoints[j+1]);
+                    }
+                    lastTouchPointsSize--;
+                }
+            }
+            return lastTouchPointsSize;
+        }
+
+        private static int appendDownEvents(List<MotionEvent> motionEvents,
+                TouchPoint[] lastTouchPoints, int lastTouchPointsSize,
+                TouchPoint[] currentTouchPoints, int currentTouchPointsSize, long currentTime) {
+            /* Look for a pointer that is just starting */
+            for (int i = 0; i < currentTouchPointsSize; i++) {
+                if (currentTouchPoints[i].mIsStartOfPath) {
+                    /* Add the point to last coords and use the new array to generate the event */
+                    lastTouchPoints[lastTouchPointsSize++].copyFrom(currentTouchPoints[i]);
+                    int action = (lastTouchPointsSize == 1) ? MotionEvent.ACTION_DOWN
+                            : MotionEvent.ACTION_POINTER_DOWN;
+                    long downTime = (action == MotionEvent.ACTION_DOWN) ? currentTime :
+                            motionEvents.get(motionEvents.size() - 1).getDownTime();
+                    action |= i << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+                    motionEvents.add(obtainMotionEvent(downTime, currentTime, action,
+                            lastTouchPoints, lastTouchPointsSize));
+                }
+            }
+            return lastTouchPointsSize;
+        }
+
+        private static MotionEvent obtainMotionEvent(long downTime, long eventTime, int action,
+                TouchPoint[] touchPoints, int touchPointsSize) {
+            PointerCoords[] pointerCoords = getPointerCoords(touchPointsSize);
+            PointerProperties[] pointerProperties = getPointerProps(touchPointsSize);
+            for (int i = 0; i < touchPointsSize; i++) {
+                pointerProperties[i].id = touchPoints[i].mPathIndex;
+                pointerProperties[i].toolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+                pointerCoords[i].clear();
+                pointerCoords[i].pressure = 1.0f;
+                pointerCoords[i].size = 1.0f;
+                pointerCoords[i].x = touchPoints[i].mX;
+                pointerCoords[i].y = touchPoints[i].mY;
+            }
+            return MotionEvent.obtain(downTime, eventTime, action, touchPointsSize,
+                    pointerProperties, pointerCoords, EVENT_META_STATE, EVENT_BUTTON_STATE,
+                    EVENT_X_PRECISION, EVENT_Y_PRECISION, EVENT_DEVICE_ID, EVENT_EDGE_FLAGS,
+                    EVENT_SOURCE, EVENT_FLAGS);
+        }
+
+        private static int findPointByPathIndex(TouchPoint[] touchPoints, int touchPointsSize,
+                int pathIndex) {
+            for (int i = 0; i < touchPointsSize; i++) {
+                if (touchPoints[i].mPathIndex == pathIndex) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+    }
+}
index 15666bf..6280542 100644 (file)
@@ -42,4 +42,6 @@ import android.view.KeyEvent;
     void onKeyEvent(in KeyEvent event, int sequence);
 
     void onMagnificationChanged(in Region region, float scale, float centerX, float centerY);
+
+    void onPerformGestureResult(int sequence, boolean completedSuccessfully);
 }
index 6ac50bd..a65b87b 100644 (file)
 
 package android.accessibilityservice;
 
-import android.os.Bundle;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.pm.ParceledListSlice;
 import android.graphics.Region;
+import android.os.Bundle;
 import android.view.MagnificationSpec;
+import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 import android.view.accessibility.AccessibilityWindowInfo;
@@ -79,4 +81,6 @@ interface IAccessibilityServiceConnection {
         boolean animate);
 
     void setMagnificationCallbackEnabled(boolean enabled);
+
+    void sendMotionEvents(int sequence, in ParceledListSlice events);
 }
index 8928e99..e993cca 100644 (file)
@@ -861,22 +861,23 @@ public class PropertyValuesHolder implements Cloneable {
         if (mProperty != null) {
             Object value = convertBack(mProperty.get(target));
             kf.setValue(value);
-        }
-        try {
-            if (mGetter == null) {
-                Class targetClass = target.getClass();
-                setupGetter(targetClass);
+        } else {
+            try {
                 if (mGetter == null) {
-                    // Already logged the error - just return to avoid NPE
-                    return;
+                    Class targetClass = target.getClass();
+                    setupGetter(targetClass);
+                    if (mGetter == null) {
+                        // Already logged the error - just return to avoid NPE
+                        return;
+                    }
                 }
+                Object value = convertBack(mGetter.invoke(target));
+                kf.setValue(value);
+            } catch (InvocationTargetException e) {
+                Log.e("PropertyValuesHolder", e.toString());
+            } catch (IllegalAccessException e) {
+                Log.e("PropertyValuesHolder", e.toString());
             }
-            Object value = convertBack(mGetter.invoke(target));
-            kf.setValue(value);
-        } catch (InvocationTargetException e) {
-            Log.e("PropertyValuesHolder", e.toString());
-        } catch (IllegalAccessException e) {
-            Log.e("PropertyValuesHolder", e.toString());
         }
     }
 
index 64642ac..34527c2 100644 (file)
@@ -2843,16 +2843,15 @@ public class Activity extends ContextThemeWrapper
         if (keyCode == KeyEvent.KEYCODE_MENU &&
                 mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
             return true;
-        } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
-            // Capture the Alt-up and send focus to the ActionBar
+        } else if (event.isCtrlPressed() &&
+                event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK) == '<') {
+            // Capture the Control-< and send focus to the ActionBar
             final int action = event.getAction();
             if (action == KeyEvent.ACTION_DOWN) {
-                if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                    final ActionBar actionBar = getActionBar();
-                    if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
-                        mEatKeyUpEvent = true;
-                        return true;
-                    }
+                final ActionBar actionBar = getActionBar();
+                if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
+                    mEatKeyUpEvent = true;
+                    return true;
                 }
             } else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) {
                 mEatKeyUpEvent = false;
index 8475840..dce2e51 100644 (file)
@@ -1031,6 +1031,11 @@ public final class UiAutomation {
                         float scale, float centerX, float centerY) {
                     /* do nothing */
                 }
+
+                @Override
+                public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
+                    /* do nothing */
+                }
             });
         }
     }
index 0d9e778..b899710 100644 (file)
@@ -64,6 +64,11 @@ public class JobInfo implements Parcelable {
      */
     public static final int BACKOFF_POLICY_EXPONENTIAL = 1;
 
+    /* Minimum interval for a periodic job, in milliseconds. */
+    public static final long MIN_PERIOD_MILLIS = 60 * 60 * 1000L;   // 60 minutes
+    /* Minimum flex for a periodic job, in milliseconds. */
+    public static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
+
     /**
      * Default type of backoff.
      * @hide
@@ -83,6 +88,7 @@ public class JobInfo implements Parcelable {
     private final boolean isPeriodic;
     private final boolean isPersisted;
     private final long intervalMillis;
+    private final long flexMillis;
     private final long initialBackoffMillis;
     private final int backoffPolicy;
 
@@ -165,7 +171,17 @@ public class JobInfo implements Parcelable {
      * job does not recur periodically.
      */
     public long getIntervalMillis() {
-        return intervalMillis;
+        return intervalMillis >= MIN_PERIOD_MILLIS ? intervalMillis : MIN_PERIOD_MILLIS;
+    }
+
+    /**
+     * Flex time for this job. Only valid if this is a periodic job.
+     */
+    public long getFlexMillis() {
+        long interval = getIntervalMillis();
+        long percentClamp = 5 * interval / 100;
+        long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, MIN_FLEX_MILLIS));
+        return clampedFlex <= interval ? clampedFlex : interval;
     }
 
     /**
@@ -216,6 +232,7 @@ public class JobInfo implements Parcelable {
         isPeriodic = in.readInt() == 1;
         isPersisted = in.readInt() == 1;
         intervalMillis = in.readLong();
+        flexMillis = in.readLong();
         initialBackoffMillis = in.readLong();
         backoffPolicy = in.readInt();
         hasEarlyConstraint = in.readInt() == 1;
@@ -234,6 +251,7 @@ public class JobInfo implements Parcelable {
         isPeriodic = b.mIsPeriodic;
         isPersisted = b.mIsPersisted;
         intervalMillis = b.mIntervalMillis;
+        flexMillis = b.mFlexMillis;
         initialBackoffMillis = b.mInitialBackoffMillis;
         backoffPolicy = b.mBackoffPolicy;
         hasEarlyConstraint = b.mHasEarlyConstraint;
@@ -258,6 +276,7 @@ public class JobInfo implements Parcelable {
         out.writeInt(isPeriodic ? 1 : 0);
         out.writeInt(isPersisted ? 1 : 0);
         out.writeLong(intervalMillis);
+        out.writeLong(flexMillis);
         out.writeLong(initialBackoffMillis);
         out.writeInt(backoffPolicy);
         out.writeInt(hasEarlyConstraint ? 1 : 0);
@@ -299,6 +318,7 @@ public class JobInfo implements Parcelable {
         private boolean mHasEarlyConstraint;
         private boolean mHasLateConstraint;
         private long mIntervalMillis;
+        private long mFlexMillis;
         // Back-off parameters.
         private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
         private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
@@ -373,8 +393,21 @@ public class JobInfo implements Parcelable {
          * @param intervalMillis Millisecond interval for which this job will repeat.
          */
         public Builder setPeriodic(long intervalMillis) {
+            return setPeriodic(intervalMillis, intervalMillis);
+        }
+
+        /**
+         * Specify that this job should recur with the provided interval and flex. The job can
+         * execute at any time in a window of flex length at the end of the period.
+         * @param intervalMillis Millisecond interval for which this job will repeat.
+         * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
+         *                   {@link #MIN_FLEX_MILLIS} or 5 percent of the period, whichever is
+         *                   higher.
+         */
+        public Builder setPeriodic(long intervalMillis, long flexMillis) {
             mIsPeriodic = true;
             mIntervalMillis = intervalMillis;
+            mFlexMillis = flexMillis;
             mHasEarlyConstraint = mHasLateConstraint = true;
             return this;
         }
index ef08eb9..5f97c9e 100644 (file)
@@ -121,12 +121,12 @@ public final class NetworkStats implements AutoCloseable {
      */
     public static class Bucket {
         /**
-         * Combined usage across all other states.
+         * Combined usage across all states.
          */
         public static final int STATE_ALL = -1;
 
         /**
-         * Usage not accounted in any other states.
+         * Usage not accounted for in any other state.
          */
         public static final int STATE_DEFAULT = 0x1;
 
@@ -150,8 +150,40 @@ public final class NetworkStats implements AutoCloseable {
          */
         public static final int UID_TETHERING = TrafficStats.UID_TETHERING;
 
+        /**
+         * Combined usage across all metering states.
+         */
+        public static final int METERING_ALL = -1;
+
+        /**
+         * Usage not accounted for in any other metering state.
+         */
+        public static final int METERING_DEFAULT = 0x1;
+
+        /**
+         * Metered usage.
+         */
+        public static final int METERING_METERED = 0x2;
+
+        /**
+         * Combined usage across all roaming states.
+         */
+        public static final int ROAMING_ALL = -1;
+
+        /**
+         * Usage not accounted for in any other roaming state.
+         */
+        public static final int ROAMING_DEFAULT = 0x1;
+
+        /**
+         * Roaming usage.
+         */
+        public static final int ROAMING_ROAMING = 0x2;
+
         private int mUid;
         private int mState;
+        private int mMetering;
+        private int mRoaming;
         private long mBeginTimeStamp;
         private long mEndTimeStamp;
         private long mRxBytes;
@@ -206,6 +238,30 @@ public final class NetworkStats implements AutoCloseable {
         }
 
         /**
+         * Metering state. One of the following values:<p/>
+         * <ul>
+         * <li>{@link #METERING_ALL}</li>
+         * <li>{@link #METERING_DEFAULT}</li>
+         * <li>{@link #METERING_METERED}</li>
+         * </ul>
+         */
+        public int getMetering() {
+            return mMetering;
+        }
+
+        /**
+         * Roaming state. One of the following values:<p/>
+         * <ul>
+         * <li>{@link #ROAMING_ALL}</li>
+         * <li>{@link #ROAMING_DEFAULT}</li>
+         * <li>{@link #ROAMING_ROAMING}</li>
+         * </ul>
+         */
+        public int getRoaming() {
+            return mRoaming;
+        }
+
+        /**
          * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
          * {@link java.lang.System#currentTimeMillis}.
          * @return Start of interval.
@@ -398,6 +454,9 @@ public final class NetworkStats implements AutoCloseable {
     private void fillBucketFromSummaryEntry(Bucket bucketOut) {
         bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
         bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
+        // TODO: Implement metering/roaming tracking.
+        bucketOut.mMetering = Bucket.METERING_ALL;
+        bucketOut.mRoaming = Bucket.ROAMING_ALL;
         bucketOut.mBeginTimeStamp = mStartTimeStamp;
         bucketOut.mEndTimeStamp = mEndTimeStamp;
         bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes;
@@ -444,6 +503,8 @@ public final class NetworkStats implements AutoCloseable {
                         mRecycledHistoryEntry);
                 bucketOut.mUid = Bucket.convertUid(getUid());
                 bucketOut.mState = Bucket.STATE_ALL;
+                bucketOut.mMetering = Bucket.METERING_ALL;
+                bucketOut.mRoaming = Bucket.ROAMING_ALL;
                 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
                 bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
                         mRecycledHistoryEntry.bucketDuration;
index f41928e..38abac7 100644 (file)
@@ -3258,6 +3258,8 @@ public abstract class PackageManager {
      * @see #setEphemeralCookie(byte[])
      * @see #getEphemeralCookie()
      * @see #getEphemeralCookieMaxSizeBytes()
+     *
+     * @hide
      */
     public abstract boolean isEphemeralApplication();
 
@@ -3270,6 +3272,8 @@ public abstract class PackageManager {
      * @see #isEphemeralApplication()
      * @see #setEphemeralCookie(byte[])
      * @see #getEphemeralCookie()
+     *
+     * @hide
      */
     public abstract int getEphemeralCookieMaxSizeBytes();
 
@@ -3286,6 +3290,8 @@ public abstract class PackageManager {
      * @see #isEphemeralApplication()
      * @see #setEphemeralCookie(byte[])
      * @see #getEphemeralCookieMaxSizeBytes()
+     *
+     * @hide
      */
     public abstract @NonNull byte[] getEphemeralCookie();
 
@@ -3304,6 +3310,8 @@ public abstract class PackageManager {
      * @see #isEphemeralApplication()
      * @see #getEphemeralCookieMaxSizeBytes()
      * @see #getEphemeralCookie()
+     *
+     * @hide
      */
     public abstract boolean setEphemeralCookie(@NonNull  byte[] cookie);
 
index 1349662..a0df610 100644 (file)
@@ -463,92 +463,60 @@ public class PackageParser {
                 p.featureGroups.toArray(pi.featureGroups);
             }
         }
-        if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
-            int N = p.activities.size();
+        if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+            final int N = p.activities.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.activities = new ActivityInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.activities.get(i).info.enabled) num++;
-                    }
-                    pi.activities = new ActivityInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Activity activity = p.activities.get(i);
-                    if (activity.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Activity a = p.activities.get(i);
+                    if (state.isMatch(a.info, flags)) {
+                        res[num++] = generateActivityInfo(a, flags, state, userId);
                     }
                 }
+                pi.activities = ArrayUtils.trimToSize(res, num);
             }
         }
-        if ((flags&PackageManager.GET_RECEIVERS) != 0) {
-            int N = p.receivers.size();
+        if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+            final int N = p.receivers.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.receivers = new ActivityInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.receivers.get(i).info.enabled) num++;
-                    }
-                    pi.receivers = new ActivityInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Activity activity = p.receivers.get(i);
-                    if (activity.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Activity a = p.receivers.get(i);
+                    if (state.isMatch(a.info, flags)) {
+                        res[num++] = generateActivityInfo(a, flags, state, userId);
                     }
                 }
+                pi.receivers = ArrayUtils.trimToSize(res, num);
             }
         }
-        if ((flags&PackageManager.GET_SERVICES) != 0) {
-            int N = p.services.size();
+        if ((flags & PackageManager.GET_SERVICES) != 0) {
+            final int N = p.services.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.services = new ServiceInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.services.get(i).info.enabled) num++;
-                    }
-                    pi.services = new ServiceInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Service service = p.services.get(i);
-                    if (service.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.services[j++] = generateServiceInfo(p.services.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ServiceInfo[] res = new ServiceInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Service s = p.services.get(i);
+                    if (state.isMatch(s.info, flags)) {
+                        res[num++] = generateServiceInfo(s, flags, state, userId);
                     }
                 }
+                pi.services = ArrayUtils.trimToSize(res, num);
             }
         }
-        if ((flags&PackageManager.GET_PROVIDERS) != 0) {
-            int N = p.providers.size();
+        if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+            final int N = p.providers.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.providers = new ProviderInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.providers.get(i).info.enabled) num++;
-                    }
-                    pi.providers = new ProviderInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Provider provider = p.providers.get(i);
-                    if (provider.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ProviderInfo[] res = new ProviderInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Provider pr = p.providers.get(i);
+                    if (state.isMatch(pr.info, flags)) {
+                        res[num++] = generateProviderInfo(pr, flags, state, userId);
                     }
                 }
+                pi.providers = ArrayUtils.trimToSize(res, num);
             }
         }
         if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
index 91fdf7f..38e0044 100644 (file)
 package android.content.pm;
 
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_AWARE;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 
 import android.util.ArraySet;
 
+import com.android.internal.util.ArrayUtils;
+
 /**
  * Per-user state information about a package.
  * @hide
@@ -58,12 +69,77 @@ public class PackageUserState {
         hidden = o.hidden;
         suspended = o.suspended;
         lastDisableAppCaller = o.lastDisableAppCaller;
-        disabledComponents = o.disabledComponents != null
-                ? new ArraySet<>(o.disabledComponents) : null;
-        enabledComponents = o.enabledComponents != null
-                ? new ArraySet<>(o.enabledComponents) : null;
+        disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
+        enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
         blockUninstall = o.blockUninstall;
         domainVerificationStatus = o.domainVerificationStatus;
         appLinkGeneration = o.appLinkGeneration;
     }
+
+    /**
+     * Test if this package is installed.
+     */
+    public boolean isInstalled(int flags) {
+        return (this.installed && !this.hidden)
+                || (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0;
+    }
+
+    /**
+     * Test if the given component is considered installed, enabled and a match
+     * for the given flags.
+     */
+    public boolean isMatch(ComponentInfo componentInfo, int flags) {
+        if (!isInstalled(flags)) return false;
+        if (!isEnabled(componentInfo, flags)) return false;
+
+        if ((flags & MATCH_SYSTEM_ONLY) != 0) {
+            if (!componentInfo.applicationInfo.isSystemApp()) {
+                return false;
+            }
+        }
+
+        final boolean matchesUnaware = ((flags & MATCH_ENCRYPTION_UNAWARE) != 0)
+                && !componentInfo.encryptionAware;
+        final boolean matchesAware = ((flags & MATCH_ENCRYPTION_AWARE) != 0)
+                && componentInfo.encryptionAware;
+        return matchesUnaware || matchesAware;
+    }
+
+    /**
+     * Test if the given component is considered enabled.
+     */
+    public boolean isEnabled(ComponentInfo componentInfo, int flags) {
+        if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
+            return true;
+        }
+
+        // First check if the overall package is disabled; if the package is
+        // enabled then fall through to check specific component
+        switch (this.enabled) {
+            case COMPONENT_ENABLED_STATE_DISABLED:
+            case COMPONENT_ENABLED_STATE_DISABLED_USER:
+                return false;
+            case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+                if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
+                    return false;
+                }
+            case COMPONENT_ENABLED_STATE_DEFAULT:
+                if (!componentInfo.applicationInfo.enabled) {
+                    return false;
+                }
+            case COMPONENT_ENABLED_STATE_ENABLED:
+                break;
+        }
+
+        // Check if component has explicit state before falling through to
+        // the manifest default
+        if (ArrayUtils.contains(this.enabledComponents, componentInfo.name)) {
+            return true;
+        }
+        if (ArrayUtils.contains(this.disabledComponents, componentInfo.name)) {
+            return false;
+        }
+
+        return componentInfo.enabled;
+    }
 }
index 515e9a2..cabc6fa 100644 (file)
@@ -23,7 +23,6 @@ import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
 import android.os.Handler;
@@ -46,11 +45,11 @@ import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.Protocol;
 
+import libcore.net.event.NetworkEventDispatcher;
+
 import java.net.InetAddress;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.HashMap;
-
-import libcore.net.event.NetworkEventDispatcher;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Class that answers queries about the state of network connectivity. It also
@@ -1611,7 +1610,7 @@ public class ConnectivityManager {
             // Have a provisioning app - must only let system apps (which check this app)
             // turn on tethering
             context.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.CONNECTIVITY_INTERNAL, "ConnectivityService");
+                    android.Manifest.permission.TETHER_PRIVILEGED, "ConnectivityService");
         } else {
             int uid = Binder.getCallingUid();
             Settings.checkAndNoteWriteSettingsOperation(context, uid, Settings
index a006674..d4c9944 100644 (file)
@@ -8288,7 +8288,6 @@ public final class Settings {
         /**
          * Whether to enable contacts metadata syncing or not
          * The value 1 - enable, 0 - disable
-         * @hide
          */
         public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync";
 
index aba82fa..035462d 100644 (file)
@@ -95,6 +95,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS
     /** Notification was canceled because it was an invisible member of a group. */
     public static final int REASON_GROUP_OPTIMIZATION = 13;
 
+    /** Notification was canceled by the user banning the topic. */
+    public static final int REASON_TOPIC_BANNED = 14;
+
     public class Adjustment {
         int mImportance;
         CharSequence mExplanation;
index f22cde0..24883e3 100644 (file)
@@ -16,6 +16,7 @@
 
 package android.util;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
@@ -56,10 +57,21 @@ public final class LocaleList implements Parcelable {
         return mList.length == 0;
     }
 
+    @IntRange(from=0)
     public int size() {
         return mList.length;
     }
 
+    @IntRange(from=-1)
+    public int indexOf(Locale locale) {
+        for (int i = 0; i < mList.length; i++) {
+            if (mList[i].equals(locale)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     @Override
     public boolean equals(Object other) {
         if (other == this)
@@ -69,7 +81,7 @@ public final class LocaleList implements Parcelable {
         final Locale[] otherList = ((LocaleList) other).mList;
         if (mList.length != otherList.length)
             return false;
-        for (int i = 0; i < mList.length; ++i) {
+        for (int i = 0; i < mList.length; i++) {
             if (!mList[i].equals(otherList[i]))
                 return false;
         }
@@ -79,7 +91,7 @@ public final class LocaleList implements Parcelable {
     @Override
     public int hashCode() {
         int result = 1;
-        for (int i = 0; i < mList.length; ++i) {
+        for (int i = 0; i < mList.length; i++) {
             result = 31 * result + mList[i].hashCode();
         }
         return result;
@@ -89,7 +101,7 @@ public final class LocaleList implements Parcelable {
     public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append("[");
-        for (int i = 0; i < mList.length; ++i) {
+        for (int i = 0; i < mList.length; i++) {
             sb.append(mList[i]);
             if (i < mList.length - 1) {
                 sb.append(',');
@@ -150,12 +162,12 @@ public final class LocaleList implements Parcelable {
             final Locale[] localeList = new Locale[list.length];
             final HashSet<Locale> seenLocales = new HashSet<Locale>();
             final StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < list.length; ++i) {
+            for (int i = 0; i < list.length; i++) {
                 final Locale l = list[i];
                 if (l == null) {
-                    throw new NullPointerException();
+                    throw new NullPointerException("list[" + i + "] is null");
                 } else if (seenLocales.contains(l)) {
-                    throw new IllegalArgumentException();
+                    throw new IllegalArgumentException("list[" + i + "] is a repetition");
                 } else {
                     final Locale localeClone = (Locale) l.clone();
                     localeList[i] = localeClone;
@@ -171,6 +183,55 @@ public final class LocaleList implements Parcelable {
         }
     }
 
+    /**
+     * Constructs a locale list, with the topLocale moved to the front if it already is
+     * in otherLocales, or added to the front if it isn't.
+     *
+     * {@hide}
+     */
+    public LocaleList(@NonNull Locale topLocale, LocaleList otherLocales) {
+        if (topLocale == null) {
+            throw new NullPointerException("topLocale is null");
+        }
+
+        final int inputLength = (otherLocales == null) ? 0 : otherLocales.mList.length;
+        int topLocaleIndex = -1;
+        for (int i = 0; i < inputLength; i++) {
+            if (topLocale.equals(otherLocales.mList[i])) {
+                topLocaleIndex = i;
+                break;
+            }
+        }
+
+        final int outputLength = inputLength + (topLocaleIndex == -1 ? 1 : 0);
+        final Locale[] localeList = new Locale[outputLength];
+        localeList[0] = (Locale) topLocale.clone();
+        if (topLocaleIndex == -1) {
+            // topLocale was not in otherLocales
+            for (int i = 0; i < inputLength; i++) {
+                localeList[i + 1] = (Locale) otherLocales.mList[i].clone();
+            }
+        } else {
+            for (int i = 0; i < topLocaleIndex; i++) {
+                localeList[i + 1] = (Locale) otherLocales.mList[i].clone();
+            }
+            for (int i = topLocaleIndex + 1; i < inputLength; i++) {
+                localeList[i] = (Locale) otherLocales.mList[i].clone();
+            }
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < outputLength; i++) {
+            sb.append(localeList[i].toLanguageTag());
+            if (i < outputLength - 1) {
+                sb.append(',');
+            }
+        }
+
+        mList = localeList;
+        mStringRepresentation = sb.toString();
+    }
+
     public static final Parcelable.Creator<LocaleList> CREATOR
             = new Parcelable.Creator<LocaleList>() {
         @Override
@@ -196,7 +257,7 @@ public final class LocaleList implements Parcelable {
         } else {
             final String[] tags = list.split(",");
             final Locale[] localeArray = new Locale[tags.length];
-            for (int i = 0; i < localeArray.length; ++i) {
+            for (int i = 0; i < localeArray.length; i++) {
                 localeArray[i] = Locale.forLanguageTag(tags[i]);
             }
             return new LocaleList(localeArray);
@@ -227,6 +288,7 @@ public final class LocaleList implements Parcelable {
         return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale);
     }
 
+    @IntRange(from=0, to=1)
     private static int matchScore(Locale supported, Locale desired) {
         if (supported.equals(desired)) {
             return 1;  // return early so we don't do unnecessary computation
@@ -330,18 +392,79 @@ public final class LocaleList implements Parcelable {
     private final static Object sLock = new Object();
 
     @GuardedBy("sLock")
-    private static LocaleList sDefaultLocaleList;
+    private static LocaleList sLastExplicitlySetLocaleList = null;
+    @GuardedBy("sLock")
+    private static LocaleList sDefaultLocaleList = null;
+    @GuardedBy("sLock")
+    private static Locale sLastDefaultLocale = null;
 
-    // TODO: fix this to return the default system locale list once we have that
+    /**
+     * The result is guaranteed to include the default Locale returned by Locale.getDefault(), but
+     * not necessarily at the top of the list. The default locale not being at the top of the list
+     * is an indication that the system has set the default locale to one of the user's other
+     * preferred locales, having concluded that the primary preference is not supported but a
+     * secondary preference is.
+     *
+     * Note that the default LocaleList would change if Locale.setDefault() is called. This method
+     * takes that into account by always checking the output of Locale.getDefault() and adjusting
+     * the default LocaleList if needed.
+     */
     @NonNull @Size(min=1)
     public static LocaleList getDefault() {
-        Locale defaultLocale = Locale.getDefault();
+        final Locale defaultLocale = Locale.getDefault();
         synchronized (sLock) {
-            if (sDefaultLocaleList == null || sDefaultLocaleList.size() != 1
-                    || !defaultLocale.equals(sDefaultLocaleList.getPrimary())) {
-                sDefaultLocaleList = new LocaleList(defaultLocale);
+            if (!defaultLocale.equals(sLastDefaultLocale)) {
+                sLastDefaultLocale = defaultLocale;
+                // It's either the first time someone has asked for the default locale list, or
+                // someone has called Locale.setDefault() since we last set or adjusted the default
+                // locale list. So let's adjust the locale list.
+                if (sDefaultLocaleList != null
+                        && defaultLocale.equals(sDefaultLocaleList.getPrimary())) {
+                    // The default Locale has changed, but it happens to be the first locale in the
+                    // default locale list, so we don't need to construct a new locale list.
+                    return sDefaultLocaleList;
+                }
+                sDefaultLocaleList = new LocaleList(defaultLocale, sLastExplicitlySetLocaleList);
             }
+            // sDefaultLocaleList can't be null, since it can't be set to null by
+            // LocaleList.setDefault(), and if getDefault() is called before a call to
+            // setDefault(), sLastDefaultLocale would be null and the check above would set
+            // sDefaultLocaleList.
+            return sDefaultLocaleList;
+        }
+    }
+
+    /**
+     * Also sets the default locale by calling Locale.setDefault() with the first locale in the
+     * list.
+     *
+     * @throws NullPointerException if the input is <code>null</code>.
+     * @throws IllegalArgumentException if the input is empty.
+     */
+    public static void setDefault(@NonNull @Size(min=1) LocaleList locales) {
+        setDefault(locales, 0);
+    }
+
+    /**
+     * This may be used directly by system processes to set the default locale list for apps. For
+     * such uses, the default locale list would always come from the user preferences, but the
+     * default locale may have been chosen to be a locale other than the first locale in the locale
+     * list (based on the locales the app supports).
+     *
+     * {@hide}
+     */
+    public static void setDefault(@NonNull @Size(min=1) LocaleList locales, int localeIndex) {
+        if (locales == null) {
+            throw new NullPointerException("locales is null");
+        }
+        if (locales.isEmpty()) {
+            throw new IllegalArgumentException("locales is empty");
+        }
+        synchronized (sLock) {
+            sLastDefaultLocale = locales.get(localeIndex);
+            Locale.setDefault(sLastDefaultLocale);
+            sLastExplicitlySetLocaleList = locales;
+            sDefaultLocaleList = locales;
         }
-        return sDefaultLocaleList;
     }
 }
index ecec258..a78b56a 100644 (file)
@@ -1328,4 +1328,15 @@ public interface WindowManagerPolicy {
      * @param fadeoutDuration the duration of the exit animation, in milliseconds
      */
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+
+    /**
+     * Calculates the stable insets without running a layout.
+     *
+     * @param displayRotation the current display rotation
+     * @param outInsets the insets to return
+     * @param displayWidth the current display width
+     * @param displayHeight the current display height
+     */
+    public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+            Rect outInsets);
 }
index ba5d6c2..6e5e591 100644 (file)
@@ -201,10 +201,17 @@ public class BaseInputConnection implements InputConnection {
     }
 
     /**
-     * The default implementation performs the deletion around the current
-     * selection position of the editable text.
-     * @param beforeLength
-     * @param afterLength
+     * The default implementation performs the deletion around the current selection position of the
+     * editable text.
+     *
+     * @param beforeLength The number of characters before the cursor to be deleted, in code unit.
+     *        If this is greater than the number of existing characters between the beginning of the
+     *        text and the cursor, then this method does not fail but deletes all the characters in
+     *        that range.
+     * @param afterLength The number of characters after the cursor to be deleted, in code unit.
+     *        If this is greater than the number of existing characters between the cursor and
+     *        the end of the text, then this method does not fail but deletes all the characters in
+     *        that range.
      */
     public boolean deleteSurroundingText(int beforeLength, int afterLength) {
         if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
@@ -213,7 +220,7 @@ public class BaseInputConnection implements InputConnection {
         if (content == null) return false;
 
         beginBatchEdit();
-        
+
         int a = Selection.getSelectionStart(content);
         int b = Selection.getSelectionEnd(content);
 
@@ -253,9 +260,9 @@ public class BaseInputConnection implements InputConnection {
 
             content.delete(b, end);
         }
-        
+
         endBatchEdit();
-        
+
         return true;
     }
 
index ff992d3..be7bc14 100644 (file)
@@ -335,12 +335,15 @@ public interface InputConnection {
      * but be careful to wait until the batch edit is over if one is
      * in progress.</p>
      *
-     * @param beforeLength The number of characters to be deleted before the
-     *        current cursor position.
-     * @param afterLength The number of characters to be deleted after the
-     *        current cursor position.
-     * @return true on success, false if the input connection is no longer
-     * valid.
+     * @param beforeLength The number of characters before the cursor to be deleted, in code unit.
+     *        If this is greater than the number of existing characters between the beginning of the
+     *        text and the cursor, then this method does not fail but deletes all the characters in
+     *        that range.
+     * @param afterLength The number of characters after the cursor to be deleted, in code unit.
+     *        If this is greater than the number of existing characters between the cursor and
+     *        the end of the text, then this method does not fail but deletes all the characters in
+     *        that range.
+     * @return true on success, false if the input connection is no longer valid.
      */
     public boolean deleteSurroundingText(int beforeLength, int afterLength);
 
index 9f08b21..054eafc 100644 (file)
@@ -131,6 +131,8 @@ public final class WebViewFactory {
     private static String TAG_WEBVIEW_PROVIDER = "webviewprovider";
     private static String TAG_PACKAGE_NAME = "packageName";
     private static String TAG_DESCRIPTION = "description";
+    // Whether or not the provider must be explicitly chosen by the user to be used.
+    private static String TAG_AVAILABILITY = "availableByDefault";
     private static String TAG_SIGNATURE = "signature";
 
     /**
@@ -180,8 +182,12 @@ public final class WebViewFactory {
                         throw new MissingWebViewPackageException(
                                 "WebView provider in framework resources missing description");
                     }
+                    String availableByDefault = parser.getAttributeValue(null, TAG_AVAILABILITY);
+                    if (availableByDefault == null) {
+                        availableByDefault = "false";
+                    }
                     webViewProviders.add(
-                            new WebViewProviderInfo(packageName, description,
+                            new WebViewProviderInfo(packageName, description, availableByDefault,
                                 readSignatures(parser)));
                 }
                 else {
index ac5b670..3f50fe2 100644 (file)
@@ -40,9 +40,11 @@ public class WebViewProviderInfo implements Parcelable {
         public WebViewPackageNotFoundException(Exception e) { super(e); }
     }
 
-    public WebViewProviderInfo(String packageName, String description, String[] signatures) {
+    public WebViewProviderInfo(String packageName, String description, String availableByDefault,
+            String[] signatures) {
         this.packageName = packageName;
         this.description = description;
+        this.availableByDefault = availableByDefault.equals("true");
         this.signatures = signatures;
     }
 
@@ -89,6 +91,39 @@ public class WebViewProviderInfo implements Parcelable {
         return false;
     }
 
+    /**
+     * Returns whether this package is enabled.
+     * This state can be changed by the user from Settings->Apps
+     */
+    public boolean isEnabled() {
+        try {
+            PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+            int enabled_state = pm.getApplicationEnabledSetting(packageName);
+            switch (enabled_state) {
+                case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+                    return true;
+                case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+                    ApplicationInfo applicationInfo = getPackageInfo().applicationInfo;
+                    return applicationInfo.enabled;
+                default:
+                    return false;
+            }
+        } catch (WebViewPackageNotFoundException e) {
+            return false;
+        } catch (IllegalArgumentException e) {
+            // Thrown by PackageManager.getApplicationEnabledSetting if the package does not exist
+            return false;
+        }
+    }
+
+    /**
+     * Returns whether the provider is always available as long as it is valid.
+     * If this returns false, the provider will only be used if the user chose this provider.
+     */
+    public boolean isAvailableByDefault() {
+        return availableByDefault;
+    }
+
     public PackageInfo getPackageInfo() {
         if (packageInfo == null) {
             try {
@@ -135,6 +170,7 @@ public class WebViewProviderInfo implements Parcelable {
     // fields read from framework resource
     public String packageName;
     public String description;
+    private boolean availableByDefault;
 
     private String[] signatures;
 
index 40eb375..ce4fc06 100644 (file)
@@ -27,7 +27,6 @@ import android.view.View;
  */
 public class MetricsLogger implements MetricsConstants {
     // Temporary constants go here, to await migration to MetricsConstants.
-    public static final int QS_LOCK_TILE = 257;
     public static final int QS_USER_TILE = 258;
     public static final int QS_BATTERY_TILE = 259;
     public static final int NOTIFICATION_ZEN_MODE_VISUAL_INTERRUPTIONS = 260;
@@ -62,6 +61,7 @@ public class MetricsLogger implements MetricsConstants {
      * credentials UI.
      */
     public static final int PROFILE_CHALLENGE = 271;
+    public static final int QS_BATTERY_DETAIL = 272;
 
     public static void visible(Context context, int category) throws IllegalArgumentException {
         if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
index 1b44ff3..de54d96 100644 (file)
@@ -99,6 +99,9 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
         mResizingBackgroundDrawable = resizingBackgroundDrawable;
         mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
         mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
+        if (mCaptionBackgroundDrawable == null) {
+            mCaptionBackgroundDrawable = mResizingBackgroundDrawable;
+        }
         if (statusBarColor != 0) {
             mStatusBarColor = new ColorDrawable(statusBarColor);
             addSystemBarNodeIfNeeded();
index cc2f714..cea9867 100644 (file)
@@ -1616,14 +1616,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
     void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
         mStackId = getStackId();
 
-        mResizingBackgroundDrawable = getResizingBackgroundDrawable(
-                mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
-        if (mCaptionBackgroundDrawable == null) {
-            mCaptionBackgroundDrawable = getContext().getDrawable(
-                    R.drawable.decor_caption_title_focused);
-        }
-
         if (mBackdropFrameRenderer != null) {
+            loadBackgroundDrawablesIfNeeded();
             mBackdropFrameRenderer.onResourcesLoaded(
                     this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                     mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
@@ -1645,6 +1639,17 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
         initializeElevation();
     }
 
+    private void loadBackgroundDrawablesIfNeeded() {
+        if (mResizingBackgroundDrawable == null) {
+            mResizingBackgroundDrawable = getResizingBackgroundDrawable(
+                    mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
+        }
+        if (mCaptionBackgroundDrawable == null) {
+            mCaptionBackgroundDrawable = getContext().getDrawable(
+                    R.drawable.decor_caption_title_focused);
+        }
+    }
+
     // Free floating overlapping windows require a caption.
     private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
         DecorCaptionView decorCaptionView = null;
@@ -1815,6 +1820,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
         }
         final ThreadedRenderer renderer = getHardwareRenderer();
         if (renderer != null) {
+            loadBackgroundDrawablesIfNeeded();
             mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
                     initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                     mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.internal.policy;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Rect;
 
-import com.android.systemui.statusbar.FlingAnimationUtils;
-
 import java.util.ArrayList;
 
 /**
  * Calculates the snap targets and the snap position given a position and a velocity. All positions
  * here are to be interpreted as the left/top edge of the divider rectangle.
+ *
+ * @hide
  */
 public class DividerSnapAlgorithm {
 
@@ -44,8 +45,7 @@ public class DividerSnapAlgorithm {
      */
     private static final int SNAP_ONLY_1_1 = 2;
 
-    private final Context mContext;
-    private final FlingAnimationUtils mFlingAnimationUtils;
+    private final float mMinFlingVelocityPxPerSecond;
     private final int mDisplayWidth;
     private final int mDisplayHeight;
     private final int mDividerSize;
@@ -63,18 +63,17 @@ public class DividerSnapAlgorithm {
     private final SnapTarget mDismissStartTarget;
     private final SnapTarget mDismissEndTarget;
 
-    public DividerSnapAlgorithm(Context ctx, FlingAnimationUtils flingAnimationUtils,
+    public DividerSnapAlgorithm(Resources res, float minFlingVelocityPxPerSecond,
             int displayWidth, int displayHeight, int dividerSize, boolean isHorizontalDivision,
             Rect insets) {
-        mContext = ctx;
-        mFlingAnimationUtils = flingAnimationUtils;
+        mMinFlingVelocityPxPerSecond = minFlingVelocityPxPerSecond;
         mDividerSize = dividerSize;
         mDisplayWidth = displayWidth;
         mDisplayHeight = displayHeight;
         mInsets.set(insets);
-        mSnapMode = ctx.getResources().getInteger(
+        mSnapMode = res.getInteger(
                 com.android.internal.R.integer.config_dockedStackDividerSnapMode);
-        mFixedRatio = ctx.getResources().getFraction(
+        mFixedRatio = res.getFraction(
                 com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
         calculateTargets(isHorizontalDivision);
         mFirstSplitTarget = mTargets.get(1);
@@ -84,7 +83,7 @@ public class DividerSnapAlgorithm {
     }
 
     public SnapTarget calculateSnapTarget(int position, float velocity) {
-        if (Math.abs(velocity) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+        if (Math.abs(velocity) < mMinFlingVelocityPxPerSecond) {
             return snap(position);
         }
         if (position < mFirstSplitTarget.position && velocity < 0) {
@@ -100,6 +99,17 @@ public class DividerSnapAlgorithm {
         }
     }
 
+    public SnapTarget calculateNonDismissingSnapTarget(int position) {
+        SnapTarget target = snap(position);
+        if (target == mDismissStartTarget) {
+            return mFirstSplitTarget;
+        } else if (target == mDismissEndTarget) {
+            return mLastSplitTarget;
+        } else {
+            return target;
+        }
+    }
+
     public float calculateDismissingFraction(int position) {
         if (position < mFirstSplitTarget.position) {
             return 1f - (float) position / mFirstSplitTarget.position;
diff --git a/core/java/com/android/internal/policy/DockedDividerUtils.java b/core/java/com/android/internal/policy/DockedDividerUtils.java
new file mode 100644 (file)
index 0000000..25a060e
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.internal.policy;
+
+import android.graphics.Rect;
+import android.view.WindowManager;
+
+/**
+ * Utility functions for docked stack divider used by both window manager and System UI.
+ *
+ * @hide
+ */
+public class DockedDividerUtils {
+
+    public static void calculateBoundsForPosition(int position, int dockSide, Rect outRect,
+            int displayWidth, int displayHeight, int dividerSize) {
+        outRect.set(0, 0, displayWidth, displayHeight);
+        switch (dockSide) {
+            case WindowManager.DOCKED_LEFT:
+                outRect.right = position;
+                break;
+            case WindowManager.DOCKED_TOP:
+                outRect.bottom = position;
+                break;
+            case WindowManager.DOCKED_RIGHT:
+                outRect.left = position + dividerSize;
+                break;
+            case WindowManager.DOCKED_BOTTOM:
+                outRect.top = position + dividerSize;
+                break;
+        }
+        if (outRect.left > outRect.right) {
+            outRect.left = outRect.right;
+        }
+        if (outRect.top > outRect.bottom) {
+            outRect.top = outRect.bottom;
+        }
+        if (outRect.right < outRect.left) {
+            outRect.right = outRect.left;
+        }
+        if (outRect.bottom < outRect.top) {
+            outRect.bottom = outRect.top;
+        }
+    }
+
+    public static int calculatePositionForBounds(Rect bounds, int dockSide, int dividerSize) {
+        switch (dockSide) {
+            case WindowManager.DOCKED_LEFT:
+                return bounds.right;
+            case WindowManager.DOCKED_TOP:
+                return bounds.bottom;
+            case WindowManager.DOCKED_RIGHT:
+                return bounds.left - dividerSize;
+            case WindowManager.DOCKED_BOTTOM:
+                return bounds.top - dividerSize;
+            default:
+                return 0;
+        }
+    }
+}
index 16bf9dd..ca1334c 100644 (file)
@@ -26,6 +26,7 @@ import libcore.util.EmptyArray;
 
 import java.lang.reflect.Array;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -372,6 +373,10 @@ public class ArrayUtils {
         return (array != null) ? array.clone() : null;
     }
 
+    public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
+        return (array != null) ? new ArraySet<T>(array) : null;
+    }
+
     public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) {
         if (cur == null) {
             cur = new ArraySet<>();
@@ -420,6 +425,16 @@ public class ArrayUtils {
         return (cur != null) ? cur.contains(val) : false;
     }
 
+    public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) {
+        if (array == null || size == 0) {
+            return null;
+        } else if (array.length == size) {
+            return array;
+        } else {
+            return Arrays.copyOf(array, size);
+        }
+    }
+
     /**
      * Returns true if the two ArrayLists are equal with respect to the objects they contain.
      * The objects must be in the same order and be reference equal (== not .equals()).
index 21ba793..75077df 100644 (file)
     <permission android:name="android.permission.READ_WIFI_CREDENTIAL"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi @hide Allows applications to change tether state and run
+         tether carrier provisioning.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.TETHER_PRIVILEGED"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi @hide Allow system apps to receive broadcast
          when a wifi network credential is changed.
          <p>Not for use by third-party applications. -->
index 6d505a5..e0f9eca 100644 (file)
@@ -3316,7 +3316,14 @@ i
              </p>
          -->
         <attr name="canControlMagnification" format="boolean" />
-        <!-- Short description of the accessibility serivce purpose or behavior.-->
+        <!-- Attribute whether the accessibility service wants to be able to perform gestures.
+             <p>
+             Required to allow setting the {@link android.accessibilityservice
+             #AccessibilityServiceInfo#FLAG_CAN_PERFORM_GESTURES} flag.
+             </p>
+         -->
+        <attr name="canPerformGestures" format="boolean" />
+        <!-- Short description of the accessibility service purpose or behavior.-->
         <attr name="description" />
     </declare-styleable>
 
index ebb1b37..acea461 100644 (file)
     <public type="attr" name="tickMark" />
     <public type="attr" name="tickMarkTint" />
     <public type="attr" name="tickMarkTintMode" />
+    <public type="attr" name="canPerformGestures" />
 
     <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
     <public type="style" name="Widget.Material.SeekBar.Discrete" />
index be9ba62..997371e 100644 (file)
     <string name="capability_desc_canControlMagnification">Control the display\'s zoom level and
         positioning.</string>
 
+    <!-- Title for the capability of an accessibility service to perform gestures. -->
+    <string name="capability_title_canPerformGestures">Perform gestures</string>
+    <!-- Description for the capability of an accessibility service to perform gestures. -->
+    <string name="capability_desc_canPerformGestures">Can tap, swipe, pinch, and perform other
+        gestures.</string>
+
     <!--  Permissions -->
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
index a4654e8..706dd20 100644 (file)
   <java-symbol type="string" name="capability_title_canRetrieveWindowContent" />
   <java-symbol type="string" name="capability_desc_canControlMagnification" />
   <java-symbol type="string" name="capability_title_canControlMagnification" />
+  <java-symbol type="string" name="capability_desc_canPerformGestures" />
+  <java-symbol type="string" name="capability_title_canPerformGestures" />
   <java-symbol type="string" name="cfTemplateForwarded" />
   <java-symbol type="string" name="cfTemplateForwardedTime" />
   <java-symbol type="string" name="cfTemplateNotForwarded" />
index fd443c1..f062b59 100644 (file)
@@ -16,6 +16,6 @@
 
 <webviewproviders>
     <!-- The default WebView implementation -->
-    <webviewprovider description="Android WebView" packageName="com.android.webview">
+    <webviewprovider description="Android WebView" packageName="com.android.webview" availableByDefault="true">
     </webviewprovider>
 </webviewproviders>
index 6bd8f6e..a391e1f 100644 (file)
@@ -75,6 +75,7 @@
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <!-- This permission is added for API call setAirplaneMode() in ConnectivityManager -->
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/core/tests/coretests/src/android/util/LocaleListTest.java b/core/tests/coretests/src/android/util/LocaleListTest.java
new file mode 100644 (file)
index 0000000..de1382d
--- /dev/null
@@ -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.
+ */
+package android.util;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.LocaleList;
+
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+public class LocaleListTest extends TestCase {
+    @SmallTest
+    public void testConstructor() throws Exception {
+        LocaleList ll;
+        ll = new LocaleList(Locale.forLanguageTag("fr"), null);
+        assertEquals("fr", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.getEmptyLocaleList());
+        assertEquals("fr", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("fr"));
+        assertEquals("fr", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de"));
+        assertEquals("fr,de", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de,ja"));
+        assertEquals("fr,de,ja", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de,fr,ja"));
+        assertEquals("fr,de,ja", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de,fr"));
+        assertEquals("fr,de", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("fr,de"));
+        assertEquals("fr,de", ll.toLanguageTags());
+    }
+
+    @SmallTest
+    public void testConstructor_nullThrows() throws Exception {
+        try {
+            final LocaleList ll = new LocaleList(null, LocaleList.getEmptyLocaleList());
+            fail("Constructing with locale and locale list should throw with a null locale.");
+        } catch (Throwable e) {
+            assertEquals(NullPointerException.class, e.getClass());
+        }
+    }
+
+    @SmallTest
+    public void testGetDefault_localeSetDefaultCalledButNoChangeNecessary() throws Exception {
+        final Locale originalLocale = Locale.getDefault();
+        final LocaleList originalLocaleList = LocaleList.getDefault();
+        final int originalLocaleIndex = originalLocaleList.indexOf(originalLocale);
+
+        // This simulates a situation potentially set by the system processes
+        LocaleList.setDefault(LocaleList.forLanguageTags("ae,en,ja"), 1 /* en */);
+
+        // check our assumptions about input
+        assertEquals("en", Locale.getDefault().toLanguageTag());
+        final LocaleList firstResult = LocaleList.getDefault();
+        assertEquals("ae,en,ja", LocaleList.getDefault().toLanguageTags());
+
+        Locale.setDefault(Locale.forLanguageTag("ae"));
+        assertSame(firstResult, LocaleList.getDefault());
+
+        // restore the original values
+        LocaleList.setDefault(originalLocaleList, originalLocaleIndex);
+    }
+}
index 11056d4..0932e89 100644 (file)
@@ -109,7 +109,8 @@ ifeq (true, $(HWUI_NEW_OPS))
         BakedOpDispatcher.cpp \
         BakedOpRenderer.cpp \
         BakedOpState.cpp \
-        OpReorderer.cpp \
+        FrameReorderer.cpp \
+        LayerReorderer.cpp \
         RecordingCanvas.cpp
 
     hwui_cflags += -DHWUI_NEW_OPS
@@ -236,8 +237,8 @@ LOCAL_SRC_FILES += \
 ifeq (true, $(HWUI_NEW_OPS))
     LOCAL_SRC_FILES += \
         tests/unit/BakedOpStateTests.cpp \
-        tests/unit/RecordingCanvasTests.cpp \
-        tests/unit/OpReordererTests.cpp
+        tests/unit/FrameReordererTests.cpp \
+        tests/unit/RecordingCanvasTests.cpp
 endif
 
 include $(BUILD_NATIVE_TEST)
@@ -298,7 +299,7 @@ LOCAL_SRC_FILES += \
 
 ifeq (true, $(HWUI_NEW_OPS))
     LOCAL_SRC_FILES += \
-        tests/microbench/OpReordererBench.cpp
+        tests/microbench/FrameReordererBench.cpp
 endif
 
 include $(BUILD_EXECUTABLE)
index 195d235..23aca89 100644 (file)
@@ -338,8 +338,8 @@ static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& stat
 static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
         PathTexture& texture, const RecordedOp& op) {
     Rect dest(texture.width, texture.height);
-    dest.translate(texture.left + op.unmappedBounds.left - texture.offset,
-            texture.top + op.unmappedBounds.top - texture.offset);
+    dest.translate(texture.left - texture.offset,
+            texture.top - texture.offset);
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
             .setRoundRectClipState(state.roundRectClipState)
index b9c13e6..c1f19a3 100644 (file)
@@ -201,6 +201,7 @@ void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices,
 }
 
 void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) {
+    LOG_ALWAYS_FATAL_IF(clip->mode != ClipMode::RectangleList, "can't rectlist clip without rectlist");
     auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList;
     int quadCount = rectList.getTransformedRectanglesCount();
     std::vector<Vertex> rectangleVertices;
@@ -234,6 +235,7 @@ void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) {
 }
 
 void BakedOpRenderer::setupStencilRegion(const ClipBase* clip) {
+    LOG_ALWAYS_FATAL_IF(clip->mode != ClipMode::Region, "can't region clip without region");
     auto&& region = reinterpret_cast<const ClipRegion*>(clip)->region;
 
     std::vector<Vertex> regionVertices;
index f1cc846..f0406fa 100644 (file)
@@ -48,10 +48,10 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s
     const Rect& clipRect = clipState->rect;
     if (CC_UNLIKELY(clipRect.isEmpty() || !clippedBounds.intersects(clipRect))) {
         // Rejected based on either empty clip, or bounds not intersecting with clip
-        if (clipState) {
-            allocator.rewindIfLastAlloc(clipState);
-            clipState = nullptr;
-        }
+
+        // Note: we could rewind the clipState object in situations where the clipRect is empty,
+        // but *only* if the caching logic within ClipArea was aware of the rewind.
+        clipState = nullptr;
         clippedBounds.setEmpty();
     } else {
         // Not rejected! compute true clippedBounds and clipSideFlags
index 5c7b43f..3db28c9 100644 (file)
@@ -99,6 +99,7 @@ class BakedOpState {
 public:
     static BakedOpState* tryConstruct(LinearAllocator& allocator,
             Snapshot& snapshot, const RecordedOp& recordedOp) {
+        if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
         BakedOpState* bakedState = new (allocator) BakedOpState(
                 allocator, snapshot, recordedOp, false);
         if (bakedState->computedState.clippedBounds.isEmpty()) {
@@ -118,6 +119,7 @@ public:
 
     static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator,
             Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
+        if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
         bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
                 ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
                 : true;
@@ -126,6 +128,7 @@ public:
                 allocator, snapshot, recordedOp, expandForStroke);
         if (bakedState->computedState.clippedBounds.isEmpty()) {
             // bounds are empty, so op is rejected
+            // NOTE: this won't succeed if a clip was allocated
             allocator.rewindIfLastAlloc(bakedState);
             return nullptr;
         }
@@ -134,7 +137,7 @@ public:
 
     static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
             Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
-        if (snapshot.getRenderTargetClip().isEmpty()) return nullptr;
+        if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
 
         // clip isn't empty, so construct the op
         return new (allocator) BakedOpState(allocator, snapshot, shadowOpPtr);
similarity index 64%
rename from libs/hwui/OpReorderer.cpp
rename to libs/hwui/FrameReorderer.cpp
index cff4f3c..4bfc0b4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "OpReorderer.h"
+#include "FrameReorderer.h"
 
 #include "LayerUpdateQueue.h"
 #include "RenderNode.h"
 namespace android {
 namespace uirenderer {
 
-class BatchBase {
-public:
-    BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
-            : mBatchId(batchId)
-            , mMerging(merging) {
-        mBounds = op->computedState.clippedBounds;
-        mOps.push_back(op);
-    }
-
-    bool intersects(const Rect& rect) const {
-        if (!rect.intersects(mBounds)) return false;
-
-        for (const BakedOpState* op : mOps) {
-            if (rect.intersects(op->computedState.clippedBounds)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    batchid_t getBatchId() const { return mBatchId; }
-    bool isMerging() const { return mMerging; }
-
-    const std::vector<BakedOpState*>& getOps() const { return mOps; }
-
-    void dump() const {
-        ALOGD("    Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
-                this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
-    }
-protected:
-    batchid_t mBatchId;
-    Rect mBounds;
-    std::vector<BakedOpState*> mOps;
-    bool mMerging;
-};
-
-class OpBatch : public BatchBase {
-public:
-    static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
-    }
-
-    OpBatch(batchid_t batchId, BakedOpState* op)
-            : BatchBase(batchId, op, false) {
-    }
-
-    void batchOp(BakedOpState* op) {
-        mBounds.unionWith(op->computedState.clippedBounds);
-        mOps.push_back(op);
-    }
-};
-
-class MergingOpBatch : public BatchBase {
-public:
-    static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
-    }
-
-    MergingOpBatch(batchid_t batchId, BakedOpState* op)
-            : BatchBase(batchId, op, true)
-            , mClipSideFlags(op->computedState.clipSideFlags) {
-    }
-
-    /*
-     * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
-     * and clip side flags. Positive bounds delta means new bounds fit in old.
-     */
-    static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
-            float boundsDelta) {
-        bool currentClipExists = currentFlags & side;
-        bool newClipExists = newFlags & side;
-
-        // if current is clipped, we must be able to fit new bounds in current
-        if (boundsDelta > 0 && currentClipExists) return false;
-
-        // if new is clipped, we must be able to fit current bounds in new
-        if (boundsDelta < 0 && newClipExists) return false;
-
-        return true;
-    }
-
-    static bool paintIsDefault(const SkPaint& paint) {
-        return paint.getAlpha() == 255
-                && paint.getColorFilter() == nullptr
-                && paint.getShader() == nullptr;
-    }
-
-    static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) {
-        return a.getAlpha() == b.getAlpha()
-                && a.getColorFilter() == b.getColorFilter()
-                && a.getShader() == b.getShader();
-    }
-
-    /*
-     * Checks if a (mergeable) op can be merged into this batch
-     *
-     * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
-     * important to consider all paint attributes used in the draw calls in deciding both a) if an
-     * op tries to merge at all, and b) if the op can merge with another set of ops
-     *
-     * False positives can lead to information from the paints of subsequent merged operations being
-     * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
-     */
-    bool canMergeWith(BakedOpState* op) const {
-        bool isTextBatch = getBatchId() == OpBatchType::Text
-                || getBatchId() == OpBatchType::ColorText;
-
-        // Overlapping other operations is only allowed for text without shadow. For other ops,
-        // multiDraw isn't guaranteed to overdraw correctly
-        if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) {
-            if (intersects(op->computedState.clippedBounds)) return false;
-        }
-
-        const BakedOpState* lhs = op;
-        const BakedOpState* rhs = mOps[0];
-
-        if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false;
-
-        // Identical round rect clip state means both ops will clip in the same way, or not at all.
-        // As the state objects are const, we can compare their pointers to determine mergeability
-        if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
-        if (lhs->projectionPathMask != rhs->projectionPathMask) return false;
-
-        /* Clipping compatibility check
-         *
-         * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
-         * clip for that side.
-         */
-        const int currentFlags = mClipSideFlags;
-        const int newFlags = op->computedState.clipSideFlags;
-        if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) {
-            const Rect& opBounds = op->computedState.clippedBounds;
-            float boundsDelta = mBounds.left - opBounds.left;
-            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false;
-            boundsDelta = mBounds.top - opBounds.top;
-            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false;
-
-            // right and bottom delta calculation reversed to account for direction
-            boundsDelta = opBounds.right - mBounds.right;
-            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false;
-            boundsDelta = opBounds.bottom - mBounds.bottom;
-            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false;
-        }
-
-        const SkPaint* newPaint = op->op->paint;
-        const SkPaint* oldPaint = mOps[0]->op->paint;
-
-        if (newPaint == oldPaint) {
-            // if paints are equal, then modifiers + paint attribs don't need to be compared
-            return true;
-        } else if (newPaint && !oldPaint) {
-            return paintIsDefault(*newPaint);
-        } else if (!newPaint && oldPaint) {
-            return paintIsDefault(*oldPaint);
-        }
-        return paintsAreEquivalent(*newPaint, *oldPaint);
-    }
-
-    void mergeOp(BakedOpState* op) {
-        mBounds.unionWith(op->computedState.clippedBounds);
-        mOps.push_back(op);
-
-        // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat
-        // check, and doesn't extend past a side of the clip that's in use by the merged batch.
-        // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect.
-        mClipSideFlags |= op->computedState.clipSideFlags;
-    }
-
-    int getClipSideFlags() const { return mClipSideFlags; }
-    const Rect& getClipRect() const { return mBounds; }
-
-private:
-    int mClipSideFlags;
-};
-
-OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
-        const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
-        : width(width)
-        , height(height)
-        , repaintRect(repaintRect)
-        , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
-        , beginLayerOp(beginLayerOp)
-        , renderNode(renderNode)
-        , viewportClip(Rect(width, height)) {}
-
-// iterate back toward target to see if anything drawn since should overlap the new op
-// if no target, merging ops still iterate to find similar batch to insert after
-void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
-        BatchBase** targetBatch, size_t* insertBatchIndex) const {
-    for (int i = mBatches.size() - 1; i >= 0; i--) {
-        BatchBase* overBatch = mBatches[i];
-
-        if (overBatch == *targetBatch) break;
-
-        // TODO: also consider shader shared between batch types
-        if (batchId == overBatch->getBatchId()) {
-            *insertBatchIndex = i + 1;
-            if (!*targetBatch) break; // found insert position, quit
-        }
-
-        if (overBatch->intersects(clippedBounds)) {
-            // NOTE: it may be possible to optimize for special cases where two operations
-            // of the same batch/paint could swap order, such as with a non-mergeable
-            // (clipped) and a mergeable text operation
-            *targetBatch = nullptr;
-            break;
-        }
-    }
-}
-
-void OpReorderer::LayerReorderer::deferLayerClear(const Rect& rect) {
-    mClearRects.push_back(rect);
-}
-
-void OpReorderer::LayerReorderer::flushLayerClears(LinearAllocator& allocator) {
-    if (CC_UNLIKELY(!mClearRects.empty())) {
-        const int vertCount = mClearRects.size() * 4;
-        // put the verts in the frame allocator, since
-        //     1) SimpleRectsOps needs verts, not rects
-        //     2) even if mClearRects stored verts, std::vectors will move their contents
-        Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex));
-
-        Vertex* currentVert = verts;
-        Rect bounds = mClearRects[0];
-        for (auto&& rect : mClearRects) {
-            bounds.unionWith(rect);
-            Vertex::set(currentVert++, rect.left, rect.top);
-            Vertex::set(currentVert++, rect.right, rect.top);
-            Vertex::set(currentVert++, rect.left, rect.bottom);
-            Vertex::set(currentVert++, rect.right, rect.bottom);
-        }
-        mClearRects.clear(); // discard rects before drawing so this method isn't reentrant
-
-        // One or more unclipped saveLayers have been enqueued, with deferred clears.
-        // Flush all of these clears with a single draw
-        SkPaint* paint = allocator.create<SkPaint>();
-        paint->setXfermodeMode(SkXfermode::kClear_Mode);
-        SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds,
-                Matrix4::identity(), nullptr, paint,
-                verts, vertCount);
-        BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
-                &viewportClip, bounds, *op);
-
-
-        deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
-    }
-}
-
-void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
-        BakedOpState* op, batchid_t batchId) {
-    if (batchId != OpBatchType::CopyToLayer) {
-        // if first op after one or more unclipped saveLayers, flush the layer clears
-        flushLayerClears(allocator);
-    }
-
-    OpBatch* targetBatch = mBatchLookup[batchId];
-
-    size_t insertBatchIndex = mBatches.size();
-    if (targetBatch) {
-        locateInsertIndex(batchId, op->computedState.clippedBounds,
-                (BatchBase**)(&targetBatch), &insertBatchIndex);
-    }
-
-    if (targetBatch) {
-        targetBatch->batchOp(op);
-    } else  {
-        // new non-merging batch
-        targetBatch = new (allocator) OpBatch(batchId, op);
-        mBatchLookup[batchId] = targetBatch;
-        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
-    }
-}
-
-void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
-        BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
-    if (batchId != OpBatchType::CopyToLayer) {
-        // if first op after one or more unclipped saveLayers, flush the layer clears
-        flushLayerClears(allocator);
-    }
-    MergingOpBatch* targetBatch = nullptr;
-
-    // Try to merge with any existing batch with same mergeId
-    auto getResult = mMergingBatchLookup[batchId].find(mergeId);
-    if (getResult != mMergingBatchLookup[batchId].end()) {
-        targetBatch = getResult->second;
-        if (!targetBatch->canMergeWith(op)) {
-            targetBatch = nullptr;
-        }
-    }
-
-    size_t insertBatchIndex = mBatches.size();
-    locateInsertIndex(batchId, op->computedState.clippedBounds,
-            (BatchBase**)(&targetBatch), &insertBatchIndex);
-
-    if (targetBatch) {
-        targetBatch->mergeOp(op);
-    } else  {
-        // new merging batch
-        targetBatch = new (allocator) MergingOpBatch(batchId, op);
-        mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
-
-        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
-    }
-}
-
-void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg,
-        BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const {
-    ATRACE_NAME("flush drawing commands");
-    for (const BatchBase* batch : mBatches) {
-        size_t size = batch->getOps().size();
-        if (size > 1 && batch->isMerging()) {
-            int opId = batch->getOps()[0]->op->opId;
-            const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch);
-            MergedBakedOpList data = {
-                    batch->getOps().data(),
-                    size,
-                    mergingBatch->getClipSideFlags(),
-                    mergingBatch->getClipRect()
-            };
-            mergedReceivers[opId](arg, data);
-        } else {
-            for (const BakedOpState* op : batch->getOps()) {
-                unmergedReceivers[op->op->opId](arg, *op);
-            }
-        }
-    }
-}
-
-void OpReorderer::LayerReorderer::dump() const {
-    ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
-            this, width, height, offscreenBuffer, beginLayerOp, renderNode);
-    for (const BatchBase* batch : mBatches) {
-        batch->dump();
-    }
-}
-
-OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+FrameReorderer::FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
         uint32_t viewportWidth, uint32_t viewportHeight,
         const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)
         : mCanvasState(*this) {
@@ -413,11 +77,11 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
     }
 }
 
-void OpReorderer::onViewportInitialized() {}
+void FrameReorderer::onViewportInitialized() {}
 
-void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+void FrameReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
 
-void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
+void FrameReorderer::deferNodePropsAndOps(RenderNode& node) {
     const RenderProperties& properties = node.properties();
     const Outline& outline = properties.getOutline();
     if (properties.getAlpha() <= 0
@@ -549,7 +213,7 @@ static size_t findNonNegativeIndex(const V& zTranslatedNodes) {
 }
 
 template <typename V>
-void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+void FrameReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
     const int size = zTranslatedNodes.size();
     if (size == 0
             || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
@@ -599,7 +263,7 @@ void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedN
     }
 }
 
-void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
+void FrameReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
     auto& node = *casterNodeOp.renderNode;
     auto& properties = node.properties();
 
@@ -655,7 +319,7 @@ void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
     }
 }
 
-void OpReorderer::deferProjectedChildren(const RenderNode& renderNode) {
+void FrameReorderer::deferProjectedChildren(const RenderNode& renderNode) {
     const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
     int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
 
@@ -688,15 +352,15 @@ void OpReorderer::deferProjectedChildren(const RenderNode& renderNode) {
 }
 
 /**
- * Used to define a list of lambdas referencing private OpReorderer::onXX::defer() methods.
+ * Used to define a list of lambdas referencing private FrameReorderer::onXX::defer() methods.
  *
  * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
- * E.g. a BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
+ * E.g. a BitmapOp op then would be dispatched to FrameReorderer::onBitmapOp(const BitmapOp&)
  */
 #define OP_RECEIVER(Type) \
-        [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
-void OpReorderer::deferNodeOps(const RenderNode& renderNode) {
-    typedef void (*OpDispatcher) (OpReorderer& reorderer, const RecordedOp& op);
+        [](FrameReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
+void FrameReorderer::deferNodeOps(const RenderNode& renderNode) {
+    typedef void (*OpDispatcher) (FrameReorderer& reorderer, const RecordedOp& op);
     static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER);
 
     // can't be null, since DL=null node rejection happens before deferNodePropsAndOps
@@ -720,7 +384,7 @@ void OpReorderer::deferNodeOps(const RenderNode& renderNode) {
     }
 }
 
-void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
+void FrameReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
     if (op.renderNode->nothingToDraw()) return;
     int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
 
@@ -735,7 +399,7 @@ void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
     mCanvasState.restoreToCount(count);
 }
 
-void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+void FrameReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
     if (!op.skipInOrderDraw) {
         deferRenderNodeOpImpl(op);
     }
@@ -745,7 +409,7 @@ void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
  * Defers an unmergeable, strokeable op, accounting correctly
  * for paint's style on the bounds being computed.
  */
-void OpReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+void FrameReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
         BakedOpState::StrokeBehavior strokeBehavior) {
     // Note: here we account for stroke when baking the op
     BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
@@ -767,7 +431,7 @@ static batchid_t tessBatchId(const RecordedOp& op) {
             : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
 }
 
-void OpReorderer::deferArcOp(const ArcOp& op) {
+void FrameReorderer::deferArcOp(const ArcOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
@@ -776,7 +440,7 @@ static bool hasMergeableClip(const BakedOpState& state) {
             || state.computedState.clipState->mode == ClipMode::Rectangle;
 }
 
-void OpReorderer::deferBitmapOp(const BitmapOp& op) {
+void FrameReorderer::deferBitmapOp(const BitmapOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
@@ -796,19 +460,19 @@ void OpReorderer::deferBitmapOp(const BitmapOp& op) {
     }
 }
 
-void OpReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
+void FrameReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
 }
 
-void OpReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
+void FrameReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
 }
 
-void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
+void FrameReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
     // allocate a temporary oval op (with mAllocator, so it persists until render), so the
     // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
     float x = *(op.x);
@@ -823,22 +487,22 @@ void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
     deferOvalOp(*resolvedOp);
 }
 
-void OpReorderer::deferFunctorOp(const FunctorOp& op) {
+void FrameReorderer::deferFunctorOp(const FunctorOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor);
 }
 
-void OpReorderer::deferLinesOp(const LinesOp& op) {
+void FrameReorderer::deferLinesOp(const LinesOp& op) {
     batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
     deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
 }
 
-void OpReorderer::deferOvalOp(const OvalOp& op) {
+void FrameReorderer::deferOvalOp(const OvalOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
-void OpReorderer::deferPatchOp(const PatchOp& op) {
+void FrameReorderer::deferPatchOp(const PatchOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
@@ -856,24 +520,24 @@ void OpReorderer::deferPatchOp(const PatchOp& op) {
     }
 }
 
-void OpReorderer::deferPathOp(const PathOp& op) {
+void FrameReorderer::deferPathOp(const PathOp& op) {
     deferStrokeableOp(op, OpBatchType::Bitmap);
 }
 
-void OpReorderer::deferPointsOp(const PointsOp& op) {
+void FrameReorderer::deferPointsOp(const PointsOp& op) {
     batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
     deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
 }
 
-void OpReorderer::deferRectOp(const RectOp& op) {
+void FrameReorderer::deferRectOp(const RectOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
-void OpReorderer::deferRoundRectOp(const RoundRectOp& op) {
+void FrameReorderer::deferRoundRectOp(const RoundRectOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
-void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
+void FrameReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
     // allocate a temporary round rect op (with mAllocator, so it persists until render), so the
     // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
     const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
@@ -884,7 +548,7 @@ void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
     deferRoundRectOp(*resolvedOp);
 }
 
-void OpReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
+void FrameReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
@@ -895,7 +559,7 @@ static batchid_t textBatchId(const SkPaint& paint) {
     return paint.getColor() == SK_ColorBLACK ? OpBatchType::Text : OpBatchType::ColorText;
 }
 
-void OpReorderer::deferTextOp(const TextOp& op) {
+void FrameReorderer::deferTextOp(const TextOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
@@ -910,19 +574,19 @@ void OpReorderer::deferTextOp(const TextOp& op) {
     }
 }
 
-void OpReorderer::deferTextOnPathOp(const TextOnPathOp& op) {
+void FrameReorderer::deferTextOnPathOp(const TextOnPathOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));
 }
 
-void OpReorderer::deferTextureLayerOp(const TextureLayerOp& op) {
+void FrameReorderer::deferTextureLayerOp(const TextureLayerOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);
 }
 
-void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+void FrameReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
         float contentTranslateX, float contentTranslateY,
         const Rect& repaintRect,
         const Vector3& lightCenter,
@@ -941,7 +605,7 @@ void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
     mLayerReorderers.emplace_back(layerWidth, layerHeight, repaintRect, beginLayerOp, renderNode);
 }
 
-void OpReorderer::restoreForLayer() {
+void FrameReorderer::restoreForLayer() {
     // restore canvas, and pop finished layer off of the stack
     mCanvasState.restore();
     mLayerStack.pop_back();
@@ -949,7 +613,7 @@ void OpReorderer::restoreForLayer() {
 
 // TODO: defer time rejection (when bounds become empty) + tests
 // Option - just skip layers with no bounds at playback + defer?
-void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
+void FrameReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
     uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
     uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
 
@@ -994,7 +658,7 @@ void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
             &op, nullptr);
 }
 
-void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
+void FrameReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
     const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
     int finishedLayerIndex = mLayerStack.back();
 
@@ -1022,7 +686,7 @@ void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
     }
 }
 
-void OpReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
+void FrameReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
     Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform));
     boundsTransform.multiply(op.localMatrix);
 
@@ -1057,7 +721,7 @@ void OpReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
     currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
 }
 
-void OpReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
+void FrameReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
     LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
 
     BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
similarity index 65%
rename from libs/hwui/OpReorderer.h
rename to libs/hwui/FrameReorderer.h
index 8d9d90b..562e6a1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 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.
  * limitations under the License.
  */
 
-#ifndef ANDROID_HWUI_OP_REORDERER_H
-#define ANDROID_HWUI_OP_REORDERER_H
+#pragma once
 
 #include "BakedOpState.h"
 #include "CanvasState.h"
 #include "DisplayList.h"
+#include "LayerReorderer.h"
 #include "RecordedOp.h"
 
 #include <vector>
@@ -31,114 +31,34 @@ namespace android {
 namespace uirenderer {
 
 class BakedOpState;
-class BatchBase;
 class LayerUpdateQueue;
-class MergingOpBatch;
 class OffscreenBuffer;
-class OpBatch;
 class Rect;
 
-typedef int batchid_t;
-typedef const void* mergeid_t;
-
-namespace OpBatchType {
-    enum {
-        Bitmap,
-        MergedPatch,
-        AlphaVertices,
-        Vertices,
-        AlphaMaskTexture,
-        Text,
-        ColorText,
-        Shadow,
-        TextureLayer,
-        Functor,
-        CopyToLayer,
-        CopyFromLayer,
-
-        Count // must be last
-    };
-}
-
-class OpReorderer : public CanvasStateClient {
-    typedef void (*BakedOpReceiver)(void*, const BakedOpState&);
-    typedef void (*MergedOpReceiver)(void*, const MergedBakedOpList& opList);
-
-    /**
-     * Stores the deferred render operations and state used to compute ordering
-     * for a single FBO/layer.
-     */
-    class LayerReorderer {
-    public:
-        // Create LayerReorderer for Fbo0
-        LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect)
-                : LayerReorderer(width, height, repaintRect, nullptr, nullptr) {};
-
-        // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
-        // saveLayer, renderNode is present for a HW layer.
-        LayerReorderer(uint32_t width, uint32_t height,
-                const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
-
-        // iterate back toward target to see if anything drawn since should overlap the new op
-        // if no target, merging ops still iterate to find similar batch to insert after
-        void locateInsertIndex(int batchId, const Rect& clippedBounds,
-                BatchBase** targetBatch, size_t* insertBatchIndex) const;
-
-        void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId);
-
-        // insertion point of a new batch, will hopefully be immediately after similar batch
-        // (generally, should be similar shader)
-        void deferMergeableOp(LinearAllocator& allocator,
-                BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
-
-        void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers, MergedOpReceiver*) const;
-
-        void deferLayerClear(const Rect& dstRect);
-
-        bool empty() const {
-            return mBatches.empty();
-        }
-
-        void clear() {
-            mBatches.clear();
-        }
-
-        void dump() const;
-
-        const uint32_t width;
-        const uint32_t height;
-        const Rect repaintRect;
-        OffscreenBuffer* offscreenBuffer;
-        const BeginLayerOp* beginLayerOp;
-        const RenderNode* renderNode;
-        const ClipRect viewportClip;
-
-        // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
-        std::vector<BakedOpState*> activeUnclippedSaveLayers;
-    private:
-        void flushLayerClears(LinearAllocator& allocator);
-
-        std::vector<BatchBase*> mBatches;
-
-        /**
-         * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
-         * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
-         * collide, which avoids the need to resolve mergeid collisions.
-         */
-        std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count];
-
-        // Maps batch ids to the most recent *non-merging* batch of that id
-        OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
-
-        std::vector<Rect> mClearRects;
-    };
-
+/**
+ * Traverses all of the drawing commands from the layers and RenderNodes passed into it, preparing
+ * them to be rendered.
+ *
+ * Resolves final drawing state for each operation (including clip, alpha and matrix), and then
+ * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
+ * from the LayerUpdateQueue, or temporary layers created by saveLayer operations in the
+ * draw stream) will create different reorder contexts, each in its own LayerReorderer.
+ *
+ * Then the prepared or 'baked' drawing commands can be issued by calling the templated
+ * replayBakedOps() function, which will dispatch them (including any created merged op collections)
+ * to a Dispatcher and Renderer. See BakedOpDispatcher for how these baked drawing operations are
+ * resolved into Glops and rendered via BakedOpRenderer.
+ *
+ * This class is also the authoritative source for traversing RenderNodes, both for standard op
+ * traversal within a DisplayList, and for out of order RenderNode traversal for Z and projection.
+ */
+class FrameReorderer : public CanvasStateClient {
 public:
-    OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+    FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
             uint32_t viewportWidth, uint32_t viewportHeight,
             const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
 
-    virtual ~OpReorderer() {}
+    virtual ~FrameReorderer() {}
 
     /**
      * replayBakedOps() is templated based on what class will receive ops being replayed.
@@ -253,7 +173,7 @@ private:
             BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
 
     /**
-     * Declares all OpReorderer::deferXXXXOp() methods for every RecordedOp type.
+     * Declares all FrameReorderer::deferXXXXOp() methods for every RecordedOp type.
      *
      * These private methods are called from within deferImpl to defer each individual op
      * type differently.
@@ -287,5 +207,3 @@ private:
 
 }; // namespace uirenderer
 }; // namespace android
-
-#endif // ANDROID_HWUI_OP_REORDERER_H
diff --git a/libs/hwui/LayerReorderer.cpp b/libs/hwui/LayerReorderer.cpp
new file mode 100644 (file)
index 0000000..9a17e93
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2016 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 "LayerReorderer.h"
+
+#include "BakedOpState.h"
+#include "RenderNode.h"
+#include "utils/PaintUtils.h"
+#include "utils/TraceUtils.h"
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+namespace uirenderer {
+
+class BatchBase {
+public:
+    BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
+            : mBatchId(batchId)
+            , mMerging(merging) {
+        mBounds = op->computedState.clippedBounds;
+        mOps.push_back(op);
+    }
+
+    bool intersects(const Rect& rect) const {
+        if (!rect.intersects(mBounds)) return false;
+
+        for (const BakedOpState* op : mOps) {
+            if (rect.intersects(op->computedState.clippedBounds)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    batchid_t getBatchId() const { return mBatchId; }
+    bool isMerging() const { return mMerging; }
+
+    const std::vector<BakedOpState*>& getOps() const { return mOps; }
+
+    void dump() const {
+        ALOGD("    Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
+                this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
+    }
+protected:
+    batchid_t mBatchId;
+    Rect mBounds;
+    std::vector<BakedOpState*> mOps;
+    bool mMerging;
+};
+
+class OpBatch : public BatchBase {
+public:
+    static void* operator new(size_t size, LinearAllocator& allocator) {
+        return allocator.alloc(size);
+    }
+
+    OpBatch(batchid_t batchId, BakedOpState* op)
+            : BatchBase(batchId, op, false) {
+    }
+
+    void batchOp(BakedOpState* op) {
+        mBounds.unionWith(op->computedState.clippedBounds);
+        mOps.push_back(op);
+    }
+};
+
+class MergingOpBatch : public BatchBase {
+public:
+    static void* operator new(size_t size, LinearAllocator& allocator) {
+        return allocator.alloc(size);
+    }
+
+    MergingOpBatch(batchid_t batchId, BakedOpState* op)
+            : BatchBase(batchId, op, true)
+            , mClipSideFlags(op->computedState.clipSideFlags) {
+    }
+
+    /*
+     * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
+     * and clip side flags. Positive bounds delta means new bounds fit in old.
+     */
+    static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
+            float boundsDelta) {
+        bool currentClipExists = currentFlags & side;
+        bool newClipExists = newFlags & side;
+
+        // if current is clipped, we must be able to fit new bounds in current
+        if (boundsDelta > 0 && currentClipExists) return false;
+
+        // if new is clipped, we must be able to fit current bounds in new
+        if (boundsDelta < 0 && newClipExists) return false;
+
+        return true;
+    }
+
+    static bool paintIsDefault(const SkPaint& paint) {
+        return paint.getAlpha() == 255
+                && paint.getColorFilter() == nullptr
+                && paint.getShader() == nullptr;
+    }
+
+    static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) {
+        // Note: don't check color, since all currently mergeable ops can merge across colors
+        return a.getAlpha() == b.getAlpha()
+                && a.getColorFilter() == b.getColorFilter()
+                && a.getShader() == b.getShader();
+    }
+
+    /*
+     * Checks if a (mergeable) op can be merged into this batch
+     *
+     * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
+     * important to consider all paint attributes used in the draw calls in deciding both a) if an
+     * op tries to merge at all, and b) if the op can merge with another set of ops
+     *
+     * False positives can lead to information from the paints of subsequent merged operations being
+     * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
+     */
+    bool canMergeWith(BakedOpState* op) const {
+        bool isTextBatch = getBatchId() == OpBatchType::Text
+                || getBatchId() == OpBatchType::ColorText;
+
+        // Overlapping other operations is only allowed for text without shadow. For other ops,
+        // multiDraw isn't guaranteed to overdraw correctly
+        if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) {
+            if (intersects(op->computedState.clippedBounds)) return false;
+        }
+
+        const BakedOpState* lhs = op;
+        const BakedOpState* rhs = mOps[0];
+
+        if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false;
+
+        // Identical round rect clip state means both ops will clip in the same way, or not at all.
+        // As the state objects are const, we can compare their pointers to determine mergeability
+        if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
+        if (lhs->projectionPathMask != rhs->projectionPathMask) return false;
+
+        /* Clipping compatibility check
+         *
+         * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
+         * clip for that side.
+         */
+        const int currentFlags = mClipSideFlags;
+        const int newFlags = op->computedState.clipSideFlags;
+        if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) {
+            const Rect& opBounds = op->computedState.clippedBounds;
+            float boundsDelta = mBounds.left - opBounds.left;
+            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false;
+            boundsDelta = mBounds.top - opBounds.top;
+            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false;
+
+            // right and bottom delta calculation reversed to account for direction
+            boundsDelta = opBounds.right - mBounds.right;
+            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false;
+            boundsDelta = opBounds.bottom - mBounds.bottom;
+            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false;
+        }
+
+        const SkPaint* newPaint = op->op->paint;
+        const SkPaint* oldPaint = mOps[0]->op->paint;
+
+        if (newPaint == oldPaint) {
+            // if paints are equal, then modifiers + paint attribs don't need to be compared
+            return true;
+        } else if (newPaint && !oldPaint) {
+            return paintIsDefault(*newPaint);
+        } else if (!newPaint && oldPaint) {
+            return paintIsDefault(*oldPaint);
+        }
+        return paintsAreEquivalent(*newPaint, *oldPaint);
+    }
+
+    void mergeOp(BakedOpState* op) {
+        mBounds.unionWith(op->computedState.clippedBounds);
+        mOps.push_back(op);
+
+        // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat
+        // check, and doesn't extend past a side of the clip that's in use by the merged batch.
+        // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect.
+        mClipSideFlags |= op->computedState.clipSideFlags;
+    }
+
+    int getClipSideFlags() const { return mClipSideFlags; }
+    const Rect& getClipRect() const { return mBounds; }
+
+private:
+    int mClipSideFlags;
+};
+
+LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
+        const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
+        : width(width)
+        , height(height)
+        , repaintRect(repaintRect)
+        , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
+        , beginLayerOp(beginLayerOp)
+        , renderNode(renderNode)
+        , viewportClip(Rect(width, height)) {}
+
+// iterate back toward target to see if anything drawn since should overlap the new op
+// if no target, merging ops still iterate to find similar batch to insert after
+void LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
+        BatchBase** targetBatch, size_t* insertBatchIndex) const {
+    for (int i = mBatches.size() - 1; i >= 0; i--) {
+        BatchBase* overBatch = mBatches[i];
+
+        if (overBatch == *targetBatch) break;
+
+        // TODO: also consider shader shared between batch types
+        if (batchId == overBatch->getBatchId()) {
+            *insertBatchIndex = i + 1;
+            if (!*targetBatch) break; // found insert position, quit
+        }
+
+        if (overBatch->intersects(clippedBounds)) {
+            // NOTE: it may be possible to optimize for special cases where two operations
+            // of the same batch/paint could swap order, such as with a non-mergeable
+            // (clipped) and a mergeable text operation
+            *targetBatch = nullptr;
+            break;
+        }
+    }
+}
+
+void LayerReorderer::deferLayerClear(const Rect& rect) {
+    mClearRects.push_back(rect);
+}
+
+void LayerReorderer::flushLayerClears(LinearAllocator& allocator) {
+    if (CC_UNLIKELY(!mClearRects.empty())) {
+        const int vertCount = mClearRects.size() * 4;
+        // put the verts in the frame allocator, since
+        //     1) SimpleRectsOps needs verts, not rects
+        //     2) even if mClearRects stored verts, std::vectors will move their contents
+        Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex));
+
+        Vertex* currentVert = verts;
+        Rect bounds = mClearRects[0];
+        for (auto&& rect : mClearRects) {
+            bounds.unionWith(rect);
+            Vertex::set(currentVert++, rect.left, rect.top);
+            Vertex::set(currentVert++, rect.right, rect.top);
+            Vertex::set(currentVert++, rect.left, rect.bottom);
+            Vertex::set(currentVert++, rect.right, rect.bottom);
+        }
+        mClearRects.clear(); // discard rects before drawing so this method isn't reentrant
+
+        // One or more unclipped saveLayers have been enqueued, with deferred clears.
+        // Flush all of these clears with a single draw
+        SkPaint* paint = allocator.create<SkPaint>();
+        paint->setXfermodeMode(SkXfermode::kClear_Mode);
+        SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds,
+                Matrix4::identity(), nullptr, paint,
+                verts, vertCount);
+        BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
+                &viewportClip, bounds, *op);
+
+
+        deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
+    }
+}
+
+void LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
+        BakedOpState* op, batchid_t batchId) {
+    if (batchId != OpBatchType::CopyToLayer) {
+        // if first op after one or more unclipped saveLayers, flush the layer clears
+        flushLayerClears(allocator);
+    }
+
+    OpBatch* targetBatch = mBatchLookup[batchId];
+
+    size_t insertBatchIndex = mBatches.size();
+    if (targetBatch) {
+        locateInsertIndex(batchId, op->computedState.clippedBounds,
+                (BatchBase**)(&targetBatch), &insertBatchIndex);
+    }
+
+    if (targetBatch) {
+        targetBatch->batchOp(op);
+    } else  {
+        // new non-merging batch
+        targetBatch = new (allocator) OpBatch(batchId, op);
+        mBatchLookup[batchId] = targetBatch;
+        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+    }
+}
+
+void LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
+        BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
+    if (batchId != OpBatchType::CopyToLayer) {
+        // if first op after one or more unclipped saveLayers, flush the layer clears
+        flushLayerClears(allocator);
+    }
+    MergingOpBatch* targetBatch = nullptr;
+
+    // Try to merge with any existing batch with same mergeId
+    auto getResult = mMergingBatchLookup[batchId].find(mergeId);
+    if (getResult != mMergingBatchLookup[batchId].end()) {
+        targetBatch = getResult->second;
+        if (!targetBatch->canMergeWith(op)) {
+            targetBatch = nullptr;
+        }
+    }
+
+    size_t insertBatchIndex = mBatches.size();
+    locateInsertIndex(batchId, op->computedState.clippedBounds,
+            (BatchBase**)(&targetBatch), &insertBatchIndex);
+
+    if (targetBatch) {
+        targetBatch->mergeOp(op);
+    } else  {
+        // new merging batch
+        targetBatch = new (allocator) MergingOpBatch(batchId, op);
+        mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
+
+        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+    }
+}
+
+void LayerReorderer::replayBakedOpsImpl(void* arg,
+        BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const {
+    ATRACE_NAME("flush drawing commands");
+    for (const BatchBase* batch : mBatches) {
+        size_t size = batch->getOps().size();
+        if (size > 1 && batch->isMerging()) {
+            int opId = batch->getOps()[0]->op->opId;
+            const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch);
+            MergedBakedOpList data = {
+                    batch->getOps().data(),
+                    size,
+                    mergingBatch->getClipSideFlags(),
+                    mergingBatch->getClipRect()
+            };
+            mergedReceivers[opId](arg, data);
+        } else {
+            for (const BakedOpState* op : batch->getOps()) {
+                unmergedReceivers[op->op->opId](arg, *op);
+            }
+        }
+    }
+}
+
+void LayerReorderer::dump() const {
+    ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
+            this, width, height, offscreenBuffer, beginLayerOp, renderNode);
+    for (const BatchBase* batch : mBatches) {
+        batch->dump();
+    }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/LayerReorderer.h b/libs/hwui/LayerReorderer.h
new file mode 100644 (file)
index 0000000..83cda81
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "ClipArea.h"
+#include "Rect.h"
+
+#include <vector>
+#include <unordered_map>
+
+struct SkRect;
+
+namespace android {
+namespace uirenderer {
+
+class BakedOpState;
+struct BeginLayerOp;
+class BatchBase;
+class LinearAllocator;
+struct MergedBakedOpList;
+class MergingOpBatch;
+class OffscreenBuffer;
+class OpBatch;
+class RenderNode;
+
+typedef int batchid_t;
+typedef const void* mergeid_t;
+
+namespace OpBatchType {
+    enum {
+        Bitmap,
+        MergedPatch,
+        AlphaVertices,
+        Vertices,
+        AlphaMaskTexture,
+        Text,
+        ColorText,
+        Shadow,
+        TextureLayer,
+        Functor,
+        CopyToLayer,
+        CopyFromLayer,
+
+        Count // must be last
+    };
+}
+
+typedef void (*BakedOpReceiver)(void*, const BakedOpState&);
+typedef void (*MergedOpReceiver)(void*, const MergedBakedOpList& opList);
+
+/**
+ * Stores the deferred render operations and state used to compute ordering
+ * for a single FBO/layer.
+ */
+class LayerReorderer {
+public:
+    // Create LayerReorderer for Fbo0
+    LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect)
+            : LayerReorderer(width, height, repaintRect, nullptr, nullptr) {};
+
+    // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
+    // saveLayer, renderNode is present for a HW layer.
+    LayerReorderer(uint32_t width, uint32_t height,
+            const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+
+    // iterate back toward target to see if anything drawn since should overlap the new op
+    // if no target, merging ops still iterate to find similar batch to insert after
+    void locateInsertIndex(int batchId, const Rect& clippedBounds,
+            BatchBase** targetBatch, size_t* insertBatchIndex) const;
+
+    void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId);
+
+    // insertion point of a new batch, will hopefully be immediately after similar batch
+    // (generally, should be similar shader)
+    void deferMergeableOp(LinearAllocator& allocator,
+            BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
+
+    void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers, MergedOpReceiver*) const;
+
+    void deferLayerClear(const Rect& dstRect);
+
+    bool empty() const {
+        return mBatches.empty();
+    }
+
+    void clear() {
+        mBatches.clear();
+    }
+
+    void dump() const;
+
+    const uint32_t width;
+    const uint32_t height;
+    const Rect repaintRect;
+    OffscreenBuffer* offscreenBuffer;
+    const BeginLayerOp* beginLayerOp;
+    const RenderNode* renderNode;
+    const ClipRect viewportClip;
+
+    // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
+    std::vector<BakedOpState*> activeUnclippedSaveLayers;
+private:
+    void flushLayerClears(LinearAllocator& allocator);
+
+    std::vector<BatchBase*> mBatches;
+
+    /**
+     * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
+     * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
+     * collide, which avoids the need to resolve mergeid collisions.
+     */
+    std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count];
+
+    // Maps batch ids to the most recent *non-merging* batch of that id
+    OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
+
+    std::vector<Rect> mClearRects;
+};
+
+}; // namespace uirenderer
+}; // namespace android
index b6f50b1..612cdfd 100644 (file)
@@ -87,7 +87,7 @@ class RenderNode;
  */
 class RenderNode : public VirtualLightRefBase {
 friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
-friend class OpReorderer;
+friend class FrameReorderer;
 public:
     enum DirtyPropertyMask {
         GENERIC         = 1 << 1,
index 1af8f80..fff8e09 100644 (file)
@@ -31,7 +31,7 @@
 #include "utils/TimeUtils.h"
 
 #if HWUI_NEW_OPS
-#include "OpReorderer.h"
+#include "FrameReorderer.h"
 #endif
 
 #include <cutils/properties.h>
@@ -338,7 +338,7 @@ void CanvasContext::draw() {
     mEglManager.damageFrame(frame, dirty);
 
 #if HWUI_NEW_OPS
-    OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
+    FrameReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
             mRenderNodes, mLightCenter);
     mLayerUpdateQueue.clear();
     BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -19,8 +19,8 @@
 #include "BakedOpState.h"
 #include "BakedOpDispatcher.h"
 #include "BakedOpRenderer.h"
+#include "FrameReorderer.h"
 #include "LayerUpdateQueue.h"
-#include "OpReorderer.h"
 #include "RecordedOp.h"
 #include "RecordingCanvas.h"
 #include "tests/common/TestContext.h"
@@ -61,20 +61,20 @@ static std::vector<sp<RenderNode>> createTestNodeList() {
     return vec;
 }
 
-BENCHMARK_NO_ARG(BM_OpReorderer_defer);
-void BM_OpReorderer_defer::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_defer);
+void BM_FrameBuilder_defer::Run(int iters) {
     auto nodes = createTestNodeList();
     StartBenchmarkTiming();
     for (int i = 0; i < iters; i++) {
-        OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+        FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
                 nodes, sLightCenter);
         MicroBench::DoNotOptimize(&reorderer);
     }
     StopBenchmarkTiming();
 }
 
-BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender);
-void BM_OpReorderer_deferAndRender::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_deferAndRender);
+void BM_FrameBuilder_deferAndRender::Run(int iters) {
     TestUtils::runOnRenderThread([this, iters](RenderThread& thread) {
         auto nodes = createTestNodeList();
         BakedOpRenderer::LightInfo lightInfo = {50.0f, 128, 128 };
@@ -84,7 +84,7 @@ void BM_OpReorderer_deferAndRender::Run(int iters) {
 
         StartBenchmarkTiming();
         for (int i = 0; i < iters; i++) {
-            OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+            FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
                     nodes, sLightCenter);
 
             BakedOpRenderer renderer(caches, renderState, true, lightInfo);
@@ -117,7 +117,7 @@ static void benchDeferScene(testing::Benchmark& benchmark, int iters, const char
     auto nodes = getSyncedSceneNodes(sceneName);
     benchmark.StartBenchmarkTiming();
     for (int i = 0; i < iters; i++) {
-        OpReorderer reorderer(sEmptyLayerUpdateQueue,
+        FrameReorderer reorderer(sEmptyLayerUpdateQueue,
                 SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
                 nodes, sLightCenter);
         MicroBench::DoNotOptimize(&reorderer);
@@ -136,7 +136,7 @@ static void benchDeferAndRenderScene(testing::Benchmark& benchmark,
 
         benchmark.StartBenchmarkTiming();
         for (int i = 0; i < iters; i++) {
-            OpReorderer reorderer(sEmptyLayerUpdateQueue,
+            FrameReorderer reorderer(sEmptyLayerUpdateQueue,
                     SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
                     nodes, sLightCenter);
 
@@ -148,13 +148,13 @@ static void benchDeferAndRenderScene(testing::Benchmark& benchmark,
     });
 }
 
-BENCHMARK_NO_ARG(BM_OpReorderer_listview_defer);
-void BM_OpReorderer_listview_defer::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_listview_defer);
+void BM_FrameBuilder_listview_defer::Run(int iters) {
     benchDeferScene(*this, iters, "listview");
 }
 
-BENCHMARK_NO_ARG(BM_OpReorderer_listview_deferAndRender);
-void BM_OpReorderer_listview_deferAndRender::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_listview_deferAndRender);
+void BM_FrameBuilder_listview_deferAndRender::Run(int iters) {
     benchDeferAndRenderScene(*this, iters, "listview");
 }
 
index 3fd822d..b350686 100644 (file)
@@ -176,29 +176,26 @@ TEST(ResolvedRenderState, construct_expandForStroke) {
 }
 
 TEST(BakedOpState, tryConstruct) {
-    LinearAllocator allocator;
-
     Matrix4 translate100x0;
     translate100x0.loadTranslate(100, 0, 0);
 
     SkPaint paint;
     ClipRect clip(Rect(100, 200));
-    {
-        RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, &clip, &paint);
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
-        BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
-
-        EXPECT_EQ(nullptr, bakedState); // rejected by clip, so not constructed
-        EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
-    }
-    {
-        RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), &clip, &paint);
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
-        BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
 
-        EXPECT_NE(nullptr, bakedState); // NOT rejected by clip, so will be constructed
-        EXPECT_LE(64u, allocator.usedSize()); // relatively large alloc for non-rejected op
-    }
+    LinearAllocator allocator;
+    RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), &clip, &paint);
+    auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+    EXPECT_NE(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, successOp))
+            << "successOp NOT rejected by clip, so should be constructed";
+    size_t successAllocSize = allocator.usedSize();
+    EXPECT_LE(64u, successAllocSize) << "relatively large alloc for non-rejected op";
+
+    RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, &clip, &paint);
+    EXPECT_EQ(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, rejectOp))
+            << "rejectOp rejected by clip, so should not be constructed";
+
+    // NOTE: this relies on the clip having already been serialized by the op above
+    EXPECT_EQ(successAllocSize, allocator.usedSize()) << "no extra allocation used for rejected op";
 }
 
 TEST(BakedOpState, tryShadowOpConstruct) {
@@ -207,15 +204,16 @@ TEST(BakedOpState, tryShadowOpConstruct) {
         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
         BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
 
-        EXPECT_EQ(nullptr, bakedState); // rejected by clip, so not constructed
-        EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
+        EXPECT_EQ(nullptr, bakedState) << "op should be rejected by clip, so not constructed";
+        EXPECT_EQ(0u, allocator.usedSize()) << "no serialization, even for clip,"
+                "since op is quick rejected based on snapshot clip";
     }
     {
         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
         BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
 
-        ASSERT_NE(nullptr, bakedState); // NOT rejected by clip, so will be constructed
-        EXPECT_LE(64u, allocator.usedSize()); // relatively large alloc for non-rejected op
+        ASSERT_NE(nullptr, bakedState) << "NOT rejected by clip, so op should be constructed";
+        EXPECT_LE(64u, allocator.usedSize()) << "relatively large alloc for non-rejected op";
     }
 }
 
similarity index 93%
rename from libs/hwui/tests/unit/OpReordererTests.cpp
rename to libs/hwui/tests/unit/FrameReordererTests.cpp
index 701e446..9d2eb98 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -18,8 +18,8 @@
 
 #include <BakedOpState.h>
 #include <DeferredLayerUpdater.h>
+#include <FrameReorderer.h>
 #include <LayerUpdateQueue.h>
-#include <OpReorderer.h>
 #include <RecordedOp.h>
 #include <RecordingCanvas.h>
 #include <tests/common/TestUtils.h>
@@ -113,7 +113,7 @@ public:
 
 class FailRenderer : public TestRendererBase {};
 
-TEST(OpReorderer, simple) {
+TEST(FrameReorderer, simple) {
     class SimpleTestRenderer : public TestRendererBase {
     public:
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -138,14 +138,14 @@ TEST(OpReorderer, simple) {
         canvas.drawRect(0, 0, 100, 200, SkPaint());
         canvas.drawBitmap(bitmap, 10, 10, nullptr);
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
             createSyncedNodeList(node), sLightCenter);
     SimpleTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
 }
 
-TEST(OpReorderer, simpleStroke) {
+TEST(FrameReorderer, simpleStroke) {
     class SimpleStrokeTestRenderer : public TestRendererBase {
     public:
         void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
@@ -164,14 +164,14 @@ TEST(OpReorderer, simpleStroke) {
         strokedPaint.setStrokeWidth(10);
         canvas.drawPoint(50, 50, strokedPaint);
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
             createSyncedNodeList(node), sLightCenter);
     SimpleStrokeTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-TEST(OpReorderer, simpleRejection) {
+TEST(FrameReorderer, simpleRejection) {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -179,14 +179,14 @@ TEST(OpReorderer, simpleRejection) {
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
 
     FailRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(OpReorderer, simpleBatching) {
+TEST(FrameReorderer, simpleBatching) {
     const int LOOPS = 5;
     class SimpleBatchingTestRenderer : public TestRendererBase {
     public:
@@ -214,7 +214,7 @@ TEST(OpReorderer, simpleBatching) {
         canvas.restore();
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     SimpleBatchingTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -222,7 +222,7 @@ TEST(OpReorderer, simpleBatching) {
             << "Expect number of ops = 2 * loop count";
 }
 
-TEST(OpReorderer, clippedMerging) {
+TEST(FrameReorderer, clippedMerging) {
     class ClippedMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -255,14 +255,14 @@ TEST(OpReorderer, clippedMerging) {
         canvas.drawBitmap(bitmap, 40, 70, nullptr);
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
             createSyncedNodeList(node), sLightCenter);
     ClippedMergingTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(OpReorderer, textMerging) {
+TEST(FrameReorderer, textMerging) {
     class TextMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -283,14 +283,14 @@ TEST(OpReorderer, textMerging) {
         TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
         TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
             createSyncedNodeList(node), sLightCenter);
     TextMergingTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
 }
 
-TEST(OpReorderer, textStrikethrough) {
+TEST(FrameReorderer, textStrikethrough) {
     const int LOOPS = 5;
     class TextStrikethroughTestRenderer : public TestRendererBase {
     public:
@@ -314,7 +314,7 @@ TEST(OpReorderer, textStrikethrough) {
             TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
         }
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
             createSyncedNodeList(node), sLightCenter);
     TextStrikethroughTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -322,7 +322,7 @@ TEST(OpReorderer, textStrikethrough) {
             << "Expect number of ops = 2 * loop count";
 }
 
-RENDERTHREAD_TEST(OpReorderer, textureLayer) {
+RENDERTHREAD_TEST(FrameReorderer, textureLayer) {
     class TextureLayerTestRenderer : public TestRendererBase {
     public:
         void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -348,14 +348,14 @@ RENDERTHREAD_TEST(OpReorderer, textureLayer) {
         canvas.drawLayer(layerUpdater.get());
         canvas.restore();
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     TextureLayerTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-TEST(OpReorderer, renderNode) {
+TEST(FrameReorderer, renderNode) {
     class RenderNodeTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -393,13 +393,13 @@ TEST(OpReorderer, renderNode) {
         canvas.restore();
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(parent), sLightCenter);
     RenderNodeTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(OpReorderer, clipped) {
+TEST(FrameReorderer, clipped) {
     class ClippedTestRenderer : public TestRendererBase {
     public:
         void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
@@ -416,14 +416,14 @@ TEST(OpReorderer, clipped) {
         canvas.drawBitmap(bitmap, 0, 0, nullptr);
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue,
             SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
             200, 200, createSyncedNodeList(node), sLightCenter);
     ClippedTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(OpReorderer, saveLayer_simple) {
+TEST(FrameReorderer, saveLayer_simple) {
     class SaveLayerSimpleTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -459,14 +459,14 @@ TEST(OpReorderer, saveLayer_simple) {
         canvas.drawRect(10, 10, 190, 190, SkPaint());
         canvas.restore();
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     SaveLayerSimpleTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(OpReorderer, saveLayer_nested) {
+TEST(FrameReorderer, saveLayer_nested) {
     /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
      * - startTemporaryLayer2, rect2 endLayer2
      * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
@@ -531,14 +531,14 @@ TEST(OpReorderer, saveLayer_nested) {
         canvas.restore();
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
             createSyncedNodeList(node), sLightCenter);
     SaveLayerNestedTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex());
 }
 
-TEST(OpReorderer, saveLayer_contentRejection) {
+TEST(FrameReorderer, saveLayer_contentRejection) {
         auto node = TestUtils::createNode(0, 0, 200, 200,
                 [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -551,7 +551,7 @@ TEST(OpReorderer, saveLayer_contentRejection) {
         canvas.restore();
         canvas.restore();
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
 
     FailRenderer renderer;
@@ -559,7 +559,7 @@ TEST(OpReorderer, saveLayer_contentRejection) {
     reorderer.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(OpReorderer, saveLayerUnclipped_simple) {
+TEST(FrameReorderer, saveLayerUnclipped_simple) {
     class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -594,14 +594,14 @@ TEST(OpReorderer, saveLayerUnclipped_simple) {
         canvas.drawRect(0, 0, 200, 200, SkPaint());
         canvas.restore();
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     SaveLayerUnclippedSimpleTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(OpReorderer, saveLayerUnclipped_mergedClears) {
+TEST(FrameReorderer, saveLayerUnclipped_mergedClears) {
     class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -648,7 +648,7 @@ TEST(OpReorderer, saveLayerUnclipped_mergedClears) {
         canvas.drawRect(0, 0, 100, 100, SkPaint());
         canvas.restoreToCount(restoreTo);
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     SaveLayerUnclippedMergedClearsTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -660,7 +660,7 @@ TEST(OpReorderer, saveLayerUnclipped_mergedClears) {
  * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
  * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
  */
-TEST(OpReorderer, saveLayerUnclipped_complex) {
+TEST(FrameReorderer, saveLayerUnclipped_complex) {
     class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
@@ -710,14 +710,14 @@ TEST(OpReorderer, saveLayerUnclipped_complex) {
         canvas.restore();
         canvas.restore();
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
             createSyncedNodeList(node), sLightCenter);
     SaveLayerUnclippedComplexTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(12, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(OpReorderer, hwLayer_simple) {
+RENDERTHREAD_TEST(FrameReorderer, hwLayer_simple) {
     class HwLayerSimpleTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -768,7 +768,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayer_simple) {
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
 
-    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             syncedNodeList, sLightCenter);
     HwLayerSimpleTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -778,7 +778,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayer_simple) {
     *layerHandle = nullptr;
 }
 
-RENDERTHREAD_TEST(OpReorderer, hwLayer_complex) {
+RENDERTHREAD_TEST(FrameReorderer, hwLayer_complex) {
     /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
      * - startRepaintLayer(child), rect(grey), endLayer
      * - startTemporaryLayer, drawLayer(child), endLayer
@@ -869,7 +869,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayer_complex) {
     layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
 
-    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             syncedList, sLightCenter);
     HwLayerComplexTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -894,7 +894,7 @@ static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder,
     node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
     canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
 }
-TEST(OpReorderer, zReorder) {
+TEST(FrameReorderer, zReorder) {
     class ZReorderTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -918,14 +918,14 @@ TEST(OpReorderer, zReorder) {
         drawOrderedRect(&canvas, 8);
         drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
             createSyncedNodeList(parent), sLightCenter);
     ZReorderTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex());
 };
 
-TEST(OpReorderer, projectionReorder) {
+TEST(FrameReorderer, projectionReorder) {
     static const int scrollX = 5;
     static const int scrollY = 10;
     class ProjectionReorderTestRenderer : public TestRendererBase {
@@ -1001,7 +1001,7 @@ TEST(OpReorderer, projectionReorder) {
         canvas.restore();
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
             createSyncedNodeList(parent), sLightCenter);
     ProjectionReorderTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -1020,7 +1020,7 @@ static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
     });
 }
 
-TEST(OpReorderer, shadow) {
+TEST(FrameReorderer, shadow) {
     class ShadowTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1044,14 +1044,14 @@ TEST(OpReorderer, shadow) {
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(parent), sLightCenter);
     ShadowTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-TEST(OpReorderer, shadowSaveLayer) {
+TEST(FrameReorderer, shadowSaveLayer) {
     class ShadowSaveLayerTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -1085,14 +1085,14 @@ TEST(OpReorderer, shadowSaveLayer) {
         canvas.restoreToCount(count);
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
     ShadowSaveLayerTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
+RENDERTHREAD_TEST(FrameReorderer, shadowHwLayer) {
     class ShadowHwLayerTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1135,7 +1135,7 @@ RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
     auto syncedList = createSyncedNodeList(parent);
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
-    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             syncedList, (Vector3) { 100, 100, 100 });
     ShadowHwLayerTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -1145,7 +1145,7 @@ RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
     *layerHandle = nullptr;
 }
 
-TEST(OpReorderer, shadowLayering) {
+TEST(FrameReorderer, shadowLayering) {
     class ShadowLayeringTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1164,7 +1164,7 @@ TEST(OpReorderer, shadowLayering) {
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             createSyncedNodeList(parent), sLightCenter);
     ShadowLayeringTestRenderer renderer;
     reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -1192,14 +1192,14 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac
         canvas.drawRect(0, 0, 100, 100, paint);
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
             createSyncedNodeList(node), sLightCenter);
     PropertyTestRenderer renderer(opValidateCallback);
     reorderer.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
 }
 
-TEST(OpReorderer, renderPropOverlappingRenderingAlpha) {
+TEST(FrameReorderer, renderPropOverlappingRenderingAlpha) {
     testProperty([](RenderProperties& properties) {
         properties.setAlpha(0.5f);
         properties.setHasOverlappingRendering(false);
@@ -1208,7 +1208,7 @@ TEST(OpReorderer, renderPropOverlappingRenderingAlpha) {
     });
 }
 
-TEST(OpReorderer, renderPropClipping) {
+TEST(FrameReorderer, renderPropClipping) {
     testProperty([](RenderProperties& properties) {
         properties.setClipToBounds(true);
         properties.setClipBounds(Rect(10, 20, 300, 400));
@@ -1218,7 +1218,7 @@ TEST(OpReorderer, renderPropClipping) {
     });
 }
 
-TEST(OpReorderer, renderPropRevealClip) {
+TEST(FrameReorderer, renderPropRevealClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableRevealClip().set(true, 50, 50, 25);
     }, [](const RectOp& op, const BakedOpState& state) {
@@ -1229,7 +1229,7 @@ TEST(OpReorderer, renderPropRevealClip) {
     });
 }
 
-TEST(OpReorderer, renderPropOutlineClip) {
+TEST(FrameReorderer, renderPropOutlineClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableOutline().setShouldClip(true);
         properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -1241,7 +1241,7 @@ TEST(OpReorderer, renderPropOutlineClip) {
     });
 }
 
-TEST(OpReorderer, renderPropTransform) {
+TEST(FrameReorderer, renderPropTransform) {
     testProperty([](RenderProperties& properties) {
         properties.setLeftTopRightBottom(10, 10, 110, 110);
 
@@ -1334,7 +1334,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
     });
     auto nodes = createSyncedNodeList(node); // sync before querying height
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
+    FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
     SaveLayerAlphaClipTestRenderer renderer(outObservedData);
     reorderer.replayBakedOps<TestDispatcher>(renderer);
 
@@ -1342,7 +1342,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
     ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
 }
 
-TEST(OpReorderer, renderPropSaveLayerAlphaClipBig) {
+TEST(FrameReorderer, renderPropSaveLayerAlphaClipBig) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setTranslationX(10); // offset rendering content
@@ -1358,7 +1358,7 @@ TEST(OpReorderer, renderPropSaveLayerAlphaClipBig) {
             << "expect content to be translated as part of being clipped";
 }
 
-TEST(OpReorderer, renderPropSaveLayerAlphaRotate) {
+TEST(FrameReorderer, renderPropSaveLayerAlphaRotate) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         // Translate and rotate the view so that the only visible part is the top left corner of
@@ -1377,7 +1377,7 @@ TEST(OpReorderer, renderPropSaveLayerAlphaRotate) {
     EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
 }
 
-TEST(OpReorderer, renderPropSaveLayerAlphaScale) {
+TEST(FrameReorderer, renderPropSaveLayerAlphaScale) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setPivotX(0);
index 6ec16db..dc89a56 100644 (file)
@@ -18,15 +18,152 @@ package android.mtp;
 
 /**
  * This class encapsulates information about a MTP event.
- * Event constants are defined by the USB-IF MTP specification.
+ * This corresponds to the events described in appendix G of the MTP specification.
  */
 public class MtpEvent {
     private int mEventCode = MtpConstants.EVENT_UNDEFINED;
 
+    // Parameters for event. The interpretation of event parameters depends upon mEventCode.
+    private int mParameter1;
+    private int mParameter2;
+    private int mParameter3;
+
     /**
      * Returns event code of MTP event.
      * See the USB-IF MTP specification for the details of event constants.
      * @return event code
      */
     public int getEventCode() { return mEventCode; }
+
+    /**
+     * Obtains the first event parameter.
+     */
+    public int getParameter1() { return mParameter1; }
+
+    /**
+     * Obtains the second event parameter.
+     */
+    public int getParameter2() { return mParameter2; }
+
+    /**
+     * Obtains the third event parameter.
+     */
+    public int getParameter3() { return mParameter3; }
+
+    /**
+     * Obtains objectHandle event parameter.
+     *
+     * @see MtpConstants#EVENT_OBJECT_ADDED
+     * @see MtpConstants#EVENT_OBJECT_REMOVED
+     * @see MtpConstants#EVENT_OBJECT_INFO_CHANGED
+     * @see MtpConstants#EVENT_REQUEST_OBJECT_TRANSFER
+     * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED
+     * @see MtpConstants#EVENT_OBJECT_REFERENCES_CHANGED
+     */
+    public int getObjectHandle() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_OBJECT_ADDED:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_REMOVED:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_INFO_CHANGED:
+                return mParameter1;
+            case MtpConstants.EVENT_REQUEST_OBJECT_TRANSFER:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_PROP_CHANGED:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_REFERENCES_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("objectHandle", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains storageID event parameter.
+     *
+     * @see MtpConstants#EVENT_STORE_ADDED
+     * @see MtpConstants#EVENT_STORE_REMOVED
+     * @see MtpConstants#EVENT_STORE_FULL
+     * @see MtpConstants#EVENT_STORAGE_INFO_CHANGED
+     */
+    public int getStorageId() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_STORE_ADDED:
+                return mParameter1;
+            case MtpConstants.EVENT_STORE_REMOVED:
+                return mParameter1;
+            case MtpConstants.EVENT_STORE_FULL:
+                return mParameter1;
+            case MtpConstants.EVENT_STORAGE_INFO_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("storageID", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains devicePropCode event parameter.
+     *
+     * @see MtpConstants#EVENT_DEVICE_PROP_CHANGED
+     */
+    public int getDevicePropCode() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_DEVICE_PROP_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("devicePropCode", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains transactionID event parameter.
+     *
+     * @see MtpConstants#EVENT_CAPTURE_COMPLETE
+     */
+    public int getTransactionId() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_CAPTURE_COMPLETE:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("transactionID", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains objectPropCode event parameter.
+     *
+     * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED
+     * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED
+     */
+    public int getObjectPropCode() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_OBJECT_PROP_CHANGED:
+                return mParameter2;
+            case MtpConstants.EVENT_OBJECT_PROP_DESC_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("objectPropCode", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains objectFormatCode event parameter.
+     *
+     * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED
+     */
+    public int getObjectFormatCode() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_OBJECT_PROP_DESC_CHANGED:
+                return mParameter2;
+            default:
+                throw new IllegalParameterAccess("objectFormatCode", mEventCode);
+        }
+    }
+
+    private static class IllegalParameterAccess extends UnsupportedOperationException {
+        public IllegalParameterAccess(String propertyName, int eventCode) {
+            super("Cannot obtain " + propertyName + " for the event: " + eventCode + ".");
+        }
+    }
 }
index 4aa12c2..130dfe5 100644 (file)
@@ -98,6 +98,9 @@ static jfieldID field_objectInfo_keywords;
 
 // MtpEvent fields
 static jfieldID field_event_eventCode;
+static jfieldID field_event_parameter1;
+static jfieldID field_event_parameter2;
+static jfieldID field_event_parameter3;
 
 class JavaArrayWriter {
 public:
@@ -573,13 +576,17 @@ static jobject android_mtp_MtpDevice_reap_event_request(JNIEnv *env, jobject thi
         env->ThrowNew(clazz_io_exception, "");
         return NULL;
     }
-    const int eventCode = device->reapEventRequest(seq);
+    uint32_t parameters[3];
+    const int eventCode = device->reapEventRequest(seq, &parameters);
     if (eventCode <= 0) {
         env->ThrowNew(clazz_operation_canceled_exception, "");
         return NULL;
     }
     jobject result = env->NewObject(clazz_event, constructor_event);
     env->SetIntField(result, field_event_eventCode, eventCode);
+    env->SetIntField(result, field_event_parameter1, static_cast<jint>(parameters[0]));
+    env->SetIntField(result, field_event_parameter2, static_cast<jint>(parameters[1]));
+    env->SetIntField(result, field_event_parameter3, static_cast<jint>(parameters[2]));
     return result;
 }
 
@@ -832,6 +839,21 @@ int register_android_mtp_MtpDevice(JNIEnv *env)
         ALOGE("Can't find MtpObjectInfo.mEventCode");
         return -1;
     }
+    field_event_parameter1 = env->GetFieldID(clazz, "mParameter1", "I");
+    if (field_event_parameter1 == NULL) {
+        ALOGE("Can't find MtpObjectInfo.mParameter1");
+        return -1;
+    }
+    field_event_parameter2 = env->GetFieldID(clazz, "mParameter2", "I");
+    if (field_event_parameter2 == NULL) {
+        ALOGE("Can't find MtpObjectInfo.mParameter2");
+        return -1;
+    }
+    field_event_parameter3 = env->GetFieldID(clazz, "mParameter3", "I");
+    if (field_event_parameter3 == NULL) {
+        ALOGE("Can't find MtpObjectInfo.mParameter3");
+        return -1;
+    }
     clazz_event = (jclass)env->NewGlobalRef(clazz);
 
     clazz = env->FindClass("android/mtp/MtpDevice");
index 359a7a9..9c01f4f 100644 (file)
@@ -161,7 +161,7 @@ import android.view.SurfaceView;
  * </pre>
  *
  */
-public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {
     private final static String TAG = "GLSurfaceView";
     private final static boolean LOG_ATTACH_DETACH = false;
     private final static boolean LOG_THREADS = false;
@@ -542,6 +542,16 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
     }
 
     /**
+     * This method is part of the SurfaceHolder.Callback interface, and is
+     * not normally called or subclassed by clients of GLSurfaceView.
+     */
+    @Override
+    public void surfaceRedrawNeeded(SurfaceHolder holder) {
+        mGLThread.requestRenderAndWait();
+    }
+
+
+    /**
      * Inform the view that the activity is paused. The owner of this view must
      * call this method when the activity is paused. Calling this method will
      * pause the rendering thread.
@@ -1226,6 +1236,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
             mHeight = 0;
             mRequestRender = true;
             mRenderMode = RENDERMODE_CONTINUOUSLY;
+            mWantRenderNotification = false;
             mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
         }
 
@@ -1271,6 +1282,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
             mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
             mHaveEglContext = false;
             mHaveEglSurface = false;
+            mWantRenderNotification = false;
+
             try {
                 GL10 gl = null;
                 boolean createEglContext = false;
@@ -1278,7 +1291,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
                 boolean createGlInterface = false;
                 boolean lostEglContext = false;
                 boolean sizeChanged = false;
-                boolean wantRenderNotification = false;
                 boolean doRenderNotification = false;
                 boolean askedToReleaseEglContext = false;
                 int w = 0;
@@ -1383,7 +1395,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
                                 if (LOG_SURFACE) {
                                     Log.i("GLThread", "sending render notification tid=" + getId());
                                 }
-                                wantRenderNotification = false;
+                                mWantRenderNotification = false;
                                 doRenderNotification = false;
                                 mRenderComplete = true;
                                 sGLThreadManager.notifyAll();
@@ -1422,7 +1434,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
                                         sizeChanged = true;
                                         w = mWidth;
                                         h = mHeight;
-                                        wantRenderNotification = true;
+                                        mWantRenderNotification = true;
                                         if (LOG_SURFACE) {
                                             Log.i("GLThread",
                                                     "noticing that we want render notification tid="
@@ -1562,7 +1574,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
                             break;
                     }
 
-                    if (wantRenderNotification) {
+                    if (mWantRenderNotification) {
                         doRenderNotification = true;
                     }
                 }
@@ -1611,6 +1623,23 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
             }
         }
 
+        public void requestRenderAndWait() {
+            synchronized(sGLThreadManager) {
+                mWantRenderNotification = true;
+                mRequestRender = true;
+                mRenderComplete = false;
+                sGLThreadManager.notifyAll();
+                while (!mExited && !mPaused && mRenderComplete == false) {
+                    try {
+                        sGLThreadManager.wait();
+                    } catch (InterruptedException ex) {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+
+            }
+        }
+
         public void surfaceCreated() {
             synchronized(sGLThreadManager) {
                 if (LOG_THREADS) {
@@ -1766,6 +1795,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
         private int mHeight;
         private int mRenderMode;
         private boolean mRequestRender;
+        private boolean mWantRenderNotification;
         private boolean mRenderComplete;
         private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
         private boolean mSizeChanged = true;
index 7e0649b..a3cfde8 100644 (file)
 -->
 
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
+<!-- showAsAction flag impacts the behavior of SearchView.
+     When set to collapseActionView, collapsing SearchView to icon is the
+     default behavior. It would fit UX, however after expanding SearchView is
+     shown on the left site of the toolbar (replacing title). Since no way to
+     prevent this behavior was found, the flag is set to always. SearchView is
+     always visible by default and it is being collapse manually by calling
+     setIconified() method
+-->
     <item
         android:id="@+id/menu_search"
         android:title="@string/menu_search"
         android:icon="@drawable/ic_menu_search"
-        android:showAsAction="always|collapseActionView"
+        android:showAsAction="always"
         android:actionViewClass="android.widget.SearchView"
         android:imeOptions="actionSearch" />
     <item
index 153c673..c868d34 100644 (file)
@@ -33,4 +33,6 @@
     <color name="item_doc_background">#fffafafa</color>
     <color name="item_doc_background_selected">#ffe0f2f1</color>
 
+    <color name="menu_search_background">#ff676f74</color>
+
 </resources>
index 7f710fc..180a48e 100644 (file)
@@ -38,12 +38,14 @@ import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Root;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.MenuItem.OnActionExpandListener;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemSelectedListener;
@@ -218,6 +220,7 @@ public abstract class BaseActivity extends Activity {
                 case R.id.menu_advanced:
                 case R.id.menu_file_size:
                 case R.id.menu_new_window:
+                case R.id.menu_search:
                     break;
                 default:
                     item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
@@ -318,6 +321,8 @@ public abstract class BaseActivity extends Activity {
      * the (abstract) directoryChanged method will be called.
      * @param anim
      */
+    // TODO: Refactor the usage of the method - now it is called not only when the directory
+    // changed, but also to refresh the content of the directory while searching
     final void onCurrentDirectoryChanged(int anim) {
         mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
         onDirectoryChanged(anim);
@@ -328,7 +333,11 @@ public abstract class BaseActivity extends Activity {
         }
 
         updateActionBar();
-        invalidateOptionsMenu();
+
+        // Prevents searchView from being recreated while searching
+        if (!mSearchManager.isSearching()) {
+            invalidateOptionsMenu();
+        }
     }
 
     final List<String> getExcludedAuthorities() {
@@ -720,7 +729,7 @@ public abstract class BaseActivity extends Activity {
      * Facade over the various search parts in the menu.
      */
     final class SearchManager implements
-            SearchView.OnCloseListener, OnActionExpandListener, OnQueryTextListener,
+            SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener,
             DocumentsToolBar.OnActionViewCollapsedListener {
 
         private boolean mSearchExpanded;
@@ -738,9 +747,10 @@ public abstract class BaseActivity extends Activity {
             mView = (SearchView) mMenu.getActionView();
 
             mActionBar.setOnActionViewCollapsedListener(this);
-            mMenu.setOnActionExpandListener(this);
             mView.setOnQueryTextListener(this);
             mView.setOnCloseListener(this);
+            mView.setOnSearchClickListener(this);
+            mView.setOnQueryTextFocusChangeListener(this);
         }
 
         /**
@@ -793,19 +803,13 @@ public abstract class BaseActivity extends Activity {
          *     search currently.
          */
         boolean cancelSearch() {
-            boolean collapsed = false;
-            boolean closed = false;
-
-            if (mActionBar.hasExpandedActionView()) {
-                mActionBar.collapseActionView();
-                collapsed = true;
-            }
-
             if (isExpanded() || isSearching()) {
-                onClose();
-                closed = true;
+                // If the query string is not empty search view won't get iconified
+                mView.setQuery("", false);
+                mView.setIconified(true);
+                return true;
             }
-            return collapsed || closed;
+            return false;
         }
 
         boolean isSearching() {
@@ -816,6 +820,11 @@ public abstract class BaseActivity extends Activity {
             return mSearchExpanded;
         }
 
+        /**
+         * Clears the search.
+         * @return True if the default behavior of clearing/dismissing SearchView should be
+         *      overridden. False otherwise.
+         */
         @Override
         public boolean onClose() {
             mSearchExpanded = false;
@@ -824,33 +833,33 @@ public abstract class BaseActivity extends Activity {
                 return false;
             }
 
-            mState.currentSearch = null;
-            onCurrentDirectoryChanged(ANIM_NONE);
+            mView.setBackgroundColor(
+                    getResources().getColor(android.R.color.transparent, null));
+
+            // Refresh the directory if a search was done
+            if(mState.currentSearch != null) {
+                mState.currentSearch = null;
+                onCurrentDirectoryChanged(ANIM_NONE);
+            }
+
             return false;
         }
 
+        /**
+         * Sets mSearchExpanded.
+         * Called when search icon is clicked to start search.
+         * Used to detect when the view expanded instead of onMenuItemActionExpand, because
+         * SearchView has showAsAction set to always and onMenuItemAction* methods are not called.
+         */
         @Override
-        public boolean onMenuItemActionExpand(MenuItem item) {
+        public void onClick (View v) {
             mSearchExpanded = true;
-            updateActionBar();
-            return true;
-        }
-
-        @Override
-        public boolean onMenuItemActionCollapse(MenuItem item) {
-            mSearchExpanded = false;
-            if (mIgnoreNextCollapse) {
-                mIgnoreNextCollapse = false;
-                return true;
-            }
-            mState.currentSearch = null;
-            onCurrentDirectoryChanged(ANIM_NONE);
-            return true;
+            mView.setBackgroundColor(
+                    getResources().getColor(R.color.menu_search_background, null));
         }
 
         @Override
         public boolean onQueryTextSubmit(String query) {
-            mSearchExpanded = true;
             mState.currentSearch = query;
             mView.clearFocus();
             onCurrentDirectoryChanged(ANIM_NONE);
@@ -863,6 +872,18 @@ public abstract class BaseActivity extends Activity {
         }
 
         @Override
+        public void onFocusChange(View v, boolean hasFocus) {
+            if(!hasFocus) {
+                if(mState.currentSearch == null) {
+                    mView.setIconified(true);
+                }
+                else if(TextUtils.isEmpty(mView.getQuery())) {
+                    cancelSearch();
+                }
+            }
+        }
+
+        @Override
         public void onActionViewCollapsed() {
             updateActionBar();
         }
index ca8ef2e..223af89 100644 (file)
@@ -308,8 +308,10 @@ public class DocumentsActivity extends BaseActivity {
         mSearchManager.showMenu(!picking);
 
         // No display options in recent directories
-        grid.setVisible(!(picking && recents));
-        list.setVisible(!(picking && recents));
+        if (picking && recents) {
+            grid.setVisible(false);
+            list.setVisible(false);
+        }
 
         fileSize.setVisible(fileSize.isVisible() && !picking);
         settings.setVisible(false);
index 4c844c4..beff196 100644 (file)
@@ -297,7 +297,7 @@ public class RootsFragment extends Fragment {
 
             for (final RootInfo root : roots) {
                 final RootItem item = new RootItem(root);
-                if (root.isLibrary() || root.isHome()) {
+                if (root.isLibrary()) {
                     libraries.add(item);
                 } else {
                     others.add(item);
index 84ab85e..898713f 100644 (file)
@@ -407,7 +407,6 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
                     state.derivedMode = result.mode;
                 }
                 state.derivedSortOrder = result.sortOrder;
-                ((BaseActivity) context).onStateChanged();
 
                 updateDisplayState();
 
index 21585e9..7527f54 100644 (file)
@@ -19,6 +19,8 @@ package com.android.mtp;
 import android.content.Context;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
+import android.mtp.MtpConstants;
+import android.mtp.MtpEvent;
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
 import android.os.SystemClock;
@@ -83,6 +85,19 @@ public class MtpManagerTest extends InstrumentationTestCase {
         getInstrumentation().show(Arrays.toString(records[0].operationsSupported));
     }
 
+    public void testEventObjectAdded() throws Exception {
+        while (true) {
+            getInstrumentation().show("Please take a photo by using connected MTP device.");
+            final CancellationSignal signal = new CancellationSignal();
+            MtpEvent event = mManager.readEvent(mUsbDevice.getDeviceId(), signal);
+            if (event.getEventCode() != MtpConstants.EVENT_OBJECT_ADDED) {
+                continue;
+            }
+            assertTrue(event.getObjectHandle() != 0);
+            break;
+        }
+    }
+
     private Context getContext() {
         return getInstrumentation().getContext();
     }
index 0f34e9e..6179b4b 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Kanselleer tans <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Drukkerfout by <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Drukker het <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> geblokkeer"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>-druktake</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>-druktaak</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Kanselleer"</string>
     <string name="restart" msgid="2472034227037808749">"Herbegin"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met drukker nie"</string>
index a6e1abf..2f94e1e 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ን በመተው ላይ"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"የአታሚ ስህተት <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"አታሚ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ን አግዷል"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> የህትመት ስራ</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> የህትመት ስራ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ይቅር"</string>
     <string name="restart" msgid="2472034227037808749">"እንደገና ጀምር"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ከአታሚ ጋር ምንም ግንኙነት የለም"</string>
index 0291b7d..29767c3 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"جارٍ إلغاء <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"خطا في الطابعة <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"رفضت الطابعة <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="zero"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> مهمة طباعة</item>
-      <item quantity="two">مهمتا طباعة (<xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>)</item>
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> مهام طباعة</item>
-      <item quantity="many"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> مهمة طباعة</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> من مهام الطباعة</item>
-      <item quantity="one">مهمة طباعة واحدة (<xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>)</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"إلغاء"</string>
     <string name="restart" msgid="2472034227037808749">"إعادة تشغيل"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"لا يوجد اتصال بالطابعة"</string>
index e162793..3316b30 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ləğv edilir"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printer xətası <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> işini blokladı"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> çap işi</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> çap işi</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Ləğv et"</string>
     <string name="restart" msgid="2472034227037808749">"Yenidən başlat"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Printerə heç bir bağlantı yoxdur"</string>
index d6e439c..86baf10 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazuje se <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Greška štampača <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Štampač je blokirao <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">Zadaci štampanja <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="few">Zadaci štampanja <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Zadaci štampanja <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Otkaži"</string>
     <string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze sa štampačem"</string>
index c239354..2c02b74 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ се анулира"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Грешка в принтера при „<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Принтерът блокира при „<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Задания за отпечатване: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Задание за отпечатване: <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Отказ"</string>
     <string name="restart" msgid="2472034227037808749">"Рестартиране"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Няма връзка с принтера"</string>
index 17a1a35..d48d91d 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> বাতিল করা হচ্ছে"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> মুদ্রক ত্রুটি"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"মুদ্রক <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> অবরুদ্ধ করেছে"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> মুদ্রণ কার্যগুলি</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> মুদ্রণ কার্যগুলি</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"বাতিল করুন"</string>
     <string name="restart" msgid="2472034227037808749">"পুনর্সূচনা"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"মুদ্রকে কোনো সংযোগ নেই"</string>
index 2551206..d0fd75e 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"S\'està cancel·lant <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Error d\'impressora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Impressora bloquejada <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tasques d\'impressió</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> tasca d\'impressió</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancel·la"</string>
     <string name="restart" msgid="2472034227037808749">"Reinicia"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No hi ha connexió amb la impressora"</string>
index 0bb48f8..bec34ac 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Rušení úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Chyba tiskárny u úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Tiskárna blokuje úlohu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="few">Tiskové úlohy: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="many">Tiskové úlohy: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Tiskové úlohy: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Tiskové úlohy: <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Zrušit"</string>
     <string name="restart" msgid="2472034227037808749">"Restartovat"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nelze se připojit k tiskárně"</string>
index a9d042b..de29146 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> annulleres"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Udskriften <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> mislykkedes"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printeren har blokeret <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>-udskriftsjob</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>-udskriftsjob</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annuller"</string>
     <string name="restart" msgid="2472034227037808749">"Genstart"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse til printer"</string>
index 4eb5d6a..054454e 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> wird abgebrochen..."</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Druckerfehler <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Drucker hat <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> blockiert."</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Druckaufträge \"<xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>\"</item>
-      <item quantity="one">Druckauftrag \"<xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>\"</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Abbrechen"</string>
     <string name="restart" msgid="2472034227037808749">"Neu starten"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Keine Verbindung zum Drucker"</string>
index cd35785..abae961 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Ακύρωση <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Σφάλμα εκτυπωτή <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Ο εκτυπωτής απέκλεισε <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> εργασίες εκτύπωσης</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> εργασία εκτύπωσης</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Ακύρωση"</string>
     <string name="restart" msgid="2472034227037808749">"Επανεκκίνηση"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Δεν υπάρχει σύνδεση με εκτυπωτή"</string>
index 753d9df..b656dcd 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer blocked <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> print jobs</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> print job</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancel"</string>
     <string name="restart" msgid="2472034227037808749">"Restart"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
index 753d9df..b656dcd 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer blocked <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> print jobs</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> print job</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancel"</string>
     <string name="restart" msgid="2472034227037808749">"Restart"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
index 753d9df..b656dcd 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer blocked <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> print jobs</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> print job</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancel"</string>
     <string name="restart" msgid="2472034227037808749">"Restart"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
index 1a0d5d8..712bb80 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Error de impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"La impresora bloqueó <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>."</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Trabajos de impresión: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Trabajo de impresión: <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora."</string>
index eac568d..dfd6a62 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Error de impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"La impresora ha bloqueado <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Trabajos de impresión <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Trabajo de impresión <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Volver a empezar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora"</string>
index 2cde258..7b962af 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Prinditöö <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> tühistamine"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printeri viga: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer blokeeris töö <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> prinditööd</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> prinditöö</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Tühista"</string>
     <string name="restart" msgid="2472034227037808749">"Taaskäivita"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Printeriühendus puudub"</string>
index 96a3273..4551cc2 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> bertan behera uzten"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Errorea <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> inprimatzean"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Inprimag. <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> blokeatu du"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> inprimatze-lanak</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> inprimatze-lana</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Utzi"</string>
     <string name="restart" msgid="2472034227037808749">"Berrabiarazi"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Inprimagailua ez dago konektatuta"</string>
index fdc3989..d85978d 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"در حال لغو <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"خطای چاپگر <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"چاپگر، کار <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> را مسدود کرد"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">کار چاپ <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">کار چاپ <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"لغو"</string>
     <string name="restart" msgid="2472034227037808749">"راه‌اندازی مجدد"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"اتصال با چاپگر برقرار نیست"</string>
index 9267393..4472459 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Peruutetaan työ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Tulostinvirhe työlle <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Tulostin esti työn <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tulostustyötä</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> tulostustyö</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Peruuta"</string>
     <string name="restart" msgid="2472034227037808749">"Käynnistä uudelleen"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Ei yhteyttä tulostimeen"</string>
index bfb4862..1d45315 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annulation de « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> »…"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erreur impression : « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> »"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Impression de « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> » bloquée"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tâche d\'impression</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tâches d\'impression</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annuler"</string>
     <string name="restart" msgid="2472034227037808749">"Recommencer"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante"</string>
index de55e29..df7dfef 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annulation de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" en cours…"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erreur impression pour \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Impression de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" bloquée"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tâche d\'impression</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tâches d\'impression</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annuler"</string>
     <string name="restart" msgid="2472034227037808749">"Redémarrer"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante."</string>
index dc66084..eae6450 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erro da impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A impresora bloqueou <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> traballos de impresión</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> traballo de impresión</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Non hai conexión coa impresora"</string>
index d05a392..8028274 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ને રદ કરી રહ્યું છે"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"પ્રિન્ટર ભૂલ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"પ્રિન્ટરે <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> અવરોધિત કર્યું"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> છાપ જોબ</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> છાપ જોબ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"રદ કરો"</string>
     <string name="restart" msgid="2472034227037808749">"પુનઃપ્રારંભ કરો"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"પ્રિન્ટર માટે કોઈ કનેક્શન નથી"</string>
index 8051900..5bfcc6e 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> रद्द हो रहा है"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिंटर त्रुटि <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"प्रिंटर अवरोधित <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> प्रिंट कार्य</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> प्रिंट कार्य</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"अभी नहीं"</string>
     <string name="restart" msgid="2472034227037808749">"पुन: आरंभ करें"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटर के लिए कोई कनेक्शन नहीं"</string>
index 4dab4cc..bf244c5 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazivanje zadatka <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Pogreška pisača <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Pisač je blokirao <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadatak ispisa</item>
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadatka ispisa</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadataka ispisa</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Odustani"</string>
     <string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze s pisačem"</string>
index 1a56ee7..6c608f1 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"A(z) <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> törlése"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Nyomtatási hiba: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A(z) <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> letiltva."</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> nyomtatási feladat</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> nyomtatási feladat</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Mégse"</string>
     <string name="restart" msgid="2472034227037808749">"Újraindítás"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nincs kapcsolat a nyomtatóval"</string>
index 7b99dcf..801410b 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>-ը չեղարկվում է"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Տպիչի սխալ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Տպիչն արգելափակել է <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>-ը"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> տպման աշխատանք</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> տպման աշխատանք</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Չեղարկել"</string>
     <string name="restart" msgid="2472034227037808749">"Վերագործարկել"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Տպիչի հետ կապ չկա"</string>
index a991272..227a372 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Membatalkan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Ada kesalahan printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer memblokir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Tugas cetak <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Tugas cetak <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Batal"</string>
     <string name="restart" msgid="2472034227037808749">"Mulai Ulang"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Tidak ada sambungan ke printer"</string>
index e93f702..f078ff8 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Hættir við <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Prentaravilla <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Prentari útilokaði <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> prentverk</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> prentverk</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Hætta við"</string>
     <string name="restart" msgid="2472034227037808749">"Endurræsa"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Engin tenging við prentara"</string>
index ffba353..10b3601 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annullamento di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Errore della stampante: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"La stampante ha bloccato <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> processi di stampa</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> processo di stampa</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annulla"</string>
     <string name="restart" msgid="2472034227037808749">"Riavvia"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nessun collegamento alla stampante"</string>
index 2ac1093..8a04c76 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"מבטל את <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"שגיאת מדפסת ב-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"המדפסת חסמה את <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="two"> עבודות הדפסה <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="many"> עבודות הדפסה <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other"> עבודות הדפסה <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one"> עבודת הדפסה <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"בטל"</string>
     <string name="restart" msgid="2472034227037808749">"הפעל מחדש"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"אין חיבור למדפסת"</string>
index 2c3c24d..a132fb1 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>をキャンセルしています"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"プリンタエラー: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>をブロックしました"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>の印刷ジョブ</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>の印刷ジョブ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"キャンセル"</string>
     <string name="restart" msgid="2472034227037808749">"再試行"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"プリンタに接続されていません"</string>
index 2b0285d..675245b 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"მიმდინარეობს <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>-ის გაუქმება"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ბეჭდვის შეცდომა <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"პრინტერმა დაბლოკა <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ბეჭდვის დავალება</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> ბეჭდვის დავალება</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"გაუქმება"</string>
     <string name="restart" msgid="2472034227037808749">"გადატვირთვა"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"პრინტერთან კავშირი არ არის"</string>
index fc099c9..4c7c4f4 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> жұмысын тоқтатуда"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> принтер қателігі"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Принтер <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> жұмысын бөгеді"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> баспа тапсырмасы</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> баспа тапсырмасы</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Тоқтату"</string>
     <string name="restart" msgid="2472034227037808749">"Қайта бастау"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Принтермен байланыс жоқ"</string>
index b51091e..fac7c0c 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"ការ​បោះបង់ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"កំហុស​ម៉ាស៊ីន​បោះពុម្ព <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"ម៉ាស៊ីន​បោះពុម្ព​បាន​ទប់ស្កាត់ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">ការងារបោះពុម្ព <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">ការងារបោះពុម្ព <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"បោះបង់"</string>
     <string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព​"</string>
index 5d5dee8..61279fc 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ರದ್ದು ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ಮುದ್ರಕ ದೋಷ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"ಮುದ್ರಕವು <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ನಿರ್ಬಂಧಿಸಿದೆ"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ಮುದ್ರಣ ಕಾರ್ಯಗಳು</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ಮುದ್ರಣ ಕಾರ್ಯಗಳು</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ರದ್ದುಮಾಡು"</string>
     <string name="restart" msgid="2472034227037808749">"ಮರುಪ್ರಾರಂಭಿಸು"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ಮುದ್ರಕಕ್ಕೆ ಸಂಪರ್ಕವಿಲ್ಲ"</string>
index 98617e7..fe47e55 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> 취소 중"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"프린터 오류: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"차단된 프린터: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> 인쇄 작업</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> 인쇄 작업</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"취소"</string>
     <string name="restart" msgid="2472034227037808749">"다시 시작"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"프린터와 연결되지 않음"</string>
index 2a11ff8..79b38d1 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> токтотулууда"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Принтерде ката кетти: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Принтер бөгөттөдү: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> басуу тапшырмасы</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> басуу тапшырмасы</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Айнуу"</string>
     <string name="restart" msgid="2472034227037808749">"Кайра баштоо"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер менен байланыш жок"</string>
index 788e5aa..3140a25 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"ກຳລັງຍົກເລີກ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ເຄື່ອງພິມເກີດຂໍ້ຜິດພາດ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"ເຄື່ອງພິມຖືກບລອກ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ງານ​ພິມ</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> ງານ​ພິມ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ຍົກເລີກ"</string>
     <string name="restart" msgid="2472034227037808749">"ປິດເປີດໃໝ່"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ບໍ່ມີການເຊື່ອມຕໍ່ຫາເຄື່ອງພິມ"</string>
index 1826e8e..4f0772e 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Atšaukiama: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Spausdintuvo klaida: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Spausdintuvas užblokavo: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> spausdinimo užduotis</item>
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> spausdinimo užduotys</item>
-      <item quantity="many"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> spausdinimo užduoties</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> spausdinimo užduočių</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Atšaukti"</string>
     <string name="restart" msgid="2472034227037808749">"Paleisti iš naujo"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nėra ryšio su spausdintuvu"</string>
index 5c17efe..0efa50f 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Pārtrauc drukas darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>…"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printera kļūda ar darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printeris bloķēja darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="zero"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> drukas darbi</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> drukas darbs</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> drukas darbi</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Atcelt"</string>
     <string name="restart" msgid="2472034227037808749">"Restartēt"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nav savienojuma ar printeri"</string>
index ebc1181..2805dee 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> се откажува"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Грешка при печатење <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Печатачот го блокираше <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> работа за печатење</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> работи за печатење</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Откажи"</string>
     <string name="restart" msgid="2472034227037808749">"Рестартирај"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Нема поврзување со печатач"</string>
index c08a3d4..9617a7a 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> റദ്ദാക്കുന്നു"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"പ്രിന്റർ പിശക് <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"പ്രിന്റർ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> തടഞ്ഞു"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> പ്രിന്റ് ജോലികൾ</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> പ്രിന്റ് ജോലി</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"റദ്ദാക്കുക"</string>
     <string name="restart" msgid="2472034227037808749">"പുനരാരംഭിക്കുക"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"പ്രിന്ററിൽ കണക്ഷനൊന്നുമില്ല"</string>
index dcef28f..59bb9bf 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Цуцлаж байна <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Принтерийн алдаа <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Принтер хориглогдсон <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ажлыг хэвлэх</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> ажлыг хэвлэх</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Цуцлах"</string>
     <string name="restart" msgid="2472034227037808749">"Дахин эхлүүлэх"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер холбогдоогүй байна"</string>
index 384f0de..2b6661e 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> रद्द करीत आहे"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिंटर त्रुटी <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"प्रिंटरने <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> अवरोधित केले"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> मुद्रण कार्य</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> मुद्रण कार्ये</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"रद्द करा"</string>
     <string name="restart" msgid="2472034227037808749">"रीस्टार्ट करा"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटरवर कोणतेही कनेक्‍शन नाही"</string>
index 19a6e76..c066627 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Membatalkan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Ralat pencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Pencetak disekat <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Kerja cetakan <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Kerja cetakan <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Batal"</string>
     <string name="restart" msgid="2472034227037808749">"Mulakan semula"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Tiada sambungan ke pencetak"</string>
index d3c0672..a63e85e 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ကို ပယ်ဖျက်နေပါသည်"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"စာထုတ်စက်မှ အမှား <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ကိုစာထုတ်စက်ကငြင်းလိုက်သည်"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> စာထုတ်စရာများ</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>စာထုတ်စရာ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ပယ်ဖျက်"</string>
     <string name="restart" msgid="2472034227037808749">"အစက ပြန်စရန်"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"စာထုတ်စက်နဲ့ ဆက်သွယ်ထားမှု မရှိပါ"</string>
index c34e7bc..8128011 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Avbryter <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Skriverfeil <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Skriveren blokkerte <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> utskriftsjobber</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> utskriftsjobb</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Avbryt"</string>
     <string name="restart" msgid="2472034227037808749">"Start på nytt"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse med skriveren"</string>
index d1959d9..89b2fbb 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"रद्द गरिँदै <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिन्टर त्रुटि <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"प्रिन्टर ब्लक गरियो <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> कार्यहरू प्रिन्ट गर्नुहोस्</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> कार्य प्रिन्ट गर्नुहोस्</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"रद्द गर्नुहोस्"</string>
     <string name="restart" msgid="2472034227037808749">"पुनःस्टार्ट गर्नुहोस्"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिन्टरमा कुनै जडान छैन"</string>
index 5df3298..70b93e6 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> annuleren"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printerfout <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> geblokkeerd door printer"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> afdruktaken</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> afdruktaak</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annuleren"</string>
     <string name="restart" msgid="2472034227037808749">"Opnieuw starten"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met printer"</string>
index 57e9969..da81919 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ਨੂੰ ਰੱਦ ਕਰ ਰਿਹਾ ਹੈ"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ਪ੍ਰਿੰਟਰ ਅਸ਼ੁੱਧੀ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"ਪ੍ਰਿੰਟਰ ਬਲੌਕ ਕੀਤਾ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ਪ੍ਰਿੰਟ ਜੌਬਸ</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ਪ੍ਰਿੰਟ ਜੌਬਸ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ਰੱਦ ਕਰੋ"</string>
     <string name="restart" msgid="2472034227037808749">"ਰੀਸਟਾਰਟ ਕਰੋ"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ਪ੍ਰਿੰਟਰ ਲਈ ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
index 4439acb..b7613cf 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Anulowanie: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Błąd drukarki: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Drukarka zablokowała <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadania drukowania</item>
-      <item quantity="many"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadań drukowania</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadania drukowania</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> zadanie drukowania</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Anuluj"</string>
     <string name="restart" msgid="2472034227037808749">"Od nowa"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Brak połączenia z drukarką"</string>
index 63bb868..199f304 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erro ao imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A impressora bloqueou <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">Tarefas de impressão <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Tarefas de impressão <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
index d364ef4..ea94c55 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"A cancelar <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erro da impressora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A impressora bloqueou <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tarefas de impressão</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> tarefa de impressão</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Sem ligação à impressora"</string>
index 63bb868..199f304 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erro ao imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A impressora bloqueou <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">Tarefas de impressão <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Tarefas de impressão <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
index 51dfe7a..1c74aab 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Se anulează <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Eroare de printare: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printare blocată: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> activități de printare</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> de activități de printare</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> activitate de printare</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Anulați"</string>
     <string name="restart" msgid="2472034227037808749">"Reporniți"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nu există conexiune la o imprimantă"</string>
index 6ba1046..e900ab9 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Отмена задания <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>…"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Ошибка задания \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Задание \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" заблокировано"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">Задания печати: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="few">Задания печати: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="many">Задания печати: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Задания печати: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Отмена"</string>
     <string name="restart" msgid="2472034227037808749">"Повторить"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Нет связи с принтером"</string>
index 4908ea5..5731849 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"අවලංගු කෙරේ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"මුද්‍රණ දෝෂය <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"මුද්‍රණ යන්ත්‍රය <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> අවුරා ඇති"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">මුද්‍රණ කාර්ය <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">මුද්‍රණ කාර්ය <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"අවලංගු කරන්න"</string>
     <string name="restart" msgid="2472034227037808749">"යළි අරඹන්න"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"මුද්‍රණ යන්ත්‍රය වෙත සම්බන්ධය නැත"</string>
index 418363d..1c7b740 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Prebieha zrušenie úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Chyba tlačiarne – úloha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Tlačiareň zablok. úlohu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tlačové úlohy</item>
-      <item quantity="many"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tlačovej úlohy</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tlačových úloh</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> tlačová úloha</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Zrušiť"</string>
     <string name="restart" msgid="2472034227037808749">"Spustiť znova"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Žiadne pripojenie k tlačiarni"</string>
index e2be161..4c794dc 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Preklic: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Napaka tiskalnika: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Tiskalnik je blokiral <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tiskalno opravilo</item>
-      <item quantity="two"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tiskalni opravili</item>
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tiskalna opravila</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tiskalnih opravil</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Prekliči"</string>
     <string name="restart" msgid="2472034227037808749">"Začni znova"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Ni povezave s tiskalnikom"</string>
index d5ebf32..aff55f6 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Po anulon <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printeri ndeshi në gabim gjatë punës: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printeri bllokoi <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> punë për printim</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> punë për printim</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Anulo"</string>
     <string name="restart" msgid="2472034227037808749">"Rifillo"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Printeri nuk është i lidhur"</string>
index 166e5dd..043048e 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Отказује се <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Грешка штампача <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Штампач је блокирао <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">Задаци штампања <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="few">Задаци штампања <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Задаци штампања <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Откажи"</string>
     <string name="restart" msgid="2472034227037808749">"Поново покрени"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Нема везе са штампачем"</string>
index 033d583..fd8581f 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Avbryter <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Skrivarfel för <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Skrivaren har blockerat <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> utskriftsjobb</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> utskriftsjobb</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Avbryt"</string>
     <string name="restart" msgid="2472034227037808749">"Starta om"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen anslutning till skrivaren"</string>
index 0e2dcdd..0950f57 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Inaghairi <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Hitilafu ya kuchapisha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printa imefungwa <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Kazi ya kuchapisha ya <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Kazi ya kuchapisha ya <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> </item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Ghairi"</string>
     <string name="restart" msgid="2472034227037808749">"Anzisha upya"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Hakuna muunganisho kwa printa"</string>
index 2e90d38..bf6feae 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ஐ ரத்துசெய்கிறது"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"பிரிண்டர் பிழை <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"பிரிண்டர் <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ஐத் தடுத்தது"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> அச்சுப் பணிகள்</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> அச்சுப் பணி</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ரத்துசெய்"</string>
     <string name="restart" msgid="2472034227037808749">"மீண்டும் தொடங்கு"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"அச்சுப்பொறியுடன் இணைக்கப்படவில்லை"</string>
index 6bdbd5c..4d14b8c 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ను రద్దు చేస్తోంది"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ప్రింటర్ లోపం <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"ప్రింటర్ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ను బ్లాక్ చేసింది"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ముద్రణ జాబ్‌లు</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> ముద్రణ జాబ్</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"రద్దు చేయి"</string>
     <string name="restart" msgid="2472034227037808749">"పునఃప్రారంభించు"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ప్రింటర్‌కు కనెక్షన్ లేదు"</string>
index a581357..e1f82e1 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"กำลังยกเลิก <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ข้อผิดพลาดเครื่องพิมพ์ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"เครื่องพิมพ์ได้บล็อก <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> งานพิมพ์</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> งานพิมพ์</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ยกเลิก"</string>
     <string name="restart" msgid="2472034227037808749">"เริ่มต้นใหม่"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ไม่มีการเชื่อมต่อไปยังเครื่องพิมพ์"</string>
index 325ce8c..052d8cc 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Kinakansela ang <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Error sa printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Naka-block ang Printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ipi-print</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> na ipi-print</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Kanselahin"</string>
     <string name="restart" msgid="2472034227037808749">"I-restart"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Hindi nakakonekta sa printer"</string>
index d945979..7f9e6f5 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> iptal ediliyor"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Yazıcı hatası: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Yazıcı <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> işini engelledi"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> yazdırma işi</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> yazdırma işi</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"İptal"</string>
     <string name="restart" msgid="2472034227037808749">"Yeniden başlat"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Yazıcı bağlantısı yok"</string>
index ffdfde0..d34aa2a 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" скасовується"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Помилка завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" заблоковано"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> завдання друку</item>
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> завдання друку</item>
-      <item quantity="many"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> завдань друку</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> завдань друку</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Скасувати"</string>
     <string name="restart" msgid="2472034227037808749">"Перезапустити"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Немає з’єднання з принтером"</string>
index 72a6ab9..ccf6f56 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> کو منسوخ کر رہا ہے"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"پرنٹر کی خرابی <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"پرنٹر نے <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> کو مسدود کر دیا"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> پرنٹ جابز</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> پرنٹ جاب</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"منسوخ کریں"</string>
     <string name="restart" msgid="2472034227037808749">"دوبارہ شروع کریں"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"پرنٹر کے ساتھ کوئی کنکشن نہیں ہے"</string>
index c7b4263..d1fc47f 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> bekor qilinmoqda"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printerda xatolik: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ni taqiqladi"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> chop qilish vazifalari</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> chop qilish vazifasi</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Bekor qilish"</string>
     <string name="restart" msgid="2472034227037808749">"Qayta boshlash"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Printer ulanmagan"</string>
index 771d57c..eb5de95 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Hủy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Lỗi máy in <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Máy in đã chặn <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Lệnh in <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Lệnh in <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Hủy"</string>
     <string name="restart" msgid="2472034227037808749">"Bắt đầu lại"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Không có kết nối nào với máy in"</string>
index bea91d7..e39849e 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"打印机在打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”时出错"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"打印机拒绝打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">“<xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>”打印作业</item>
-      <item quantity="one">“<xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>”打印作业</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"取消"</string>
     <string name="restart" msgid="2472034227037808749">"重新开始"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"未与打印机建立连接"</string>
index 4fbef0d..1148226 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"打印機錯誤:<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"打印機已封鎖 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> 項列印工作</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> 項列印工作</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"取消"</string>
     <string name="restart" msgid="2472034227037808749">"重新開始"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與打印機連線"</string>
index 2fdcaac..c0dd3de 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"印表機發生錯誤:<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"印表機封鎖了 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> 個列印工作</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> 個列印工作</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"取消"</string>
     <string name="restart" msgid="2472034227037808749">"重新開始"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與印表機建立連線"</string>
index 92595aa..231b1bf 100644 (file)
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Ikhansela i-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Iphutha lephrinta ye-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Iphrinta engu-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ivinjelwe"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> imisebenzi yokuphrinta</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> imisebenzi yokuphrinta</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Khansela"</string>
     <string name="restart" msgid="2472034227037808749">"Qala kabusha"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Akukho ukuxhumana kuphrinta"</string>
index b662c58..76292a1 100644 (file)
     <!-- Description of printer info icon. [CHAR LIMIT=50] -->
     <string name="printer_info_desc">More information about this printer</string>
 
+    <!-- Notification that print services as disabled. [CHAR LIMIT=50] -->
+    <string name="print_services_disabled_toast">Some print services are disabled.</string>
+
     <!-- Add printer dialog  -->
 
     <!-- Title for the alert dialog for selecting a print service. [CHAR LIMIT=50] -->
index ea11ae4..684a1de 100644 (file)
@@ -173,15 +173,19 @@ public final class RemotePrintDocument {
         if (DEBUG) {
             Log.i(LOG_TAG, "[CALLED] start()");
         }
-        if (mState != STATE_INITIAL) {
-            throw new IllegalStateException("Cannot start in state:" + stateToString(mState));
-        }
-        try {
-            mPrintDocumentAdapter.start();
-            mState = STATE_STARTED;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error calling start()", re);
-            mState = STATE_FAILED;
+        if (mState == STATE_FAILED) {
+            Log.w(LOG_TAG, "Failed before start.");
+        } else {
+            if (mState != STATE_INITIAL) {
+                throw new IllegalStateException("Cannot start in state:" + stateToString(mState));
+            }
+            try {
+                mPrintDocumentAdapter.start();
+                mState = STATE_STARTED;
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "Error calling start()", re);
+                mState = STATE_FAILED;
+            }
         }
     }
 
index 5525774..cd30e26 100644 (file)
@@ -207,6 +207,7 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>>
             PrinterInfo favoritePrinter = favoritePrinters.get(i).first;
             if (!alreadyAddedPrinter.contains(favoritePrinter.getId())) {
                 updateAndAddPrinter(printers, favoritePrinter, discoveredPrinters);
+                alreadyAddedPrinter.add(favoritePrinter.getId());
             }
         }
 
index 81727ab..13105aa 100644 (file)
@@ -65,6 +65,7 @@ import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.SearchView;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import com.android.internal.content.PackageMonitor;
 import com.android.printspooler.R;
@@ -147,6 +148,14 @@ public final class SelectPrinterActivity extends Activity {
         });
 
         registerForContextMenu(mListView);
+
+        // Display a notification about disabled services if there are disabled services
+        String disabledServicesSetting = Settings.Secure.getString(getContentResolver(),
+                Settings.Secure.DISABLED_PRINT_SERVICES);
+        if (!TextUtils.isEmpty(disabledServicesSetting)) {
+            Toast.makeText(this, getString(R.string.print_services_disabled_toast),
+                    Toast.LENGTH_LONG).show();
+        }
     }
 
     @Override
index a1e3ef4..f781159 100644 (file)
@@ -134,6 +134,11 @@ public class ApprovedPrintServices {
     public void pruneApprovedServices(List<ComponentName> serviceNamesToKeep) {
         synchronized (sLock) {
             Set<String> approvedServices = getApprovedServices();
+
+            if (approvedServices == null) {
+                return;
+            }
+
             Set<String> newApprovedServices = new ArraySet<>(approvedServices.size());
 
             final int numServiceNamesToKeep = serviceNamesToKeep.size();
index 54d2dfa..3fd4f7f 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Omogućava upisivanje svih aplikacija u spoljnu memoriju, bez obzira na vrednosti manifesta"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Prinudno omogući promenu veličine aktivnosti"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Omogućava promenu veličine svih aktivnosti za režim sa više prozora, bez obzira na vrednosti manifesta."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Omogući prozore proizvoljnog formata"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Omogućava podršku za eksperimentalne prozore proizvoljnog formata."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Lozinka rezervne kopije za računar"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Rezervne kopije čitavog sistema trenutno nisu zaštićene"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Dodirnite da biste promenili ili uklonili lozinku za pravljenje rezervnih kopija čitavog sistema na računaru"</string>
index 95badc5..cd4529f 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Позволява прилож. да се записват във външ. хранил. независимо от стойностите в манифеста"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Възможност за преоразмеряване на активностите"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Дава възможност за преоразмеряване на всички активности в режима за няколко прозореца независимо от стойностите в манифеста."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Активиране на прозорците в свободна форма"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Активира поддръжката за експерименталните прозорци в свободна форма."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Наст. комп.: Парола"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Понастоящем пълните резервни копия за настолен компютър не са защитени"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Докоснете, за да промените или премахнете паролата за пълни резервни копия на настолния компютър"</string>
index e4d97d7..7c59822 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ম্যানিফেস্ট মানগুলি নির্বিশেষে যেকোনো অ্যাপ্লিকেশানকে বাহ্যিক সঞ্চয়স্থানে লেখার উপযুক্ত বানায়"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"আকার পরিবর্তনযোগ্য করার জন্য ক্রিয়াকলাপগুলিকে জোর করুন"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ম্যানিফেস্ট মানগুলির নির্বিশেষে মাল্টি-উইন্ডোর জন্য সমস্ত ক্রিয়াকলাপগুলিকে আকার পরিবর্তনযোগ্য করে তোলে৷"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"ফ্রি-ফর্ম উইন্ডোগুলি সক্ষম করুন"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"পরীক্ষামূলক ফ্রি-ফর্ম উইন্ডোগুলির জন্য সহায়তা সক্ষম করুন৷"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ডেস্কটপ ব্যাকআপ পাসওয়ার্ড"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ডেস্কটপ পূর্ণ ব্যাকআপ বর্তমানে সুরক্ষিত নয়"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ডেস্কটপ পুরো ব্যাকআপের জন্য পাসওয়ার্ড পরিবর্তন বা মুছে ফেলার জন্য স্পর্শ করুন"</string>
index 93b6438..1349a7a 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Permet que les aplicacions es puguin escriure en un dispositiu d’emmagatzematge extern, independentment dels valors definits"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Força l\'ajust de la mida de les activitats"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permet ajustar la mida de totes les activitats per al mode multifinestra, independentment dels valors definits."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Activa les finestres de format lliure"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Activa la compatibilitat amb les finestres de format lliure experimentals."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Contrasenya per a còpies d\'ordinador"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Les còpies de seguretat d\'ordinador completes no estan protegides"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toca per canviar o eliminar la contrasenya per a còpies de seguretat d\'ordinador completes"</string>
index 0b1238b..4d9d4fb 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Ermöglicht es jeder qualifizierten App, Daten auf externen Speicher zu schreiben, unabhängig von den Manifestwerten"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Anpassen der Größe von Aktivitäten erzwingen"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Ermöglicht es, die Größe aller Aktivitäten an den Mehrfenstermodus anzupassen, unabhängig von den Manifestwerten."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Freiform-Fenster zulassen"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Unterstützt experimentelle Freiform-Fenster."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop-Sicherungspasswort"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Vollständige Desktop-Sicherungen sind momentan nicht passwortgeschützt."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Zum Ändern oder Entfernen des Passworts für vollständige Desktop-Sicherungen berühren"</string>
index dae40d9..833fb62 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Makes any app eligible to be written to external storage, regardless of manifest values"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Force activities to be re-sizable"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Makes all activities re-sizable for multi-window, regardless of manifest values."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Enable freeform windows"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Enables support for experimental freeform windows."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop backup password"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Desktop full backups aren\'t currently protected"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Touch to change or remove the password for desktop full backups"</string>
index dae40d9..833fb62 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Makes any app eligible to be written to external storage, regardless of manifest values"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Force activities to be re-sizable"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Makes all activities re-sizable for multi-window, regardless of manifest values."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Enable freeform windows"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Enables support for experimental freeform windows."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop backup password"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Desktop full backups aren\'t currently protected"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Touch to change or remove the password for desktop full backups"</string>
index dae40d9..833fb62 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Makes any app eligible to be written to external storage, regardless of manifest values"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Force activities to be re-sizable"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Makes all activities re-sizable for multi-window, regardless of manifest values."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Enable freeform windows"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Enables support for experimental freeform windows."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop backup password"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Desktop full backups aren\'t currently protected"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Touch to change or remove the password for desktop full backups"</string>
index 855b72c..4d4ab24 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Cualquier aplicación puede escribirse en una memoria externa, independientemente de los valores del manifiesto."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forzar actividades para que cambien de tamaño"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permite que todas las actividades puedan cambiar de tamaño para el modo multiventana, sin importar los valores del manifiesto."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Habilitar ventanas de forma libre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Habilita la admisión de ventanas de forma libre experimentales."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Contraseñas"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Tus copias de seguridad de escritorio no están protegidas por contraseña."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toca para cambiar o eliminar la contraseña de las copias de seguridad completas de tu escritorio."</string>
index fe5fc29..6405ca8 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Aplikazioek kanpoko memorian idatz dezakete, manifestuaren balioak kontuan izan gabe"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Behartu jardueren tamaina doitu ahal izatea"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Manifestuan jartzen duena jartzen duela ere, jarduera guztien tamaina doitzeko aukera ematen du, hainbat leihotan erabili ahal izan daitezen."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Gaitu estilo libreko leihoak"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Estilo libreko leiho esperimentalak onartzen ditu."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Tokiko babeskop. pasahitza"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Une honetan, ordenagailuko babeskopia osoak ez daude babestuta."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Ukitu ordenagailuko babeskopia osoak egiteko pasahitza aldatzeko edo kentzeko"</string>
index e1a35f7..d591426 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"بدون توجه به مقادیر مانیفست، هر برنامه‌ای را برای نوشتن در حافظه خارجی واجد شرایط می‌کند"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"اجبار فعالیت‌ها به قابل تغییر اندازه بودن"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"بدون درنظر گرفتن مقادیر مانیفست، همه فعالیت‌ها را برای چندپنجره قابل تغییر اندازه می‌کند."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"فعال کردن پنجره‌های آزاد"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"پشتیبانی برای پنجره‌های آزاد آزمایشی را امکان‌پذیر می‌کند"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"گذرواژه پشتیبان‌گیری محلی"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"پشتیبان‌گیری کامل رایانه درحال حاضر محافظت نمی‌شود"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"برای تغییر یا حذف گذرواژه برای نسخه‌های پشتیبان کامل دسک‌تاپ لمس کنید"</string>
index 498f204..ce36de3 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Mahdollistaa sovellusten tallentamisen ulkoiseen tall.tilaan luettelosta riippumatta"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Pakota kaikki toiminnot hyväksymään koon muutos"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Pakottaa kaikki toiminnot hyväksymään koon muuttamisen rinnakkaisnäkymään luettelon arvoista riippumatta."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Ota käyttöön vapaamuotoiset ikkunat"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ottaa käyttöön kokeellisten vapaamuotoisten ikkunoiden tuen."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Varmuuskop. salasana"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Tietokoneen kaikkien tietojen varmuuskopiointia ei ole tällä hetkellä suojattu"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Muuta tai vaihda tietokoneen kaikkien tietojen varmuuskopioinnin salasana koskettamalla"</string>
index b32bfb7..3ee0775 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Permet enreg. d\'applis sur espace stockage externe"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forcer les activités à être redimensionnables"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permet de redimensionner toutes les activités pour le mode multifenêtre, indépendamment des valeurs du fichier manifeste."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Activer les fenêtres de forme libre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Active la compatibilité avec les fenêtres de forme libre expérimentales."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Mot de passe sauvegarde PC"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Les sauvegardes complètes sur PC ne sont pas protégées actuellement."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Appuyez pour modifier ou supprimer le mot de passe utilisé pour les sauvegardes complètes sur PC."</string>
index 0190454..c0098d2 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Rend possible enregistrement de toute appli sur espace stockage externe, indépendamment valeurs fichier manifeste."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forcer possibilité de redimensionner les activités"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permet de redimensionner toutes les activités pour le mode multifenêtre, indépendamment des valeurs du fichier manifeste."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Activer les fenêtres de forme libre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Active la compatibilité avec les fenêtres de forme libre expérimentales."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Mot de passe sauvegarde PC"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Les sauvegardes complètes sur PC ne sont pas protégées actuellement."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Appuyez pour modifier ou supprimer le mot de passe utilisé pour les sauvegardes complètes sur PC."</string>
index 4b72648..2a0ff45 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"મેનિફેસ્ટ મૂલ્યોને ધ્યાનમાં લીધા સિવાય, કોઈપણ એપ્લિકેશનને બાહ્ય સ્ટોરેજ પર લખાવા માટે લાયક બનાવે છે"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"પ્રવૃત્તિઓને ફરીથી કદ યોગ્ય થવા માટે ફરજ પાડો"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"તમામ પ્રવૃત્તિઓને મલ્ટી-વિંડો માટે ફરીથી કદ બદલી શકે તેવી બનાવે છે, મેનીફેસ્ટ મુલ્યોને ધ્યાનમાં લીધા સિવાય."</string>
-    <string name="enable_freeform_support" msgid="1461893351278940416">"મà«\81àª\95à«\8dતાàª\95ાર વિંડોઝ સક્ષમ કરો"</string>
-    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"પà«\8dરાયà«\8bàª\97િàª\95 àª®à«\81àª\95à«\8dતાàª\95ાર વિંડોઝ માટે સમર્થનને સક્ષમ કરે છે."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"ફà«\8dરિફà«\8bરà«\8dમ વિંડોઝ સક્ષમ કરો"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"પà«\8dરાયà«\8bàª\97િàª\95 àª«à«\8dરિફà«\8bરà«\8dમ વિંડોઝ માટે સમર્થનને સક્ષમ કરે છે."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ડેસ્કટૉપ બેકઅપ પાસવર્ડ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ડેસ્કટૉપ સંપૂર્ણ બેકઅપ હાલમાં સુરક્ષિત નથી"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ડેસ્કટૉપ સંપૂર્ણ બેકઅપ્સ માટેનો પાસવર્ડ બદલવા અથવા દૂર કરવા માટે ટચ કરો"</string>
index 5cde233..0a46b27 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Aplikacije se mogu zapisivati u vanjsku pohranu neovisno o manifestu"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Nametni mogućnost promjene veličine za aktivnosti"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Veličina svih aktivnosti može se mijenjati za više prozora, neovisno o vrijednostima manifesta."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Omogući prozore slobodnog oblika"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Omogućuje podršku za eksperimentalne prozore slobodnog oblika."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Zaporka sigurnosne kopije"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Potpune sigurnosne kopije na stolnom računalu trenutačno nisu zaštićene"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Odaberite za promjenu ili uklanjanje zaporke u potpunim sigurnosnim kopijama na stolnom računalu"</string>
index 42c9a05..fb401f0 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Թույլ է տալիս պահել հավելվածը արտաքին սարքում՝ մանիֆեստի արժեքներից անկախ"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Ստիպել, որ ակտիվությունների չափերը լինեն փոփոխելի"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Բոլոր ակտիվությունների չափերը բազմապատուհան ռեժիմի համար դարձնել փոփոխելի՝ մանիֆեստի արժեքներից անկախ:"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Ակտիվացնել կամայական ձևի պատուհանները"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ակտիվացնում է կամայական ձևի փորձնական պատուհանների աջակցումը:"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Աշխատասեղանի պահուստավորման գաղտնաբառ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Աշխատասեղանի ամբողջական պահուստավորումները այժմ պաշտպանված չեն"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Աշխատասեղանի ամբողջական պահուստավորման համար ընտրել փոխել կամ հեռացնել գաղտնաբառը"</string>
index 6988a73..1262512 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Membuat semua aplikasi dapat ditulis ke penyimpanan eksterna"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Paksa aktivitas agar ukurannya dapat diubah"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Membuat semua aktivitas dapat diubah ukurannya untuk banyak jendela, terlepas dari nilai manifes."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Aktifkan jendela berformat bebas"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Mengaktifkan dukungan untuk jendela eksperimental berformat bebas."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Sandi cadangan desktop"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Saat ini cadangan desktop penuh tidak dilindungi"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Sentuh guna mengubah atau menghapus sandi untuk cadangan lengkap desktop"</string>
index 8d912e9..3e53e94 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Gerir hvaða forriti sem er kleift að skrifa í ytri geymslu, burtséð frá gildum í upplýsingaskrá"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Þvinga breytanlega stærð virkni"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Gerir stærð allrar virkni breytanlega svo að hún henti fyrir marga glugga, óháð gildum í upplýsingaskrá."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Virkja glugga með frjálsu sniði"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Kveikir á stuðningi við glugga með frjálsu sniði á tilraunastigi."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Aðgangsorð tölvuafritunar"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Heildarafritun á tölvu er ekki varin sem stendur."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Snertu til að breyta eða fjarlægja aðgangsorðið fyrir heildarafritun á tölvu"</string>
index e175208..54099c7 100644 (file)
     <string name="hdcp_checking_title" msgid="8605478913544273282">"‏בדיקת HDCP"</string>
     <string name="hdcp_checking_dialog_title" msgid="5141305530923283">"‏הגדר אופן בדיקת HDCP"</string>
     <string name="debug_debugging_category" msgid="6781250159513471316">"ניפוי באגים"</string>
-    <string name="debug_app" msgid="8349591734751384446">"בחר אפליקציה לניפוי"</string>
+    <string name="debug_app" msgid="8349591734751384446">"בחר אפליקציה לניפוי באגים"</string>
     <string name="debug_app_not_set" msgid="718752499586403499">"לא הוגדרה אפליקציה לניפוי"</string>
     <string name="debug_app_set" msgid="2063077997870280017">"אפליקציה לניפוי: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="select_application" msgid="5156029161289091703">"בחר אפליקציה"</string>
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"מאפשר כתיבה של כל אפליקציה באחסון חיצוני, ללא התחשבות בערכי המניפסט"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"אלץ יכולת קביעת גודל של הפעילויות"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"מאפשר יכולת קביעת גודל של כל הפעילויות לריבוי חלונות, ללא קשר לערך המניפסט."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"הפעל חלונות בצורה חופשית"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"מפעיל תמיכה בתכונה הניסיונית של חלונות בצורה חופשית."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"סיסמת גיבוי מקומי"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"גיבויים מלאים בשולחן העבודה אינם מוגנים כעת"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"גע כדי לשנות או להסיר את הסיסמה עבור גיבויים מלאים בשולחן העבודה"</string>
index bf9bf8a..7e45fb2 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"マニフェストの値に関係なく、すべてのアプリを外部ストレージに書き込めるようになります"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"アクティビティをサイズ変更可能にする"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"マニフェストの値に関係なく、マルチウィンドウですべてのアクティビティのサイズを変更できるようになります。"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"フリーフォーム ウィンドウの有効化"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"テスト段階のフリーフォーム ウィンドウのサポートを有効にします。"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"PCバックアップパスワード"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"デスクトップのフルバックアップは現在保護されていません"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"デスクトップのフルバックアップ用のパスワードを変更または削除する場合にタップします"</string>
index da9d204..a8f25c6 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"აპები ჩაიწერ. გარე მეხს.-ზე აღწ. ფაილის მნიშვნ. მიუხედ."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"ზომაცვლადი აქტივობების იძულება"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"მანიფესტის მნიშვნელობების მიუხედავად, ყველა აქტივობას მრავალი ფანჯრის რეჟიმისთვის ზომაცვლადად აქცევს."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"თავისუფალი ფორმის მქონე ფანჯრების ჩართვა"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"ჩართავს თავისუფალი ფორმის მქონე ფანჯრების მხარდაჭერის ექსპერიმენტულ ფუნქციას"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"დესკტოპის სარეზერვო ასლის პაროლი"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"დესკტოპის სრული სარეზერვო ასლები ამჟამად დაცული არ არის"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"შეეხეთ დესკტოპის სრული სარეზერვო ასლების პაროლის შესაცვლელად ან წასაშლელად"</string>
index 549711c..300aaa3 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Манифест мәндеріне қарамастан кез келген қолданбаны сыртқы жадқа жазуға жарамды етеді"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Әрекеттерді өлшемін өзгертуге болатын етуге мәжбүрлеу"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Манифест мәндеріне қарамастан барлық әрекеттерді бірнеше терезе үшін өлшемін өзгертуге болатын етеді."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Еркін пішіндегі терезелерді қосу"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Эксперименттік еркін пішіндегі терезелерді қолдауды қосады."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Компьютер үстелінің сақтық көшірмесі"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Жұмыс үстелінің сақтық көшірмелері қазір қорғалмаған"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Жұмыс үстелінің толық сақтық көшірмесінің кілтсөзін өзгерту немесе жою үшін түртіңіз"</string>
index aed4365..a414668 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ធ្វើឲ្យកម្មវិធីទាំងឡាយមានសិទ្ធិសរសេរទៅកាន់ឧបករណ៍ផ្ទុកខាងក្រៅ ដោយមិនគិតពីតម្លៃជាក់លាក់"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"បង្ខំឲ្យសកម្មភាពអាចប្តូរទំហំបាន"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"កំណត់ឲ្យសកម្មភាពទាំងអស់អាចប្តូរទំហំបានសម្រាប់ពហុផ្ទាំងវិនដូ ដោយមិនគិតពីតម្លៃមេនីហ្វេសឡើយ។"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"បើកដំណើរការផ្ទាំងវិនដូទម្រង់សេរី"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"បើកដំណើរការគាំទ្រផ្ទាំងវិនដូទម្រង់សេរីសាកល្បង"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ពាក្យ​សម្ងាត់​បម្រុង​ទុក​លើ​ផ្ទៃតុ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ការ​បម្រុង​ទុក​ពេញលេញ​លើ​ផ្ទៃតុ​បច្ចុប្បន្ន​មិន​ត្រូវ​បាន​ការពារ​ទេ។"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ប៉ះ ដើម្បី​ប្ដូរ ឬ​លុប​ពាក្យ​សម្ងាត់​សម្រាប់​ការ​បម្រុងទុក​ពេញលេញ​លើ​ផ្ទៃតុ"</string>
index ea5dc00..c23a8a7 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"매니페스트 값에 관계없이 앱을 외부 저장소에 작성"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"활동의 크기가 조정 가능하도록 설정"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"모든 활동을 매니페스트 값에 관계없이 멀티 윈도우용으로 크기 조정 가능하도록 설정"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"자유 형식 창 사용"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"자유 형식 창(베타) 지원 사용"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"데스크톱 백업 비밀번호"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"데스크톱 전체 백업에 비밀번호가 설정되어 있지 않음"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"데스크톱 전체 백업에 대한 비밀번호를 변경하거나 삭제하려면 터치하세요."</string>
index 6a47406..84bfe94 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Манифест маанилерине карабастан бардык колдонмолорду тышкы сактагычка сактоого уруксат берет"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Аракеттердин өлчөмүн өзгөртүүнү мажбурлоо"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Манифест маанилерине карабастан бардык аракеттерди мульти-терезеге өлчөмү өзгөртүлгүдөй кылат."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Эркин формадагы терезелерди түзүүнү иштетүү"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Эркин формадагы терезелерди түзүү боюнча сынамык функцияны иштетүү"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Компүтердеги бэкаптын сырсөзү"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Компүтердеги толук бэкап учурда корголгон эмес"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Тийип, компүтердеги толук бэкаптын сырсөзүн өзгөртүңүз же жок кылыңыз"</string>
index 538a38c..4b5359a 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ເຮັດ​ໃຫ້ທຸກແອັບ​ມີ​ສິດ​ໄດ້ຮັບການຂຽນ​ໃສ່​ບ່ອນ​ຈັດ​ເກັບ​ພາຍນອກ, ໂດຍ​ບໍ່​ຄຳ​ນຶງ​ເຖິງ​ຄ່າ​ທີ່​ຈະ​ແຈ້ງ"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"ບັງ​ຄັງ​ໃຫ້​ກິດ​ຈະ​ກຳ​ປ່ຽນ​ຂະ​ໜາດ​ໄດ້"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ເຮັດ​ໃຫ້​ທຸກ​ກິດ​ຈະ​ກຳ​ປ່ຽນ​ຂະ​ໜາດ​ໄດ້​ສຳ​ລັບ​ຫຼາຍ​ໜ້າ​ຕ່າງ, ໂດຍ​ບໍ່​ຄຳ​ນຶງ​ເຖິງ​ຄ່າ​ທີ່​ຈະ​ແຈ້ງ."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"ເປີດໃຊ້ໜ້າຕ່າງຮູບແບບອິດສະຫຼະ"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"ເປີດໃຊ້ການຮອງຮັບໜ້າຕ່າງຮູບແບບອິດສະຫຼະທີ່ທົດລອງໃຊ້."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ລະຫັດຜ່ານການສຳຮອງຂໍ້ມູນເດັກສະທັອບ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ການ​ສຳຮອງ​ຂໍ້ມູນ​ເຕັມຮູບແບບ​ໃນ​ເດັກສະທັອບ​ຍັງ​ບໍ່​ໄດ້​ຮັບ​ການ​ປ້ອງກັນ​ໃນ​ເວລາ​ນີ້"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ແຕະເພື່ອປ່ຽນ ຫຼືລຶບລະຫັດຂອງການສຳຮອງຂໍ້ມູນເຕັມຮູບແບບໃນເດັກສະທັອບ"</string>
index 014a6fb..6b21041 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Vis. pr. gal. įr. į vid. saug. nepais. apr. vert."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Priv. nust., kad veiksm. b. g. atl. kelių d. lang."</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Nustatoma, kad visus veiksmus būtų galima atlikti kelių dydžių languose, nepaisant aprašo verčių."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Įgalinti laisvos formos langus"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Įgalinamas eksperimentinių laisvos formos langų palaikymas."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Viet. atsrg. kop. slapt."</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Šiuo metu visos vietinės atsarginės kopijos neapsaugotos"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Jei norite pakeisti ar pašalinti visų vietinių atsarginių kopijų slaptažodį, palieskite"</string>
index 87da105..0652b05 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Ļauj jebkuru lietotni ierakstīt ārējā krātuvē neatkarīgi no manifesta vērtības."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Pielāgot darbības"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Pielāgo visas darbības vairāku logu režīmam neatkarīgi no vērtībām manifestā."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Iespējot brīvās formas logus"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Iespējo eksperimentālo brīvās formas logu atbalstu."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Datora dublējuma parole"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Darbvirsmas pilnie dublējumi pašlaik nav aizsargāti."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Pieskarieties, lai mainītu vai noņemtu paroli pilniem darbvirsmas dublējumiem."</string>
index d76e87f..2457847 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"മാനിഫെസ്റ്റ് മൂല്യങ്ങൾ പരിഗണിക്കാതെ, ബാഹ്യ സ്റ്റോറേജിലേക്ക് എഴുതപ്പെടുന്നതിന് ഏതൊരു ആപ്പിനെയും യോഗ്യമാക്കുന്നു"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"വലിപ്പം മാറ്റാൻ പ്രവർത്തനങ്ങളെ നിർബന്ധിക്കുക"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"മാനിഫെസ്റ്റ് മൂല്യങ്ങൾ പരിഗണിക്കാതെ, എല്ലാ പ്രവർത്തനങ്ങളെയും മൾട്ടി-വിൻഡോയ്ക്കായി വലിപ്പം മാറ്റുന്നു."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"ഫ്രീഫോം വിൻഡോകൾ പ്രവർത്തനക്ഷമമാക്കുക"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"പരീക്ഷണാത്മക ഫ്രീഫോം വിൻഡോകൾക്കുള്ള പിന്തുണ പ്രവർത്തനക്ഷമമാക്കുന്നു."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ഡെ‌സ്‌ക്ടോപ്പ് ബാക്കപ്പ് പാസ്‌വേഡ്"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ഡെസ്‌ക്‌ടോപ്പ് പൂർണ്ണ ബാക്കപ്പുകൾ നിലവിൽ പരിരക്ഷിച്ചിട്ടില്ല"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ഡെസ്‌ക്‌ടോപ്പ് പൂർണ്ണ ബാക്കപ്പുകൾക്കായി പാസ്‌വേഡുകൾ മാറ്റാനോ നീക്കംചെയ്യാനോ സ്‌പർശിക്കുക"</string>
index 4aea631..622c13f 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Манифест утгыг нь үл хамааран дурын апп-ыг гадаад санах ойд бичих боломжтой болгодог"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Үйл ажиллагааны хэмжээг өөрчилж болохуйц болгох"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Тодорхойлогч файлын утгыг үл хамааран, бүх үйл ажиллагааг олон цонхонд хэмжээг нь өөрчилж болохуйц болгох."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Чөлөөт хэлбэрийн цонхыг идэвхжүүлэх"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Туршилтын чөлөөт хэлбэрийн цонхны дэмжлэгийг идэвхжүүлдэг."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Десктоп нөөшлөлтийн нууц үг"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Десктоп бүрэн нөөцлөлт одоогоор хамгаалалтгүй байна"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Десктоп дээрх бүрэн нөөшлөлтийн нууц үгийг өөрчлөх буюу арилгахын тулд хүрнэ үү"</string>
index 1f72d04..5f27232 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Menjadikan sebarang apl layak ditulis ke storan luaran, walau apa juga nilai manifesnya"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Paksa aktiviti supaya boleh diubah saiz"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Menjadikan semua aktiviti boleh diubah saiz untuk berbilang tetingkap, tanpa mengambil kira nilai manifes."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Dayakan tetingkap bentuk bebas"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Mendayakan sokongan untuk tetingkap bentuk bebas percubaan."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Kata laluan sandaran komputer meja"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Sandaran penuh komputer meja tidak dilindungi pada masa ini"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Sentuh untuk menukar atau mengalih keluar kata laluan untuk sandaran penuh komputer meja"</string>
index cd4812f..0a86cdb 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ပြနေတဲ့ တန်ဖိုး ဘယ်လိုပဲရှိနေနေ၊ ဘယ် appကို မဆို အပြင် သိုလှောင်ခန်းသို့ ရေးသားခွင့် ပေးတယ်"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"လုပ်ဆောင်ချက်များ ဆိုက်ညှိရနိုင်ရန် လုပ်ခိုင်းပါ"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"မန်နီးဖက်စ် တန်ဖိုးမရွေး၊ လုပ်ဆောင်ချက် အားလုံး ဆိုက်ညှိရနိုင်အောင် လုပ်ပေးပါ။"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"အခမဲ့ပုံစံ ဝင်းဒိုးကို ဖွင့်ပါ"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"စမ်းသပ်မှု အခမဲ့ပုံစံ ဝင်းဒိုးများအတွက် ပံ့ပိုးမှုကို ဖွင့်ပါ။"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop အရန်စကားဝှက်"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"အလုပ်ခုံတွင် အရန်သိမ်းဆည်းခြင်းများကို လောလောဆယ် မကာကွယ်နိုင်ပါ။"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"အလုပ်ခုံ တွင် အရန်သိမ်းဆည်းခြင်းအပြည့်လုပ်ရန် အတွက် စကားဝှက်ဖယ်ရန် သို့ ပြောင်းရန် တို့ထိပါ။"</string>
index ae0b15d..99ee44c 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Gjør at apper kan skrives til ekstern lagring, uavhengig av manifestverdier"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Tving aktiviteter til å kunne endre størrelse"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Dette gjør at alle aktivitene kan endre størrelse for flervindusmodus, uavhengig av manifestverdier."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Slå på vinduer i fritt format"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Slår på støtte for vinduer i eksperimentelt fritt format."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Passord for sikkerhetskopiering på datamaskin"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Fullstendig sikkerhetskopiering på datamaskin beskyttes ikke for øyeblikket."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Trykk for å endre eller fjerne passordet for fullstendige sikkerhetskopier på datamaskinen"</string>
index 5885513..a109197 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"म्यानिफेेस्टको उपेक्षा गरी, कुनै पनि अनुप्रयोगलाई बाह्य भण्डारणमा लेख्न योग्य बनाउँछ"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"गतिविधिहरू रिसाइज गर्नको लागि बाध्य गर्नुहोस्"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"म्यानिफेेस्ट मानहरूको ख्याल नगरी, बहु-विन्डोको लागि सबै रिसाइज गर्न सकिने गतिविधिहरू बनाउँछ।"</string>
-    <string name="enable_freeform_support" msgid="1461893351278940416">"फà¥\8dरà¥\80फर्म विन्डोहरू सक्रिय गर्नुहोस्"</string>
-    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"पà¥\8dरयà¥\8bà¤\97ातà¥\8dमà¤\95 à¤«à¥\8dरà¥\80फर्म विन्डोहरूका लागि समर्थनलाई सक्रिय गर्छ।"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"फà¥\8dरिफर्म विन्डोहरू सक्रिय गर्नुहोस्"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"पà¥\8dरयà¥\8bà¤\97ातà¥\8dमà¤\95 à¤«à¥\8dरिफर्म विन्डोहरूका लागि समर्थनलाई सक्रिय गर्छ।"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"डेस्कटप ब्याकअप पासवर्ड"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"डेस्कटप पूर्ण जगेडाहरू हाललाई सुरक्षित छैनन्"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"डेस्कटप पूर्ण ब्याकअपको लागि पासवर्ड बदल्न वा हटाउन छुनुहोस्"</string>
index dd4b58d..98a3121 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Qualifica apps p/ gravação em armazenamento externo, independentemente de valores de manifestos"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forçar atividades a serem redimensionáveis"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Torna todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Ativar janelas de forma livre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ativa a compatibilidade com janelas de forma livre experimentais."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Senha do backup local"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Os backups completos do computador não estão protegidos no momento"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toque para alterar ou remover a senha de backups completos do desktop"</string>
index dd4b58d..98a3121 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Qualifica apps p/ gravação em armazenamento externo, independentemente de valores de manifestos"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forçar atividades a serem redimensionáveis"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Torna todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Ativar janelas de forma livre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ativa a compatibilidade com janelas de forma livre experimentais."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Senha do backup local"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Os backups completos do computador não estão protegidos no momento"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toque para alterar ou remover a senha de backups completos do desktop"</string>
index a666da0..00ff92b 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Face orice aplicație eligibilă să fie scrisă în stocarea externă, indiferent de valorile manifestului"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forțați redimensionarea activităților"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permite redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Activați ferestrele cu formă liberă"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Activează compatibilitatea pentru ferestrele experimentale cu formă liberă."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Parolă copie rez. desktop"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"În prezent, copiile de rezervă complete pe desktop nu sunt protejate"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Atingeţi pentru a modifica sau pentru a elimina parola pentru copiile de rezervă complete pe desktop"</string>
index e93bae1..9a3a7e6 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Разрешает сохранение приложений на внешние накопители независимо от значения манифеста"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Изменение размера в многооконном режиме"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Позволяет менять размер в многооконном режиме (независимо от значений манифеста)"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Разрешить создание окон произвольной формы"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Включить экспериментальную функцию создания окон произвольной формы"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Пароль для резервного копирования"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Полные резервные копии в настоящее время не защищены"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Изменить или удалить пароль для резервного копирования"</string>
index 6753624..083dd05 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"මැනිෆෙස්ට් අගයන් නොසලකා, ඕනෑම යෙදුමක් අභ්‍යන්තර ගබඩාවට ලිවීමට සුදුසුකම් ලබා දෙයි"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"ක්‍රියාකාරකම් ප්‍රතිප්‍රමාණ කළ හැකි බවට බල කරන්න"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"මැනිෆෙස්ට් අගයන් නොසලකා, සියලු ක්‍රියාකාරකම් බහු-කවුළු සඳහා ප්‍රතිප්‍රමාණ කළ හැකි බවට පත් කරයි."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"අනියම් හැඩැති කවුළු සබල කරන්න"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"පරීක්ෂණාත්මක අනියම් හැඩැති කවුළු සඳහා සහාය සබල කරයි."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ඩෙස්ක්ටොප් උපස්ථ මුරපදය"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ඩෙස්ක්ටොප් සම්පූර්ණ උපස්ථ දැනට ආරක්ෂා කර නොමැත"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ඩෙස්ක්ටොප් සම්පූර්ණ උපස්ථ සඳහා මුරපදය වෙනස් කිරීමට හෝ ඉවත් කිරීමට ස්පර්ශ කරන්න"</string>
index 7db7835..e12f435 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Umožňuje zapísať akúkoľvek aplikáciu do externého úložiska bez ohľadu na hodnoty v manifeste"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Vynútiť možnosť zmeny veľkosti aktivít"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Veľkosti všetkých aktivít bude možné zmeniť na niekoľko okien (bez ohľadu na hodnoty manifestu)."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Povoliť okná s voľným tvarom"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Povolenie podpory pre experimentálne okná s voľným tvarom."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Heslo pre zálohy v počítači"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Úplné zálohy na počítači nie sú momentálne chránené"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Dotykom zmeníte alebo odstránite heslo pre úplné zálohy do počítača"</string>
index ed37933..1f66583 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Poskrbi, da je ne glede na vrednosti v manifestu mogoče vsako aplikacijo zapisati v zunanjo shrambo"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Vsili povečanje velikosti za aktivnosti"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Poskrbi, da je ne glede na vrednosti v manifestu mogoče vsem aktivnostim povečati velikost za način z več okni."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Omogočanje oken svobodne oblike"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Omogočanje podpore za poskusna okna svobodne oblike"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Geslo za varn. kop. rač."</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Popolne varnostne kopije namizja trenutno niso zaščitene"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Dotaknite se, če želite spremeniti ali odstraniti geslo za popolno varnostno kopiranje namizja."</string>
index 9596ce5..ab96afd 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Омогућава уписивање свих апликација у спољну меморију, без обзира на вредности манифеста"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Принудно омогући промену величине активности"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Омогућава промену величине свих активности за режим са више прозора, без обзира на вредности манифеста."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Омогући прозоре произвољног формата"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Омогућава подршку за експерименталне прозоре произвољног формата."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Лозинка резервне копије за рачунар"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Резервне копије читавог система тренутно нису заштићене"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Додирните да бисте променили или уклонили лозинку за прављење резервних копија читавог система на рачунару"</string>
index 7d0d0a0..f8901c9 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Appen kan skrivas till extern lagring, oavsett manifestvärden"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Framtvinga storleksanpassning för aktiviteter"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Detta gör det möjligt att ändra storleken på alla aktiviteter i flerfönsterläge, oavsett manifestvärden."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Aktivera frihandsfönster"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktiverar stöd för experimentella frihandsfönster."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Lösenord för säkerhetskopia av datorn"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"De fullständiga säkerhetskopiorna av datorn är för närvarande inte skyddade"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tryck om du vill ändra eller ta bort lösenordet för fullständig säkerhetskopiering av datorn"</string>
index d1041da..8a33946 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Huweka programu kwenye hifadhi ya nje, bila kujali maelezo"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Lazimisha shughuli ziweze kubadilishwa ukubwa"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Fanya shughuli zote ziweze kubadilishwa ukubwa kwa ajili ya dirisha nyingi, bila kujali thamani za faili ya maelezo."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Washa madirisha yenye muundo huru"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Huwasha uwezo wa kutumia madirisha ya majaribio yenye muundo huru."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Nenosiri la hifadhi rudufu ya eneo kazi"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Hifadhi rudufu kamili za eneo kazi hazijalindwa kwa sasa"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Gusa ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
index e66f073..d386039 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"மேனிஃபெஸ்ட் மதிப்புகளை பொருட்படுத்தாமல், எந்தப் பயன்பாட்டையும் வெளிப்புற சேமிப்பிடத்தில் எழுத அனுமதிக்கும்"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"செயல்பாடுகளை அளவுமாறக்கூடியதாக அமை"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"மேனிஃபெஸ்ட் மதிப்புகளைப் பொருட்படுத்தாமல், பல சாளரத்திற்கு எல்லா செயல்பாடுகளையும் அளவுமாறக்கூடியதாக அமைக்கும்."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"குறிப்பிட்ட வடிவமில்லாத சாளரங்களை இயக்கு"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"பரிசோதனைக்குரிய குறிப்பிட்ட வடிவமில்லாத சாளரங்களுக்கான ஆதரவை இயக்கும்."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"டெஸ்க்டாப் காப்புப்பிரதி கடவுச்சொல்"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"டெஸ்க்டாப்பின் முழு காப்புப்பிரதிகள் தற்போது பாதுகாக்கப்படவில்லை"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"டெஸ்க்டாப்பின் முழுமையான காப்புப்பிரதிகளுக்கான கடவுச்சொல்லை மாற்றுவதற்கு அல்லது அகற்றுவதற்குத் தொடவும்"</string>
index 6826419..b17b516 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ให้สามารถเขียนแอปต่างๆ ไปยังที่เก็บภายนอกได้ โดยไม่คำนึงถึงค่าในไฟล์ Manifest"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"บังคับให้กิจกรรมปรับขนาดได้"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ทำให้กิจกรรมทั้งหมดปรับขนาดได้สำหรับหน้าต่างหลายบาน โดยไม่คำนึงถึงค่าในไฟล์ Manifest"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"เปิดใช้หน้าต่างรูปแบบอิสระ"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"เปิดการสนับสนุนหน้าต่างรูปแบบอิสระแบบทดลอง"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"รหัสผ่านการสำรองข้อมูลในเดสก์ท็อป"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"การสำรองข้อมูลเต็มรูปแบบในเดสก์ท็อป ไม่ได้รับการป้องกันในขณะนี้"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"แตะเพื่อเปลี่ยนหรือลบรหัสผ่านสำหรับการสำรองข้อมูลเต็มรูปแบบในเดสก์ท็อป"</string>
index 8f62326..d0e7f87 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Mara-write na sa external storage ang anumang app, anuman ang manifest value"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Sapilitang gawing resizable ang mga aktibidad"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Gawing resizable para sa multi-window ang lahat ng aktibidad, anuman ang mga manifest value."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"I-enable ang mga freeform window"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ine-enable ang suporta para sa mga pang-eksperimentong freeform window."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Password ng pag-backup ng desktop"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Kasalukuyang hindi pinoprotektahan ang mga buong pag-backup ng desktop"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Pindutin upang baguhin o alisin ang password para sa mga buong pag-backup ng desktop"</string>
index 5e0839a..e6ddcdd 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Bildirilen değerlerden bağımsız olarak uygulamaları harici depolamaya yazmak için uygun hale getirir"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Etkinlikleri yeniden boyutlandırılabilmeye zorla"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Manifest değerlerinden bağımsız olarak, tüm etkinlikleri birden fazla pencerede yeniden boyutlandırılabilir hale getirir."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Serbest biçimli pencereleri etkinleştir"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Deneysel serbest biçimli pencereleri etkinleştirir."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Masaüstü yedekleme şifresi"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Masaüstü tam yedeklemeleri şu an korunmuyor"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Masaüstü tam yedeklemelerinin şifresini değiştirmek veya kaldırmak için dokunun"</string>
index 0834303..e9fe7e4 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"‏manifest اقدار سے قطع نظر، کسی بھی ایپ کو بیرونی اسٹوریج پر لکھے جانے کا اہل بناتا ہے"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"سرگرمیوں کو ری سائز ایبل بنائیں"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"‏manifest اقدار سے قطع نظر، ملٹی ونڈو کیلئے تمام سرگرمیوں کو ری سائز ایبل بناتا ہے۔"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"‏freeform ونڈوز فعال کریں"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"‏تجرباتی freeform ونڈوز کے لئے سپورٹ فعال کرتا ہے۔"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ڈیسک ٹاپ کا بیک اپ پاس ورڈ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ڈیسک ٹاپ کے مکمل بیک اپس فی الحال محفوظ کیے ہوئے نہیں ہیں"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ڈیسک ٹاپ کے مکمل بیک اپس کیلئے پاس ورڈ کو تبدیل کرنے یا ہٹانے کیلئے ٹچ کریں"</string>
index d2d8b76..e53336b 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Manifest qiymatidan qat’i nazar istalgan ilovani tashqi xotiraga saqlash imkonini beradi"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Harakatlarni moslashuvchan o‘lchamga keltirish"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Manifest qiymatidan qat’i nazar barcha harakatlarni ko‘p oynali rejimga moslashtiradi."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Erkin shakldagi oynalarni yoqish"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Tajribaviy erkin shakldagi oynalar ta’minotini yoqadi"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Zaxira nusxa uchun parol"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Kompyuterdagi zaxira nusxalar hozirgi vaqtda himoyalanmagan"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Ish stoli to\'liq zaxira nusxalari parolini o‘zgartirish yoki o‘chirish uchun bu yerni bosing."</string>
index 178e301..193f066 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Giúp ứng dụng bất kỳ đủ điều kiện được ghi vào bộ nhớ ngoài bất kể giá trị tệp kê khai là gì"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Buộc các hoạt động có thể thay đổi kích thước"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Giúp tất cả hoạt động có thể thay đổi kích thước cho nhiều cửa sổ bất kể giá trị tệp kê khai là gì."</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Bật cửa sổ dạng tự do"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Bật tính năng hỗ trợ cửa sổ dạng tự do thử nghiệm."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Mật khẩu sao lưu của máy tính"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Sao lưu toàn bộ máy tính hiện không được bảo vệ"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Chạm để thay đổi hoặc xóa mật khẩu dành cho bộ sao lưu toàn bộ tới máy tính"</string>
index 5b96383..de8996e 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"允许将任何应用写入外部存储设备(无论清单值是什么)"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"强制将活动设为可调整大小"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"将所有活动设为可配合多窗口环境调整大小(无论清单值是什么)。"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"启用可自由调整的窗口"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"启用可自由调整的窗口这一实验性功能。"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"桌面备份密码"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"桌面完整备份当前未设置密码保护"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"触摸可更改或删除用于桌面完整备份的密码"</string>
index db5f09b..9a86f3a 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"允許將所有應用程式寫入到外部儲存完間 (所有資訊清單值)"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"強制可變更活動尺寸"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"在任何資訊清單值下,允許為多個視窗變更所有活動的尺寸。"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"啟用自由形態視窗"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"啟用實驗版自由形態視窗的支援功能。"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"桌面電腦備份密碼"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"桌上電腦的完整備份目前未受保護"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"輕觸即可更改或移除桌上電腦完整備份的密碼"</string>
index 787a442..3ab60f5 100644 (file)
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"允許將任何應用程式寫入外部儲存空間 (無論資訊清單值為何)"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"將活動強制設為可調整大小"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"將所有活動設為可配合多重視窗環境調整大小 (無論資訊清單值為何)。"</string>
-    <!-- no translation found for enable_freeform_support (1461893351278940416) -->
-    <skip />
-    <!-- no translation found for enable_freeform_support_summary (2252563497485436534) -->
-    <skip />
+    <string name="enable_freeform_support" msgid="1461893351278940416">"啟用自由形式視窗"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"啟用實驗版自由形式視窗的支援功能。"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"電腦備份密碼"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"電腦完整備份目前未受保護"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"輕觸即可變更或移除電腦完整備份的密碼"</string>
index f7e25db..ac19cf5 100644 (file)
     <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
     <string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
 
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+    <string name="power_discharging_duration"><xliff:g id="level">%1$s</xliff:g>
+        - approx. <xliff:g id="time">%2$s</xliff:g> left</string>
+
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
+    <string name="power_charging"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="state">%2$s</xliff:g></string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration_ac"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full on AC</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration_usb"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full over USB</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration_wireless"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full from wireless</string>
+
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_unknown">Unknown</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging from an unknown source.  -->
+    <string name="battery_info_status_charging">Charging</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging on AC.  -->
+    <string name="battery_info_status_charging_ac">Charging on AC</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging over USB.  -->
+    <string name="battery_info_status_charging_usb">Charging over USB</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging over a wireless connection.  -->
+    <string name="battery_info_status_charging_wireless">Charging wirelessly</string>
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_discharging">Not charging</string>
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_not_charging">Not charging</string>
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_full">Full</string>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
new file mode 100644 (file)
index 0000000..d81bdeb
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 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.settingslib;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.text.format.Formatter;
+import com.android.internal.os.BatteryStatsHelper;
+
+public class BatteryInfo {
+
+    public String mChargeLabelString;
+    public int mBatteryLevel;
+    public boolean mDischarging = true;
+    public long remainingTimeUs = 0;
+
+    public interface Callback {
+        void onBatteryInfoLoaded(BatteryInfo info);
+    }
+
+    public static void getBatteryInfo(final Context context, final Callback callback) {
+        new AsyncTask<Void, Void, BatteryStats>() {
+            @Override
+            protected BatteryStats doInBackground(Void... params) {
+                BatteryStatsHelper statsHelper = new BatteryStatsHelper(context, true);
+                statsHelper.create((Bundle) null);
+                return statsHelper.getStats();
+            }
+
+            @Override
+            protected void onPostExecute(BatteryStats batteryStats) {
+                final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
+                Intent batteryBroadcast = context.registerReceiver(null,
+                        new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+                BatteryInfo batteryInfo = BatteryInfo.getBatteryInfo(context,
+                        batteryBroadcast, batteryStats, elapsedRealtimeUs);
+                callback.onBatteryInfoLoaded(batteryInfo);
+            }
+        }.execute();
+    }
+
+    public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
+            BatteryStats stats, long elapsedRealtimeUs) {
+        BatteryInfo info = new BatteryInfo();
+        info.mBatteryLevel = Utils.getBatteryLevel(batteryBroadcast);
+        String batteryPercentString = Utils.formatPercentage(info.mBatteryLevel);
+        if (batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) == 0) {
+            final long drainTime = stats.computeBatteryTimeRemaining(elapsedRealtimeUs);
+            if (drainTime > 0) {
+                info.remainingTimeUs = drainTime;
+                String timeString = Formatter.formatShortElapsedTime(context,
+                        drainTime / 1000);
+                info.mChargeLabelString = context.getResources().getString(
+                        R.string.power_discharging_duration, batteryPercentString, timeString);
+            } else {
+                info.mChargeLabelString = batteryPercentString;
+            }
+        } else {
+            final long chargeTime = stats.computeChargeTimeRemaining(elapsedRealtimeUs);
+            final String statusLabel = Utils.getBatteryStatus(
+                    context.getResources(), batteryBroadcast);
+            final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
+                    BatteryManager.BATTERY_STATUS_UNKNOWN);
+            if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
+                info.mDischarging = false;
+                info.remainingTimeUs = chargeTime;
+                String timeString = Formatter.formatShortElapsedTime(context,
+                        chargeTime / 1000);
+                int plugType = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+                int resId;
+                if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
+                    resId = R.string.power_charging_duration_ac;
+                } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
+                    resId = R.string.power_charging_duration_usb;
+                } else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+                    resId = R.string.power_charging_duration_wireless;
+                } else {
+                    resId = R.string.power_charging_duration;
+                }
+                info.mChargeLabelString = context.getResources().getString(
+                        resId, batteryPercentString, timeString);
+            } else {
+                info.mChargeLabelString = context.getResources().getString(
+                        R.string.power_charging, batteryPercentString, statusLabel);
+            }
+        }
+        return info;
+    }
+}
index 621a09c..72df96d 100644 (file)
@@ -1,17 +1,21 @@
 package com.android.settingslib;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Drawable;
 import android.net.ConnectivityManager;
+import android.os.BatteryManager;
 import android.os.UserManager;
-
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.drawable.CircleFramedDrawable;
 
-public final class Utils {
+import java.text.NumberFormat;
+
+public class Utils {
 
     /**
      * Return string resource that best describes combination of tethering
@@ -81,4 +85,57 @@ public final class Utils {
         return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
                 UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
     }
+
+    /** Formats the ratio of amount/total as a percentage. */
+    public static String formatPercentage(long amount, long total) {
+        return formatPercentage(((double) amount) / total);
+    }
+
+    /** Formats an integer from 0..100 as a percentage. */
+    public static String formatPercentage(int percentage) {
+        return formatPercentage(((double) percentage) / 100.0);
+    }
+
+    /** Formats a double from 0.0..1.0 as a percentage. */
+    private static String formatPercentage(double percentage) {
+      return NumberFormat.getPercentInstance().format(percentage);
+    }
+
+    public static int getBatteryLevel(Intent batteryChangedIntent) {
+        int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+        int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
+        return (level * 100) / scale;
+    }
+
+    public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
+        final Intent intent = batteryChangedIntent;
+
+        int plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
+                BatteryManager.BATTERY_STATUS_UNKNOWN);
+        String statusString;
+        if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+            int resId;
+            if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
+                resId = R.string.battery_info_status_charging_ac;
+            } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
+                resId = R.string.battery_info_status_charging_usb;
+            } else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+                resId = R.string.battery_info_status_charging_wireless;
+            } else {
+                resId = R.string.battery_info_status_charging;
+            }
+            statusString = res.getString(resId);
+        } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+            statusString = res.getString(R.string.battery_info_status_discharging);
+        } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
+            statusString = res.getString(R.string.battery_info_status_not_charging);
+        } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
+            statusString = res.getString(R.string.battery_info_status_full);
+        } else {
+            statusString = res.getString(R.string.battery_info_status_unknown);
+        }
+
+        return statusString;
+    }
 }
index 9546c8d..6201fd6 100644 (file)
@@ -58,6 +58,7 @@
     <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
     <uses-permission android:name="android.permission.CONTROL_VPN" />
     <uses-permission android:name="android.permission.PEERS_MAC_ADDRESS"/>
     <!-- Block notifications inline notifications -->
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
 
+    <!-- Access battery information -->
+    <uses-permission android:name="android.permission.BATTERY_STATS" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
diff --git a/packages/SystemUI/res/layout/battery_detail.xml b/packages/SystemUI/res/layout/battery_detail.xml
new file mode 100644 (file)
index 0000000..ea4db4b
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="16dp"
+    android:background="?android:attr/selectableItemBackground"
+    android:clickable="true">
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:scaleType="fitCenter"
+        android:adjustViewBounds="true"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentStart="true"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="32dp" />
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_toStartOf="@android:id/toggle"
+        android:layout_toEndOf="@android:id/icon"
+        android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary"
+        android:text="@string/battery_detail_switch_title" />
+
+    <TextView
+        android:id="@android:id/summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/title"
+        android:layout_toStartOf="@android:id/toggle"
+        android:layout_toEndOf="@android:id/icon"
+        android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary"
+        android:text="@string/battery_detail_switch_summary" />
+
+    <Switch
+        android:id="@android:id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        android:layout_marginEnd="16dp"
+        android:clickable="false"
+        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+</RelativeLayout>
index 7ac9c41..39da8d0 100644 (file)
               android:layout_width="match_parent"
               android:layout_height="match_parent"/>
 
-    <com.android.systemui.statusbar.phone.PanelHolder
-        android:id="@+id/panel_holder"
+    <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@color/transparent" >
-        <include layout="@layout/status_bar_expanded"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:visibility="gone" />
-    </com.android.systemui.statusbar.phone.PanelHolder>
+        android:visibility="gone" />
 
     <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front"
         android:layout_width="match_parent"
index bb8f4ad..1e2dba3 100644 (file)
@@ -77,7 +77,7 @@
     <string name="usb_preference_title" msgid="6551050377388882787">"USB文件传输选项"</string>
     <string name="use_mtp_button_title" msgid="4333504413563023626">"作为媒体播放器(MTP)装载"</string>
     <string name="use_ptp_button_title" msgid="7517127540301625751">"作为相机(PTP)装载"</string>
-    <string name="installer_cd_button_title" msgid="2312667578562201583">"安装适用于Mac的Android文件传输应用"</string>
+    <string name="installer_cd_button_title" msgid="2312667578562201583">"安装适用于 Mac 的 Android 文件传输应用"</string>
     <string name="accessibility_back" msgid="567011538994429120">"返回"</string>
     <string name="accessibility_home" msgid="8217216074895377641">"主屏幕"</string>
     <string name="accessibility_menu" msgid="316839303324695949">"菜单"</string>
     <string name="notification_importance_default" msgid="4926529615920610817">"显示这些通知,但不发出提示音"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"在通知列表顶部显示,并发出提示音"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"在屏幕上持续显示,并发出提示音"</string>
-    <!-- no translation found for notification_more_settings (816306283396553571) -->
-    <skip />
-    <!-- no translation found for notification_done (5279426047273930175) -->
-    <skip />
+    <string name="notification_more_settings" msgid="816306283396553571">"更多设置"</string>
+    <string name="notification_done" msgid="5279426047273930175">"完成"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"常规颜色"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"夜间颜色"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"自定义颜色"</string>
index 6102aa6..bf0cba2 100644 (file)
         <item>157</item><item>334</item>
         <item>0</item>  <item>334</item>
     </array>
+    <array name="batterymeter_plus_points">
+        <item>3</item><item>0</item>
+        <item>5</item><item>0</item>
+        <item>5</item><item>3</item>
+        <item>8</item><item>3</item>
+        <item>8</item><item>5</item>
+        <item>5</item><item>5</item>
+        <item>5</item><item>8</item>
+        <item>3</item><item>8</item>
+        <item>3</item><item>5</item>
+        <item>0</item><item>5</item>
+        <item>0</item><item>3</item>
+        <item>3</item><item>3</item>
+    </array>
 </resources>
index 0ccc236..905da13 100644 (file)
@@ -62,8 +62,6 @@
     <color name="recents_task_bar_light_icon_color">#ffeeeeee</color>
     <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
     <color name="recents_task_bar_dark_icon_color">#99000000</color>
-    <!-- The recents task bar highlight color. -->
-    <color name="recents_task_bar_highlight_color">#28ffffff</color>
     <!-- The lock to task button background color. -->
     <color name="recents_task_view_lock_to_app_button_background_color">#ffe6e6e6</color>
     <!-- The lock to task button foreground color. -->
index 035f564..097c352 100644 (file)
     <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
 
     <!-- The min translation in the Z index for the last task. -->
-    <dimen name="recents_task_view_z_min">20dp</dimen>
+    <dimen name="recents_task_view_z_min">16dp</dimen>
 
     <!-- The max translation in the Z index for the last task. -->
-    <dimen name="recents_task_view_z_max">80dp</dimen>
+    <dimen name="recents_task_view_z_max">48dp</dimen>
 
     <!-- The amount to translate when animating the removal of a task. -->
     <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
 
     <!-- The amount of highlight to make on each task view. -->
-    <dimen name="recents_task_view_highlight">1.5dp</dimen>
+    <dimen name="recents_task_view_highlight">1dp</dimen>
 
     <!-- The amount to offset when animating into an affiliate group. -->
     <dimen name="recents_task_view_affiliate_group_enter_offset">64dp</dimen>
index 876c21e..4136c11 100644 (file)
     <string name="color_modification_g" translatable="false">G</string>
     <string name="color_modification_b" translatable="false">B</string>
 
+    <!-- Title of the battery settings detail panel [CHAR LIMIT=20] -->
+    <string name="battery_panel_title">Battery (<xliff:g name="pattery_percent" example="52">%1$d</xliff:g>%%)</string>
+
+    <!-- Summary of battery saver not available [CHAR LIMIT=NONE] -->
+    <string name="battery_detail_charging_summary">Battery Saver not available during charging</string>
+
+    <!-- Title of switch for battery saver [CHAR LIMIT=NONE] -->
+    <string name="battery_detail_switch_title">Battery Saver</string>
+
+    <!-- Summary of switch for battery saver [CHAR LIMIT=NONE] -->
+    <string name="battery_detail_switch_summary">Reduces performance and background data</string>
+
 </resources>
index 3eb1271..38ae345 100755 (executable)
@@ -49,7 +49,8 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode,
     private float mButtonHeightFraction;
     private float mSubpixelSmoothingLeft;
     private float mSubpixelSmoothingRight;
-    private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
+    private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint,
+            mPlusPaint;
     private float mTextHeight, mWarningTextHeight;
     private int mIconTint = Color.WHITE;
 
@@ -60,10 +61,13 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode,
     private int mChargeColor;
     private final float[] mBoltPoints;
     private final Path mBoltPath = new Path();
+    private final float[] mPlusPoints;
+    private final Path mPlusPath = new Path();
 
     private final RectF mFrame = new RectF();
     private final RectF mButtonFrame = new RectF();
     private final RectF mBoltFrame = new RectF();
+    private final RectF mPlusFrame = new RectF();
 
     private final Path mShapePath = new Path();
     private final Path mClipPath = new Path();
@@ -141,6 +145,9 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode,
         mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color));
         mBoltPoints = loadBoltPoints(res);
 
+        mPlusPaint = new Paint(mBoltPaint);
+        mPlusPoints = loadPlusPoints(res);
+
         mDarkModeBackgroundColor =
                 context.getColor(R.color.dark_mode_icon_color_dual_tone_background);
         mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill);
@@ -187,8 +194,8 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode,
     }
 
     @Override
-    public void onPowerSaveChanged() {
-        mPowerSaveEnabled = mBatteryController.isPowerSave();
+    public void onPowerSaveChanged(boolean isPowerSave) {
+        mPowerSaveEnabled = isPowerSave;
         invalidateSelf();
     }
 
@@ -207,6 +214,21 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode,
         return ptsF;
     }
 
+    private static float[] loadPlusPoints(Resources res) {
+        final int[] pts = res.getIntArray(R.array.batterymeter_plus_points);
+        int maxX = 0, maxY = 0;
+        for (int i = 0; i < pts.length; i += 2) {
+            maxX = Math.max(maxX, pts[i]);
+            maxY = Math.max(maxY, pts[i + 1]);
+        }
+        final float[] ptsF = new float[pts.length];
+        for (int i = 0; i < pts.length; i += 2) {
+            ptsF[i] = (float)pts[i] / maxX;
+            ptsF[i + 1] = (float)pts[i + 1] / maxY;
+        }
+        return ptsF;
+    }
+
     @Override
     public void setBounds(int left, int top, int right, int bottom) {
         super.setBounds(left, top, right, bottom);
@@ -328,9 +350,9 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode,
 
         if (mPluggedIn) {
             // define the bolt shape
-            final float bl = mFrame.left + mFrame.width() / 4.5f;
+            final float bl = mFrame.left + mFrame.width() / 4f;
             final float bt = mFrame.top + mFrame.height() / 6f;
-            final float br = mFrame.right - mFrame.width() / 7f;
+            final float br = mFrame.right - mFrame.width() / 4f;
             final float bb = mFrame.bottom - mFrame.height() / 10f;
             if (mBoltFrame.left != bl || mBoltFrame.top != bt
                     || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
@@ -358,6 +380,39 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode,
                 // otherwise cut the bolt out of the overall shape
                 mShapePath.op(mBoltPath, Path.Op.DIFFERENCE);
             }
+        } else if (mPowerSaveEnabled) {
+            // define the plus shape
+            final float pw = mFrame.width() * 2 / 3;
+            final float pl = mFrame.left + (mFrame.width() - pw) / 2;
+            final float pt = mFrame.top + (mFrame.height() - pw) / 2;
+            final float pr = mFrame.right - (mFrame.width() - pw) / 2;
+            final float pb = mFrame.bottom - (mFrame.height() - pw) / 2;
+            if (mPlusFrame.left != pl || mPlusFrame.top != pt
+                    || mPlusFrame.right != pr || mPlusFrame.bottom != pb) {
+                mPlusFrame.set(pl, pt, pr, pb);
+                mPlusPath.reset();
+                mPlusPath.moveTo(
+                        mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),
+                        mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());
+                for (int i = 2; i < mPlusPoints.length; i += 2) {
+                    mPlusPath.lineTo(
+                            mPlusFrame.left + mPlusPoints[i] * mPlusFrame.width(),
+                            mPlusFrame.top + mPlusPoints[i + 1] * mPlusFrame.height());
+                }
+                mPlusPath.lineTo(
+                        mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),
+                        mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());
+            }
+
+            float boltPct = (mPlusFrame.bottom - levelTop) / (mPlusFrame.bottom - mPlusFrame.top);
+            boltPct = Math.min(Math.max(boltPct, 0), 1);
+            if (boltPct <= BOLT_LEVEL_THRESHOLD) {
+                // draw the bolt if opaque
+                c.drawPath(mPlusPath, mPlusPaint);
+            } else {
+                // otherwise cut the bolt out of the overall shape
+                mShapePath.op(mPlusPath, Path.Op.DIFFERENCE);
+            }
         }
 
         // compute percentage text
index 6cb8da4..cdbdc22 100644 (file)
@@ -74,7 +74,7 @@ public class BatteryMeterView extends ImageView implements BatteryController.Bat
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
 
     }
 
index 7f6cda0..99028a6 100644 (file)
@@ -32,7 +32,7 @@ public interface RecentsComponent {
     /**
      * Docks the top-most task and opens recents.
      */
-    void dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds);
+    boolean dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds);
 
     /**
      * Called during a drag-from-navbar-in gesture.
index 19b65f7..ea1c9bf 100644 (file)
@@ -52,7 +52,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
 
     private static final int SHOWING_NOTHING = 0;
     private static final int SHOWING_WARNING = 1;
-    private static final int SHOWING_SAVER = 2;
     private static final int SHOWING_INVALID_CHARGER = 3;
     private static final String[] SHOWING_STRINGS = {
         "SHOWING_NOTHING",
@@ -63,7 +62,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
 
     private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings";
     private static final String ACTION_START_SAVER = "PNW.startSaver";
-    private static final String ACTION_STOP_SAVER = "PNW.stopSaver";
     private static final String ACTION_DISMISSED_WARNING = "PNW.dismissedWarning";
 
     private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
@@ -77,7 +75,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
     private final Handler mHandler = new Handler();
     private final Receiver mReceiver = new Receiver();
     private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY);
-    private final Intent mOpenSaverSettings = settings(Settings.ACTION_BATTERY_SAVER_SETTINGS);
 
     private int mBatteryLevel;
     private int mBucket;
@@ -86,7 +83,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
 
     private long mBucketDroppedNegativeTimeMs;
 
-    private boolean mSaver;
     private boolean mWarning;
     private boolean mPlaySound;
     private boolean mInvalidCharger;
@@ -101,7 +97,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
 
     @Override
     public void dump(PrintWriter pw) {
-        pw.print("mSaver="); pw.println(mSaver);
         pw.print("mWarning="); pw.println(mWarning);
         pw.print("mPlaySound="); pw.println(mPlaySound);
         pw.print("mInvalidCharger="); pw.println(mInvalidCharger);
@@ -121,27 +116,15 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
         mScreenOffTime = screenOffTime;
     }
 
-    @Override
-    public void showSaverMode(boolean mode) {
-        mSaver = mode;
-        if (mSaver && mSaverConfirmation != null) {
-            mSaverConfirmation.dismiss();
-        }
-        updateNotification();
-    }
-
     private void updateNotification() {
         if (DEBUG) Slog.d(TAG, "updateNotification mWarning=" + mWarning + " mPlaySound="
-                + mPlaySound + " mSaver=" + mSaver + " mInvalidCharger=" + mInvalidCharger);
+                + mPlaySound + " mInvalidCharger=" + mInvalidCharger);
         if (mInvalidCharger) {
             showInvalidChargerNotification();
             mShowing = SHOWING_INVALID_CHARGER;
         } else if (mWarning) {
             showWarningNotification();
             mShowing = SHOWING_WARNING;
-        } else if (mSaver) {
-            showSaverNotification();
-            mShowing = SHOWING_SAVER;
         } else {
             mNoMan.cancelAsUser(TAG_NOTIFICATION, R.id.notification_power, UserHandle.ALL);
             mShowing = SHOWING_NOTHING;
@@ -165,8 +148,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
     }
 
     private void showWarningNotification() {
-        final int textRes = mSaver ? R.string.battery_low_percent_format_saver_started
-                : R.string.battery_low_percent_format;
+        final int textRes = R.string.battery_low_percent_format;
         final String percentage = NumberFormat.getPercentInstance().format((double) mBatteryLevel / 100.0);
         final Notification.Builder nb = new Notification.Builder(mContext)
                 .setSmallIcon(R.drawable.ic_power_low)
@@ -184,13 +166,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
         if (hasBatterySettings()) {
             nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
         }
-        if (!mSaver) {
-            nb.addAction(0,
-                    mContext.getString(R.string.battery_saver_start_action),
-                    pendingBroadcast(ACTION_START_SAVER));
-        } else {
-            addStopSaverAction(nb);
-        }
+        nb.addAction(0,
+                mContext.getString(R.string.battery_saver_start_action),
+                pendingBroadcast(ACTION_START_SAVER));
         if (mPlaySound) {
             attachLowBatterySound(nb);
             mPlaySound = false;
@@ -199,35 +177,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
         mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, n, UserHandle.ALL);
     }
 
-    private void showSaverNotification() {
-        final Notification.Builder nb = new Notification.Builder(mContext)
-                .setSmallIcon(R.drawable.ic_power_saver)
-                .setContentTitle(mContext.getString(R.string.battery_saver_notification_title))
-                .setContentText(mContext.getString(R.string.battery_saver_notification_text))
-                .setOngoing(true)
-                .setShowWhen(false)
-                .setVisibility(Notification.VISIBILITY_PUBLIC)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.battery_saver_mode_color));
-        addStopSaverAction(nb);
-        if (hasSaverSettings()) {
-            nb.setContentIntent(pendingActivity(mOpenSaverSettings));
-        }
-        mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, nb.build(), UserHandle.ALL);
-    }
-
-    private void addStopSaverAction(Notification.Builder nb) {
-        nb.addAction(0,
-                mContext.getString(R.string.battery_saver_notification_action_text),
-                pendingBroadcast(ACTION_STOP_SAVER));
-    }
-
-    private void dismissSaverNotification() {
-        if (mSaver) Slog.i(TAG, "dismissing saver notification");
-        mSaver = false;
-        updateNotification();
-    }
-
     private PendingIntent pendingActivity(Intent intent) {
         return PendingIntent.getActivityAsUser(mContext,
                 0, intent, 0, null, UserHandle.CURRENT);
@@ -272,10 +221,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
         return mOpenBatterySettings.resolveActivity(mContext.getPackageManager()) != null;
     }
 
-    private boolean hasSaverSettings() {
-        return mOpenSaverSettings.resolveActivity(mContext.getPackageManager()) != null;
-    }
-
     @Override
     public void showLowBatteryWarning(boolean playSound) {
         Slog.i(TAG,
@@ -367,7 +312,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
             IntentFilter filter = new IntentFilter();
             filter.addAction(ACTION_SHOW_BATTERY_SETTINGS);
             filter.addAction(ACTION_START_SAVER);
-            filter.addAction(ACTION_STOP_SAVER);
             filter.addAction(ACTION_DISMISSED_WARNING);
             mContext.registerReceiverAsUser(this, UserHandle.ALL, filter,
                     android.Manifest.permission.STATUS_BAR_SERVICE, mHandler);
@@ -383,10 +327,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
             } else if (action.equals(ACTION_START_SAVER)) {
                 dismissLowBatteryNotification();
                 showStartSaverConfirmation();
-            } else if (action.equals(ACTION_STOP_SAVER)) {
-                dismissSaverNotification();
-                dismissLowBatteryNotification();
-                setSaverMode(false);
             } else if (action.equals(ACTION_DISMISSED_WARNING)) {
                 dismissLowBatteryWarning();
             }
index 9459740..522d533 100644 (file)
@@ -76,10 +76,6 @@ public class PowerUI extends SystemUI {
         mReceiver.init();
     }
 
-    private void setSaverMode(boolean mode) {
-        mWarnings.showSaverMode(mode);
-    }
-
     void updateBatteryWarningLevels() {
         int critLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
@@ -141,11 +137,6 @@ public class PowerUI extends SystemUI {
             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
             mContext.registerReceiver(this, filter, null, mHandler);
-            updateSaverMode();
-        }
-
-        private void updateSaverMode() {
-            setSaverMode(mPowerManager.isPowerSaveMode());
         }
 
         @Override
@@ -210,10 +201,6 @@ public class PowerUI extends SystemUI {
                 mScreenOffTime = -1;
             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 mWarnings.userSwitched();
-            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
-                updateSaverMode();
-            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGING.equals(action)) {
-                setSaverMode(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
             } else {
                 Slog.w(TAG, "unknown intent: " + intent);
             }
@@ -251,7 +238,6 @@ public class PowerUI extends SystemUI {
 
     public interface WarningsUI {
         void update(int batteryLevel, int bucket, long screenOffTime);
-        void showSaverMode(boolean mode);
         void dismissLowBatteryWarning();
         void showLowBatteryWarning(boolean playSound);
         void dismissInvalidChargerWarning();
index 16fd9eb..91f88b9 100644 (file)
@@ -466,7 +466,7 @@ public class QSPanel extends FrameLayout implements Tunable {
             MetricsLogger.visible(mContext, detailAdapter.getMetricsCategory());
             announceForAccessibility(mContext.getString(
                     R.string.accessibility_quick_settings_detail,
-                    mContext.getString(detailAdapter.getTitle())));
+                    detailAdapter.getTitle()));
             setDetailRecord(r);
             listener = mHideGridContentWhenDone;
             if (r instanceof TileRecord && visibleDiff) {
index b6776bb..8ce6da2 100644 (file)
@@ -109,7 +109,7 @@ public abstract class QSTile<TState extends State> implements Listenable {
     }
 
     public interface DetailAdapter {
-        int getTitle();
+        CharSequence getTitle();
         Boolean getToggleState();
         View createDetailView(Context context, View convertView, ViewGroup parent);
         Intent getSettingsIntent();
index 84eac65..60238fc 100644 (file)
@@ -19,7 +19,14 @@ import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Checkable;
+import android.widget.ImageView;
+import android.widget.TextView;
 import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.BatteryInfo;
 import com.android.systemui.BatteryMeterDrawable;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -31,8 +38,11 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll
 
     private final BatteryMeterDrawable mDrawable;
     private final BatteryController mBatteryController;
+    private final BatteryDetail mBatteryDetail = new BatteryDetail();
 
     private int mLevel;
+    private boolean mPowerSave;
+    private boolean mCharging;
 
     public BatteryTile(Host host) {
         super(host);
@@ -48,6 +58,11 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll
     }
 
     @Override
+    public DetailAdapter getDetailAdapter() {
+        return mBatteryDetail;
+    }
+
+    @Override
     public int getMetricsCategory() {
         return MetricsLogger.QS_BATTERY_TILE;
     }
@@ -64,8 +79,16 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll
     }
 
     @Override
+    public void setDetailListening(boolean listening) {
+        super.setDetailListening(listening);
+        if (!listening) {
+            mBatteryDetail.mCurrentView = null;
+        }
+    }
+
+    @Override
     protected void handleClick() {
-        mHost.startActivityDismissingKeyguard(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
+        showDetail(true);
     }
 
     @Override
@@ -85,11 +108,98 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll
     @Override
     public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
         mLevel = level;
+        mCharging = charging;
         refreshState((Integer) level);
+        if (mBatteryDetail.mCurrentView != null) {
+            mBatteryDetail.bindView();
+        }
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
+        mPowerSave = isPowerSave;
+        if (mBatteryDetail.mCurrentView != null) {
+            mBatteryDetail.bindView();
+        }
+    }
+
+    private final class BatteryDetail implements DetailAdapter, View.OnClickListener {
+        private final BatteryMeterDrawable mDrawable = new BatteryMeterDrawable(mHost.getContext(),
+                new Handler(), mHost.getContext().getColor(R.color.batterymeter_frame_color));
+        private View mCurrentView;
+
+        @Override
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.battery_panel_title, mLevel);
+        }
+
+        @Override
+        public Boolean getToggleState() {
+            return null;
+        }
+
+        @Override
+        public View createDetailView(Context context, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(mContext).inflate(R.layout.battery_detail, parent,
+                        false);
+            }
+            mCurrentView = convertView;
+            bindView();
+            return convertView;
+        }
+
+        private void bindView() {
+            mDrawable.onBatteryLevelChanged(100, false, false);
+            mDrawable.onPowerSaveChanged(true);
+            ((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
+            Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle);
+            checkbox.setChecked(mPowerSave);
+            if (mCharging) {
+                BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() {
+                    @Override
+                    public void onBatteryInfoLoaded(BatteryInfo info) {
+                        if (mCurrentView != null && mCharging) {
+                            ((TextView) mCurrentView.findViewById(android.R.id.title)).setText(
+                                    info.mChargeLabelString);
+                        }
+                    }
+                });
+                ((TextView) mCurrentView.findViewById(android.R.id.summary)).setText(
+                        R.string.battery_detail_charging_summary);
+                mCurrentView.setClickable(false);
+                mCurrentView.findViewById(android.R.id.icon).setVisibility(View.INVISIBLE);
+                mCurrentView.findViewById(android.R.id.toggle).setVisibility(View.INVISIBLE);
+            } else {
+                ((TextView) mCurrentView.findViewById(android.R.id.title)).setText(
+                        R.string.battery_detail_switch_title);
+                ((TextView) mCurrentView.findViewById(android.R.id.summary)).setText(
+                        R.string.battery_detail_switch_summary);
+                mCurrentView.setClickable(true);
+                mCurrentView.findViewById(android.R.id.icon).setVisibility(View.VISIBLE);
+                mCurrentView.findViewById(android.R.id.toggle).setVisibility(View.VISIBLE);
+                mCurrentView.setOnClickListener(this);
+            }
+        }
 
+        @Override
+        public void onClick(View v) {
+            mBatteryController.setPowerSaveMode(!mPowerSave);
+        }
+
+        @Override
+        public Intent getSettingsIntent() {
+            return new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+        }
+
+        @Override
+        public void setToggleState(boolean state) {
+            // No toggle state.
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsLogger.QS_BATTERY_DETAIL;
+        }
     }
 }
index cfc09a0..3750290 100644 (file)
@@ -165,8 +165,8 @@ public class BluetoothTile extends QSTile<QSTile.BooleanState>  {
         private QSDetailItems mItems;
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_bluetooth_label;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_bluetooth_label);
         }
 
         @Override
index a8e139c..de4c21c 100644 (file)
@@ -160,8 +160,8 @@ public class CastTile extends QSTile<QSTile.BooleanState> {
         private QSDetailItems mItems;
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_cast_title;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_cast_title);
         }
 
         @Override
index 6c7b337..c1dcfea 100644 (file)
@@ -223,8 +223,8 @@ public class CellularTile extends QSTile<QSTile.SignalState> {
     private final class CellularDetailAdapter implements DetailAdapter {
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_cellular_detail_title;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_cellular_detail_title);
         }
 
         @Override
index 4f9f46d..4d9b266 100644 (file)
@@ -218,8 +218,8 @@ public class DndTile extends QSTile<QSTile.BooleanState> {
     private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_dnd_label;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_dnd_label);
         }
 
         @Override
index 48b4096..95ea3f8 100644 (file)
@@ -240,8 +240,8 @@ public class WifiTile extends QSTile<QSTile.SignalState> {
         private AccessPoint[] mAccessPoints;
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_wifi_label;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_wifi_label);
         }
 
         public Intent getSettingsIntent() {
index b81c23a..2baefd5 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.systemui.recents;
 
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
@@ -205,7 +206,7 @@ public class Recents extends SystemUI
     public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -242,7 +243,7 @@ public class Recents extends SystemUI
     public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -277,7 +278,7 @@ public class Recents extends SystemUI
     public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -312,7 +313,7 @@ public class Recents extends SystemUI
     public void preloadRecents() {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -340,7 +341,7 @@ public class Recents extends SystemUI
     public void cancelPreloadingRecents() {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -365,11 +366,20 @@ public class Recents extends SystemUI
     }
 
     @Override
-    public void dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) {
-        mImpl.dockTopTask(draggingInRecents, stackCreateMode,initialBounds);
-        if (draggingInRecents) {
-            mDraggingInRecentsCurrentUser = sSystemServicesProxy.getCurrentUser();
+    public boolean dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) {
+        // Ensure the device has been provisioned before allowing the user to interact with
+        // recents
+        if (!isUserSetup()) {
+            return false;
         }
+
+        if (mImpl.dockTopTask(draggingInRecents, stackCreateMode,initialBounds)) {
+            if (draggingInRecents) {
+                mDraggingInRecentsCurrentUser = sSystemServicesProxy.getCurrentUser();
+            }
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -422,7 +432,7 @@ public class Recents extends SystemUI
     public void showNextAffiliatedTask() {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -433,7 +443,7 @@ public class Recents extends SystemUI
     public void showPrevAffiliatedTask() {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -559,11 +569,12 @@ public class Recents extends SystemUI
     }
 
     /**
-     * @return whether this device is provisioned.
+     * @return whether this device is provisioned and the current user is set up.
      */
-    private boolean isDeviceProvisioned() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+    private boolean isUserSetup() {
+        ContentResolver cr = mContext.getContentResolver();
+        return (Settings.Global.getInt(cr, Settings.Global.DEVICE_PROVISIONED, 0) != 0) &&
+                (Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0);
     }
 
     /**
index 213018a..ddeb8dc 100644 (file)
@@ -76,8 +76,6 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         ActivityOptions.OnAnimationFinishedListener {
 
     private final static String TAG = "RecentsImpl";
-    private final static boolean DEBUG = false;
-
     // The minimum amount of time between each recents button press that we will handle
     private final static int MIN_TOGGLE_DELAY_MS = 350;
     // The duration within which the user releasing the alt tab (from when they pressed alt tab)
@@ -186,8 +184,6 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         mContext = context;
         mHandler = new Handler();
         mAppWidgetHost = new RecentsAppWidgetHost(mContext, RecentsAppWidgetHost.HOST_ID);
-        Resources res = mContext.getResources();
-        LayoutInflater inflater = LayoutInflater.from(mContext);
 
         // Initialize the static foreground thread
         ForegroundThread.get();
@@ -198,14 +194,8 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         ssp.registerTaskStackListener(mTaskStackListener);
 
         // Initialize the static configuration resources
-        mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
-        mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
-        mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
-        mTaskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
-        mDummyStackView = new TaskStackView(mContext, new TaskStack());
-        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
-                null, false);
-        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
+        reloadHeaderBarLayout();
+        updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
 
         // When we start, preload the data associated with the previous recent tasks.
         // We can use a new plan since the caches will be the same.
@@ -221,11 +211,12 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
 
     public void onBootCompleted() {
         mBootCompleted = true;
-        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
+        updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
     }
 
-    @Override
     public void onConfigurationChanged() {
+        reloadHeaderBarLayout();
+        updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
         // Don't reuse task stack views if the configuration changes
         mCanReuseTaskStackViews = false;
         Recents.getConfiguration().updateOnConfigurationChange();
@@ -257,7 +248,6 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         }
     }
 
-    @Override
     public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
             boolean animate, boolean reloadTasks) {
         mTriggeredFromAltTab = triggeredFromAltTab;
@@ -300,7 +290,6 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         }
     }
 
-    @Override
     public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         if (mBootCompleted) {
             if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
@@ -321,7 +310,6 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         }
     }
 
-    @Override
     public void toggleRecents() {
         // Skip this toggle if we are already waiting to trigger recents via alt-tab
         if (mFastAltTabTrigger.isDozing()) {
@@ -375,7 +363,6 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         }
     }
 
-    @Override
     public void preloadRecents() {
         // Preload only the raw task list into a new load plan (which will be consumed by the
         // RecentsActivity) only if there is a task to animate to.
@@ -396,17 +383,14 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         }
     }
 
-    @Override
     public void cancelPreloadingRecents() {
         // Do nothing
     }
 
-    @Override
     public void onDraggingInRecents(float distanceFromTop) {
         EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop));
     }
 
-    @Override
     public void onDraggingInRecentsEnded(float velocity) {
         EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
     }
@@ -547,14 +531,18 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         showRelativeAffiliatedTask(false);
     }
 
-    public void dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) {
+    public boolean dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) {
         SystemServicesProxy ssp = Recents.getSystemServices();
         ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
-        if (topTask != null && !SystemServicesProxy.isHomeStack(topTask.stackId)) {
+        boolean screenPinningActive = ssp.isScreenPinningActive();
+        boolean isTopTaskHome = SystemServicesProxy.isHomeStack(topTask.stackId);
+        if (topTask != null && !isTopTaskHome && !screenPinningActive) {
             ssp.moveTaskToDockedStack(topTask.id, stackCreateMode, initialBounds);
             showRecents(false /* triggeredFromAltTab */, draggingInRecents, false /* animate */,
                     true /* reloadTasks*/);
+            return true;
         }
+        return false;
     }
 
     /**
@@ -567,6 +555,26 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
     }
 
     /**
+     * Reloads all the layouts for the header bar transition.
+     */
+    private void reloadHeaderBarLayout() {
+        Resources res = mContext.getResources();
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+
+        mStatusBarHeight = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+        mNavBarHeight = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.navigation_bar_height);
+        mNavBarWidth = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.navigation_bar_width);
+        mTaskBarHeight = res.getDimensionPixelSize(
+                R.dimen.recents_task_bar_height);
+        mDummyStackView = new TaskStackView(mContext, new TaskStack());
+        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
+                null, false);
+    }
+
+    /**
      * Prepares the header bar layout for the next transition, if the task view bounds has changed
      * since the last call, it will attempt to re-measure and layout the header bar to the new size.
      *
@@ -574,7 +582,8 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
      *                               is not already bound (can be expensive)
      * @param stack the stack to initialize the stack layout with
      */
-    private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget, TaskStack stack) {
+    private void updateHeaderBarLayout(boolean tryAndBindSearchWidget,
+            TaskStack stack) {
         RecentsConfiguration config = Recents.getConfiguration();
         SystemServicesProxy ssp = Recents.getSystemServices();
         Rect windowRect = ssp.getWindowRect();
@@ -639,7 +648,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         preloadIcon(topTask);
 
         // Update the header bar if necessary
-        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
+        updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
 
         // Update the destination rect
         mDummyStackView.updateLayoutForStack(stack);
@@ -825,7 +834,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
         TaskStack stack = sInstanceLoadPlan.getTaskStack();
 
         // Update the header bar if necessary
-        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
+        updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
 
         // Prepare the dummy stack for the transition
         mDummyStackView.updateLayoutForStack(stack);
index dfcf41b..3406da9 100644 (file)
@@ -506,7 +506,7 @@ public class SystemServicesProxy {
     public void sendCloseSystemWindows(String reason) {
         if (ActivityManagerNative.isSystemReady()) {
             try {
-                ActivityManagerNative.getDefault().closeSystemDialogs(reason);
+                mIam.closeSystemDialogs(reason);
             } catch (RemoteException e) {
             }
         }
@@ -779,6 +779,19 @@ public class SystemServicesProxy {
     }
 
     /**
+     * Returns whether the current task is in screen-pinning mode.
+     */
+    public boolean isScreenPinningActive() {
+        if (mIam == null) return false;
+
+        try {
+            return mIam.isInLockTaskMode();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Returns a global setting.
      */
     public int getGlobalSetting(Context context, String setting) {
index 086fb58..2bf2ccb 100644 (file)
@@ -88,15 +88,6 @@ public class Utilities {
     }
 
     /**
-     * Cancels an animation.
-     */
-    public static void cancelAnimation(Animator animator) {
-        if (animator != null) {
-            animator.cancel();
-        }
-    }
-
-    /**
      * Cancels an animation ensuring that if it has listeners, onCancel and onEnd
      * are not called.
      */
index fce916b..890713e 100644 (file)
@@ -147,27 +147,11 @@ public class FreeformWorkspaceLayoutAlgorithm {
     public TaskViewTransform getTransform(Task task, TaskViewTransform transformOut,
             TaskStackLayoutAlgorithm stackLayout) {
         if (mTaskRectMap.containsKey(task.key)) {
-            final Rect taskRect = stackLayout.mTaskRect;
             final RectF ffRect = mTaskRectMap.get(task.key);
 
             transformOut.scale = 1f;
             transformOut.alpha = 1f;
             transformOut.translationZ = stackLayout.mMaxTranslationZ;
-            if (task.thumbnail != null) {
-                if (task.bounds == null) {
-                    // This is a stack task that has no freeform thumbnail, so keep the same bitmap
-                    // scale as it had in the stack
-                    transformOut.thumbnailScale = (float) taskRect.width() /
-                            task.thumbnail.getWidth();
-                } else {
-                    // This is a freeform rect so fit the bitmap to the task bounds
-                    transformOut.thumbnailScale = Math.min(
-                            ffRect.width() / task.thumbnail.getWidth(),
-                            ffRect.height() / task.thumbnail.getHeight());
-                }
-            } else {
-                transformOut.thumbnailScale = 1f;
-            }
             transformOut.rect.set(ffRect);
             transformOut.rect.offset(stackLayout.mFreeformRect.left, stackLayout.mFreeformRect.top);
             transformOut.visible = true;
index 726e453..c2bb745 100644 (file)
@@ -592,12 +592,8 @@ public class TaskStackLayoutAlgorithm {
                 transformOut.reset();
                 return transformOut;
             }
-            getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut,
+            return getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut,
                     frontTransform);
-            if (task.thumbnail != null) {
-                transformOut.thumbnailScale = (float) mTaskRect.width() / task.thumbnail.getWidth();
-            }
-            return transformOut;
         }
     }
 
@@ -661,7 +657,6 @@ public class TaskStackLayoutAlgorithm {
         Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
         transformOut.visible = (transformOut.rect.top < mStackRect.bottom) &&
                 (frontTransform == null || transformOut.rect.top != frontTransform.rect.top);
-        transformOut.thumbnailScale = 1f;
         transformOut.p = relP;
         return transformOut;
     }
index e8652f5..bc441b2 100644 (file)
@@ -200,8 +200,15 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
-        mHeaderView.onTaskViewSizeChanged(w, h);
-        mThumbnailView.onTaskViewSizeChanged(w, h);
+        if (w > 0 && h > 0) {
+            mHeaderView.onTaskViewSizeChanged(w, h);
+            mThumbnailView.onTaskViewSizeChanged(w, h);
+        }
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
     }
 
     @Override
@@ -244,24 +251,17 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
     void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform,
             TaskViewAnimation toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) {
         RecentsConfiguration config = Recents.getConfiguration();
-        Utilities.cancelAnimation(mTransformAnimation);
+        Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
 
         // Compose the animations for the transform
         mTmpAnimators.clear();
-        boolean requiresHwLayers = toTransform.applyToTaskView(this, mTmpAnimators, toAnimation,
-                !config.fakeShadows);
+        toTransform.applyToTaskView(this, mTmpAnimators, toAnimation, !config.fakeShadows);
         if (toAnimation.isImmediate()) {
-            mThumbnailView.setBitmapScale(toTransform.thumbnailScale);
             setTaskProgress(toTransform.p);
             if (toAnimation.listener != null) {
                 toAnimation.listener.onAnimationEnd(null);
             }
         } else {
-            if (Float.compare(mThumbnailView.getBitmapScale(), toTransform.thumbnailScale) != 0) {
-                mTmpAnimators.add(ObjectAnimator.ofFloat(mThumbnailView,
-                        TaskViewThumbnail.BITMAP_SCALE, mThumbnailView.getBitmapScale(),
-                        toTransform.thumbnailScale));
-            }
             if (Float.compare(getTaskProgress(), toTransform.p) != 0) {
                 mTmpAnimators.add(ObjectAnimator.ofFloat(this, TASK_PROGRESS, getTaskProgress(),
                         toTransform.p));
@@ -272,22 +272,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
 
             // Create the animator
             mTransformAnimation = toAnimation.createAnimator(mTmpAnimators);
-            if (requiresHwLayers) {
-                setLayerType(View.LAYER_TYPE_HARDWARE, null);
-                mTransformAnimation.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        setLayerType(View.LAYER_TYPE_NONE, null);
-                    }
-                });
-            }
             mTransformAnimation.start();
         }
     }
 
     /** Resets this view's properties */
     void resetViewProperties() {
-        Utilities.cancelAnimation(mTransformAnimation);
+        Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
         setDim(0);
         setVisibility(View.VISIBLE);
         getViewBounds().reset();
@@ -303,7 +294,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
      * Cancels any current transform animations.
      */
     public void cancelTransformAnimation() {
-        Utilities.cancelAnimation(mTransformAnimation);
+        Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
     }
 
     /** Enables/disables handling touch on this task view. */
@@ -389,7 +380,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
         } else {
             float dimAlpha = mDimAlpha / 255.0f;
             mThumbnailView.setDimAlpha(dimAlpha);
-            mHeaderView.setDimAlpha(dim);
+            mHeaderView.setDimAlpha(dimAlpha);
         }
     }
 
index e8b7574..6a47424 100644 (file)
 
 package com.android.systemui.recents.views;
 
+import android.annotation.Nullable;
 import android.content.Context;
-import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
 import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
+import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.RippleDrawable;
+import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.animation.AnimationUtils;
@@ -36,6 +36,7 @@ import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
 import com.android.internal.logging.MetricsLogger;
+
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
@@ -55,6 +56,66 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 public class TaskViewHeader extends FrameLayout
         implements View.OnClickListener, View.OnLongClickListener {
 
+    private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.125f;
+
+    /**
+     * A color drawable that draws a slight highlight at the top to help it stand out.
+     */
+    private class HighlightColorDrawable extends Drawable {
+
+        private Paint mHighlightPaint = new Paint();
+        private Paint mBackgroundPaint = new Paint();
+
+        private float[] mTmpHSL = new float[3];
+
+        public HighlightColorDrawable() {
+            mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0));
+            mBackgroundPaint.setAntiAlias(true);
+            mHighlightPaint.setColor(Color.argb(255, 255, 255, 255));
+            mHighlightPaint.setAntiAlias(true);
+        }
+
+        public void setColorAndDim(int color, float dimAlpha) {
+            mBackgroundPaint.setColor(color);
+
+            ColorUtils.colorToHSL(color, mTmpHSL);
+            // TODO: Consider using the saturation of the color to adjust the lightness as well
+            mTmpHSL[2] = Math.min(1f,
+                    mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
+            mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
+
+            invalidateSelf();
+        }
+
+        @Override
+        public void setColorFilter(@Nullable ColorFilter colorFilter) {
+            // Do nothing
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+            // Do nothing
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            // Draw the highlight at the top edge (but put the bottom edge just out of view)
+            canvas.drawRoundRect(0, 0, mTaskViewRect.width(),
+                    2 * Math.max(mHighlightHeight, mCornerRadius),
+                    mCornerRadius, mCornerRadius, mHighlightPaint);
+
+            // Draw the background with the rounded corners
+            canvas.drawRoundRect(0, mHighlightHeight, mTaskViewRect.width(),
+                    getHeight() + mCornerRadius,
+                    mCornerRadius, mCornerRadius, mBackgroundPaint);
+        }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.OPAQUE;
+        }
+    }
+
     Task mTask;
 
     // Header views
@@ -68,17 +129,18 @@ public class TaskViewHeader extends FrameLayout
     Rect mTaskViewRect = new Rect();
     int mCornerRadius;
     int mHighlightHeight;
+    float mDimAlpha;
     Drawable mLightDismissDrawable;
     Drawable mDarkDismissDrawable;
-    RippleDrawable mBackground;
-    GradientDrawable mBackgroundColorDrawable;
+    int mTaskBarViewLightTextColor;
+    int mTaskBarViewDarkTextColor;
     String mDismissContentDescription;
 
-    // Static highlight that we draw at the top of each view
-    static Paint sHighlightPaint;
+    // Header background
+    private HighlightColorDrawable mBackground;
 
     // Header dim, which is only used when task view hardware layers are not used
-    Paint mDimLayerPaint = new Paint();
+    private Paint mDimLayerPaint = new Paint();
 
     Interpolator mFastOutSlowInInterpolator;
     Interpolator mFastOutLinearInInterpolator;
@@ -100,29 +162,26 @@ public class TaskViewHeader extends FrameLayout
         setWillNotDraw(false);
 
         // Load the dismiss resources
-        mDimLayerPaint.setColor(Color.argb(0, 0, 0, 0));
+        Resources res = context.getResources();
         mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
         mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
-        mDismissContentDescription =
-                context.getString(R.string.accessibility_recents_item_will_be_dismissed);
-        mCornerRadius = getResources().getDimensionPixelSize(
-                R.dimen.recents_task_view_rounded_corners_radius);
-        mHighlightHeight = getResources().getDimensionPixelSize(
-                R.dimen.recents_task_view_highlight);
+        mDismissContentDescription = context.getString(
+                R.string.accessibility_recents_item_will_be_dismissed);
+        mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+        mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
+        mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
+        mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
         mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_linear_in);
 
-        // Configure the highlight paint
-        if (sHighlightPaint == null) {
-            sHighlightPaint = new Paint();
-            sHighlightPaint.setStyle(Paint.Style.STROKE);
-            sHighlightPaint.setStrokeWidth(mHighlightHeight);
-            sHighlightPaint.setColor(context.getColor(R.color.recents_task_bar_highlight_color));
-            sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
-            sHighlightPaint.setAntiAlias(true);
-        }
+        // Configure the background and dim
+        mBackground = new HighlightColorDrawable();
+        mBackground.setColorAndDim(Color.argb(255, 0, 0, 0), 0f);
+        setBackground(mBackground);
+        mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0));
+        mDimLayerPaint.setAntiAlias(true);
     }
 
     @Override
@@ -139,16 +198,6 @@ public class TaskViewHeader extends FrameLayout
         if (mIconView.getBackground() instanceof RippleDrawable) {
             mIconView.setBackground(null);
         }
-
-        mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(
-                R.drawable.recents_task_view_header_bg_color);
-        // Copy the ripple drawable since we are going to be manipulating it
-        mBackground = (RippleDrawable)
-                getContext().getDrawable(R.drawable.recents_task_view_header_bg);
-        mBackground = (RippleDrawable) mBackground.mutate().getConstantState().newDrawable();
-        mBackground.setColor(ColorStateList.valueOf(0));
-        mBackground.setDrawableByLayerId(mBackground.getId(0), mBackgroundColorDrawable);
-        setBackground(mBackground);
     }
 
     /**
@@ -156,6 +205,11 @@ public class TaskViewHeader extends FrameLayout
      * to match the frame changes.
      */
     public void onTaskViewSizeChanged(int width, int height) {
+        // Return early if the bounds have not changed
+        if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
+            return;
+        }
+
         mTaskViewRect.set(0, 0, width, height);
         boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE;
         int appIconWidth = mIconView.getMeasuredWidth();
@@ -201,31 +255,39 @@ public class TaskViewHeader extends FrameLayout
     }
 
     @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        onTaskViewSizeChanged(mTaskViewRect.width(), mTaskViewRect.height());
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || (who == mBackground);
     }
 
     @Override
-    protected void onDraw(Canvas canvas) {
-        // Draw the highlight at the top edge (but put the bottom edge just out of view)
-        float offset = (float) Math.ceil(mHighlightHeight / 2f);
-        float radius = mCornerRadius;
-        int count = canvas.save(Canvas.CLIP_SAVE_FLAG);
-        canvas.clipRect(0, 0, mTaskViewRect.width(), getMeasuredHeight());
-        canvas.drawRoundRect(-offset, 0f, (float) mTaskViewRect.width() + offset,
-                getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
-        canvas.restoreToCount(count);
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+
+        // Draw the dim layer with the rounded corners
+        canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight() + mCornerRadius,
+                mCornerRadius, mCornerRadius, mDimLayerPaint);
     }
 
     /**
      * Sets the dim alpha, only used when we are not using hardware layers.
      * (see RecentsConfiguration.useHardwareLayers)
      */
-    void setDimAlpha(int alpha) {
-        mDimLayerPaint.setColor(Color.argb(alpha, 0, 0, 0));
+    void setDimAlpha(float dimAlpha) {
+        mDimAlpha = dimAlpha;
+        updateBackgroundColor(dimAlpha);
         invalidate();
     }
 
+    /**
+     * Updates the background and highlight colors for this header.
+     */
+    private void updateBackgroundColor(float dimAlpha) {
+        if (mTask != null) {
+            mBackground.setColorAndDim(mTask.colorPrimary, dimAlpha);
+            mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
+        }
+    }
+
     /** Binds the bar view to the task */
     public void rebindToTask(Task t) {
         SystemServicesProxy ssp = Recents.getSystemServices();
@@ -233,6 +295,7 @@ public class TaskViewHeader extends FrameLayout
 
         // If an activity icon is defined, then we use that as the primary icon to show in the bar,
         // otherwise, we fall back to the application icon
+        updateBackgroundColor(mDimAlpha);
         if (t.icon != null) {
             mIconView.setImageDrawable(t.icon);
         }
@@ -240,20 +303,8 @@ public class TaskViewHeader extends FrameLayout
             mTitleView.setText(t.title);
         }
         mTitleView.setContentDescription(t.contentDescription);
-
-        // Try and apply the system ui tint
-        int existingBgColor = (getBackground() instanceof ColorDrawable) ?
-                ((ColorDrawable) getBackground()).getColor() : 0;
-        if (existingBgColor != t.colorPrimary) {
-            mBackgroundColorDrawable.setColor(t.colorPrimary);
-        }
-
-        int taskBarViewLightTextColor = getResources().getColor(
-                R.color.recents_task_bar_light_text_color);
-        int taskBarViewDarkTextColor = getResources().getColor(
-                R.color.recents_task_bar_dark_text_color);
         mTitleView.setTextColor(t.useLightOnPrimaryColor ?
-                taskBarViewLightTextColor : taskBarViewDarkTextColor);
+                mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
         mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(String.format(mDismissContentDescription,
@@ -273,7 +324,9 @@ public class TaskViewHeader extends FrameLayout
                         ? R.drawable.recents_move_task_freeform_light
                         : R.drawable.recents_move_task_freeform_dark);
             }
-            mMoveTaskButton.setVisibility(View.VISIBLE);
+            if (mMoveTaskButton.getVisibility() != View.VISIBLE) {
+                mMoveTaskButton.setVisibility(View.VISIBLE);
+            }
             mMoveTaskButton.setOnClickListener(this);
         }
 
@@ -329,15 +382,6 @@ public class TaskViewHeader extends FrameLayout
     }
 
     @Override
-    protected void dispatchDraw(Canvas canvas) {
-        super.dispatchDraw(canvas);
-
-        // Draw the dim layer with the rounded corners
-        canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight(),
-                mCornerRadius, mCornerRadius, mDimLayerPaint);
-    }
-
-    @Override
     public void onClick(View v) {
         if (v == mIconView) {
             // In accessibility, a single click on the focused app info button will show it
index 8edfae0..39d0604 100644 (file)
@@ -42,26 +42,15 @@ import com.android.systemui.recents.model.Task;
  */
 public class TaskViewThumbnail extends View {
 
-    public static final Property<TaskViewThumbnail, Float> BITMAP_SCALE =
-            new FloatProperty<TaskViewThumbnail>("bitmapScale") {
-                @Override
-                public void setValue(TaskViewThumbnail object, float scale) {
-                    object.setBitmapScale(scale);
-                }
-
-                @Override
-                public Float get(TaskViewThumbnail object) {
-                    return object.getBitmapScale();
-                }
-            };
+    private Task mTask;
 
     // Drawing
+    Rect mThumbnailRect = new Rect();
     Rect mTaskViewRect = new Rect();
     int mCornerRadius;
     float mDimAlpha;
     Matrix mScaleMatrix = new Matrix();
     Paint mDrawPaint = new Paint();
-    float mBitmapScale = 1f;
     BitmapShader mBitmapShader;
     LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
 
@@ -104,7 +93,13 @@ public class TaskViewThumbnail extends View {
      * to match the frame changes.
      */
     public void onTaskViewSizeChanged(int width, int height) {
+        // Return early if the bounds have not changed
+        if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
+            return;
+        }
+
         mTaskViewRect.set(0, 0, width, height);
+        updateThumbnailScale();
         invalidate();
     }
 
@@ -125,9 +120,12 @@ public class TaskViewThumbnail extends View {
             mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP,
                     Shader.TileMode.CLAMP);
             mDrawPaint.setShader(mBitmapShader);
+            mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight());
+            updateThumbnailScale();
         } else {
             mBitmapShader = null;
             mDrawPaint.setShader(null);
+            mThumbnailRect.setEmpty();
         }
         invalidate();
     }
@@ -151,12 +149,23 @@ public class TaskViewThumbnail extends View {
     }
 
     /**
-     * Sets the scale of the bitmap relative to this view.
+     * Updates the scale of the bitmap relative to this view.
      */
-    public void setBitmapScale(float scale) {
+    public void updateThumbnailScale() {
         if (mBitmapShader != null) {
-            mBitmapScale = scale;
-            mScaleMatrix.setScale(mBitmapScale, mBitmapScale);
+            float thumbnailScale;
+            if (!mTask.isFreeformTask() || mTask.bounds == null) {
+                // If this is a stack task, or a stack task moved into the freeform workspace, then
+                // just scale this thumbnail to fit the width of the view
+                thumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
+            } else {
+                // Otherwise, if this is a freeform task with task bounds, then scale the thumbnail
+                // to fit the entire bitmap into the task bounds
+                thumbnailScale = Math.min(
+                        (float) mTaskViewRect.width() / mThumbnailRect.width(),
+                        (float) mTaskViewRect.height() / mThumbnailRect.height());
+            }
+            mScaleMatrix.setScale(thumbnailScale, thumbnailScale);
             mBitmapShader.setLocalMatrix(mScaleMatrix);
         }
         if (!mInvisible) {
@@ -164,10 +173,6 @@ public class TaskViewThumbnail extends View {
         }
     }
 
-    public float getBitmapScale() {
-        return mBitmapScale;
-    }
-
     /** Updates the clip rect based on the given task bar. */
     void updateClipToTaskBar(View taskBar) {
         mTaskBar = taskBar;
@@ -200,6 +205,7 @@ public class TaskViewThumbnail extends View {
 
     /** Binds the thumbnail view to the task */
     void rebindToTask(Task t) {
+        mTask = t;
         if (t.thumbnail != null) {
             setThumbnail(t.thumbnail);
         } else {
@@ -209,6 +215,7 @@ public class TaskViewThumbnail extends View {
 
     /** Unbinds the thumbnail view from the task */
     void unbindFromTask() {
+        mTask = null;
         setThumbnail(null);
     }
 }
index 14bab64..538c248 100644 (file)
@@ -86,7 +86,6 @@ public class TaskViewTransform {
     public float translationZ = 0;
     public float scale = 1f;
     public float alpha = 1f;
-    public float thumbnailScale = 1f;
 
     public boolean visible = false;
     float p = 0f;
@@ -101,7 +100,6 @@ public class TaskViewTransform {
         translationZ = 0;
         scale = 1f;
         alpha = 1f;
-        thumbnailScale = 1f;
         visible = false;
         rect.setEmpty();
         p = 0f;
@@ -127,15 +125,12 @@ public class TaskViewTransform {
 
     /**
      * Applies this transform to a view.
-     *
-     * @return whether hardware layers are required for this animation.
      */
-    public boolean applyToTaskView(TaskView v, ArrayList<Animator> animators,
+    public void applyToTaskView(TaskView v, ArrayList<Animator> animators,
             TaskViewAnimation taskAnimation, boolean allowShadows) {
         // Return early if not visible
-        boolean requiresHwLayers = false;
         if (!visible) {
-            return requiresHwLayers;
+            return;
         }
 
         if (taskAnimation.isImmediate()) {
@@ -165,7 +160,6 @@ public class TaskViewTransform {
             }
             if (hasAlphaChangedFrom(v.getAlpha())) {
                 animators.add(ObjectAnimator.ofFloat(v, View.ALPHA, v.getAlpha(), alpha));
-                requiresHwLayers = true;
             }
             if (hasRectChangedFrom(v)) {
                 animators.add(ObjectAnimator.ofPropertyValuesHolder(v,
@@ -175,7 +169,6 @@ public class TaskViewTransform {
                         PropertyValuesHolder.ofInt(BOTTOM, v.getBottom(), (int) rect.bottom)));
             }
         }
-        return requiresHwLayers;
     }
 
     /** Reset the transform on a view. */
@@ -188,6 +181,5 @@ public class TaskViewTransform {
         v.setAlpha(1f);
         v.getViewBounds().setClipBottom(0);
         v.setLeftTopRightBottom(0, 0, 0, 0);
-        v.mThumbnailView.setBitmapScale(1f);
     }
 }
index 109cf47..824d10a 100644 (file)
@@ -28,7 +28,6 @@ import android.graphics.Rect;
 import android.graphics.Region.Op;
 import android.hardware.display.DisplayManager;
 import android.util.AttributeSet;
-import android.util.MathUtils;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.MotionEvent;
@@ -39,7 +38,6 @@ import android.view.View.OnTouchListener;
 import android.view.ViewConfiguration;
 import android.view.ViewTreeObserver.InternalInsetsInfo;
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
-import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.animation.AnimationUtils;
@@ -48,8 +46,10 @@ import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
 
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DockedDividerUtils;
 import com.android.systemui.R;
-import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget;
+import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 
 import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
@@ -167,7 +167,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
     public boolean startDragging(boolean animate) {
         mHandle.setTouching(true, animate);
         mDockSide = mWindowManagerProxy.getDockSide();
-        mSnapAlgorithm = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils, mDisplayWidth,
+        mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
+                mFlingAnimationUtils.getMinVelocityPxPerSecond(), mDisplayWidth,
                 mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets);
         if (mDockSide != WindowManager.DOCKED_INVALID) {
             mWindowManagerProxy.setResizing(true);
@@ -362,36 +363,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
         return mStartPosition + touchY - mStartY;
     }
 
-    public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
-        outRect.set(0, 0, mDisplayWidth, mDisplayHeight);
-        switch (dockSide) {
-            case WindowManager.DOCKED_LEFT:
-                outRect.right = position;
-                break;
-            case WindowManager.DOCKED_TOP:
-                outRect.bottom = position;
-                break;
-            case WindowManager.DOCKED_RIGHT:
-                outRect.left = position + mDividerWindowWidth - 2 * mDividerInsets;
-                break;
-            case WindowManager.DOCKED_BOTTOM:
-                outRect.top = position + mDividerWindowWidth - 2 * mDividerInsets;
-                break;
-        }
-        if (outRect.left > outRect.right) {
-            outRect.left = outRect.right;
-        }
-        if (outRect.top > outRect.bottom) {
-            outRect.top = outRect.bottom;
-        }
-        if (outRect.right < outRect.left) {
-            outRect.right = outRect.left;
-        }
-        if (outRect.bottom < outRect.top) {
-            outRect.bottom = outRect.top;
-        }
-    }
-
     private int invertDockSide(int dockSide) {
         switch (dockSide) {
             case WindowManager.DOCKED_LEFT:
@@ -421,6 +392,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                 containingRect.right, containingRect.bottom);
     }
 
+    public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
+        DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth,
+                mDisplayHeight, mDividerSize);
+    }
+
     public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
         calculateBoundsForPosition(position, mDockSide, mDockedRect);
 
index b93fc76..f41e47b 100644 (file)
@@ -166,7 +166,7 @@ public class KeyguardStatusBarView extends RelativeLayout
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
         // could not care less
     }
 
index cc85d0f..e5b4f4d 100644 (file)
@@ -29,8 +29,7 @@ import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget;
-import com.android.systemui.stackdivider.DividerView;
+import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.systemui.tuner.TunerService;
 
 import static android.view.WindowManager.*;
@@ -166,16 +165,19 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL
         int y = (int) event.getY();
         int xDiff = Math.abs(x - mTouchDownX);
         int yDiff = Math.abs(y - mTouchDownY);
+        if (mDivider == null || mRecentsComponent == null) {
+            return false;
+        }
         if (!mDockWindowTouchSlopExceeded) {
             boolean touchSlopExceeded = !mIsVertical
                     ? yDiff > mScrollTouchSlop && yDiff > xDiff
                     : xDiff > mScrollTouchSlop && xDiff > yDiff;
             if (touchSlopExceeded && mDivider.getView().getWindowManagerProxy().getDockSide()
                     == DOCKED_INVALID) {
-                mDragMode = calculateDragMode();
                 Rect initialBounds = null;
+                int dragMode = calculateDragMode();
                 int createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-                if (mDragMode == DRAG_MODE_DIVIDER) {
+                if (dragMode == DRAG_MODE_DIVIDER) {
                     initialBounds = new Rect();
                     mDivider.getView().calculateBoundsForPosition(mIsVertical
                                     ? (int) event.getRawX()
@@ -184,19 +186,23 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL
                                     ? DOCKED_TOP
                                     : DOCKED_LEFT,
                             initialBounds);
-                } else if (mDragMode == DRAG_MODE_RECENTS && mTouchDownX
+                } else if (dragMode == DRAG_MODE_RECENTS && mTouchDownX
                         < mContext.getResources().getDisplayMetrics().widthPixels / 2) {
                     createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
                 }
-                mRecentsComponent.dockTopTask(mDragMode == DRAG_MODE_RECENTS, createMode,
-                        initialBounds);
-                if (mDragMode == DRAG_MODE_DIVIDER) {
-                    mDivider.getView().startDragging(false /* animate */);
+                boolean docked = mRecentsComponent.dockTopTask(dragMode == DRAG_MODE_RECENTS,
+                        createMode, initialBounds);
+                if (docked) {
+                    mDragMode = dragMode;
+                    if (mDragMode == DRAG_MODE_DIVIDER) {
+                        mDivider.getView().startDragging(false /* animate */);
+                    }
+                    mDockWindowTouchSlopExceeded = true;
+                    MetricsLogger.action(mContext,
+                            MetricsLogger.ACTION_WINDOW_DOCK_SWIPE);
+
+                    return true;
                 }
-                mDockWindowTouchSlopExceeded = true;
-                MetricsLogger.action(mContext,
-                        MetricsLogger.ACTION_WINDOW_DOCK_SWIPE);
-                return true;
             }
         } else {
             if (mDragMode == DRAG_MODE_DIVIDER) {
@@ -214,7 +220,7 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL
     private void handleDragActionUpEvent(MotionEvent event) {
         mVelocityTracker.addMovement(event);
         mVelocityTracker.computeCurrentVelocity(1000);
-        if (mDockWindowTouchSlopExceeded) {
+        if (mDockWindowTouchSlopExceeded && mDivider != null && mRecentsComponent != null) {
             if (mDragMode == DRAG_MODE_DIVIDER) {
                 mDivider.getView().stopDragging(mIsVertical
                                 ? (int) event.getRawX()
@@ -254,7 +260,7 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL
         float absVelY = Math.abs(velocityY);
         boolean isValidFling = absVelX > mMinFlingVelocity &&
                 mIsVertical ? (absVelY > absVelX) : (absVelX > absVelY);
-        if (isValidFling) {
+        if (isValidFling && mRecentsComponent != null) {
             boolean showNext;
             if (!mIsRTL) {
                 showNext = mIsVertical ? (velocityY < 0) : (velocityX < 0);
index 6aa072f..f2c57e5 100644 (file)
@@ -23,8 +23,6 @@ import android.view.MotionEvent;
 import android.view.View;
 import android.widget.FrameLayout;
 
-import java.util.ArrayList;
-
 public abstract class PanelBar extends FrameLayout {
     public static final boolean DEBUG = false;
     public static final String TAG = PanelBar.class.getSimpleName();
@@ -39,14 +37,10 @@ public abstract class PanelBar extends FrameLayout {
     public static final int STATE_OPENING = 1;
     public static final int STATE_OPEN = 2;
 
-    PanelHolder mPanelHolder;
-    ArrayList<PanelView> mPanels = new ArrayList<PanelView>();
-    PanelView mTouchingPanel;
+    PanelView mPanel;
     private int mState = STATE_CLOSED;
     private boolean mTracking;
 
-    float mPanelExpandedFractionSum;
-
     public void go(int state) {
         if (DEBUG) LOG("go state: %d -> %d", mState, state);
         mState = state;
@@ -61,54 +55,28 @@ public abstract class PanelBar extends FrameLayout {
         super.onFinishInflate();
     }
 
-    public void addPanel(PanelView pv) {
-        mPanels.add(pv);
+    public void setPanel(PanelView pv) {
+        mPanel = pv;
         pv.setBar(this);
     }
 
-    public void setPanelHolder(PanelHolder ph) {
-        if (ph == null) {
-            Log.e(TAG, "setPanelHolder: null PanelHolder", new Throwable());
-            return;
-        }
-        mPanelHolder = ph;
-        final int N = ph.getChildCount();
-        for (int i=0; i<N; i++) {
-            final View v = ph.getChildAt(i);
-            if (v != null && v instanceof PanelView) {
-                addPanel((PanelView) v);
-            }
-        }
-    }
-
     public void setBouncerShowing(boolean showing) {
         int important = showing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 
         setImportantForAccessibility(important);
 
-        if (mPanelHolder != null) {
-            mPanelHolder.setImportantForAccessibility(important);
-        }
+        if (mPanel != null) mPanel.setImportantForAccessibility(important);
     }
 
-    public float getBarHeight() {
-        return getMeasuredHeight();
-    }
-
-    public PanelView selectPanelForTouch(MotionEvent touch) {
-        final int N = mPanels.size();
-        return mPanels.get((int)(N * touch.getX() / getMeasuredWidth()));
-    }
-
-    public boolean panelsEnabled() {
+    public boolean panelEnabled() {
         return true;
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         // Allow subclasses to implement enable/disable semantics
-        if (!panelsEnabled()) {
+        if (!panelEnabled()) {
             if (event.getAction() == MotionEvent.ACTION_DOWN) {
                 Log.v(TAG, String.format("onTouch: all panels disabled, ignoring touch at (%d,%d)",
                         (int) event.getX(), (int) event.getY()));
@@ -116,14 +84,12 @@ public abstract class PanelBar extends FrameLayout {
             return false;
         }
 
-        // figure out which panel needs to be talked to here
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            final PanelView panel = selectPanelForTouch(event);
+            final PanelView panel = mPanel;
             if (panel == null) {
                 // panel is not there, so we'll eat the gesture
                 Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
                         (int) event.getX(), (int) event.getY()));
-                mTouchingPanel = null;
                 return true;
             }
             boolean enabled = panel.isEnabled();
@@ -134,90 +100,65 @@ public abstract class PanelBar extends FrameLayout {
                 Log.v(TAG, String.format(
                         "onTouch: panel (%s) is disabled, ignoring touch at (%d,%d)",
                         panel, (int) event.getX(), (int) event.getY()));
-                mTouchingPanel = null;
                 return true;
             }
-            startOpeningPanel(panel);
-        }
-        final boolean result = mTouchingPanel != null
-                ? mTouchingPanel.onTouchEvent(event)
-                : true;
-        return result;
-    }
-
-    // called from PanelView when self-expanding, too
-    public void startOpeningPanel(PanelView panel) {
-        if (DEBUG) LOG("startOpeningPanel: " + panel);
-        mTouchingPanel = panel;
-        mPanelHolder.setSelectedPanel(mTouchingPanel);
-        for (PanelView pv : mPanels) {
-            if (pv != panel) {
-                pv.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
-            }
         }
+        return mPanel == null || mPanel.onTouchEvent(event);
     }
 
     public abstract void panelScrimMinFractionChanged(float minFraction);
 
     /**
-     * @param panel the panel which changed its expansion state
      * @param frac the fraction from the expansion in [0, 1]
      * @param expanded whether the panel is currently expanded; this is independent from the
      *                 fraction as the panel also might be expanded if the fraction is 0
      */
-    public void panelExpansionChanged(PanelView panel, float frac, boolean expanded) {
+    public void panelExpansionChanged(float frac, boolean expanded) {
         boolean fullyClosed = true;
-        PanelView fullyOpenedPanel = null;
-        if (SPEW) LOG("panelExpansionChanged: start state=%d panel=%s", mState, panel.getName());
-        mPanelExpandedFractionSum = 0f;
-        for (PanelView pv : mPanels) {
-            pv.setVisibility(expanded ? View.VISIBLE : View.INVISIBLE);
-            // adjust any other panels that may be partially visible
-            if (expanded) {
-                if (mState == STATE_CLOSED) {
-                    go(STATE_OPENING);
-                    onPanelPeeked();
-                }
-                fullyClosed = false;
-                final float thisFrac = pv.getExpandedFraction();
-                mPanelExpandedFractionSum += thisFrac;
-                if (SPEW) LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
-                if (panel == pv) {
-                    if (thisFrac == 1f) fullyOpenedPanel = panel;
-                }
+        boolean fullyOpened = false;
+        if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
+        PanelView pv = mPanel;
+        pv.setVisibility(expanded ? View.VISIBLE : View.INVISIBLE);
+        // adjust any other panels that may be partially visible
+        if (expanded) {
+            if (mState == STATE_CLOSED) {
+                go(STATE_OPENING);
+                onPanelPeeked();
             }
+            fullyClosed = false;
+            final float thisFrac = pv.getExpandedFraction();
+            if (SPEW) LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
+            fullyOpened = thisFrac >= 1f;
         }
-        mPanelExpandedFractionSum /= mPanels.size();
-        if (fullyOpenedPanel != null && !mTracking) {
+        if (fullyOpened && !mTracking) {
             go(STATE_OPEN);
-            onPanelFullyOpened(fullyOpenedPanel);
+            onPanelFullyOpened();
         } else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
             go(STATE_CLOSED);
-            onAllPanelsCollapsed();
+            onPanelCollapsed();
         }
 
         if (SPEW) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
-                (fullyOpenedPanel!=null)?" fullyOpened":"", fullyClosed?" fullyClosed":"");
+                fullyOpened?" fullyOpened":"", fullyClosed?" fullyClosed":"");
     }
 
-    public void collapseAllPanels(boolean animate, boolean delayed, float speedUpFactor) {
+    public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) {
         boolean waiting = false;
-        for (PanelView pv : mPanels) {
-            if (animate && !pv.isFullyCollapsed()) {
-                pv.collapse(delayed, speedUpFactor);
-                waiting = true;
-            } else {
-                pv.resetViews();
-                pv.setExpandedFraction(0); // just in case
-                pv.cancelPeek();
-            }
+        PanelView pv = mPanel;
+        if (animate && !pv.isFullyCollapsed()) {
+            pv.collapse(delayed, speedUpFactor);
+            waiting = true;
+        } else {
+            pv.resetViews();
+            pv.setExpandedFraction(0); // just in case
+            pv.cancelPeek();
         }
-        if (DEBUG) LOG("collapseAllPanels: animate=%s waiting=%s", animate, waiting);
+        if (DEBUG) LOG("collapsePanel: animate=%s waiting=%s", animate, waiting);
         if (!waiting && mState != STATE_CLOSED) {
             // it's possible that nothing animated, so we replicate the termination
             // conditions of panelExpansionChanged here
             go(STATE_CLOSED);
-            onAllPanelsCollapsed();
+            onPanelCollapsed();
         }
     }
 
@@ -225,19 +166,19 @@ public abstract class PanelBar extends FrameLayout {
         if (DEBUG) LOG("onPanelPeeked");
     }
 
-    public void onAllPanelsCollapsed() {
-        if (DEBUG) LOG("onAllPanelsCollapsed");
+    public void onPanelCollapsed() {
+        if (DEBUG) LOG("onPanelCollapsed");
     }
 
-    public void onPanelFullyOpened(PanelView openPanel) {
+    public void onPanelFullyOpened() {
         if (DEBUG) LOG("onPanelFullyOpened");
     }
 
-    public void onTrackingStarted(PanelView panel) {
+    public void onTrackingStarted() {
         mTracking = true;
     }
 
-    public void onTrackingStopped(PanelView panel, boolean expand) {
+    public void onTrackingStopped(boolean expand) {
         mTracking = false;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
deleted file mode 100644 (file)
index 5095ebb..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.EventLog;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-import com.android.systemui.EventLogTags;
-
-public class PanelHolder extends FrameLayout {
-    public static final boolean DEBUG_GESTURES = true;
-
-    private int mSelectedPanelIndex = -1;
-
-    public PanelHolder(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setChildrenDrawingOrderEnabled(true);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        setChildrenDrawingOrderEnabled(true);
-    }
-
-    public int getPanelIndex(PanelView pv) {
-        final int N = getChildCount();
-        for (int i=0; i<N; i++) {
-            final PanelView v = (PanelView) getChildAt(i);
-            if (pv == v) return i;
-        }
-        return -1;
-    }
-
-    public void setSelectedPanel(PanelView pv) {
-        mSelectedPanelIndex = getPanelIndex(pv);
-    }
-
-    @Override
-    protected int getChildDrawingOrder(int childCount, int i) {
-        if (mSelectedPanelIndex == -1) {
-            return i;
-        } else {
-            if (i == childCount - 1) {
-                return mSelectedPanelIndex;
-            } else if (i >= mSelectedPanelIndex) {
-                return i + 1;
-            } else {
-                return i;
-            }
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (DEBUG_GESTURES) {
-            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
-                EventLog.writeEvent(EventLogTags.SYSUI_PANELHOLDER_TOUCH,
-                        event.getActionMasked(), (int) event.getX(), (int) event.getY());
-            }
-        }
-        return false;
-    }
-}
index 7b2498f..aa01bf2 100644 (file)
@@ -432,7 +432,7 @@ public abstract class PanelView extends FrameLayout {
 
     protected void onTrackingStopped(boolean expand) {
         mTracking = false;
-        mBar.onTrackingStopped(PanelView.this, expand);
+        mBar.onTrackingStopped(expand);
         notifyBarPanelExpansionChanged();
     }
 
@@ -440,7 +440,7 @@ public abstract class PanelView extends FrameLayout {
         endClosing();
         mTracking = true;
         mCollapseAfterPeek = false;
-        mBar.onTrackingStarted(PanelView.this);
+        mBar.onTrackingStarted();
         notifyExpandingStarted();
         notifyBarPanelExpansionChanged();
     }
@@ -893,7 +893,6 @@ public abstract class PanelView extends FrameLayout {
                                 != mStatusBar.getStatusBarHeight()) {
                             getViewTreeObserver().removeOnGlobalLayoutListener(this);
                             if (animate) {
-                                mBar.startOpeningPanel(PanelView.this);
                                 notifyExpandingStarted();
                                 fling(0, true /* expand */);
                             } else {
@@ -1025,7 +1024,7 @@ public abstract class PanelView extends FrameLayout {
     }
 
     protected void notifyBarPanelExpansionChanged() {
-        mBar.panelExpansionChanged(this, mExpandedFraction, mExpandedFraction > 0f || mPeekPending
+        mBar.panelExpansionChanged(mExpandedFraction, mExpandedFraction > 0f || mPeekPending
                 || mPeekAnimator != null || mInstantExpanding || isPanelVisibleBecauseOfHeadsUp()
                 || mTracking || mHeightAnimator != null);
     }
index d721a77..50e88d3 100644 (file)
@@ -81,7 +81,6 @@ import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.ThreadedRenderer;
-import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewStub;
@@ -342,7 +341,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
 
     // Tracking finger for opening/closing.
     boolean mTracking;
-    VelocityTracker mVelocityTracker;
 
     int[] mAbsPos = new int[2];
     ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
@@ -692,16 +690,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
             }
         });
 
-        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
-        mStatusBarView.setBar(this);
-
-        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
-        mStatusBarView.setPanelHolder(holder);
-
         mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
                 R.id.notification_panel);
         mNotificationPanel.setStatusBar(this);
 
+        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
+        mStatusBarView.setBar(this);
+        mStatusBarView.setPanel(mNotificationPanel);
+
         if (!ActivityManager.isHighEndGfx()) {
             mStatusBarWindow.setBackground(null);
             mNotificationPanel.setBackground(new FastColorDrawable(context.getColor(
@@ -814,10 +810,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         mBatteryController = new BatteryController(mContext);
         mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
             @Override
-            public void onPowerSaveChanged() {
+            public void onPowerSaveChanged(boolean isPowerSave) {
                 mHandler.post(mCheckBarModes);
                 if (mDozeServiceHost != null) {
-                    mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave());
+                    mDozeServiceHost.firePowerSaveChanged(isPowerSave);
                 }
             }
             @Override
@@ -1128,12 +1124,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         @Override
         public boolean onLongClick(View v) {
             if (mRecents != null) {
-                mRecents.dockTopTask(false /* draggingInRecents */,
+                boolean docked = mRecents.dockTopTask(false /* draggingInRecents */,
                         ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
                         null /* initialBounds */);
-                MetricsLogger.action(mContext,
-                        MetricsLogger.ACTION_WINDOW_DOCK_LONGPRESS);
-                return true;
+                if (docked) {
+                    MetricsLogger.action(mContext,
+                            MetricsLogger.ACTION_WINDOW_DOCK_LONGPRESS);
+                    return true;
+                }
             }
             return false;
         }
@@ -2275,7 +2273,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
             mStatusBarWindowManager.setStatusBarFocusable(false);
 
             mStatusBarWindow.cancelExpandHelper();
-            mStatusBarView.collapseAllPanels(true /* animate */, delayed, speedUpFactor);
+            mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
         }
     }
 
@@ -2324,7 +2322,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
 
     public void animateCollapseQuickSettings() {
         if (mState == StatusBarState.SHADE) {
-            mStatusBarView.collapseAllPanels(true, false /* delayed */, 1.0f /* speedUpFactor */);
+            mStatusBarView.collapsePanel(true, false /* delayed */, 1.0f /* speedUpFactor */);
         }
     }
 
@@ -2337,7 +2335,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         }
 
         // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
-        mStatusBarView.collapseAllPanels(/*animate=*/ false, false /* delayed*/,
+        mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
                 1.0f /* speedUpFactor */);
 
         mNotificationPanel.closeQs();
@@ -2429,7 +2427,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
             mStatusBarWindowState = state;
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
             if (!showing && mState == StatusBarState.SHADE) {
-                mStatusBarView.collapseAllPanels(false /* animate */, false /* delayed */,
+                mStatusBarView.collapsePanel(false /* animate */, false /* delayed */,
                         1.0f /* speedUpFactor */);
             }
         }
index ab37e6a..813a167 100644 (file)
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.util.AttributeSet;
 import android.util.EventLog;
 import android.view.MotionEvent;
@@ -26,7 +25,6 @@ import android.view.accessibility.AccessibilityEvent;
 
 import com.android.systemui.DejankUtils;
 import com.android.systemui.EventLogTags;
-import com.android.systemui.R;
 
 public class PhoneStatusBarView extends PanelBar {
     private static final String TAG = "PhoneStatusBarView";
@@ -35,8 +33,7 @@ public class PhoneStatusBarView extends PanelBar {
 
     PhoneStatusBar mBar;
 
-    PanelView mLastFullyOpenedPanel = null;
-    PanelView mNotificationPanel;
+    boolean mIsFullyOpenedPanel = false;
     private final PhoneStatusBarTransitions mBarTransitions;
     private ScrimController mScrimController;
     private float mMinFraction;
@@ -72,15 +69,7 @@ public class PhoneStatusBarView extends PanelBar {
     }
 
     @Override
-    public void addPanel(PanelView pv) {
-        super.addPanel(pv);
-        if (pv.getId() == R.id.notification_panel) {
-            mNotificationPanel = pv;
-        }
-    }
-
-    @Override
-    public boolean panelsEnabled() {
+    public boolean panelEnabled() {
         return mBar.panelsEnabled();
     }
 
@@ -100,24 +89,17 @@ public class PhoneStatusBarView extends PanelBar {
     }
 
     @Override
-    public PanelView selectPanelForTouch(MotionEvent touch) {
-        return mNotificationPanel.getExpandedHeight() > 0
-                ? null
-                : mNotificationPanel;
-    }
-
-    @Override
     public void onPanelPeeked() {
         super.onPanelPeeked();
         mBar.makeExpandedVisible(false);
     }
 
     @Override
-    public void onAllPanelsCollapsed() {
-        super.onAllPanelsCollapsed();
+    public void onPanelCollapsed() {
+        super.onPanelCollapsed();
         // Close the status bar in the next frame so we can show the end of the animation.
         DejankUtils.postAfterTraversal(mHideExpandedRunnable);
-        mLastFullyOpenedPanel = null;
+        mIsFullyOpenedPanel = false;
     }
 
     public void removePendingHideExpandedRunnables() {
@@ -125,12 +107,12 @@ public class PhoneStatusBarView extends PanelBar {
     }
 
     @Override
-    public void onPanelFullyOpened(PanelView openPanel) {
-        super.onPanelFullyOpened(openPanel);
-        if (openPanel != mLastFullyOpenedPanel) {
-            openPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+    public void onPanelFullyOpened() {
+        super.onPanelFullyOpened();
+        if (!mIsFullyOpenedPanel) {
+            mPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
         }
-        mLastFullyOpenedPanel = openPanel;
+        mIsFullyOpenedPanel = true;
     }
 
     @Override
@@ -149,8 +131,8 @@ public class PhoneStatusBarView extends PanelBar {
     }
 
     @Override
-    public void onTrackingStarted(PanelView panel) {
-        super.onTrackingStarted(panel);
+    public void onTrackingStarted() {
+        super.onTrackingStarted();
         mBar.onTrackingStarted();
         mScrimController.onTrackingStarted();
     }
@@ -162,8 +144,8 @@ public class PhoneStatusBarView extends PanelBar {
     }
 
     @Override
-    public void onTrackingStopped(PanelView panel, boolean expand) {
-        super.onTrackingStopped(panel, expand);
+    public void onTrackingStopped(boolean expand) {
+        super.onTrackingStopped(expand);
         mBar.onTrackingStopped(expand);
     }
 
@@ -187,8 +169,8 @@ public class PhoneStatusBarView extends PanelBar {
     }
 
     @Override
-    public void panelExpansionChanged(PanelView panel, float frac, boolean expanded) {
-        super.panelExpansionChanged(panel, frac, expanded);
+    public void panelExpansionChanged(float frac, boolean expanded) {
+        super.panelExpansionChanged(frac, expanded);
         mPanelFraction = frac;
         updateScrimFraction();
     }
index 3d21f44..d2f1ca9 100644 (file)
@@ -419,7 +419,7 @@ public class StatusBarHeaderView extends BaseStatusBarHeader implements View.OnC
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
         // could not care less
     }
 
index 5071df0..bb3e116 100644 (file)
@@ -70,9 +70,14 @@ public class BatteryController extends BroadcastReceiver {
         pw.print("  mPowerSave="); pw.println(mPowerSave);
     }
 
+    public void setPowerSaveMode(boolean powerSave) {
+        mPowerManager.setPowerSaveMode(powerSave);
+    }
+
     public void addStateChangedCallback(BatteryStateChangeCallback cb) {
         mChangeCallbacks.add(cb);
         cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+        cb.onPowerSaveChanged(mPowerSave);
     }
 
     public void removeStateChangedCallback(BatteryStateChangeCallback cb) {
@@ -158,12 +163,12 @@ public class BatteryController extends BroadcastReceiver {
     private void firePowerSaveChanged() {
         final int N = mChangeCallbacks.size();
         for (int i = 0; i < N; i++) {
-            mChangeCallbacks.get(i).onPowerSaveChanged();
+            mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
         }
     }
 
     public interface BatteryStateChangeCallback {
         void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
-        void onPowerSaveChanged();
+        void onPowerSaveChanged(boolean isPowerSave);
     }
 }
index 0340984..4ae0321 100644 (file)
@@ -35,25 +35,25 @@ public class BrightnessMirrorController {
 
     private final ScrimView mScrimBehind;
     private final View mBrightnessMirror;
-    private final View mPanelHolder;
+    private final View mNotificationPanel;
     private final int[] mInt2Cache = new int[2];
 
     public BrightnessMirrorController(StatusBarWindowView statusBarWindow) {
         mScrimBehind = (ScrimView) statusBarWindow.findViewById(R.id.scrim_behind);
         mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror);
-        mPanelHolder = statusBarWindow.findViewById(R.id.panel_holder);
+        mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel);
     }
 
     public void showMirror() {
         mBrightnessMirror.setVisibility(View.VISIBLE);
         mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, PhoneStatusBar.ALPHA_OUT);
-        outAnimation(mPanelHolder.animate())
+        outAnimation(mNotificationPanel.animate())
                 .withLayer();
     }
 
     public void hideMirror() {
         mScrimBehind.animateViewAlpha(1.0f, TRANSITION_DURATION_IN, PhoneStatusBar.ALPHA_IN);
-        inAnimation(mPanelHolder.animate())
+        inAnimation(mNotificationPanel.animate())
                 .withLayer()
                 .withEndAction(new Runnable() {
             @Override
index b010761..f3a3554 100644 (file)
@@ -643,8 +643,8 @@ public class UserSwitcherController {
         private final Intent USER_SETTINGS_INTENT = new Intent("android.settings.USER_SETTINGS");
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_user_title;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_user_title);
         }
 
         @Override
index 5f6cbf9..232c080 100644 (file)
@@ -69,6 +69,13 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
      */
     static final int FLAG_FEATURE_AUTOCLICK = 0x00000008;
 
+    /**
+     * Flag for enabling motion event injectsion
+     *
+     * @see #setUserAndEnabledFeatures(int, int)
+     */
+    static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010;
+
     private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
         @Override
         public void run() {
@@ -104,6 +111,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
 
     private MagnificationGestureHandler mMagnificationGestureHandler;
 
+    private MotionEventInjector mMotionEventInjector;
+
     private AutoclickController mAutoclickController;
 
     private KeyboardInterceptor mKeyboardInterceptor;
@@ -191,6 +200,10 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
         }
     }
 
+    public MotionEventInjector getMotionEventInjector() {
+        return mMotionEventInjector;
+    }
+
     /**
      * Gets current event stream state associated with an input event.
      * @return The event stream state that should be used for the event. Null if the event should
@@ -351,6 +364,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
     private void enableFeatures() {
         resetStreamState();
 
+        if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
+            mMotionEventInjector = new MotionEventInjector(mContext.getMainLooper());
+            addFirstEventHandler(mMotionEventInjector);
+            mAms.setMotionEventInjector(mMotionEventInjector);
+        }
+
         if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
             mAutoclickController = new AutoclickController(mContext, mUserId);
             addFirstEventHandler(mAutoclickController);
@@ -388,6 +407,11 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
     }
 
     private void disableFeatures() {
+        if (mMotionEventInjector != null) {
+            mAms.setMotionEventInjector(null);
+            mMotionEventInjector.onDestroy();
+            mMotionEventInjector = null;
+        }
         if (mAutoclickController != null) {
             mAutoclickController.onDestroy();
             mAutoclickController = null;
index 8cf25b3..39d5952 100644 (file)
@@ -37,6 +37,7 @@ import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
@@ -72,6 +73,7 @@ import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MagnificationSpec;
+import android.view.MotionEvent;
 import android.view.WindowInfo;
 import android.view.WindowManager;
 import android.view.WindowManagerInternal;
@@ -186,6 +188,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
 
     private KeyEventDispatcher mKeyEventDispatcher;
 
+    private MotionEventInjector mMotionEventInjector;
+
     private final Set<ComponentName> mTempComponentNameSet = new HashSet<>();
 
     private final List<AccessibilityServiceInfo> mTempAccessibilityServiceInfoList =
@@ -779,6 +783,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
     }
 
     /**
+     * Called by AccessibilityInputFilter when it creates or destroys the motionEventInjector.
+     * Not using a getter because the AccessibilityInputFilter isn't thread-safe
+     *
+     * @param motionEventInjector The new value of the motionEventInjector. May be null.
+     */
+    void setMotionEventInjector(MotionEventInjector motionEventInjector) {
+        synchronized (mLock) {
+            mMotionEventInjector = motionEventInjector;
+        }
+    }
+
+    /**
      * Gets a point within the accessibility focused node where we can send down
      * and up events to perform a click.
      *
@@ -1273,6 +1289,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
             if (userState.mIsAutoclickEnabled) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
             }
+            if (userState.mIsPerformGesturesEnabled) {
+                flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS;
+            }
             if (flags != 0) {
                 if (!mHasInputFilter) {
                     mHasInputFilter = true;
@@ -1363,6 +1382,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
         updateAccessibilityFocusBehaviorLocked(userState);
         updateFilterKeyEventsLocked(userState);
         updateTouchExplorationLocked(userState);
+        updatePerformGesturesLocked(userState);
         updateEnhancedWebAccessibilityLocked(userState);
         updateDisplayColorAdjustmentSettingsLocked(userState);
         updateMagnificationLocked(userState);
@@ -1451,6 +1471,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
         }
     }
 
+    private void updatePerformGesturesLocked(UserState userState) {
+        final int serviceCount = userState.mBoundServices.size();
+        for (int i = 0; i < serviceCount; i++) {
+            Service service = userState.mBoundServices.get(i);
+            if ((service.mAccessibilityServiceInfo.getCapabilities()
+                    & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) {
+                userState.mIsPerformGesturesEnabled = true;
+                return;
+            }
+        }
+        userState.mIsPerformGesturesEnabled = false;
+    }
+
     private void updateFilterKeyEventsLocked(UserState userState) {
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
@@ -2564,6 +2597,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
         }
 
         @Override
+        public void sendMotionEvents(int sequence, ParceledListSlice events) {
+            synchronized (mLock) {
+                if (mSecurityPolicy.canPerformGestures(this) && (mMotionEventInjector != null)) {
+                    mMotionEventInjector.injectEvents((List<MotionEvent>) events.getList(),
+                            mServiceInterface, sequence);
+                    return;
+                }
+            }
+            try {
+                mServiceInterface.onPerformGestureResult(sequence, false);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error sending motion event injection failure to "
+                        + mServiceInterface, re);
+            }
+        }
+
+        @Override
         public boolean performAccessibilityAction(int accessibilityWindowId,
                 long accessibilityNodeId, int action, Bundle arguments, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
@@ -3734,6 +3784,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                     & AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION) != 0;
         }
 
+        public boolean canPerformGestures(Service service) {
+            return (service.mAccessibilityServiceInfo.getCapabilities()
+                    & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0;
+        }
+
         private int resolveProfileParentLocked(int userId) {
             if (userId != mCurrentUserId) {
                 final long identity = Binder.clearCallingIdentity();
@@ -3878,6 +3933,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
         public boolean mIsEnhancedWebAccessibilityEnabled;
         public boolean mIsDisplayMagnificationEnabled;
         public boolean mIsAutoclickEnabled;
+        public boolean mIsPerformGesturesEnabled;
         public boolean mIsFilterKeyEventsEnabled;
         public boolean mHasDisplayColorAdjustment;
         public boolean mAccessibilityFocusOnlyInActiveWindow;
diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
new file mode 100644 (file)
index 0000000..800f0e1
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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.server.accessibility;
+
+import android.accessibilityservice.IAccessibilityServiceClient;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy;
+import android.view.accessibility.AccessibilityEvent;
+import com.android.internal.os.SomeArgs;
+import com.android.server.accessibility.AccessibilityManagerService.Service;
+
+import java.util.List;
+
+/**
+ * Injects MotionEvents to permit {@code AccessibilityService}s to touch the screen on behalf of
+ * users.
+ *
+ * All methods except {@code injectEvents} must be called only from the main thread.
+ */
+public class MotionEventInjector implements EventStreamTransformation {
+    private static final String LOG_TAG = "MotionEventInjector";
+    private static final int MESSAGE_SEND_MOTION_EVENT = 1;
+    private static final int MESSAGE_INJECT_EVENTS = 2;
+    private static final int MAX_POINTERS = 11; // Non-binding maximum
+
+    private final Handler mHandler;
+    private final SparseArray<Boolean> mOpenGesturesInProgress = new SparseArray<>();
+
+    // These two arrays must be the same length
+    private MotionEvent.PointerProperties[] mPointerProperties =
+            new MotionEvent.PointerProperties[MAX_POINTERS];
+    private MotionEvent.PointerCoords[] mPointerCoords =
+            new MotionEvent.PointerCoords[MAX_POINTERS];
+    private EventStreamTransformation mNext;
+    private IAccessibilityServiceClient mServiceInterfaceForCurrentGesture;
+    private int mSequenceForCurrentGesture;
+    private int mSourceOfInjectedGesture = InputDevice.SOURCE_UNKNOWN;
+    private boolean mIsDestroyed = false;
+
+    /**
+     * @param looper A looper on the main thread to use for dispatching new events
+     */
+    public MotionEventInjector(Looper looper) {
+        mHandler = new Handler(looper, new Callback());
+    }
+
+    /**
+     * Schedule a series of events for injection. These events must comprise a complete, valid
+     * sequence. All gestures currently in progress will be cancelled, and all {@code downTime}
+     * and {@code eventTime} fields will be offset by the current time.
+     *
+     * @param events The events to inject. Must all be from the same source.
+     * @param serviceInterface The interface to call back with a result when the gesture is
+     * either complete or cancelled.
+     */
+    public void injectEvents(List<MotionEvent> events,
+            IAccessibilityServiceClient serviceInterface, int sequence) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = events;
+        args.arg2 = serviceInterface;
+        args.argi1 = sequence;
+        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_INJECT_EVENTS, args));
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelAnyPendingInjectedEvents();
+        sendMotionEventToNext(event, rawEvent, policyFlags);
+    }
+
+    @Override
+    public void onKeyEvent(KeyEvent event, int policyFlags) {
+        if (mNext != null) {
+            mNext.onKeyEvent(event, policyFlags);
+        }
+    }
+
+    @Override
+    public void onAccessibilityEvent(AccessibilityEvent event) {
+        if (mNext != null) {
+            mNext.onAccessibilityEvent(event);
+        }
+    }
+
+    @Override
+    public void setNext(EventStreamTransformation next) {
+        mNext = next;
+    }
+
+    @Override
+    public void clearEvents(int inputSource) {
+        /*
+         * Reset state for motion events passing through so we won't send a cancel event for
+         * them.
+         */
+        if (!mHandler.hasMessages(MESSAGE_SEND_MOTION_EVENT)) {
+            mOpenGesturesInProgress.put(inputSource, false);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        cancelAnyPendingInjectedEvents();
+        mIsDestroyed = true;
+    }
+
+    private void injectEventsMainThread(List<MotionEvent> events,
+            IAccessibilityServiceClient serviceInterface, int sequence) {
+        if (mIsDestroyed) {
+            try {
+                serviceInterface.onPerformGestureResult(sequence, false);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error sending status with mIsDestroyed to " + serviceInterface,
+                        re);
+            }
+            return;
+        }
+        cancelAnyPendingInjectedEvents();
+        mSourceOfInjectedGesture = events.get(0).getSource();
+        cancelAnyGestureInProgress(mSourceOfInjectedGesture);
+        mServiceInterfaceForCurrentGesture = serviceInterface;
+        mSequenceForCurrentGesture = sequence;
+        if (mNext == null) {
+            notifyService(false);
+            return;
+        }
+
+        long startTime = SystemClock.uptimeMillis();
+        for (int i = 0; i < events.size(); i++) {
+            MotionEvent event = events.get(i);
+            int numPointers = event.getPointerCount();
+            if (numPointers > mPointerCoords.length) {
+                mPointerCoords = new MotionEvent.PointerCoords[numPointers];
+                mPointerProperties = new MotionEvent.PointerProperties[numPointers];
+            }
+            for (int j = 0; j < numPointers; j++) {
+                if (mPointerCoords[j] == null) {
+                    mPointerCoords[j] = new MotionEvent.PointerCoords();
+                    mPointerProperties[j] = new MotionEvent.PointerProperties();
+                }
+                event.getPointerCoords(j, mPointerCoords[j]);
+                event.getPointerProperties(j, mPointerProperties[j]);
+            }
+
+            /*
+             * MotionEvent doesn't have a setEventTime() method (it carries around history data,
+             * which could become inconsistent), so we need to obtain a new one.
+             */
+            MotionEvent offsetEvent = MotionEvent.obtain(startTime + event.getDownTime(),
+                    startTime + event.getEventTime(), event.getAction(), numPointers,
+                    mPointerProperties, mPointerCoords, event.getMetaState(),
+                    event.getButtonState(), event.getXPrecision(), event.getYPrecision(),
+                    event.getDeviceId(), event.getEdgeFlags(), event.getSource(),
+                    event.getFlags());
+            Message message = mHandler.obtainMessage(MESSAGE_SEND_MOTION_EVENT, offsetEvent);
+            mHandler.sendMessageDelayed(message, event.getEventTime());
+        }
+    }
+
+    private void sendMotionEventToNext(MotionEvent event, MotionEvent rawEvent,
+            int policyFlags) {
+        if (mNext != null) {
+            mNext.onMotionEvent(event, rawEvent, policyFlags);
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mOpenGesturesInProgress.put(event.getSource(), true);
+            }
+            if ((event.getActionMasked() == MotionEvent.ACTION_UP)
+                    || (event.getActionMasked() == MotionEvent.ACTION_CANCEL)) {
+                mOpenGesturesInProgress.put(event.getSource(), false);
+            }
+        }
+    }
+
+    private void cancelAnyGestureInProgress(int source) {
+        if ((mNext != null) && mOpenGesturesInProgress.get(source, false)) {
+            long now = SystemClock.uptimeMillis();
+            MotionEvent cancelEvent =
+                    MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+            sendMotionEventToNext(cancelEvent, cancelEvent,
+                    WindowManagerPolicy.FLAG_PASS_TO_USER);
+        }
+    }
+
+    private void cancelAnyPendingInjectedEvents() {
+        if (mHandler.hasMessages(MESSAGE_SEND_MOTION_EVENT)) {
+            cancelAnyGestureInProgress(mSourceOfInjectedGesture);
+            mHandler.removeMessages(MESSAGE_SEND_MOTION_EVENT);
+            notifyService(false);
+        }
+
+    }
+
+    private void notifyService(boolean success) {
+        try {
+            mServiceInterfaceForCurrentGesture.onPerformGestureResult(
+                    mSequenceForCurrentGesture, success);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error sending motion event injection status to "
+                    + mServiceInterfaceForCurrentGesture, re);
+        }
+    }
+
+    private class Callback implements Handler.Callback {
+        @Override
+        public boolean handleMessage(Message message) {
+            if (message.what == MESSAGE_INJECT_EVENTS) {
+                SomeArgs args = (SomeArgs) message.obj;
+                injectEventsMainThread((List<MotionEvent>) args.arg1,
+                        (IAccessibilityServiceClient) args.arg2, args.argi1);
+                args.recycle();
+                return true;
+            }
+            if (message.what != MESSAGE_SEND_MOTION_EVENT) {
+                throw new IllegalArgumentException("Unknown message: " + message.what);
+            }
+            MotionEvent motionEvent = (MotionEvent) message.obj;
+            sendMotionEventToNext(motionEvent, motionEvent,
+                    WindowManagerPolicy.FLAG_PASS_TO_USER);
+            // If the message queue is now empty, then this gesture is complete
+            if (!mHandler.hasMessages(MESSAGE_SEND_MOTION_EVENT)) {
+                notifyService(true);
+            }
+            return true;
+        }
+    }
+}
index f21eba1..4f75810 100644 (file)
@@ -255,7 +255,9 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.content.pm.PackageManager.GET_PROVIDERS;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -515,9 +517,9 @@ public final class ActivityManagerService extends ActivityManagerNative
     private Installer mInstaller;
 
     /** Run all ActivityStacks through this */
-    ActivityStackSupervisor mStackSupervisor;
+    final ActivityStackSupervisor mStackSupervisor;
 
-    ActivityStarter mActivityStarter;
+    final ActivityStarter mActivityStarter;
 
     /** Task stack change listeners. */
     private RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
@@ -1954,8 +1956,10 @@ public final class ActivityManagerService extends ActivityManagerNative
                 break;
             }
             case SYSTEM_USER_UNLOCK_MSG: {
-                mSystemServiceManager.unlockUser(msg.arg1);
-                mRecentTasks.cleanupLocked(msg.arg1);
+                final int userId = msg.arg1;
+                mSystemServiceManager.unlockUser(userId);
+                mRecentTasks.cleanupLocked(userId);
+                installEncryptionUnawareProviders(userId);
                 break;
             }
             case SYSTEM_USER_CURRENT_MSG: {
@@ -3643,11 +3647,9 @@ public final class ActivityManagerService extends ActivityManagerNative
             return false;
         }
         Intent intent = getHomeIntent();
-        ActivityInfo aInfo =
-            resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
+        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
         if (aInfo != null) {
-            intent.setComponent(new ComponentName(
-                    aInfo.applicationInfo.packageName, aInfo.name));
+            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
             // Don't do this if the home app is currently being
             // instrumented.
             aInfo = new ActivityInfo(aInfo);
@@ -10826,6 +10828,41 @@ public final class ActivityManagerService extends ActivityManagerNative
     }
 
     /**
+     * When a user is unlocked, we need to install encryption-unaware providers
+     * belonging to any running apps.
+     */
+    private void installEncryptionUnawareProviders(int userId) {
+        synchronized (this) {
+            final int NP = mProcessNames.getMap().size();
+            for (int ip = 0; ip < NP; ip++) {
+                final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+                final int NA = apps.size();
+                for (int ia = 0; ia < NA; ia++) {
+                    final ProcessRecord app = apps.valueAt(ia);
+                    if (app.userId != userId || app.thread == null) continue;
+
+                    final int NG = app.pkgList.size();
+                    for (int ig = 0; ig < NG; ig++) {
+                        try {
+                            final String pkgName = app.pkgList.keyAt(ig);
+                            final PackageInfo pkgInfo = AppGlobals.getPackageManager()
+                                    .getPackageInfo(pkgName,
+                                            GET_PROVIDERS | MATCH_ENCRYPTION_UNAWARE, userId);
+                            if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
+                                for (ProviderInfo provInfo : pkgInfo.providers) {
+                                    Log.v(TAG, "Installing " + provInfo);
+                                    app.thread.scheduleInstallProvider(provInfo);
+                                }
+                            }
+                        } catch (RemoteException ignored) {
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Allows apps to retrieve the MIME type of a URI.
      * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
      * users, then it does not need permission to access the ContentProvider.
index e123dbd..c44b4cf 100644 (file)
@@ -3368,9 +3368,9 @@ final class ActivityStack {
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
                             destIntent.getComponent(), 0, srec.userId);
-                    int res = mService.mActivityStarter.startActivityLocked(srec.app.thread, destIntent,
-                            null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null, null,
-                            parent.appToken, null, 0, -1, parent.launchedFromUid,
+                    int res = mService.mActivityStarter.startActivityLocked(srec.app.thread,
+                            destIntent, null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null,
+                            null, parent.appToken, null, 0, -1, parent.launchedFromUid,
                             parent.launchedFromPackage, -1, parent.launchedFromUid, 0, null,
                             false, true, null, null, null);
                     foundParentInTask = res == ActivityManager.START_SUCCESS;
index f712613..cfa4433 100644 (file)
@@ -32,12 +32,12 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
 import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
 import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
@@ -45,6 +45,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
@@ -63,8 +64,8 @@ import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
+import static com.android.server.am.EventLogTags.AM_NEW_INTENT;
 
-import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
@@ -529,7 +530,10 @@ class ActivityStarter {
             // switch...  just dismiss the keyguard now, because we
             // probably want to see whatever is behind it.
             mSupervisor.notifyActivityDrawnForKeyguard();
+        } else {
+            launchRecentsAppIfNeeded(stack);
         }
+
         return err;
     }
 
@@ -863,6 +867,28 @@ class ActivityStarter {
                 intentActivity.task.setIntent(mStartActivity);
             }
 
+            // This code path leads to delivering a new intent, we want to make sure we schedule it
+            // as the first operation, in case the activity will be resumed as a result of later
+            // operations.
+            if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
+                    || mLaunchSingleInstance || mLaunchSingleTask) {
+                // In this situation we want to remove all activities from the task up to the one
+                // being started. In most cases this means we are resetting the task to its initial
+                // state.
+                final ActivityRecord top = intentActivity.task.performClearTaskLocked(
+                        mStartActivity, mLaunchFlags);
+                if (top != null) {
+                    if (top.frontOfTask) {
+                        // Activity aliases may mean we use different intents for the top activity,
+                        // so make sure the task now has the identity of the new intent.
+                        top.task.setIntent(mStartActivity);
+                    }
+                    ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task);
+                    top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
+                            mStartActivity.launchedFromPackage);
+                }
+            }
+
             intentActivity = setTargetStackAndMoveToFrontIfNeeded(intentActivity);
 
             if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -904,7 +930,7 @@ class ActivityStarter {
                 && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                 || mLaunchSingleTop || mLaunchSingleTask);
         if (dontStart) {
-            ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
+            ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
             // For paranoia, make sure we have correctly resumed the top activity.
             topStack.mLastPausedActivity = null;
             if (mDoResume) {
@@ -1006,6 +1032,16 @@ class ActivityStarter {
         return START_SUCCESS;
     }
 
+    private void launchRecentsAppIfNeeded(ActivityStack topStack) {
+        if (topStack.mStackId == HOME_STACK_ID && mTargetStack.mStackId == DOCKED_STACK_ID) {
+            // We launch an activity while being in home stack, which means either launcher or
+            // recents into docked stack. We don't want the launched activity to be alone in a
+            // docked stack, so we want to immediately launch recents too.
+            if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
+            mWindowManager.showRecentApps();
+        }
+    }
+
     private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
             boolean doResume, int startFlags, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
@@ -1305,20 +1341,9 @@ class ActivityStarter {
             mReuseTask.setIntent(mStartActivity);
         } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                 || mLaunchSingleInstance || mLaunchSingleTask) {
-            // In this situation we want to remove all activities from the task up to the one
-            // being started. In most cases this means we are resetting the task to its initial
-            // state.
             ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity,
                     mLaunchFlags);
-            if (top != null) {
-                if (top.frontOfTask) {
-                    // Activity aliases may mean we use different intents for the top activity,
-                    // so make sure the task now has the identity of the new intent.
-                    top.task.setIntent(mStartActivity);
-                }
-                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, mStartActivity, top.task);
-                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
-            } else {
+            if (top == null) {
                 // A special case: we need to start the activity because it is not currently
                 // running, and the caller has asked to clear the current task to have this
                 // activity at the top.
@@ -1343,7 +1368,7 @@ class ActivityStarter {
             // desires.
             if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
                     && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
-                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, mStartActivity,
+                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
                         intentActivity.task);
                 if (intentActivity.frontOfTask) {
                     intentActivity.task.setIntent(mStartActivity);
@@ -1439,7 +1464,7 @@ class ActivityStarter {
             ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
             mKeepCurTransition = true;
             if (top != null) {
-                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, mStartActivity, top.task);
+                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task);
                 top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
                 // For paranoia, make sure we have correctly resumed the top activity.
                 mTargetStack.mLastPausedActivity = null;
@@ -1458,7 +1483,7 @@ class ActivityStarter {
                 final TaskRecord task = top.task;
                 task.moveActivityToFrontLocked(top);
                 top.updateOptionsLocked(mOptions);
-                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, mStartActivity, task);
+                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
                 top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
@@ -1495,7 +1520,7 @@ class ActivityStarter {
         if (top != null && top.realActivity.equals(mStartActivity.realActivity) && top.userId == mStartActivity.userId) {
             if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                     || mLaunchSingleTop || mLaunchSingleTask) {
-                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
+                ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
                 if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                     // We don't need to start a new activity, and the client said not to do
                     // anything if that is the case, so this is it!
index 4eabe36..3530d80 100644 (file)
@@ -613,9 +613,10 @@ public class JobSchedulerService extends com.android.server.SystemService
         if (periodicToReschedule.hasDeadlineConstraint()) {
             runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
         }
-        long newEarliestRunTimeElapsed = elapsedNow + runEarly;
+        long flex = periodicToReschedule.getJob().getFlexMillis();
         long period = periodicToReschedule.getJob().getIntervalMillis();
-        long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
+        long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
+        long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
 
         if (DEBUG) {
             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
index 472e8f6..b8aa9dd 100644 (file)
@@ -397,6 +397,7 @@ public class JobStore {
             if (jobStatus.getJob().isPeriodic()) {
                 out.startTag(null, XML_TAG_PERIODIC);
                 out.attribute(null, "period", Long.toString(job.getIntervalMillis()));
+                out.attribute(null, "flex", Long.toString(job.getFlexMillis()));
             } else {
                 out.startTag(null, XML_TAG_ONEOFF);
             }
@@ -594,13 +595,17 @@ public class JobStore {
                     String val = parser.getAttributeValue(null, "period");
                     final long periodMillis = Long.valueOf(val);
                     jobBuilder.setPeriodic(periodMillis);
-                    // As a sanity check, cap the recreated run time to be no later than 2 periods
+                    val = parser.getAttributeValue(null, "flex");
+                    final long flexMillis = (val != null) ? Long.valueOf(val) : periodMillis;
+                    // As a sanity check, cap the recreated run time to be no later than flex+period
                     // from now. This is the latest the periodic could be pushed out. This could
-                    // happen if the periodic ran early (at the start of its period), and then the
+                    // happen if the periodic ran early (at flex time before period), and then the
                     // device rebooted.
-                    if (elapsedRuntimes.second > elapsedNow + 2 * periodMillis) {
-                        final long clampedEarlyRuntimeElapsed = elapsedNow + periodMillis;
-                        final long clampedLateRuntimeElapsed = elapsedNow + 2 * periodMillis;
+                    if (elapsedRuntimes.second > elapsedNow + periodMillis + flexMillis) {
+                        final long clampedLateRuntimeElapsed = elapsedNow + flexMillis
+                                + periodMillis;
+                        final long clampedEarlyRuntimeElapsed = clampedLateRuntimeElapsed
+                                - flexMillis;
                         Slog.w(TAG,
                                 String.format("Periodic job for uid='%d' persisted run-time is" +
                                                 " too big [%s, %s]. Clamping to [%s,%s]",
index c02611f..060a93e 100644 (file)
@@ -96,8 +96,8 @@ public class JobStatus {
         final long elapsedNow = SystemClock.elapsedRealtime();
 
         if (job.isPeriodic()) {
-            earliestRunTimeElapsedMillis = elapsedNow;
             latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
+            earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
         } else {
             earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
                     elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
index e787eda..018bf2d 100644 (file)
@@ -28,6 +28,7 @@ import static android.service.notification.NotificationAssistantService.REASON_L
 import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL_ALL;
 import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_BANNED;
 import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_CHANGED;
+import static android.service.notification.NotificationAssistantService.REASON_TOPIC_BANNED;
 import static android.service.notification.NotificationAssistantService.REASON_USER_STOPPED;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
@@ -741,7 +742,7 @@ public class NotificationManagerService extends SystemService {
                     for (String pkgName : pkgList) {
                         if (cancelNotifications) {
                             cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
-                                    changeUserId, REASON_PACKAGE_CHANGED, null);
+                                    changeUserId, REASON_PACKAGE_CHANGED, null, null);
                         }
                     }
                 }
@@ -774,7 +775,7 @@ public class NotificationManagerService extends SystemService {
                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                 if (userHandle >= 0) {
                     cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
-                            REASON_USER_STOPPED, null);
+                            REASON_USER_STOPPED, null, null);
                 }
             } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
                 // turn off LED when user passes through lock screen
@@ -1051,7 +1052,7 @@ public class NotificationManagerService extends SystemService {
         // Now, cancel any outstanding notifications that are part of a just-disabled app
         if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
             cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
-                    REASON_PACKAGE_BANNED, null);
+                    REASON_PACKAGE_BANNED, null, null);
         }
     }
 
@@ -1209,7 +1210,7 @@ public class NotificationManagerService extends SystemService {
             // running foreground services.
             cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
                     pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
-                    REASON_APP_CANCEL_ALL, null);
+                    REASON_APP_CANCEL_ALL, null, null);
         }
 
         @Override
@@ -1266,6 +1267,11 @@ public class NotificationManagerService extends SystemService {
         public void setTopicImportance(String pkg, int uid, Notification.Topic topic,
                 int importance) {
             enforceSystemOrSystemUI("Caller not system or systemui");
+            if (NotificationListenerService.Ranking.IMPORTANCE_NONE == importance) {
+                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true,
+                        UserHandle.getUserId(uid),
+                        REASON_TOPIC_BANNED, topic, null);
+            }
             mRankingHelper.setTopicImportance(pkg, uid, topic, importance);
             savePolicyFile();
         }
@@ -2284,8 +2290,9 @@ public class NotificationManagerService extends SystemService {
                     mRankingHelper.extractSignals(r);
                     savePolicyFile();
 
-                    // blocked apps
-                    if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
+                    // blocked apps/topics
+                    if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
+                            || !noteNotificationOp(pkg, callingUid)) {
                         if (!isSystemNotification) {
                             Slog.e(TAG, "Suppressing notification from package " + pkg
                                     + " by user request.");
@@ -3067,11 +3074,11 @@ public class NotificationManagerService extends SystemService {
     }
 
     /**
-     * Cancels all notifications from a given package that have all of the
+     * Cancels all notifications from a given package or topic that have all of the
      * {@code mustHaveFlags}.
      */
     boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
-            int mustNotHaveFlags, boolean doit, int userId, int reason,
+            int mustNotHaveFlags, boolean doit, int userId, int reason, Notification.Topic topic,
             ManagedServiceInfo listener) {
         String listenerName = listener == null ? null : listener.component.toShortString();
         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
@@ -3099,6 +3106,10 @@ public class NotificationManagerService extends SystemService {
                 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
                     continue;
                 }
+                if (topic != null
+                        && !topic.getId().equals(r.getNotification().getTopic().getId())) {
+                    continue;
+                }
                 if (canceledNotifications == null) {
                     canceledNotifications = new ArrayList<>();
                 }
index f1fd42c..ce4ecd3 100644 (file)
@@ -423,14 +423,16 @@ public class RankingHelper implements RankingConfig {
 
     /**
      * Sets the default importance for all new topics that appear in the future, and resets
-     * the importance of all current topics.
+     * the importance of all current topics (unless the app is being blocked).
      */
     @Override
     public void setAppImportance(String pkgName, int uid, int importance) {
         final Record r = getOrCreateRecord(pkgName, uid);
         r.importance = importance;
-        for (Topic t :  r.topics.values()) {
-            t.importance = importance;
+        if (Ranking.IMPORTANCE_NONE != importance) {
+            for (Topic t : r.topics.values()) {
+                t.importance = importance;
+            }
         }
         updateConfig();
     }
@@ -483,7 +485,9 @@ public class RankingHelper implements RankingConfig {
             pw.print(prefix);
             pw.println("per-package config:");
         }
+        pw.println("Records:");
         dumpRecords(pw, prefix, filter, mRecords);
+        pw.println("Restored without uid:");
         dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
     }
 
index 870ae89..e3ed0c1 100644 (file)
@@ -318,6 +318,8 @@ public class PackageManagerService extends IPackageManager.Stub {
 
     static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
 
+    private static final boolean DISABLE_EPHEMERAL_APPS = true;
+
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
@@ -3119,7 +3121,7 @@ public class PackageManagerService extends IPackageManager.Stub {
     /**
      * Update given flags based on encryption status of current user.
      */
-    private int updateFlagsForEncryption(int flags, int userId) {
+    private int updateFlags(int flags, int userId) {
         if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE
                 | PackageManager.MATCH_ENCRYPTION_AWARE)) != 0) {
             // Caller expressed an explicit opinion about what encryption
@@ -3133,6 +3135,12 @@ public class PackageManagerService extends IPackageManager.Stub {
                 flags |= PackageManager.MATCH_ENCRYPTION_AWARE;
             }
         }
+
+        // Safe mode means we should ignore any third-party apps
+        if (mSafeMode) {
+            flags |= PackageManager.MATCH_SYSTEM_ONLY;
+        }
+
         return flags;
     }
 
@@ -3160,7 +3168,7 @@ public class PackageManagerService extends IPackageManager.Stub {
             Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
                     + " with flags 0x" + Integer.toHexString(flags), new Throwable());
         }
-        return updateFlagsForEncryption(flags, userId);
+        return updateFlags(flags, userId);
     }
 
     /**
@@ -3192,7 +3200,7 @@ public class PackageManagerService extends IPackageManager.Stub {
             Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
                     + " with flags 0x" + Integer.toHexString(flags), new Throwable());
         }
-        return updateFlagsForEncryption(flags, userId);
+        return updateFlags(flags, userId);
     }
 
     /**
@@ -4469,6 +4477,9 @@ public class PackageManagerService extends IPackageManager.Stub {
     private boolean isEphemeralAllowed(
             Intent intent, List<ResolveInfo> resolvedActivites, int userId) {
         // Short circuit and return early if possible.
+        if (DISABLE_EPHEMERAL_APPS) {
+            return false;
+        }
         final int callingUser = UserHandle.getCallingUserId();
         if (callingUser != UserHandle.USER_SYSTEM) {
             return false;
@@ -5803,6 +5814,10 @@ public class PackageManagerService extends IPackageManager.Stub {
 
     @Override
     public ParceledListSlice<EphemeralApplicationInfo> getEphemeralApplications(int userId) {
+        if (DISABLE_EPHEMERAL_APPS) {
+            return null;
+        }
+
         mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
                 "getEphemeralApplications");
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
@@ -5821,6 +5836,10 @@ public class PackageManagerService extends IPackageManager.Stub {
     public boolean isEphemeralApplication(String packageName, int userId) {
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
                 "isEphemeral");
+        if (DISABLE_EPHEMERAL_APPS) {
+            return false;
+        }
+
         if (!isCallerSameApp(packageName)) {
             return false;
         }
@@ -5835,6 +5854,10 @@ public class PackageManagerService extends IPackageManager.Stub {
 
     @Override
     public byte[] getEphemeralApplicationCookie(String packageName, int userId) {
+        if (DISABLE_EPHEMERAL_APPS) {
+            return null;
+        }
+
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
                 "getCookie");
         if (!isCallerSameApp(packageName)) {
@@ -5848,6 +5871,10 @@ public class PackageManagerService extends IPackageManager.Stub {
 
     @Override
     public boolean setEphemeralApplicationCookie(String packageName, byte[] cookie, int userId) {
+        if (DISABLE_EPHEMERAL_APPS) {
+            return true;
+        }
+
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
                 "setCookie");
         if (!isCallerSameApp(packageName)) {
@@ -5861,6 +5888,10 @@ public class PackageManagerService extends IPackageManager.Stub {
 
     @Override
     public Bitmap getEphemeralApplicationIcon(String packageName, int userId) {
+        if (DISABLE_EPHEMERAL_APPS) {
+            return null;
+        }
+
         mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
                 "getEphemeralApplicationIcon");
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
@@ -5916,8 +5947,6 @@ public class PackageManagerService extends IPackageManager.Stub {
                     : null;
             return ps != null
                     && mSettings.isEnabledAndMatchLPr(provider.info, flags, userId)
-                    && (!mSafeMode || (provider.info.applicationInfo.flags
-                            &ApplicationInfo.FLAG_SYSTEM) != 0)
                     ? PackageParser.generateProviderInfo(provider, flags,
                             ps.readUserState(userId), userId)
                     : null;
@@ -5972,9 +6001,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                         && (processName == null
                                 || (p.info.processName.equals(processName)
                                         && UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
-                        && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)
-                        && (!mSafeMode
-                                || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
+                        && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
                     if (finalList == null) {
                         finalList = new ArrayList<ProviderInfo>(3);
                     }
@@ -9240,10 +9267,6 @@ public class PackageManagerService extends IPackageManager.Stub {
                 return null;
             }
             final PackageParser.Activity activity = info.activity;
-            if (mSafeMode && (activity.info.applicationInfo.flags
-                    &ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return null;
-            }
             PackageSetting ps = (PackageSetting) activity.owner.mExtras;
             if (ps == null) {
                 return null;
@@ -9464,10 +9487,6 @@ public class PackageManagerService extends IPackageManager.Stub {
                 return null;
             }
             final PackageParser.Service service = info.service;
-            if (mSafeMode && (service.info.applicationInfo.flags
-                    &ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return null;
-            }
             PackageSetting ps = (PackageSetting) service.owner.mExtras;
             if (ps == null) {
                 return null;
@@ -9687,10 +9706,6 @@ public class PackageManagerService extends IPackageManager.Stub {
                 return null;
             }
             final PackageParser.Provider provider = info.provider;
-            if (mSafeMode && (provider.info.applicationInfo.flags
-                    & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return null;
-            }
             PackageSetting ps = (PackageSetting) provider.owner.mExtras;
             if (ps == null) {
                 return null;
index 3f9ce7a..1a79d3c 100644 (file)
@@ -3799,63 +3799,11 @@ final class Settings {
     }
 
     boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
-        return isEnabledLPr(componentInfo, flags, userId)
-                && isMatchLPr(componentInfo, flags);
-    }
-
-    private boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
-        if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
-            return true;
-        }
-        final PackageSetting packageSettings = mPackages.get(componentInfo.packageName);
-        if (PackageManagerService.DEBUG_SETTINGS) {
-            Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = "
-                    + componentInfo.packageName + " componentName = " + componentInfo.name);
-            Log.v(PackageManagerService.TAG, "enabledComponents: "
-                    + compToString(packageSettings.getEnabledComponents(userId)));
-            Log.v(PackageManagerService.TAG, "disabledComponents: "
-                    + compToString(packageSettings.getDisabledComponents(userId)));
-        }
-        if (packageSettings == null) {
-            return false;
-        }
-        PackageUserState ustate = packageSettings.readUserState(userId);
-        if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) != 0) {
-            if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
-                return true;
-            }
-        }
-        if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED
-                || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
-                || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
-                || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
-                    && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
-            return false;
-        }
-        if (ustate.enabledComponents != null
-                && ustate.enabledComponents.contains(componentInfo.name)) {
-            return true;
-        }
-        if (ustate.disabledComponents != null
-                && ustate.disabledComponents.contains(componentInfo.name)) {
-            return false;
-        }
-        return componentInfo.enabled;
-    }
-
-    private boolean isMatchLPr(ComponentInfo componentInfo, int flags) {
-        if ((flags & MATCH_SYSTEM_ONLY) != 0) {
-            final PackageSetting ps = mPackages.get(componentInfo.packageName);
-            if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return false;
-            }
-        }
+        final PackageSetting ps = mPackages.get(componentInfo.packageName);
+        if (ps == null) return false;
 
-        final boolean matchesUnaware = ((flags & MATCH_ENCRYPTION_UNAWARE) != 0)
-                && !componentInfo.encryptionAware;
-        final boolean matchesAware = ((flags & MATCH_ENCRYPTION_AWARE) != 0)
-                && componentInfo.encryptionAware;
-        return matchesUnaware || matchesAware;
+        final PackageUserState userState = ps.readUserState(userId);
+        return userState.isMatch(componentInfo, flags);
     }
 
     String getInstallerPackageNameLPr(String packageName) {
index f13d964..de1c1ea 100644 (file)
@@ -3788,7 +3788,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
             // size.  We need to do this directly, instead of relying on
             // it to bubble up from the nav bar, because this needs to
             // change atomically with screen rotations.
-            mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
+            mNavigationBarOnBottom = isNavigationBarOnBottom(displayWidth, displayHeight);
             if (mNavigationBarOnBottom) {
                 // It's a system nav bar or a portrait screen; nav bar goes on bottom.
                 int top = displayHeight - overscanBottom
@@ -3859,6 +3859,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
         return false;
     }
 
+    private boolean isNavigationBarOnBottom(int displayWidth, int displayHeight) {
+        return !mNavigationBarCanMove || displayWidth < displayHeight;
+    }
+
     /** {@inheritDoc} */
     @Override
     public int getSystemDecorLayerLw() {
@@ -5931,6 +5935,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
         }
     }
 
+    @Override
+    public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+            Rect outInsets) {
+        outInsets.setEmpty();
+        if (mStatusBar != null) {
+            outInsets.top = mStatusBarHeight;
+        }
+        if (mNavigationBar != null) {
+            if (isNavigationBarOnBottom(displayWidth, displayHeight)) {
+                outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
+            } else {
+                outInsets.right = getNavigationBarWidth(displayRotation, mUiMode);
+            }
+        }
+    }
+
     void sendCloseSystemWindows() {
         PhoneWindow.sendCloseSystemWindows(mContext, null);
     }
index 7be0ead..c3a6f5d 100644 (file)
@@ -291,15 +291,24 @@ public class WebViewUpdateService extends SystemService {
 
         // If the user has chosen provider, use that
         for (WebViewProviderInfo provider : providers) {
-            if (provider.packageName.equals(userChosenProvider)) {
+            if (provider.packageName.equals(userChosenProvider) && provider.isEnabled()) {
                 return provider.getPackageInfo();
             }
         }
 
-        // User did not choose, or the choice failed, use the most stable provider available
+        // User did not choose, or the choice failed; use the most stable provider that is
+        // enabled and available by default (not through user choice).
+        for (WebViewProviderInfo provider : providers) {
+            if (provider.isAvailableByDefault() && provider.isEnabled()) {
+                return provider.getPackageInfo();
+            }
+        }
+
+        // Could not find any enabled package either, use the most stable provider.
         for (WebViewProviderInfo provider : providers) {
             return provider.getPackageInfo();
         }
+
         mAnyWebViewInstalled = false;
         throw new WebViewFactory.MissingWebViewPackageException(
                 "Could not find a loadable WebView package");
index 51787b0..a9025bd 100644 (file)
@@ -393,7 +393,7 @@ class DisplayContent {
                     }
                     mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
                 }
-                if (task.isDockedInEffect() && !task.isResizeable()) {
+                if (task.isTwoFingerScrollMode()) {
                     stack.getBounds(mTmpRect);
                     mNonResizeableRegion.op(mTmpRect, Region.Op.UNION);
                     break;
index b4ddebc..6bb3e20 100644 (file)
@@ -558,8 +558,9 @@ class Task implements DimLayer.DimLayerUser {
     }
 
     boolean isResizeableByDockedStack() {
-        return mStack != null && getDisplayContent().getDockedStackLocked() != null &&
-                StackId.isTaskResizeableByDockedStack(mStack.mStackId);
+        final DisplayContent displayContent = getDisplayContent();
+        return displayContent != null && displayContent.getDockedStackLocked() != null
+                && mStack != null && StackId.isTaskResizeableByDockedStack(mStack.mStackId);
     }
 
     /**
@@ -570,6 +571,10 @@ class Task implements DimLayer.DimLayerUser {
         return inDockedWorkspace() || isResizeableByDockedStack();
     }
 
+    boolean isTwoFingerScrollMode() {
+        return isDockedInEffect() && !isResizeable();
+    }
+
     WindowState getTopVisibleAppMainWindow() {
         final AppWindowToken token = getTopVisibleAppToken();
         return token != null ? token.findMainWindow() : null;
index fc6ad70..e75780f 100644 (file)
@@ -18,6 +18,7 @@ package com.android.server.wm;
 
 import android.app.ActivityManager.StackId;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.util.EventLog;
@@ -26,6 +27,9 @@ import android.util.SparseArray;
 import android.view.DisplayInfo;
 import android.view.Surface;
 
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
+import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
@@ -126,7 +130,7 @@ public class TaskStack implements DimLayer.DimLayerUser {
             Configuration config = configs.get(task.mTaskId);
             if (config != null) {
                 Rect bounds = taskBounds.get(task.mTaskId);
-                if (!task.isResizeable() && task.isDockedInEffect()) {
+                if (task.isTwoFingerScrollMode()) {
                     // This is a non-resizeable task that's docked (or side-by-side to the docked
                     // stack). It might have been scrolled previously, and after the stack resizing,
                     // it might no longer fully cover the stack area.
@@ -245,19 +249,66 @@ public class TaskStack implements DimLayer.DimLayerUser {
                 setBounds(null);
             } else {
                 mTmpRect2.set(mBounds);
-                mDisplayContent.rotateBounds(
-                        mRotation, mDisplayContent.getDisplayInfo().rotation, mTmpRect2);
-                if (setBounds(mTmpRect2)) {
-                    // Post message to inform activity manager of the bounds change simulating
-                    // a one-way call. We do this to prevent a deadlock between window manager
-                    // lock and activity manager lock been held.
-                    mService.mH.sendMessage(mService.mH.obtainMessage(
-                            RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mBounds));
+                final int newRotation = mDisplayContent.getDisplayInfo().rotation;
+                if (mRotation == newRotation) {
+                    setBounds(mTmpRect2);
                 }
+
+                // If the rotation changes, we'll handle it in updateBoundsAfterRotation
             }
         }
     }
 
+    /**
+     * Updates the bounds after rotating the screen. We can't handle it in
+     * {@link #updateDisplayInfo} because at that point the configuration might not be fully updated
+     * yet.
+     */
+    void updateBoundsAfterRotation() {
+        final int newRotation = getDisplayInfo().rotation;
+        mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
+        if (mStackId == DOCKED_STACK_ID) {
+            snapDockedStackAfterRotation(mTmpRect2);
+        }
+
+        // Post message to inform activity manager of the bounds change simulating
+        // a one-way call. We do this to prevent a deadlock between window manager
+        // lock and activity manager lock been held.
+        mService.mH.sendMessage(mService.mH.obtainMessage(
+                RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mTmpRect2));
+    }
+
+    /**
+     * Snaps the bounds after rotation to the closest snap target for the docked stack.
+     */
+    private void snapDockedStackAfterRotation(Rect outBounds) {
+
+        // Calculate the current position.
+        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+        final int dividerSize = mService.getDefaultDisplayContentLocked()
+                .getDockedDividerController().getContentWidth();
+        final int dockSide = getDockSide(outBounds);
+        final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
+                dockSide, dividerSize);
+        final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth;
+        final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight;
+
+        // Snap the position to a target.
+        final int rotation = displayInfo.rotation;
+        final int orientation = mService.mCurConfiguration.orientation;
+        mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
+        final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
+                mService.mContext.getResources(),
+                0 /* minFlingVelocityPxPerSecond */, displayWidth, displayHeight,
+                dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds);
+        final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
+
+        // Recalculate the bounds based on the position of the target.
+        DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
+                outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight,
+                dividerSize);
+    }
+
     boolean isAnimating() {
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
@@ -682,6 +733,10 @@ public class TaskStack implements DimLayer.DimLayerUser {
      * information which side of the screen was the dock anchored.
      */
     int getDockSide() {
+        return getDockSide(mBounds);
+    }
+
+    int getDockSide(Rect bounds) {
         if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) {
             return DOCKED_INVALID;
         }
@@ -692,14 +747,14 @@ public class TaskStack implements DimLayer.DimLayerUser {
         final int orientation = mService.mCurConfiguration.orientation;
         if (orientation == Configuration.ORIENTATION_PORTRAIT) {
             // Portrait mode, docked either at the top or the bottom.
-            if (mBounds.top - mTmpRect.top < mTmpRect.bottom - mBounds.bottom) {
+            if (bounds.top - mTmpRect.top < mTmpRect.bottom - bounds.bottom) {
                 return DOCKED_TOP;
             } else {
                 return DOCKED_BOTTOM;
             }
         } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
             // Landscape mode, docked either on the left or on the right.
-            if (mBounds.left - mTmpRect.left < mTmpRect.right - mBounds.right) {
+            if (bounds.left - mTmpRect.left < mTmpRect.right - bounds.right) {
                 return DOCKED_LEFT;
             } else {
                 return DOCKED_RIGHT;
index f858abe..685df25 100644 (file)
@@ -1990,6 +1990,10 @@ public class WindowManagerService extends IWindowManager.Stub
                 }
             }
 
+            // If the window is being added to a task that's docked but non-resizeable,
+            // we need to update this new window's scroll position when it's added.
+            win.applyScrollIfNeeded();
+
             if (type == TYPE_DOCK_DIVIDER) {
                 getDefaultDisplayContentLocked().getDockedDividerController().setWindow(win);
             }
@@ -2977,7 +2981,7 @@ public class WindowManagerService extends IWindowManager.Stub
                     + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
             Animation a = mAppTransition.loadAnimation(lp, transit, enter,
                     mCurConfiguration.orientation, frame, insets, surfaceInsets, isVoiceInteraction,
-                    freeform, atoken.mTask.mTaskId);
+                    !fullscreen, atoken.mTask.mTaskId);
             if (a != null) {
                 if (DEBUG_ANIM) {
                     RuntimeException e = null;
@@ -3523,6 +3527,7 @@ public class WindowManagerService extends IWindowManager.Stub
         }
     }
 
+    @Override
     public void setNewConfiguration(Configuration config) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setNewConfiguration()")) {
@@ -3536,10 +3541,20 @@ public class WindowManagerService extends IWindowManager.Stub
                 mWaitingForConfig = false;
                 mLastFinishedFreezeSource = "new-config";
             }
+            if (orientationChanged) {
+                updateTaskStackBoundsAfterRotation();
+            }
             mWindowPlacerLocked.performSurfacePlacement();
         }
     }
 
+    private void updateTaskStackBoundsAfterRotation() {
+        for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) {
+            final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
+            stack.updateBoundsAfterRotation();
+        }
+    }
+
     @Override
     public void setAppOrientation(IApplicationToken token, int requestedOrientation) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
index b7fd60f..058fa67 100644 (file)
@@ -1458,15 +1458,24 @@ final class WindowState implements WindowManagerPolicy.WindowState {
     }
 
     boolean inDockedWorkspace() {
-        Task task = getTask();
+        final Task task = getTask();
         return task != null && task.inDockedWorkspace();
     }
 
     boolean isDockedInEffect() {
-        Task task = getTask();
+        final Task task = getTask();
         return task != null && task.isDockedInEffect();
     }
 
+    void applyScrollIfNeeded() {
+        final Task task = getTask();
+        if (task != null && task.isTwoFingerScrollMode()) {
+            task.getDimBounds(mTmpRect);
+            mXOffset = mTmpRect.left;
+            mYOffset = mTmpRect.top;
+        }
+    }
+
     int getTouchableRegion(Region region, int flags) {
         final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
         if (modal && mAppToken != null) {
index b18af33..b56ce73 100644 (file)
@@ -687,7 +687,7 @@ public final class PhoneAccount implements Parcelable {
                 .append("] PhoneAccount: ")
                 .append(mAccountHandle)
                 .append(" Capabilities: ")
-                .append(mCapabilities)
+                .append(capabilitiesToString(mCapabilities))
                 .append(" Schemes: ");
         for (String scheme : mSupportedUriSchemes) {
             sb.append(scheme)
@@ -698,4 +698,42 @@ public final class PhoneAccount implements Parcelable {
         sb.append("]");
         return sb.toString();
     }
+
+    /**
+     * Generates a string representation of a capabilities bitmask.
+     *
+     * @param capabilities The capabilities bitmask.
+     * @return String representation of the capabilities bitmask.
+     */
+    private String capabilitiesToString(int capabilities) {
+        StringBuilder sb = new StringBuilder();
+        if (hasCapabilities(CAPABILITY_VIDEO_CALLING)) {
+            sb.append("Video ");
+        }
+        if (hasCapabilities(CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)) {
+            sb.append("Presence ");
+        }
+        if (hasCapabilities(CAPABILITY_CALL_PROVIDER)) {
+            sb.append("CallProvider ");
+        }
+        if (hasCapabilities(CAPABILITY_CALL_SUBJECT)) {
+            sb.append("CallSubject ");
+        }
+        if (hasCapabilities(CAPABILITY_CONNECTION_MANAGER)) {
+            sb.append("ConnectionMgr ");
+        }
+        if (hasCapabilities(CAPABILITY_EMERGENCY_CALLS_ONLY)) {
+            sb.append("EmergOnly ");
+        }
+        if (hasCapabilities(CAPABILITY_MULTI_USER)) {
+            sb.append("MultiUser ");
+        }
+        if (hasCapabilities(CAPABILITY_PLACE_EMERGENCY_CALLS)) {
+            sb.append("PlaceEmerg ");
+        }
+        if (hasCapabilities(CAPABILITY_SIM_SUBSCRIPTION)) {
+            sb.append("SimSub ");
+        }
+        return sb.toString();
+    }
 }
index dabf706..216603c 100644 (file)
 
 package android.telecom;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Represents attributes of video calls.
  */
 public class VideoProfile implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({QUALITY_UNKNOWN, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_DEFAULT})
+    public @interface VideoQuality {}
+
     /**
      * "Unknown" video quality.
      * @hide
@@ -48,6 +58,14 @@ public class VideoProfile implements Parcelable {
      */
     public static final int QUALITY_DEFAULT = 4;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            flag = true,
+            value = {STATE_AUDIO_ONLY, STATE_TX_ENABLED, STATE_RX_ENABLED, STATE_BIDIRECTIONAL,
+                    STATE_PAUSED})
+    public @interface VideoState {}
+
     /**
      * Used when answering or dialing a call to indicate that the call does not have a video
      * component.
@@ -107,7 +125,7 @@ public class VideoProfile implements Parcelable {
      *
      * @param videoState The video state.
      */
-    public VideoProfile(int videoState) {
+    public VideoProfile(@VideoState int videoState) {
         this(videoState, QUALITY_DEFAULT);
     }
 
@@ -117,7 +135,7 @@ public class VideoProfile implements Parcelable {
      * @param videoState The video state.
      * @param quality The video quality.
      */
-    public VideoProfile(int videoState, int quality) {
+    public VideoProfile(@VideoState int videoState, @VideoQuality int quality) {
         mVideoState = videoState;
         mQuality = quality;
     }
@@ -130,6 +148,7 @@ public class VideoProfile implements Parcelable {
      * {@link VideoProfile#STATE_RX_ENABLED},
      * {@link VideoProfile#STATE_PAUSED}.
      */
+    @VideoState
     public int getVideoState() {
         return mVideoState;
     }
@@ -139,6 +158,7 @@ public class VideoProfile implements Parcelable {
      * Valid values: {@link VideoProfile#QUALITY_HIGH}, {@link VideoProfile#QUALITY_MEDIUM},
      * {@link VideoProfile#QUALITY_LOW}, {@link VideoProfile#QUALITY_DEFAULT}.
      */
+    @VideoQuality
     public int getQuality() {
         return mQuality;
     }
@@ -211,7 +231,7 @@ public class VideoProfile implements Parcelable {
      * @param videoState The video state.
      * @return String representation of the video state.
      */
-    public static String videoStateToString(int videoState) {
+    public static String videoStateToString(@VideoState int videoState) {
         StringBuilder sb = new StringBuilder();
         sb.append("Audio");
 
@@ -240,7 +260,7 @@ public class VideoProfile implements Parcelable {
      * @param videoState The video state.
      * @return {@code True} if the video state is audio only, {@code false} otherwise.
      */
-    public static boolean isAudioOnly(int videoState) {
+    public static boolean isAudioOnly(@VideoState int videoState) {
         return !hasState(videoState, VideoProfile.STATE_TX_ENABLED)
                 && !hasState(videoState, VideoProfile.STATE_RX_ENABLED);
     }
@@ -251,7 +271,7 @@ public class VideoProfile implements Parcelable {
      * @param videoState The video state.
      * @return {@code True} if video transmission or reception is enabled, {@code false} otherwise.
      */
-    public static boolean isVideo(int videoState) {
+    public static boolean isVideo(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_TX_ENABLED)
                 || hasState(videoState, VideoProfile.STATE_RX_ENABLED)
                 || hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL);
@@ -263,7 +283,7 @@ public class VideoProfile implements Parcelable {
      * @param videoState The video state.
      * @return {@code True} if video transmission is enabled, {@code false} otherwise.
      */
-    public static boolean isTransmissionEnabled(int videoState) {
+    public static boolean isTransmissionEnabled(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_TX_ENABLED);
     }
 
@@ -273,7 +293,7 @@ public class VideoProfile implements Parcelable {
      * @param videoState The video state.
      * @return {@code True} if video reception is enabled, {@code false} otherwise.
      */
-    public static boolean isReceptionEnabled(int videoState) {
+    public static boolean isReceptionEnabled(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_RX_ENABLED);
     }
 
@@ -283,7 +303,7 @@ public class VideoProfile implements Parcelable {
      * @param videoState The video state.
      * @return {@code True} if the video is bi-directional, {@code false} otherwise.
      */
-    public static boolean isBidirectional(int videoState) {
+    public static boolean isBidirectional(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL);
     }
 
@@ -293,7 +313,7 @@ public class VideoProfile implements Parcelable {
      * @param videoState The video state.
      * @return {@code True} if the video is paused, {@code false} otherwise.
      */
-    public static boolean isPaused(int videoState) {
+    public static boolean isPaused(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_PAUSED);
     }
 
@@ -304,7 +324,7 @@ public class VideoProfile implements Parcelable {
      * @param state The state to check.
      * @return {@code True} if the state is set.
      */
-    private static boolean hasState(int videoState, int state) {
+    private static boolean hasState(@VideoState int videoState, @VideoState int state) {
         return (videoState & state) == state;
     }
 
index 6ffc026..1a040bb 100644 (file)
@@ -553,6 +553,23 @@ public class CarrierConfigManager {
     public static final String BOOL_ALLOW_VIDEO_PAUSE =
             "bool_allow_video_pause";
 
+
+    /**
+     * Flag indicating whether the carrier supports RCS presence indication for video calls.  When
+     * {@code true}, the carrier supports RCS presence indication for video calls.  When presence
+     * is supported, the device should use the
+     * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the
+     * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate
+     * whether each contact supports video calling.  The UI is made aware that presence is enabled
+     * via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
+     * and can choose to hide or show the video calling icon based on whether a contact supports
+     * video.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -662,6 +679,7 @@ public class CarrierConfigManager {
         sDefaults.putString(KEY_MMS_UA_PROF_URL_STRING, "");
         sDefaults.putString(KEY_MMS_USER_AGENT_STRING, "");
         sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
+        sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false);
     }
 
     /**
index 01583f5..81aa6c6 100644 (file)
@@ -322,21 +322,25 @@ public class MockPackageManager extends PackageManager {
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
     @Override
     public byte[] getEphemeralCookie() {
         return new byte[0];
     }
 
+    /** @hide */
     @Override
     public boolean isEphemeralApplication() {
         return false;
     }
 
+    /** @hide */
     @Override
     public int getEphemeralCookieMaxSizeBytes() {
         return 0;
     }
 
+    /** @hide */
     @Override
     public boolean setEphemeralCookie(@NonNull byte[] cookie) {
         return false;
index fecfdf9..3a30230 100644 (file)
@@ -175,7 +175,7 @@ public class NotificationTestList extends TestActivity
                         .setTopic(new Notification.Topic("hello", "Hello"))
                         .build();
 
-                mNM.notify(999, n);
+                mNM.notify(70, n);
             }
         },
 
@@ -194,7 +194,7 @@ public class NotificationTestList extends TestActivity
                         .setStyle(picture)
                         .build();
 
-                mNM.notify(9999, n);
+                mNM.notify(71, n);
             }
         },
         new Test("with topic Bananas") {
@@ -211,10 +211,28 @@ public class NotificationTestList extends TestActivity
                         .setTopic(new Notification.Topic("bananas", "Bananas"))
                         .build();
 
-                mNM.notify(999, n);
+                mNM.notify(72, n);
             }
         },
 
+            new Test("with delete intent") {
+                public void run() {
+                    Notification.BigTextStyle bigText = new Notification.BigTextStyle();
+                    bigText.bigText("bananas are great\nso tasty\nyum\nyum\nyum\n");
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon1)
+                            .setStyle(bigText)
+                            .setWhen(mActivityCreateTime)
+                            .setContentTitle("bananananana")
+                            .setContentText("This is a banana!!!")
+                            .setTopic(new Notification.Topic("bananas", "Bananas"))
+                            .setDeleteIntent(makeIntent2())
+                            .build();
+
+                    mNM.notify(73, n);
+                }
+            },
+
         new Test("Whens") {
             public void run()
             {
index e1f9642..5e7d3ec 100644 (file)
@@ -582,10 +582,15 @@ bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* out
 
         if (formatted && translateable) {
             if (!util::verifyJavaStringFormat(*stringValue->value)) {
-                mDiag->error(DiagMessage(outResource->source)
-                             << "multiple substitutions specified in non-positional format; "
-                                "did you mean to add the formatted=\"false\" attribute?");
-                return false;
+                DiagMessage msg(outResource->source);
+                msg << "multiple substitutions specified in non-positional format; "
+                       "did you mean to add the formatted=\"false\" attribute?";
+                if (mOptions.errorOnPositionalArguments) {
+                    mDiag->error(msg);
+                    return false;
+                }
+
+                mDiag->warn(msg);
             }
         }
 
index 9ad749e..51cbbe1 100644 (file)
@@ -44,6 +44,11 @@ struct ResourceParserOptions {
      * Whether the default setting for this parser is to allow translation.
      */
     bool translatable = true;
+
+    /**
+     * Whether positional arguments in formatted strings are treated as errors or warnings.
+     */
+    bool errorOnPositionalArguments = true;
 };
 
 /*
index b3b0f65..c78670f 100644 (file)
@@ -107,6 +107,7 @@ struct CompileOptions {
     Maybe<std::string> resDir;
     std::vector<std::u16string> products;
     bool pseudolocalize = false;
+    bool legacyMode = false;
     bool verbose = false;
 };
 
@@ -192,6 +193,7 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options,
 
         ResourceParserOptions parserOptions;
         parserOptions.products = options.products;
+        parserOptions.errorOnPositionalArguments = !options.legacyMode;
 
         // If the filename includes donottranslate, then the default translatable is false.
         parserOptions.translatable = pathData.name.find(u"donottranslate") == std::string::npos;
@@ -438,6 +440,8 @@ int compile(const std::vector<StringPiece>& args) {
             .optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
             .optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
                             "(en-XA and ar-XB)", &options.pseudolocalize)
+            .optionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
+                            &options.legacyMode)
             .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
     if (!flags.parse("aapt2 compile", args, &std::cerr)) {
         return 1;
index 652e52f..8a87d96 100644 (file)
@@ -228,29 +228,53 @@ public:
         return {};
     }
 
+    /**
+     * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
+     * Postcondition: ResourceTable has only one package left. All others are stripped, or there
+     *                is an error and false is returned.
+     */
     bool verifyNoExternalPackages() {
+        auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
+            return mContext.getCompilationPackage() != pkg->name ||
+                    !pkg->id ||
+                    pkg->id.value() != mContext.getPackageId();
+        };
+
         bool error = false;
         for (const auto& package : mFinalTable.packages) {
-            if (mContext.getCompilationPackage() != package->name ||
-                    !package->id || package->id.value() != mContext.getPackageId()) {
+            if (isExtPackageFunc(package)) {
                 // We have a package that is not related to the one we're building!
                 for (const auto& type : package->types) {
                     for (const auto& entry : type->entries) {
+                        ResourceNameRef resName(package->name, type->type, entry->name);
+
                         for (const auto& configValue : entry->values) {
-                            mContext.getDiagnostics()->error(
-                                    DiagMessage(configValue.value->getSource())
-                                                << "defined resource '"
-                                                << ResourceNameRef(package->name,
-                                                                   type->type,
-                                                                   entry->name)
-                                                << "' for external package '"
-                                                << package->name << "'");
-                            error = true;
+                            // Special case the occurrence of an ID that is being generated for the
+                            // 'android' package. This is due to legacy reasons.
+                            if (valueCast<Id>(configValue.value.get()) &&
+                                    package->name == u"android") {
+                                mContext.getDiagnostics()->warn(
+                                        DiagMessage(configValue.value->getSource())
+                                        << "generated id '" << resName
+                                        << "' for external package '" << package->name
+                                        << "'");
+                            } else {
+                                mContext.getDiagnostics()->error(
+                                        DiagMessage(configValue.value->getSource())
+                                        << "defined resource '" << resName
+                                        << "' for external package '" << package->name
+                                        << "'");
+                                error = true;
+                            }
                         }
                     }
                 }
             }
         }
+
+        auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
+                                         isExtPackageFunc);
+        mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
         return !error;
     }
 
index 31dd3d9..db4c6dc 100644 (file)
@@ -327,12 +327,19 @@ public final class BridgeTypedArray extends TypedArray {
             return null;
         }
 
-        // let the framework inflate the ColorStateList from the XML file.
-        File f = new File(value);
-        if (f.isFile()) {
-            try {
-                XmlPullParser parser = ParserFactory.create(f);
 
+        try {
+            // Get the state list file content from callback to parse PSI file
+            XmlPullParser parser = mContext.getLayoutlibCallback().getXmlFileParser(value);
+            if (parser == null) {
+                // If used with a version of Android Studio that does not implement getXmlFileParser
+                // fall back to reading the file from disk
+                File f = new File(value);
+                if (f.isFile()) {
+                    parser = ParserFactory.create(f);
+                }
+            }
+            if (parser != null) {
                 BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
                         parser, mContext, resValue.isFramework());
                 try {
@@ -341,18 +348,18 @@ public final class BridgeTypedArray extends TypedArray {
                 } finally {
                     blockParser.ensurePopped();
                 }
-            } catch (XmlPullParserException e) {
-                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to configure parser for " + value, e, null);
-                return null;
-            } catch (Exception e) {
-                // this is an error and not warning since the file existence is checked before
-                // attempting to parse it.
-                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                        "Failed to parse file " + value, e, null);
-
-                return null;
             }
+        } catch (XmlPullParserException e) {
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                    "Failed to configure parser for " + value, e, null);
+            return null;
+        } catch (Exception e) {
+            // this is an error and not warning since the file existence is checked before
+            // attempting to parse it.
+            Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+                    "Failed to parse file " + value, e, null);
+
+            return null;
         }
 
         try {
index 60514b6..8d5863b 100644 (file)
@@ -122,4 +122,35 @@ import java.util.Set;
     /*package*/ static boolean nativeIsSeekable(FileDescriptor fd) {
         return true;
     }
+
+    /**
+     * Set the newly decoded bitmap's density based on the Options.
+     *
+     * Copied from {@link BitmapFactory#setDensityFromOptions(Bitmap, Options)}.
+     */
+    @LayoutlibDelegate
+    /*package*/ static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
+        if (outputBitmap == null || opts == null) return;
+
+        final int density = opts.inDensity;
+        if (density != 0) {
+            outputBitmap.setDensity(density);
+            final int targetDensity = opts.inTargetDensity;
+            if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
+                return;
+            }
+
+            // --- Change from original implementation begins ---
+            // LayoutLib doesn't scale the nine patch when decoding it. Hence, don't change the
+            // density of the source bitmap in case of ninepatch.
+
+            if (opts.inScaled) {
+            // --- Change from original implementation ends. ---
+                outputBitmap.setDensity(targetDensity);
+            }
+        } else if (opts.inBitmap != null) {
+            // bitmap was reused, ensure density is reset
+            outputBitmap.setDensity(Bitmap.getDefaultDensity());
+        }
+    }
 }
index 2b86bfb..d8ead23 100644 (file)
Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
new file mode 100644 (file)
index 0000000..65d1dc5
Binary files /dev/null and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png differ
index 2dca07c..dea86bf 100644 (file)
@@ -305,6 +305,11 @@ public class Main {
         renderAndVerify("array_check.xml", "array_check.png");
     }
 
+    @Test
+    public void testAllWidgetsTablet() throws ClassNotFoundException {
+        renderAndVerify("allwidgets.xml", "allwidgets_tab.png", ConfigGenerator.NEXUS_7_2012);
+    }
+
     @AfterClass
     public static void tearDown() {
         sLayoutLibLog = null;
@@ -423,6 +428,16 @@ public class Main {
      */
     private void renderAndVerify(String layoutFileName, String goldenFileName)
             throws ClassNotFoundException {
+        renderAndVerify(layoutFileName, goldenFileName, ConfigGenerator.NEXUS_5);
+    }
+
+    /**
+     * Create a new rendering session and test that rendering given layout on given device
+     * doesn't throw any exceptions and matches the provided image.
+     */
+    private void renderAndVerify(String layoutFileName, String goldenFileName,
+            ConfigGenerator deviceConfig)
+            throws ClassNotFoundException {
         // Create the layout pull parser.
         LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutFileName);
         // Create LayoutLibCallback.
@@ -430,7 +445,7 @@ public class Main {
         layoutLibCallback.initResources();
         // TODO: Set up action bar handler properly to test menu rendering.
         // Create session params.
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+        SessionParams params = getSessionParams(parser, deviceConfig,
                 layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
         renderAndVerify(params, goldenFileName);
     }
index 8e0cec6..34fc726 100644 (file)
@@ -126,6 +126,21 @@ public class ConfigGenerator {
                                                         .setSoftButtons(true)
                                                         .setNavigation(Navigation.NONAV);
 
+    public static final ConfigGenerator NEXUS_7_2012 = new ConfigGenerator()
+                                                        .setScreenHeight(1280)
+                                                        .setScreenWidth(800)
+                                                        .setXdpi(195)
+                                                        .setYdpi(200)
+                                                        .setOrientation(ScreenOrientation.PORTRAIT)
+                                                        .setDensity(Density.TV)
+                                                        .setRatio(ScreenRatio.NOTLONG)
+                                                        .setSize(ScreenSize.LARGE)
+                                                        .setKeyboard(Keyboard.NOKEY)
+                                                        .setTouchScreen(TouchScreen.FINGER)
+                                                        .setKeyboardState(KeyboardState.SOFT)
+                                                        .setSoftButtons(true)
+                                                        .setNavigation(Navigation.NONAV);
+
     private static final String TAG_ATTR = "attr";
     private static final String TAG_ENUM = "enum";
     private static final String TAG_FLAG = "flag";
index 3ca7590..6e6ad8f 100644 (file)
@@ -166,6 +166,7 @@ public final class CreateInfo implements ICreateInfo {
         "android.content.res.TypedArray#getValueAt",
         "android.content.res.TypedArray#obtain",
         "android.graphics.BitmapFactory#finishDecode",
+        "android.graphics.BitmapFactory#setDensityFromOptions",
         "android.graphics.drawable.GradientDrawable#buildRing",
         "android.graphics.Typeface#getSystemFontConfigLocation",
         "android.os.Handler#sendMessageAtTime",