Merge "Wire up lifecycle, send unlocked broadcast."
diff --git a/Android.mk b/Android.mk
index f5d5a11..d22273c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -261,6 +261,7 @@
core/java/android/view/IApplicationToken.aidl \
core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \
core/java/android/view/IAssetAtlas.aidl \
+ core/java/android/view/IDockDividerVisibilityListener.aidl \
core/java/android/view/IGraphicsStats.aidl \
core/java/android/view/IInputFilter.aidl \
core/java/android/view/IInputFilterHost.aidl \
diff --git a/api/current.txt b/api/current.txt
index c0a6bde..fc95ca3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31404,7 +31404,10 @@
public static final class Telephony.Sms.Intents {
method public static android.telephony.SmsMessage[] getMessagesFromIntent(android.content.Intent);
field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.provider.Telephony.ACTION_CHANGE_DEFAULT";
+ field public static final java.lang.String ACTION_DEFAULT_SMS_PACKAGE_CHANGED = "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED";
+ field public static final java.lang.String ACTION_EXTERNAL_PROVIDER_CHANGE = "android.provider.action.EXTERNAL_PROVIDER_CHANGE";
field public static final java.lang.String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED";
+ field public static final java.lang.String EXTRA_IS_DEFAULT_SMS_APP = "android.provider.extra.IS_DEFAULT_SMS_APP";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
field public static final int RESULT_SMS_DUPLICATED = 5; // 0x5
field public static final int RESULT_SMS_GENERIC_ERROR = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 00e99e3..409a7d5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -33520,7 +33520,10 @@
public static final class Telephony.Sms.Intents {
method public static android.telephony.SmsMessage[] getMessagesFromIntent(android.content.Intent);
field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.provider.Telephony.ACTION_CHANGE_DEFAULT";
+ field public static final java.lang.String ACTION_DEFAULT_SMS_PACKAGE_CHANGED = "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED";
+ field public static final java.lang.String ACTION_EXTERNAL_PROVIDER_CHANGE = "android.provider.action.EXTERNAL_PROVIDER_CHANGE";
field public static final java.lang.String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED";
+ field public static final java.lang.String EXTRA_IS_DEFAULT_SMS_APP = "android.provider.extra.IS_DEFAULT_SMS_APP";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
field public static final int RESULT_SMS_DUPLICATED = 5; // 0x5
field public static final int RESULT_SMS_GENERIC_ERROR = 2; // 0x2
diff --git a/api/test-current.txt b/api/test-current.txt
index c0a6bde..fc95ca3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -31404,7 +31404,10 @@
public static final class Telephony.Sms.Intents {
method public static android.telephony.SmsMessage[] getMessagesFromIntent(android.content.Intent);
field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.provider.Telephony.ACTION_CHANGE_DEFAULT";
+ field public static final java.lang.String ACTION_DEFAULT_SMS_PACKAGE_CHANGED = "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED";
+ field public static final java.lang.String ACTION_EXTERNAL_PROVIDER_CHANGE = "android.provider.action.EXTERNAL_PROVIDER_CHANGE";
field public static final java.lang.String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED";
+ field public static final java.lang.String EXTRA_IS_DEFAULT_SMS_APP = "android.provider.extra.IS_DEFAULT_SMS_APP";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
field public static final int RESULT_SMS_DUPLICATED = 5; // 0x5
field public static final int RESULT_SMS_GENERIC_ERROR = 2; // 0x2
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 273483a..468c145 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -178,15 +178,8 @@
* </p>
* <h3>Notification strategy</h3>
* <p>
- * For each feedback type only one accessibility service is notified. Services are notified
- * in the order of registration. Hence, if two services are registered for the same
- * feedback type in the same package the first one wins. It is possible however, to
- * register a service as the default one for a given feedback type. In such a case this
- * service is invoked if no other service was interested in the event. In other words, default
- * services do not compete with other services and are notified last regardless of the
- * registration order. This enables "generic" accessibility services that work reasonably
- * well with most applications to coexist with "polished" ones that are targeted for
- * specific applications.
+ * All accessibility services are notified of all events they have requested, regardless of their
+ * feedback type.
* </p>
* <p class="note">
* <strong>Note:</strong> The event notification timeout is useful to avoid propagating
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f560f8a..c92382a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4420,8 +4420,19 @@
* to receive changes in this value.
*/
public static final String LOCATION_MODE = "location_mode";
+ /**
+ * Stores the previous location mode when {@link #LOCATION_MODE} is set to
+ * {@link #LOCATION_MODE_OFF}
+ * @hide
+ */
+ public static final String LOCATION_PREVIOUS_MODE = "location_previous_mode";
/**
+ * Sets all location providers to the previous states before location was turned off.
+ * @hide
+ */
+ public static final int LOCATION_MODE_PREVIOUS = -1;
+ /**
* Location access disabled.
*/
public static final int LOCATION_MODE_OFF = 0;
@@ -5795,6 +5806,7 @@
CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES);
CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE);
+ CLONE_TO_MANAGED_PROFILE.add(LOCATION_PREVIOUS_MODE);
CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED);
CLONE_TO_MANAGED_PROFILE.add(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE);
@@ -5882,6 +5894,28 @@
}
/**
+ * Saves the current location mode into {@link #LOCATION_PREVIOUS_MODE}.
+ */
+ private static final boolean saveLocationModeForUser(ContentResolver cr, int userId) {
+ final int mode = getLocationModeForUser(cr, userId);
+ return putIntForUser(cr, Settings.Secure.LOCATION_PREVIOUS_MODE, mode, userId);
+ }
+
+ /**
+ * Restores the current location mode from {@link #LOCATION_PREVIOUS_MODE}.
+ */
+ private static final boolean restoreLocationModeForUser(ContentResolver cr, int userId) {
+ int mode = getIntForUser(cr, Settings.Secure.LOCATION_PREVIOUS_MODE,
+ LOCATION_MODE_HIGH_ACCURACY, userId);
+ // Make sure that the previous mode is never "off". Otherwise the user won't be able to
+ // turn on location any longer.
+ if (mode == LOCATION_MODE_OFF) {
+ mode = LOCATION_MODE_HIGH_ACCURACY;
+ }
+ return setLocationModeForUser(cr, mode, userId);
+ }
+
+ /**
* Thread-safe method for setting the location mode to one of
* {@link #LOCATION_MODE_HIGH_ACCURACY}, {@link #LOCATION_MODE_SENSORS_ONLY},
* {@link #LOCATION_MODE_BATTERY_SAVING}, or {@link #LOCATION_MODE_OFF}.
@@ -5899,7 +5933,11 @@
boolean gps = false;
boolean network = false;
switch (mode) {
+ case LOCATION_MODE_PREVIOUS:
+ // Retrieve the actual mode and set to that mode.
+ return restoreLocationModeForUser(cr, userId);
case LOCATION_MODE_OFF:
+ saveLocationModeForUser(cr, userId);
break;
case LOCATION_MODE_SENSORS_ONLY:
gps = true;
diff --git a/core/java/android/view/IDockDividerVisibilityListener.aidl b/core/java/android/view/IDockDividerVisibilityListener.aidl
new file mode 100644
index 0000000..a7d5cda
--- /dev/null
+++ b/core/java/android/view/IDockDividerVisibilityListener.aidl
@@ -0,0 +1,27 @@
+/**
+ * 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.view;
+
+/**
+ * Listener for showing/hiding of the dock divider. Will fire when an app is shown in side by side
+ * mode and a divider should be shown.
+ *
+ * @hide
+ */
+oneway interface IDockDividerVisibilityListener {
+ void onDockDividerVisibilityChanged(boolean visible);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 64a046e..bd65532 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -29,6 +29,7 @@
import android.os.IRemoteCallback;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.IDockDividerVisibilityListener;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowSession;
@@ -290,10 +291,10 @@
/**
* Create a screenshot of the applications currently displayed.
*
- * @param frameScale the scale to apply to the frame, only used when width = -1 and
+ * @param frameScale the scale to apply to the frame, only used when width = -1 and
* height = -1
*/
- Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight,
+ Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight,
float frameScale);
/**
@@ -348,4 +349,9 @@
* stack size.
*/
void setDockedStackResizing(boolean resizing);
+
+ /**
+ * Registers a listener that will be called when the dock divider changes its visibility.
+ */
+ void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 476c6a2..2f385c1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7225,6 +7225,7 @@
// of the intended optimizations as part of requestLayoutForChild.
nullLayouts();
requestLayout();
+ invalidate();
}
@Override
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 57338be..568b601 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1877,6 +1877,11 @@
<permission android:name="android.permission.MANAGE_APP_TOKENS"
android:protectionLevel="signature" />
+ <!-- Allows System UI to register listeners for events from Window Manager.
+ @hide -->
+ <permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows the application to temporarily freeze the screen for a
full-screen transition. -->
<permission android:name="android.permission.FREEZE_SCREEN"
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
index f6d8ee9..edfd380 100644
--- a/data/sounds/AllAudio.mk
+++ b/data/sounds/AllAudio.mk
@@ -228,6 +228,7 @@
$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:system/media/audio/ui/Trusted.ogg \
$(LOCAL_PATH)/effects/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/ogg/WirelessChargingStarted.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
$(LOCAL_PATH)/effects/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
diff --git a/data/sounds/AudioPackage10.mk b/data/sounds/AudioPackage10.mk
index 5a5eea6..c5222af 100644
--- a/data/sounds/AudioPackage10.mk
+++ b/data/sounds/AudioPackage10.mk
@@ -1,9 +1,9 @@
#
# Audio Package 10 - Mako
-#
+#
# Include this file in a product makefile to include these audio files
#
-#
+#
LOCAL_PATH:= frameworks/base/data/sounds
@@ -23,6 +23,7 @@
$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:system/media/audio/ui/LowBattery.ogg \
diff --git a/data/sounds/AudioPackage11.mk b/data/sounds/AudioPackage11.mk
index 0f85b33..43c83b9 100644
--- a/data/sounds/AudioPackage11.mk
+++ b/data/sounds/AudioPackage11.mk
@@ -1,9 +1,9 @@
#
# Audio Package 11 - Razor
-#
+#
# Include this file in a product makefile to include these audio files
#
-#
+#
LOCAL_PATH:= frameworks/base/data/sounds
@@ -23,6 +23,7 @@
$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:system/media/audio/ui/LowBattery.ogg \
diff --git a/data/sounds/AudioPackage12.mk b/data/sounds/AudioPackage12.mk
index 4251332..cd4d35b 100644
--- a/data/sounds/AudioPackage12.mk
+++ b/data/sounds/AudioPackage12.mk
@@ -13,7 +13,7 @@
RINGTONE_FILES := Callisto Dione Ganymede Luna Oberon Phobos Sedna Titania Triton Umbriel
EFFECT_FILES := Effect_Tick KeypressReturn KeypressInvalid KeypressDelete KeypressSpacebar KeypressStandard \
camera_focus Dock Undock Lock Unlock Trusted
-MATERIAL_EFFECT_FILES := camera_click VideoRecord LowBattery WirelessChargingStarted
+MATERIAL_EFFECT_FILES := camera_click VideoRecord LowBattery WirelessChargingStarted VideoStop
PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
diff --git a/data/sounds/AudioPackage12_48.mk b/data/sounds/AudioPackage12_48.mk
index 70e68d3..80758f4 100644
--- a/data/sounds/AudioPackage12_48.mk
+++ b/data/sounds/AudioPackage12_48.mk
@@ -13,7 +13,7 @@
RINGTONE_FILES := Callisto Dione Ganymede Luna Oberon Phobos Sedna Titania Triton Umbriel
EFFECT_FILES := Effect_Tick KeypressReturn KeypressInvalid KeypressDelete KeypressSpacebar KeypressStandard \
Lock Unlock Trusted
-MATERIAL_EFFECT_FILES := camera_click VideoRecord LowBattery WirelessChargingStarted
+MATERIAL_EFFECT_FILES := camera_click VideoRecord LowBattery WirelessChargingStarted VideoStop
# Alarms not yet available in 48 kHz
PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
diff --git a/data/sounds/AudioPackage13.mk b/data/sounds/AudioPackage13.mk
index cec7280..d33a4af 100644
--- a/data/sounds/AudioPackage13.mk
+++ b/data/sounds/AudioPackage13.mk
@@ -14,7 +14,7 @@
Umbriel
EFFECT_FILES := Effect_Tick KeypressReturn KeypressInvalid KeypressDelete KeypressSpacebar KeypressStandard \
camera_focus Dock Undock Lock Unlock Trusted
-MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery
+MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
diff --git a/data/sounds/AudioPackage13_48.mk b/data/sounds/AudioPackage13_48.mk
index d1b17c8..9c320ae 100644
--- a/data/sounds/AudioPackage13_48.mk
+++ b/data/sounds/AudioPackage13_48.mk
@@ -14,7 +14,7 @@
Umbriel
EFFECT_FILES := Effect_Tick KeypressReturn KeypressInvalid KeypressDelete KeypressSpacebar KeypressStandard \
Lock Unlock Trusted
-MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery
+MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
$(LOCAL_PATH)/alarms/material/ogg/$(fn)_48k.ogg:system/media/audio/alarms/$(fn).ogg)
diff --git a/data/sounds/AudioPackage2.mk b/data/sounds/AudioPackage2.mk
index ba9d7e2..40319c4 100644
--- a/data/sounds/AudioPackage2.mk
+++ b/data/sounds/AudioPackage2.mk
@@ -1,11 +1,11 @@
#
# Audio Package 2
-#
+#
# Include this file in a product makefile to include these audio files
#
# This is a larger package of sounds than the 1.0 release for devices
# that have larger internal flash.
-#
+#
LOCAL_PATH:= frameworks/base/data/sounds
@@ -34,6 +34,7 @@
$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
diff --git a/data/sounds/AudioPackage3.mk b/data/sounds/AudioPackage3.mk
index 5bfeb42..a05de72 100644
--- a/data/sounds/AudioPackage3.mk
+++ b/data/sounds/AudioPackage3.mk
@@ -1,11 +1,11 @@
#
# Audio Package 3
-#
+#
# Include this file in a product makefile to include these audio files
#
# This is a larger package of sounds than the 1.0 release for devices
# that have larger internal flash.
-#
+#
LOCAL_PATH:= frameworks/base/data/sounds
@@ -34,6 +34,7 @@
$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
diff --git a/data/sounds/AudioPackage4.mk b/data/sounds/AudioPackage4.mk
index 43dbe20..d376a2d 100644
--- a/data/sounds/AudioPackage4.mk
+++ b/data/sounds/AudioPackage4.mk
@@ -1,11 +1,11 @@
#
# Audio Package 4
-#
+#
# Include this file in a product makefile to include these audio files
#
# This is a larger package of sounds than the 1.0 release for devices
# that have larger internal flash.
-#
+#
LOCAL_PATH:= frameworks/base/data/sounds
@@ -39,6 +39,7 @@
$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
diff --git a/data/sounds/AudioPackage5.mk b/data/sounds/AudioPackage5.mk
index fbbb16a..72384c8 100644
--- a/data/sounds/AudioPackage5.mk
+++ b/data/sounds/AudioPackage5.mk
@@ -1,9 +1,9 @@
#
# Audio Package 5 - Crespo/Soju
-#
+#
# Include this file in a product makefile to include these audio files
#
-#
+#
LOCAL_PATH:= frameworks/base/data/sounds
@@ -20,6 +20,7 @@
$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
diff --git a/data/sounds/AudioPackage6.mk b/data/sounds/AudioPackage6.mk
index c843fdc..5413704 100644
--- a/data/sounds/AudioPackage6.mk
+++ b/data/sounds/AudioPackage6.mk
@@ -1,9 +1,9 @@
#
# Audio Package 6 - Trygon/Stingray
-#
+#
# Include this file in a product makefile to include these audio files
#
-#
+#
LOCAL_PATH:= frameworks/base/data/sounds
@@ -19,6 +19,7 @@
$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk
index ce82651..e4763be 100644
--- a/data/sounds/AudioPackage7.mk
+++ b/data/sounds/AudioPackage7.mk
@@ -1,9 +1,9 @@
#
# Audio Package 7 - Tuna
-#
+#
# Include this file in a product makefile to include these audio files
#
-#
+#
LOCAL_PATH:= frameworks/base/data/sounds
@@ -21,6 +21,7 @@
$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
diff --git a/data/sounds/AudioPackage7alt.mk b/data/sounds/AudioPackage7alt.mk
index db468f3..30e6173 100644
--- a/data/sounds/AudioPackage7alt.mk
+++ b/data/sounds/AudioPackage7alt.mk
@@ -21,6 +21,7 @@
$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
diff --git a/data/sounds/AudioPackage8.mk b/data/sounds/AudioPackage8.mk
index 4112c18..b38e62d 100644
--- a/data/sounds/AudioPackage8.mk
+++ b/data/sounds/AudioPackage8.mk
@@ -1,9 +1,9 @@
#
# Audio Package 7 - Tuna
-#
+#
# Include this file in a product makefile to include these audio files
#
-#
+#
LOCAL_PATH:= frameworks/base/data/sounds
@@ -23,6 +23,7 @@
$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
diff --git a/data/sounds/AudioPackage9.mk b/data/sounds/AudioPackage9.mk
index 1b430c0..dbe1350 100644
--- a/data/sounds/AudioPackage9.mk
+++ b/data/sounds/AudioPackage9.mk
@@ -23,6 +23,7 @@
$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
diff --git a/data/sounds/OriginalAudio.mk b/data/sounds/OriginalAudio.mk
index e1ca24b..f683752 100644
--- a/data/sounds/OriginalAudio.mk
+++ b/data/sounds/OriginalAudio.mk
@@ -1,8 +1,8 @@
#
# Original audio package that shipped on G1
-#
+#
# This file is included from core.mk so that all devices will have these sounds
-#
+#
# TODO: Clean up for future releases
#
@@ -32,6 +32,7 @@
$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
diff --git a/data/sounds/effects/VideoStop.ogg b/data/sounds/effects/VideoStop.ogg
new file mode 100644
index 0000000..1450522
--- /dev/null
+++ b/data/sounds/effects/VideoStop.ogg
Binary files differ
diff --git a/data/sounds/effects/VideoStop.wav b/data/sounds/effects/VideoStop.wav
new file mode 100644
index 0000000..5809d93
--- /dev/null
+++ b/data/sounds/effects/VideoStop.wav
Binary files differ
diff --git a/data/sounds/effects/material/ogg/VideoStop.ogg b/data/sounds/effects/material/ogg/VideoStop.ogg
new file mode 100644
index 0000000..e98fabc0
--- /dev/null
+++ b/data/sounds/effects/material/ogg/VideoStop.ogg
Binary files differ
diff --git a/data/sounds/effects/material/ogg/VideoStop_48k.ogg b/data/sounds/effects/material/ogg/VideoStop_48k.ogg
new file mode 100644
index 0000000..b1eb780
--- /dev/null
+++ b/data/sounds/effects/material/ogg/VideoStop_48k.ogg
Binary files differ
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index bf3982d..25346ac 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -144,6 +144,7 @@
android:name=".BugreportReceiver"
android:permission="android.permission.DUMP">
<intent-filter>
+ <action android:name="android.intent.action.BUGREPORT_STARTED" />
<action android:name="android.intent.action.BUGREPORT_FINISHED" />
</intent-filter>
</receiver>
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index 4469d38..cff36f7 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -17,6 +17,8 @@
<resources>
<string name="app_label">Shell</string>
+ <!-- Title of notification indicating a bugreport process is in progress. [CHAR LIMIT=50] -->
+ <string name="bugreport_in_progress_title">Bug report in progress</string>
<!-- Title of notification indicating a bugreport has been successfully captured. [CHAR LIMIT=50] -->
<string name="bugreport_finished_title">Bug report captured</string>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index a2030ef..d0e91d2 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -21,11 +21,16 @@
import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
+import java.text.NumberFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -45,26 +50,102 @@
import android.content.res.Configuration;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.Process;
import android.os.SystemProperties;
import android.support.v4.content.FileProvider;
+import android.text.format.DateUtils;
import android.util.Log;
import android.util.Patterns;
+import android.util.SparseArray;
import android.widget.Toast;
+/**
+ * Service used to keep progress of bug reports processes ({@code dumpstate}).
+ * <p>
+ * The workflow is:
+ * <ol>
+ * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with its pid and the
+ * estimated total effort.
+ * <li>{@link BugreportReceiver} receives the intent and delegates it to this service.
+ * <li>Upon start, this service:
+ * <ol>
+ * <li>Issues a system notification so user can watch the progresss (which is 0% initially).
+ * <li>Polls the {@link SystemProperties} for updates on the {@code dumpstate} progress.
+ * <li>If the progress changed, it updates the system notification.
+ * </ol>
+ * <li>As {@code dumpstate} progresses, it updates the system property.
+ * <li>When {@code dumpstate} finishes, it sends a {@code BUGREPORT_FINISHED} intent.
+ * <li>{@link BugreportReceiver} receives the intent and delegates it to this service, which in
+ * turn:
+ * <ol>
+ * <li>Updates the system notification so user can share the bug report.
+ * <li>Stops monitoring that {@code dumpstate} process.
+ * <li>Stops itself if it doesn't have any process left to monitor.
+ * </ol>
+ * </ol>
+ */
public class BugreportProgressService extends Service {
private static final String TAG = "Shell";
+ private static final boolean DEBUG = false;
private static final String AUTHORITY = "com.android.shell";
+ static final String INTENT_BUGREPORT_STARTED = "android.intent.action.BUGREPORT_STARTED";
+ static final String INTENT_BUGREPORT_FINISHED = "android.intent.action.BUGREPORT_FINISHED";
+
static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
+ static final String EXTRA_PID = "android.intent.extra.PID";
+ static final String EXTRA_MAX = "android.intent.extra.MAX";
+ static final String EXTRA_NAME = "android.intent.extra.NAME";
+ static final String EXTRA_ORIGINAL_INTENT = "android.intent.extra.ORIGINAL_INTENT";
+
+ private static final int MSG_SERVICE_COMMAND = 1;
+ private static final int MSG_POLL = 2;
+
+ /** Polling frequency, in milliseconds. */
+ private static final long POLLING_FREQUENCY = 500;
+
+ /** How long (in ms) a dumpstate process will be monitored if it didn't show progress. */
+ private static final long INACTIVITY_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
+
+ /** System property used for monitoring progress. */
+ private static final String PROGRESS_PROPERTY_TEMPLATE = "dumpstate.%d.progress";
+
+ /** Managed dumpstate processes (keyed by pid) */
+ private final SparseArray<BugreportInfo> mProcesses = new SparseArray<>();
+
+ private Looper mServiceLooper;
+ private ServiceHandler mServiceHandler;
+
+ @Override
+ public void onCreate() {
+ HandlerThread thread = new HandlerThread("BugreportProgressServiceThread",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+
+ mServiceLooper = thread.getLooper();
+ mServiceHandler = new ServiceHandler(mServiceLooper);
+ }
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
- onBugreportFinished(intent);
+ // Handle it in a separate thread.
+ Message msg = mServiceHandler.obtainMessage();
+ msg.what = MSG_SERVICE_COMMAND;
+ msg.obj = intent;
+ mServiceHandler.sendMessage(msg);
}
+
+ // If service is killed it cannot be recreated because it would not know which
+ // dumpstate PIDs it would have to watch.
return START_NOT_STICKY;
}
@@ -73,26 +154,249 @@
return null;
}
- private void onBugreportFinished(Intent intent) {
- final Context context = getApplicationContext();
- final Configuration conf = context.getResources().getConfiguration();
- final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
- final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
+ @Override
+ public void onDestroy() {
+ mServiceLooper.quit();
+ super.onDestroy();
+ }
- if ((conf.uiMode & Configuration.UI_MODE_TYPE_MASK) != Configuration.UI_MODE_TYPE_WATCH) {
- triggerLocalNotification(context, bugreportFile, screenshotFile);
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.printf("Monitored dumpstate processes: \n");
+ synchronized (mProcesses) {
+ for (int i = 0; i < mProcesses.size(); i++) {
+ writer.printf("\t%s\n", mProcesses.valueAt(i));
+ }
}
- stopSelf();
+ }
+
+ private final class ServiceHandler extends Handler {
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ pollProgress();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_POLL) {
+ pollProgress();
+ return;
+ }
+
+ if (msg.what != MSG_SERVICE_COMMAND) {
+ // Sanity check.
+ Log.e(TAG, "Invalid message type: " + msg.what);
+ return;
+ }
+
+ // At this point it's handling onStartCommand(), whose intent contains the extras
+ // originally received by BugreportReceiver.
+ if (!(msg.obj instanceof Intent)) {
+ // Sanity check.
+ Log.e(TAG, "Internal error: invalid msg.obj: " + msg.obj);
+ return;
+ }
+ final Parcelable parcel = ((Intent) msg.obj).getParcelableExtra(EXTRA_ORIGINAL_INTENT);
+ if (!(parcel instanceof Intent)) {
+ // Sanity check.
+ Log.e(TAG, "Internal error: msg.obj is missing extra " + EXTRA_ORIGINAL_INTENT);
+ return;
+ }
+
+ final Intent intent = (Intent) parcel;
+ final String action = intent.getAction();
+ int pid = intent.getIntExtra(EXTRA_PID, 0);
+ int max = intent.getIntExtra(EXTRA_MAX, -1);
+ String name = intent.getStringExtra(EXTRA_NAME);
+
+ if (DEBUG) Log.v(TAG, "action: " + action + ", name: " + name + ", pid: " + pid
+ + ", max: "+ max);
+ switch (action) {
+ case INTENT_BUGREPORT_STARTED:
+ if (!startProgress(name, pid, max)) {
+ stopSelfWhenDone();
+ return;
+ }
+ break;
+ case INTENT_BUGREPORT_FINISHED:
+ if (pid == -1) {
+ // Shouldn't happen, unless BUGREPORT_FINISHED is received from a legacy,
+ // out-of-sync dumpstate process.
+ Log.w(TAG, "Missing " + EXTRA_PID + " on intent " + intent);
+ }
+ stopProgress(pid, intent);
+ break;
+ default:
+ Log.w(TAG, "Unsupported intent: " + action);
+ }
+ return;
+
+ }
+
+ /**
+ * Creates the {@link BugreportInfo} for a process and issue a system notification to
+ * indicate its progress.
+ *
+ * @return whether it succeeded or not.
+ */
+ private boolean startProgress(String name, int pid, int max) {
+ if (name == null) {
+ Log.w(TAG, "Missing " + EXTRA_NAME + " on start intent");
+ name = "N/A";
+ }
+ if (pid == -1) {
+ Log.e(TAG, "Missing " + EXTRA_PID + " on start intent");
+ return false;
+ }
+ if (max <= 0) {
+ Log.e(TAG, "Invalid value for extra " + EXTRA_MAX + ": " + max);
+ return false;
+ }
+
+ final BugreportInfo info = new BugreportInfo(pid, name, max);
+ synchronized (mProcesses) {
+ if (mProcesses.indexOfKey(pid) >= 0) {
+ Log.w(TAG, "PID " + pid + " already watched");
+ } else {
+ mProcesses.put(info.pid, info);
+ }
+ }
+ updateProgress(info);
+ return true;
+ }
+
+ /**
+ * Updates the system notification for a given bug report.
+ */
+ private void updateProgress(BugreportInfo info) {
+ if (info.max <= 0 || info.progress < 0 || info.name == null) {
+ Log.e(TAG, "Invalid progress values for " + info);
+ return;
+ }
+
+ final Context context = getApplicationContext();
+ final NumberFormat nf = NumberFormat.getPercentInstance();
+ nf.setMinimumFractionDigits(2);
+ nf.setMaximumFractionDigits(2);
+ final String percentText = nf.format((double) info.progress / info.max);
+
+ final String title = context.getString(R.string.bugreport_in_progress_title);
+ final Notification notification = new Notification.Builder(context)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setContentTitle(title)
+ .setTicker(title)
+ .setContentText(info.name)
+ .setContentInfo(percentText)
+ .setProgress(info.max, info.progress, false)
+ // TODO: .setOngoing(true) once it has a CANCEL action
+ .setLocalOnly(true)
+ .setColor(context.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .build();
+
+ NotificationManager.from(context).notify(TAG, info.pid, notification);
+ }
+
+ /**
+ * Finalizes the progress on a given process and sends the finished intent.
+ */
+ private void stopProgress(int pid, Intent intent) {
+ synchronized (mProcesses) {
+ if (mProcesses.indexOfKey(pid) < 0) {
+ Log.w(TAG, "PID not watched: " + pid);
+ } else {
+ mProcesses.remove(pid);
+ }
+ stopSelfWhenDone();
+ }
+ if (DEBUG) Log.v(TAG, "stopProgress(" + pid + "): cancel notification");
+ NotificationManager.from(getApplicationContext()).cancel(TAG, pid);
+ if (intent != null) {
+ // Bug report finished fine: send a new, different notification.
+ if (DEBUG) Log.v(TAG, "stopProgress(" + pid + "): finish bug report");
+ onBugreportFinished(pid, intent);
+ }
+ }
+
+ /**
+ * Poll {@link SystemProperties} to get the progress on each monitored process.
+ */
+ private void pollProgress() {
+ synchronized (mProcesses) {
+ if (mProcesses.size() == 0) {
+ Log.d(TAG, "No process to poll progress.");
+ }
+ for (int i = 0; i < mProcesses.size(); i++) {
+ int pid = mProcesses.keyAt(i);
+ String property = String.format(PROGRESS_PROPERTY_TEMPLATE, pid);
+ int progress;
+ try {
+ progress = SystemProperties.getInt(property, 0);
+ } catch (IllegalArgumentException e) {
+ Log.v(TAG, "Could not read system property " + property, e);
+ continue;
+ }
+ if (progress == 0) {
+ Log.v(TAG, "System property " + property + " is not set yet");
+ continue;
+ }
+
+ BugreportInfo info = mProcesses.valueAt(i);
+
+ if (progress != info.progress) {
+ if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + " from "
+ + info.progress + " to " + progress);
+ info.progress = progress;
+ info.lastUpdate = System.currentTimeMillis();
+ updateProgress(info);
+ } else {
+ long inactiveTime = System.currentTimeMillis() - info.lastUpdate;
+ if (inactiveTime >= INACTIVITY_TIMEOUT) {
+ Log.w(TAG, "No progress update for process " + pid + " since "
+ + info.getFormattedLastUpdate());
+ stopProgress(info.pid, null);
+ }
+ }
+ }
+ // Keep polling...
+ sendEmptyMessageDelayed(MSG_POLL, POLLING_FREQUENCY);
+ }
+ }
+
+ /**
+ * Finishes the service when it's not monitoring any more processes.
+ */
+ private void stopSelfWhenDone() {
+ synchronized (mProcesses) {
+ if (mProcesses.size() > 0) {
+ if (DEBUG) Log.v(TAG, "Staying alive, waiting for pids " + mProcesses);
+ return;
+ }
+ Log.v(TAG, "No more pids to handle, shutting down");
+ stopSelf();
+ }
+ }
+
+ private void onBugreportFinished(int pid, Intent intent) {
+ final Context context = getApplicationContext();
+ final Configuration conf = context.getResources().getConfiguration();
+ final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
+ final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
+
+ if ((conf.uiMode & Configuration.UI_MODE_TYPE_MASK) != Configuration.UI_MODE_TYPE_WATCH) {
+ triggerLocalNotification(context, pid, bugreportFile, screenshotFile);
+ }
+ }
}
/**
- * Responsible for triggering a notification that allows the user to start a
- * "share" intent with the bug report. On watches we have other methods to allow the user to
- * start this intent (usually by triggering it on another connected device); we don't need to
- * display the notification in this case.
+ * Responsible for triggering a notification that allows the user to start a "share" intent with
+ * the bug report. On watches we have other methods to allow the user to start this intent
+ * (usually by triggering it on another connected device); we don't need to display the
+ * notification in this case.
*/
- private static void triggerLocalNotification(final Context context, final File bugreportFile,
- final File screenshotFile) {
+ private static void triggerLocalNotification(final Context context, final int pid,
+ final File bugreportFile, final File screenshotFile) {
if (!bugreportFile.exists() || !bugreportFile.canRead()) {
Log.e(TAG, "Could not read bugreport file " + bugreportFile);
Toast.makeText(context, context.getString(R.string.bugreport_unreadable_text),
@@ -103,10 +407,10 @@
boolean isPlainText = bugreportFile.getName().toLowerCase().endsWith(".txt");
if (!isPlainText) {
// Already zipped, send it right away.
- sendBugreportNotification(context, bugreportFile, screenshotFile);
+ sendBugreportNotification(context, pid, bugreportFile, screenshotFile);
} else {
// Asynchronously zip the file first, then send it.
- sendZippedBugreportNotification(context, bugreportFile, screenshotFile);
+ sendZippedBugreportNotification(context, pid, bugreportFile, screenshotFile);
}
}
@@ -155,7 +459,7 @@
/**
* Sends a bugreport notitication.
*/
- private static void sendBugreportNotification(Context context, File bugreportFile,
+ private static void sendBugreportNotification(Context context, int pid, File bugreportFile,
File screenshotFile) {
// Files are kept on private storage, so turn into Uris that we can
// grant temporary permissions for.
@@ -173,10 +477,11 @@
}
notifIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final String title = context.getString(R.string.bugreport_finished_title);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
- .setContentTitle(context.getString(R.string.bugreport_finished_title))
- .setTicker(context.getString(R.string.bugreport_finished_title))
+ .setContentTitle(title)
+ .setTicker(title)
.setContentText(context.getString(R.string.bugreport_finished_text))
.setContentIntent(PendingIntent.getActivity(
context, 0, notifIntent, PendingIntent.FLAG_CANCEL_CURRENT))
@@ -185,19 +490,19 @@
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color));
- NotificationManager.from(context).notify(TAG, 0, builder.build());
+ NotificationManager.from(context).notify(TAG, pid, builder.build());
}
/**
* Sends a zipped bugreport notification.
*/
private static void sendZippedBugreportNotification(final Context context,
- final File bugreportFile, final File screenshotFile) {
+ final int pid, final File bugreportFile, final File screenshotFile) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
File zippedFile = zipBugreport(bugreportFile);
- sendBugreportNotification(context, zippedFile, screenshotFile);
+ sendBugreportNotification(context, pid, zippedFile, screenshotFile);
return null;
}
}.execute();
@@ -213,8 +518,8 @@
Log.v(TAG, "zipping " + bugreportPath + " as " + zippedPath);
File bugreportZippedFile = new File(zippedPath);
try (InputStream is = new FileInputStream(bugreportFile);
- ZipOutputStream zos = new ZipOutputStream(
- new BufferedOutputStream(new FileOutputStream(bugreportZippedFile)))) {
+ ZipOutputStream zos = new ZipOutputStream(
+ new BufferedOutputStream(new FileOutputStream(bugreportZippedFile)))) {
ZipEntry entry = new ZipEntry(bugreportFile.getName());
entry.setTime(bugreportFile.lastModified());
zos.putNextEntry(entry);
@@ -230,8 +535,8 @@
}
return bugreportZippedFile;
} catch (IOException e) {
- Log.e(TAG, "exception zipping file " + zippedPath, e);
- return bugreportFile; // Return original.
+ Log.e(TAG, "exception zipping file " + zippedPath, e);
+ return bugreportFile; // Return original.
}
}
@@ -281,4 +586,55 @@
return null;
}
}
+
+ /**
+ * Information about a bug report process while its in progress.
+ */
+ private static final class BugreportInfo {
+ /**
+ * {@code pid} of the {@code dumpstate} process generating the bug report.
+ */
+ final int pid;
+
+ /**
+ * Name of the bug report, will be used to rename the final files.
+ * <p>
+ * Initial value is the bug report filename reported by {@code dumpstate}, but user can
+ * change it later to a more meaningful name.
+ */
+ final String name;
+
+ /**
+ * Maximum progress of the bug report generation.
+ */
+ final int max;
+
+ /**
+ * Current progress of the bug report generation.
+ */
+ int progress;
+
+ /**
+ * Time of the last progress update.
+ */
+ long lastUpdate = System.currentTimeMillis();
+
+ BugreportInfo(int pid, String name, int max) {
+ this.pid = pid;
+ this.name = name;
+ this.max = max;
+ }
+
+ String getFormattedLastUpdate() {
+ return SimpleDateFormat.getDateTimeInstance().format(new Date(lastUpdate));
+ }
+
+ @Override
+ public String toString() {
+ final float percent = ((float) progress * 100 / max);
+ return String.format("Progress for %s (pid=%d): %d/%d (%2.2f%%) Last update: %s", name,
+ pid, progress, max, percent,
+ getFormattedLastUpdate());
+ }
+ }
}
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index f1da14d..5133162 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -17,6 +17,8 @@
package com.android.shell;
import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
+import static com.android.shell.BugreportProgressService.EXTRA_ORIGINAL_INTENT;
+import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
import static com.android.shell.BugreportProgressService.getFileExtra;
import java.io.File;
@@ -50,13 +52,16 @@
// Clean up older bugreports in background
cleanupOldFiles(intent);
- // Delegate to service.
+ // Delegate intent handling to service.
Intent serviceIntent = new Intent(context, BugreportProgressService.class);
- serviceIntent.putExtras(intent.getExtras());
+ serviceIntent.putExtra(EXTRA_ORIGINAL_INTENT, intent);
context.startService(serviceIntent);
}
private void cleanupOldFiles(Intent intent) {
+ if (!INTENT_BUGREPORT_FINISHED.equals(intent.getAction())) {
+ return;
+ }
final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
final PendingResult result = goAsync();
new AsyncTask<Void, Void, Void>() {
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 1bdd9dd..33c4ef1 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -19,7 +19,12 @@
import static android.test.MoreAsserts.assertContainsRegex;
import static com.android.shell.ActionSendMultipleConsumerActivity.UI_NAME;
import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
+import static com.android.shell.BugreportProgressService.EXTRA_MAX;
+import static com.android.shell.BugreportProgressService.EXTRA_NAME;
+import static com.android.shell.BugreportProgressService.EXTRA_PID;
import static com.android.shell.BugreportProgressService.EXTRA_SCREENSHOT;
+import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
+import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_STARTED;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
@@ -96,6 +101,33 @@
cancelExistingNotifications();
}
+ public void testFullWorkflow() throws Exception {
+ final String name = "BUG, Y U NO REPORT?";
+ // TODO: call method to remove property instead
+ SystemProperties.set("dumpstate.42.progress", "-1");
+
+ Intent intent = new Intent(INTENT_BUGREPORT_STARTED);
+ intent.putExtra(EXTRA_PID, 42);
+ intent.putExtra(EXTRA_NAME, name);
+ intent.putExtra(EXTRA_MAX, 1000);
+ mContext.sendBroadcast(intent);
+
+ assertProgressNotification(name, "0.00%");
+
+ SystemProperties.set("dumpstate.42.progress", "108");
+ assertProgressNotification(name, "10.80%");
+
+ SystemProperties.set("dumpstate.42.progress", "500");
+ assertProgressNotification(name, "50.00%");
+
+ createTextFile(PLAIN_TEXT_PATH, BUGREPORT_CONTENT);
+ createTextFile(SCREENSHOT_PATH, SCREENSHOT_CONTENT);
+ Bundle extras = sendBugreportFinishedIntent(42, PLAIN_TEXT_PATH, SCREENSHOT_PATH);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT);
+
+ // TODO: assert service is down
+ }
+
public void testBugreportFinished_plainBugreportAndScreenshot() throws Exception {
createTextFile(PLAIN_TEXT_PATH, BUGREPORT_CONTENT);
createTextFile(SCREENSHOT_PATH, SCREENSHOT_CONTENT);
@@ -131,13 +163,32 @@
}
}
+ private void assertProgressNotification(String name, String percent) {
+ // TODO: it current looks for 3 distinct objects, without taking advantage of their
+ // relationship.
+ String title = mContext.getString(R.string.bugreport_in_progress_title);
+ Log.v(TAG, "Looking for progress notification title: '" + title+ "'");
+ mUiBot.getNotification(title);
+ Log.v(TAG, "Looking for progress notification details: '" + name + "-" + percent + "'");
+ mUiBot.getObject(name);
+ mUiBot.getObject(percent);
+ }
+
/**
* Sends a "bugreport finished" intent and waits for the result.
*
* @return extras sent to the bugreport finished consumer.
*/
private Bundle sendBugreportFinishedIntent(String bugreportPath, String screenshotPath) {
- Intent intent = new Intent("android.intent.action.BUGREPORT_FINISHED");
+ return sendBugreportFinishedIntent(null, bugreportPath, screenshotPath);
+ }
+
+ private Bundle sendBugreportFinishedIntent(Integer pid, String bugreportPath,
+ String screenshotPath) {
+ Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
+ if (pid != null) {
+ intent.putExtra(EXTRA_PID, pid);
+ }
if (bugreportPath != null) {
intent.putExtra(EXTRA_BUGREPORT, bugreportPath);
}
diff --git a/packages/Shell/tests/src/com/android/shell/UiBot.java b/packages/Shell/tests/src/com/android/shell/UiBot.java
index f5dd31c..fa1714e 100644
--- a/packages/Shell/tests/src/com/android/shell/UiBot.java
+++ b/packages/Shell/tests/src/com/android/shell/UiBot.java
@@ -42,32 +42,49 @@
}
/**
- * Opens the system notification and clicks a given notification.
+ * Opens the system notification and gets a given notification.
*
* @param text Notificaton's text as displayed by the UI.
+ * @return notification object.
*/
- public void clickOnNotification(String text) {
+ public UiObject getNotification(String text) {
boolean opened = mDevice.openNotification();
Log.v(TAG, "openNotification(): " + opened);
boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGED)), mTimeout);
assertTrue("could not get system ui (" + SYSTEMUI_PACKAGED + ")", gotIt);
- gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout);
- assertTrue("object with text '(" + text + "') not visible yet", gotIt);
+ return getObject(text);
+ }
- UiObject notification = getVisibleObject(text);
-
+ /**
+ * Opens the system notification and clicks a given notification.
+ *
+ * @param text Notificaton's text as displayed by the UI.
+ */
+ public void clickOnNotification(String text) {
+ UiObject notification = getNotification(text);
click(notification, "bug report notification");
}
/**
- * Gets an object which is guaranteed to be present in the current UI.\
+ * Gets an object that might not yet be available in current UI.
+ *
+ * @param text Object's text as displayed by the UI.
+ */
+ public UiObject getObject(String text) {
+ boolean gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout);
+ assertTrue("object with text '(" + text + "') not visible yet", gotIt);
+ return getVisibleObject(text);
+ }
+
+ /**
+ * Gets an object which is guaranteed to be present in the current UI.
*
* @param text Object's text as displayed by the UI.
*/
public UiObject getVisibleObject(String text) {
UiObject uiObject = mDevice.findObject(new UiSelector().text(text));
- assertTrue("could not find object with text '(" + text + "')", uiObject.exists());
+ assertTrue("could not find object with text '" + text + "'", uiObject.exists());
return uiObject;
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6fda2c6..2f79adf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -87,6 +87,7 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+ <uses-permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS" />
<uses-permission android:name="android.permission.SET_ORIENTATION" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c85ada8..82192fe 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -214,9 +214,6 @@
<!-- The amount to offset when animating into an affiliate group. -->
<dimen name="recents_task_view_affiliate_group_enter_offset">64dp</dimen>
- <!-- The alpha to apply to a task thumbnail. -->
- <item name="recents_task_view_thumbnail_alpha" format="float" type="dimen">0.9</item>
-
<!-- The height of a task view bar. -->
<dimen name="recents_task_bar_height">56dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 94c45a4..a0c481a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -724,18 +724,6 @@
getResizeTaskDebugDialog().showResizeTaskDialog(event.task, mRecentsView);
}
- public final void onBusEvent(DragStartEvent event) {
- // Lock the orientation while dragging
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
-
- // TODO: docking requires custom accessibility actions
- }
-
- public final void onBusEvent(DragEndEvent event) {
- // Unlock the orientation when dragging completes
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND);
- }
-
public final void onBusEvent(LaunchTaskSucceededEvent event) {
MetricsLogger.histogram(this, "overview_task_launch_index", event.taskIndexFromStackFront);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
index 957da94..3deeb47 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
@@ -19,7 +19,6 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.views.DragView;
import com.android.systemui.recents.views.DropTarget;
import com.android.systemui.recents.views.TaskView;
@@ -30,15 +29,13 @@
public final Task task;
public final TaskView taskView;
- public final DragView dragView;
public final DropTarget dropTarget;
public final ReferenceCountedTrigger postAnimationTrigger;
- public DragEndEvent(Task task, TaskView taskView, DragView dragView, DropTarget dropTarget,
+ public DragEndEvent(Task task, TaskView taskView, DropTarget dropTarget,
ReferenceCountedTrigger postAnimationTrigger) {
this.task = task;
this.taskView = taskView;
- this.dragView = dragView;
this.dropTarget = dropTarget;
this.postAnimationTrigger = postAnimationTrigger;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
index 2d42a0e..b81c10c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
@@ -16,9 +16,9 @@
package com.android.systemui.recents.events.ui.dragndrop;
+import android.graphics.Point;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.views.DragView;
import com.android.systemui.recents.views.TaskView;
/**
@@ -28,11 +28,11 @@
public final Task task;
public final TaskView taskView;
- public final DragView dragView;
+ public final Point tlOffset;
- public DragStartEvent(Task task, TaskView taskView, DragView dragView) {
+ public DragStartEvent(Task task, TaskView taskView, Point tlOffset) {
this.task = task;
this.taskView = taskView;
- this.dragView = dragView;
+ this.tlOffset = tlOffset;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index de40a37..2b20c07 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -56,6 +56,7 @@
import android.util.MutableBoolean;
import android.util.Pair;
import android.view.Display;
+import android.view.IDockDividerVisibilityListener;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
@@ -409,7 +410,7 @@
return thumbnail;
}
- Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
+ Bitmap thumbnail = getThumbnail(taskId);
if (thumbnail != null) {
thumbnail.setHasAlpha(false);
// We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
@@ -429,8 +430,12 @@
/**
* Returns a task thumbnail from the activity manager
*/
- public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
- ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
+ public Bitmap getThumbnail(int taskId) {
+ if (mAm == null) {
+ return null;
+ }
+
+ ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
if (taskThumbnail == null) return null;
Bitmap thumbnail = taskThumbnail.mainThumbnail;
@@ -847,4 +852,15 @@
e.printStackTrace();
}
}
+
+ public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+ if (mWm == null) return;
+
+ try {
+ WindowManagerGlobal.getWindowManagerService().registerDockDividerVisibilityListener(
+ listener);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 5921d13..3bb89a3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -128,7 +128,7 @@
boolean isStackTask = true;
if (debugFlags.isHistoryEnabled()) {
boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
- isStackTask = !isFreeformTask && (!isHistoricalTask(t) ||
+ isStackTask = isFreeformTask || (!isHistoricalTask(t) ||
(t.lastActiveTime >= lastStackActiveTime &&
i >= (taskCount - MIN_NUM_TASKS)));
if (isStackTask && newLastStackActiveTime < 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index 945fdc1..271a2a0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -16,7 +16,6 @@
package com.android.systemui.recents.views;
-import android.animation.ObjectAnimator;
import android.graphics.Outline;
import android.graphics.Rect;
import android.util.IntProperty;
@@ -65,6 +64,15 @@
mCornerRadius = cornerRadius;
}
+ /**
+ * Resets the right and bottom clip for this view.
+ */
+ public void reset() {
+ mClipRect.setEmpty();
+ mSourceView.invalidateOutline();
+ updateClipBounds();
+ }
+
@Override
public void getOutline(View view, Outline outline) {
outline.setAlpha(mMinAlpha + mAlpha / (1f - mMinAlpha));
@@ -82,15 +90,6 @@
}
}
- /**
- * Animates the bottom clip.
- */
- public void animateClipBottom(int bottom) {
- ObjectAnimator animator = ObjectAnimator.ofInt(this, CLIP_BOTTOM, getClipBottom(), bottom);
- animator.setDuration(150);
- animator.start();
- }
-
/** Sets the bottom clip. */
public void setClipBottom(int bottom, boolean force) {
if (bottom != mClipRect.bottom || force) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java b/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java
deleted file mode 100644
index 96dfaac..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.widget.ImageView;
-
-public class DragView extends ImageView {
-
- // The offset from the top-left of the dragBitmap
- Point mTopLeftOffset;
-
- public DragView(Context context, Bitmap dragBitmap, Point tlOffset) {
- super(context);
-
- mTopLeftOffset = tlOffset;
- setImageBitmap(dragBitmap);
- }
-
- public Point getTopLeftOffset() {
- return mTopLeftOffset;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
index 90d62c1..a70b66d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.views;
+import android.graphics.Rect;
import android.util.Log;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
@@ -52,8 +53,8 @@
int numFreeformTasks = stackLayout.mNumFreeformTasks;
if (!freeformTasks.isEmpty()) {
// Calculate the cell width/height depending on the number of freeform tasks
- mFreeformCellXCount = Math.max(2, (int) Math.ceil(Math.sqrt(numFreeformTasks)));
- mFreeformCellYCount = Math.max(2, (int) Math.ceil((float) numFreeformTasks /
+ mFreeformCellXCount = Math.max(1, (int) Math.ceil(Math.sqrt(numFreeformTasks)));
+ mFreeformCellYCount = Math.max(1, (int) Math.ceil((float) numFreeformTasks /
mFreeformCellXCount));
// For now, make the cells square
mFreeformCellWidth = Math.min(stackLayout.mFreeformRect.width() / mFreeformCellXCount,
@@ -94,15 +95,21 @@
public TaskViewTransform getTransform(Task task, TaskViewTransform transformOut,
TaskStackLayoutAlgorithm stackLayout) {
if (mTaskIndexMap.containsKey(task.key)) {
- // This is a freeform task, so lay it out in the freeform workspace
+ Rect taskRect = stackLayout.mTaskRect;
int taskIndex = mTaskIndexMap.get(task.key);
- int topOffset = (stackLayout.mFreeformRect.top - stackLayout.mTaskRect.top);
+ int topOffset = (stackLayout.mFreeformRect.top - taskRect.top);
int x = taskIndex % mFreeformCellXCount;
int y = taskIndex / mFreeformCellXCount;
- float scale = (float) mFreeformCellWidth / stackLayout.mTaskRect.width();
- int scaleXOffset = (int) (((1f - scale) * stackLayout.mTaskRect.width()) / 2);
- int scaleYOffset = (int) (((1f - scale) * stackLayout.mTaskRect.height()) / 2);
- transformOut.scale = scale * 0.9f;
+
+ int bitmapWidth = task.thumbnail.getWidth();
+ int bitmapHeight = task.thumbnail.getHeight();
+ float thumbnailScale = Math.min((float) mFreeformCellWidth / bitmapWidth,
+ (float) mFreeformCellHeight / bitmapHeight);
+ float thumbnailWidth = bitmapWidth * thumbnailScale;
+ float thumbnailHeight = bitmapHeight * thumbnailScale;
+ int scaleXOffset = (int) (((1f - thumbnailScale) * thumbnailWidth) / 2);
+ int scaleYOffset = (int) (((1f - thumbnailScale) * thumbnailHeight) / 2);
+ transformOut.scale = thumbnailScale * 0.9f;
transformOut.translationX = x * mFreeformCellWidth - scaleXOffset;
transformOut.translationY = topOffset + y * mFreeformCellHeight - scaleYOffset;
transformOut.translationZ = stackLayout.mMaxTranslationZ;
@@ -115,7 +122,6 @@
if (DEBUG) {
Log.d(TAG, "getTransform: " + task.key + ", " + transformOut);
}
-
return transformOut;
}
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 0557f65..5616018 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -81,7 +81,6 @@
RecentsTransitionHelper mTransitionHelper;
RecentsViewTouchHandler mTouchHandler;
- DragView mDragView;
TaskStack.DockState[] mVisibleDockStates = {
TaskStack.DockState.LEFT,
TaskStack.DockState.TOP,
@@ -341,13 +340,6 @@
mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
}
- if (mDragView != null) {
- Rect taskRect = mTaskStackView.mLayoutAlgorithm.mTaskRect;
- mDragView.measure(
- MeasureSpec.makeMeasureSpec(taskRect.width(), MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(taskRect.height(), MeasureSpec.AT_MOST));
- }
-
// Measure the history button with the full space above the stack, but width-constrained
// to the stack
Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
@@ -379,11 +371,6 @@
mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
}
- if (mDragView != null) {
- mDragView.layout(left, top, left + mDragView.getMeasuredWidth(),
- top + mDragView.getMeasuredHeight());
- }
-
// Layout the history button left-aligned with the stack, but offset from the top of the
// view
Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
@@ -461,10 +448,6 @@
/**** EventBus Events ****/
public final void onBusEvent(DragStartEvent event) {
- // Add the drag view
- mDragView = event.dragView;
- addView(mDragView);
-
updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
TaskStack.DockState.NONE.viewState.dockAreaAlpha);
}
@@ -480,65 +463,17 @@
}
public final void onBusEvent(final DragEndEvent event) {
- final Runnable cleanUpRunnable = new Runnable() {
- @Override
- public void run() {
- // Remove the drag view
- removeView(mDragView);
- mDragView = null;
- }
- };
-
// Animate the overlay alpha back to 0
updateVisibleDockRegions(null, -1);
- if (event.dropTarget == null) {
- // No drop targets for hit, so just animate the task back to its place
- event.postAnimationTrigger.increment();
- event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- cleanUpRunnable.run();
- }
- });
- // Animate the task back to where it was before then clean up afterwards
- TaskViewTransform taskTransform = new TaskViewTransform();
- TaskStackLayoutAlgorithm layoutAlgorithm = mTaskStackView.getStackAlgorithm();
- layoutAlgorithm.getStackTransform(event.task,
- mTaskStackView.getScroller().getStackScroll(), taskTransform, null);
- event.dragView.animate()
- .scaleX(taskTransform.scale)
- .scaleY(taskTransform.scale)
- .translationX((layoutAlgorithm.mTaskRect.left - event.dragView.getLeft())
- + taskTransform.translationX)
- .translationY((layoutAlgorithm.mTaskRect.top - event.dragView.getTop())
- + taskTransform.translationY)
- .setDuration(175)
- .setInterpolator(mFastOutSlowInInterpolator)
- .withEndAction(event.postAnimationTrigger.decrementAsRunnable())
- .start();
-
- } else if (event.dropTarget instanceof TaskStack.DockState) {
+ // Handle the case where we drop onto a dock region
+ if (event.dropTarget instanceof TaskStack.DockState) {
final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
- // For now, just remove the drag view and the original task
- // TODO: Animate the task to the drop target rect before launching it above
- cleanUpRunnable.run();
-
// Dock the task and launch it
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode);
launchTask(event.task, null, INVALID_STACK_ID);
-
- } else {
- // We dropped on another drop target, so just add the cleanup to the post animation
- // trigger
- event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- cleanUpRunnable.run();
- }
- });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index c7edc92..c55f383 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -64,8 +64,8 @@
private Task mDragTask;
private TaskView mTaskView;
- private DragView mDragView;
+ private Point mTaskViewOffset = new Point();
private Point mDownPos = new Point();
private boolean mDragging;
@@ -115,13 +115,16 @@
mDragging = true;
mDragTask = event.task;
mTaskView = event.taskView;
- mDragView = event.dragView;
mDropTargets.clear();
- float x = mDownPos.x - mDragView.getTopLeftOffset().x;
- float y = mDownPos.y - mDragView.getTopLeftOffset().y;
- mDragView.setTranslationX(x);
- mDragView.setTranslationY(y);
+ int[] recentsViewLocation = new int[2];
+ mRv.getLocationInWindow(recentsViewLocation);
+ mTaskViewOffset.set(mTaskView.getLeft() - recentsViewLocation[0] + event.tlOffset.x,
+ mTaskView.getTop() - recentsViewLocation[1] + event.tlOffset.y);
+ float x = mDownPos.x - mTaskViewOffset.x;
+ float y = mDownPos.y - mTaskViewOffset.y;
+ mTaskView.setTranslationX(x);
+ mTaskView.setTranslationY(y);
RecentsConfiguration config = Recents.getConfiguration();
if (!config.hasDockedTasks) {
@@ -140,7 +143,6 @@
mDragging = false;
mDragTask = null;
mTaskView = null;
- mDragView = null;
mLastDropTarget = null;
}
@@ -160,8 +162,8 @@
int height = mRv.getMeasuredHeight();
float evX = ev.getX();
float evY = ev.getY();
- float x = evX - mDragView.getTopLeftOffset().x;
- float y = evY - mDragView.getTopLeftOffset().y;
+ float x = evX - mTaskViewOffset.x;
+ float y = evY - mTaskViewOffset.y;
DropTarget currentDropTarget = null;
for (DropTarget target : mDropTargets) {
@@ -176,8 +178,8 @@
currentDropTarget));
}
- mDragView.setTranslationX(x);
- mDragView.setTranslationY(y);
+ mTaskView.setTranslationX(x);
+ mTaskView.setTranslationY(y);
}
break;
}
@@ -187,7 +189,7 @@
ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(
mRv.getContext(), null, null, null);
postAnimationTrigger.increment();
- EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView, mDragView,
+ EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
mLastDropTarget, postAnimationTrigger));
postAnimationTrigger.decrement();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 30efd5f..c02eaf8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -16,6 +16,10 @@
package com.android.systemui.recents.views;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.ComponentName;
import android.content.Context;
@@ -1420,7 +1424,12 @@
}
}
+ private AnimatorSet mDropAnimation;
+
public final void onBusEvent(DragStartEvent event) {
+ // Cancel the existing drop animation
+ Utilities.cancelAnimationWithoutCallbacks(mDropAnimation);
+
if (event.task.isFreeformTask()) {
// Animate to the front of the stack
mStackScroller.animateScroll(mStackScroller.getStackScroll(),
@@ -1441,56 +1450,84 @@
}
public final void onBusEvent(final DragEndEvent event) {
- if (event.dropTarget != mFreeformWorkspaceDropTarget &&
- event.dropTarget != mStackDropTarget) {
- return;
- }
- if (event.task.isFreeformTask() && event.dropTarget == mFreeformWorkspaceDropTarget) {
- // TODO: Animate back into view
- return;
- }
- if (!event.task.isFreeformTask() && event.dropTarget == mStackDropTarget) {
- // TODO: Animate back into view
+ // We don't handle drops on the dock regions
+ if (event.dropTarget instanceof TaskStack.DockState) {
return;
}
- // Move the task to the right position in the stack (ie. the front of the stack if freeform
- // or the front of the stack if fullscreen). Note, we MUST move the tasks before we update
- // their stack ids, otherwise, the keys will have changed.
- if (event.dropTarget == mFreeformWorkspaceDropTarget) {
- mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
- updateLayout(true);
- } else if (event.dropTarget == mStackDropTarget) {
- mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
- updateLayout(true);
+ boolean isFreeformTask = event.task.isFreeformTask();
+ boolean hasChangedStacks =
+ (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
+ (isFreeformTask && event.dropTarget == mStackDropTarget);
+ if (hasChangedStacks) {
+ ArrayList<Animator> animations = new ArrayList<>();
+
+ // Move the task to the right position in the stack (ie. the front of the stack if
+ // freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
+ // before we update their stack ids, otherwise, the keys will have changed.
+ if (event.dropTarget == mFreeformWorkspaceDropTarget) {
+ mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
+ updateLayout(true);
+
+ // Update the clipping to match the scaled bitmap rect
+ TaskViewThumbnail thumbnailView = event.taskView.mThumbnailView;
+ float thumbnailScale = thumbnailView.computeThumbnailScale(true);
+ RectF bitmapRect = thumbnailView.getScaledBitmapRect(thumbnailScale);
+ AnimateableViewBounds viewBounds = event.taskView.getViewBounds();
+ int clipRight = (int) (thumbnailView.getMeasuredWidth() - bitmapRect.width());
+ int clipBottom = (int) (thumbnailView.getMeasuredHeight() - bitmapRect.height());
+ animations.add(ObjectAnimator.ofFloat(thumbnailView, TaskViewThumbnail.BITMAP_SCALE,
+ thumbnailView.getBitmapScale(), thumbnailScale));
+ animations.add(ObjectAnimator.ofInt(viewBounds, AnimateableViewBounds.CLIP_BOTTOM,
+ viewBounds.getClipBottom(), clipBottom));
+ animations.add(ObjectAnimator.ofInt(viewBounds, AnimateableViewBounds.CLIP_RIGHT,
+ viewBounds.getClipRight(), clipRight));
+ } else if (event.dropTarget == mStackDropTarget) {
+ mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
+ updateLayout(true);
+
+ // Reset the clipping when animating to the stack
+ TaskViewThumbnail thumbnailView = event.taskView.mThumbnailView;
+ float thumbnailScale = thumbnailView.computeThumbnailScale(false);
+ AnimateableViewBounds viewBounds = event.taskView.getViewBounds();
+ animations.add(ObjectAnimator.ofFloat(thumbnailView, TaskViewThumbnail.BITMAP_SCALE,
+ thumbnailView.getBitmapScale(), thumbnailScale));
+ animations.add(ObjectAnimator.ofInt(viewBounds, AnimateableViewBounds.CLIP_BOTTOM,
+ viewBounds.getClipBottom(), 0));
+ animations.add(ObjectAnimator.ofInt(viewBounds, AnimateableViewBounds.CLIP_RIGHT,
+ viewBounds.getClipRight(), 0));
+ }
+
+ // Move the task to the new stack in the system after the animation completes
+ event.postAnimationTrigger.increment();
+ event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
+ }
+ });
+
+ // Animate the normal properties of the view
+ mDropAnimation = new AnimatorSet();
+ mDropAnimation.playTogether(animations);
+ mDropAnimation.setDuration(250);
+ mDropAnimation.setInterpolator(mFastOutSlowInInterpolator);
+ mDropAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ event.postAnimationTrigger.decrement();
+ }
+ });
+ mDropAnimation.start();
}
event.postAnimationTrigger.increment();
- event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
- }
- });
+ event.taskView.animate()
+ .withEndAction(event.postAnimationTrigger.decrementAsRunnable());
- // Animate the drag view to the new position
- mLayoutAlgorithm.getStackTransform(event.task, mStackScroller.getStackScroll(),
- mTmpTransform, null);
- event.dragView.animate()
- .scaleX(mTmpTransform.scale)
- .scaleY(mTmpTransform.scale)
- .translationX((mLayoutAlgorithm.mTaskRect.left - event.dragView.getLeft())
- + mTmpTransform.translationX)
- .translationY((mLayoutAlgorithm.mTaskRect.top - event.dragView.getTop())
- + mTmpTransform.translationY)
- .setDuration(175)
- .setInterpolator(mFastOutSlowInInterpolator)
- .withEndAction(event.postAnimationTrigger.decrementAsRunnable())
- .start();
-
- // Animate the other views into place
- requestSynchronizeStackViewsWithModel(175);
+ // Animate the tack view back into position
+ requestSynchronizeStackViewsWithModel(250);
}
public final void onBusEvent(StackViewScrolledEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 6db2eb9..fb84a22 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,8 +22,6 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
@@ -244,6 +242,8 @@
void resetViewProperties() {
setDim(0);
setLayerType(View.LAYER_TYPE_NONE, null);
+ setVisibility(View.VISIBLE);
+ getViewBounds().reset();
TaskViewTransform.reset(this);
if (mActionButtonView != null) {
mActionButtonView.setScaleX(1f);
@@ -251,18 +251,6 @@
mActionButtonView.setAlpha(1f);
mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
}
- setVisibility(View.VISIBLE);
- }
-
- /**
- * When we are un/filtering, this method will set up the transform that we are animating to,
- * in order to hide the task.
- */
- void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
- // Fade the view out and slide it away
- toTransform.alpha = 0f;
- toTransform.translationY += 200;
- toTransform.translationZ = 0;
}
/** Prepares this task view for the enter-recents animations. This is called earlier in the
@@ -296,8 +284,6 @@
}
// Apply the current dim
setDim(initialDim);
- // Prepare the thumbnail view alpha
- mThumbnailView.prepareEnterRecentsAnimation(isTaskViewLaunchTargetTask);
}
/** Animates this task view as it enters recents */
@@ -425,9 +411,6 @@
R.dimen.recents_task_view_affiliate_group_enter_offset);
if (isLaunchingTask) {
- // Animate the thumbnail alpha back into full opacity for the window animation out
- mThumbnailView.startLaunchTaskAnimation(postAnimRunnable);
-
// Animate the dim
if (mDimAlpha > 0) {
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
@@ -637,7 +620,6 @@
mIsFocused = isFocused;
mIsFocusAnimated = animated;
mHeaderView.onTaskViewFocusChanged(isFocused, animated);
- mThumbnailView.onFocusChanged(isFocused);
if (isFocused) {
if (requestViewFocus && !isFocused()) {
requestFocus();
@@ -746,54 +728,20 @@
// Start listening for drag events
setClipViewInStack(false);
+ // Enlarge the view slightly
final float finalScale = getScaleX() * 1.05f;
- final int width = getWidth();
- final int height = getHeight();
- Bitmap dragBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(dragBitmap);
- mThumbnailView.draw(c);
- mHeaderView.draw(c);
- c.setBitmap(null);
+ animate()
+ .scaleX(finalScale)
+ .scaleY(finalScale)
+ .setDuration(175)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .start();
- // The downTouchPos is relative to the currently transformed TaskView, but we will be
- // dragging a copy of the full task view, which makes it easier for us to animate them
- // when the user drops
- mDownTouchPos.x += ((1f - getScaleX()) * width) / 2;
- mDownTouchPos.y += ((1f - getScaleY()) * height) / 2;
+ mDownTouchPos.x += ((1f - getScaleX()) * getWidth()) / 2;
+ mDownTouchPos.y += ((1f - getScaleY()) * getHeight()) / 2;
- // Initiate the drag
- final DragView dragView = new DragView(getContext(), dragBitmap, mDownTouchPos);
- dragView.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRect(0, 0, width, height);
- }
- });
- dragView.setScaleX(getScaleX());
- dragView.setScaleY(getScaleY());
- dragView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- // Hide this task view after the drag view is attached
- setVisibility(View.INVISIBLE);
- // Animate the alpha slightly to indicate dragging
- dragView.setElevation(getElevation());
- dragView.setTranslationZ(getTranslationZ());
- dragView.animate()
- .scaleX(finalScale)
- .scaleY(finalScale)
- .setDuration(175)
- .setInterpolator(mFastOutSlowInInterpolator)
- .start();
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- // Do nothing
- }
- });
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
- EventBus.getDefault().send(new DragStartEvent(mTask, this, dragView));
+ EventBus.getDefault().send(new DragStartEvent(mTask, this, mDownTouchPos));
return true;
}
return false;
@@ -806,9 +754,6 @@
event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
- // Show this task view
- setVisibility(View.VISIBLE);
-
// Animate the drag view back from where it is, to the view location, then after
// it returns, update the clip state
setClipViewInStack(true);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index b3d263e..c288afb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -16,9 +16,6 @@
package com.android.systemui.recents.views;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
@@ -31,11 +28,12 @@
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Property;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.R;
-import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
@@ -45,6 +43,19 @@
*/
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
@@ -60,18 +71,6 @@
Interpolator mFastOutSlowInInterpolator;
- // Thumbnail alpha
- float mThumbnailAlpha;
- ValueAnimator mThumbnailAlphaAnimator;
- ValueAnimator.AnimatorUpdateListener mThumbnailAlphaUpdateListener
- = new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mThumbnailAlpha = (float) animation.getAnimatedValue();
- updateThumbnailPaintFilter();
- }
- };
-
// Task bar clipping, the top of this thumbnail can be clipped against the opaque header
// bar that overlaps this thumbnail
View mTaskBar;
@@ -105,17 +104,11 @@
}
@Override
- protected void onFinishInflate() {
- mThumbnailAlpha = getResources().getFloat(R.dimen.recents_task_view_thumbnail_alpha);
- updateThumbnailPaintFilter();
- }
-
- @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
mLayoutRect.set(0, 0, getWidth(), getHeight());
- updateThumbnailScale();
+ setBitmapScale(computeThumbnailScale(mTask != null ? mTask.isFreeformTask() : false));
}
}
@@ -137,12 +130,15 @@
Shader.TileMode.CLAMP);
mDrawPaint.setShader(mBitmapShader);
mBitmapRect.set(0, 0, bm.getWidth(), bm.getHeight());
- updateThumbnailScale();
} else {
mBitmapShader = null;
mDrawPaint.setShader(null);
}
- updateThumbnailPaintFilter();
+ if (mTask != null) {
+ setBitmapScale(computeThumbnailScale(mTask != null ? mTask.isFreeformTask() : false));
+ } else {
+ setBitmapScale(1f);
+ }
}
/** Updates the paint to draw the thumbnail. */
@@ -150,36 +146,61 @@
if (mInvisible) {
return;
}
- int mul = (int) ((1.0f - mDimAlpha) * mThumbnailAlpha * 255);
- int add = (int) ((1.0f - mDimAlpha) * (1 - mThumbnailAlpha) * 255);
+ int mul = (int) ((1.0f - mDimAlpha) * 255);
if (mBitmapShader != null) {
mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
- mLightingColorFilter.setColorAdd(Color.argb(0, add, add, add));
mDrawPaint.setColorFilter(mLightingColorFilter);
mDrawPaint.setColor(0xffffffff);
} else {
- int grey = mul + add;
+ int grey = mul;
mDrawPaint.setColorFilter(null);
mDrawPaint.setColor(Color.argb(255, grey, grey, grey));
}
invalidate();
}
- /** Updates the thumbnail shader's scale transform. */
- void updateThumbnailScale() {
- if (mBitmapShader != null) {
- if (mTask.isFreeformTask()) {
- // For freeform tasks, we scale the bitmap rect to fit in the layout rect
- mBitmapScale = Math.min(mLayoutRect.width() / mBitmapRect.width(),
- mLayoutRect.height() / mBitmapRect.height());
- } else {
- // For stack tasks, we scale the bitmap to fit the width
- mBitmapScale = Math.max(1f, mLayoutRect.width() / mBitmapRect.width());
- }
+ /**
+ * Returns the scale to apply to a thumbnail bitmap relative to this view rect.
+ */
+ public float computeThumbnailScale(boolean isFreeformTask) {
+ if (isFreeformTask) {
+ // For freeform tasks, we scale the bitmap rect to fit in the layout rect
+ return Math.min(mLayoutRect.width() / mBitmapRect.width(),
+ mLayoutRect.height() / mBitmapRect.height());
+ } else {
+ // For stack tasks, we scale the bitmap to fit the width
+ return Math.max(1f, mLayoutRect.width() / mBitmapRect.width());
+ }
+ }
+ /**
+ * Returns the scaled bitmap rect.
+ */
+ public RectF getScaledBitmapRect(float scale) {
+ RectF scaledBitmapRect = new RectF(mBitmapRect);
+ scaledBitmapRect.left *= scale;
+ scaledBitmapRect.top *= scale;
+ scaledBitmapRect.right *= scale;
+ scaledBitmapRect.bottom *= scale;
+ return scaledBitmapRect;
+ }
+
+ /**
+ * Sets the scale of the bitmap relative to this view.
+ */
+ public void setBitmapScale(float scale) {
+ if (mBitmapShader != null) {
+ mBitmapScale = scale;
mScaleMatrix.setScale(mBitmapScale, mBitmapScale);
mBitmapShader.setLocalMatrix(mScaleMatrix);
}
+ if (!mInvisible) {
+ invalidate();
+ }
+ }
+
+ public float getBitmapScale() {
+ return mBitmapScale;
}
/** Updates the clip rect based on the given task bar. */
@@ -227,67 +248,4 @@
mTask = null;
setThumbnail(null);
}
-
- /** Handles focus changes. */
- void onFocusChanged(boolean focused) {
- if (focused) {
- if (Float.compare(getAlpha(), 1f) != 0) {
- startFadeAnimation(1f, 150, null);
- }
- } else {
- float taskViewThumbnailAlpha = getResources().getFloat(
- R.dimen.recents_task_view_thumbnail_alpha);
- if (Float.compare(getAlpha(), taskViewThumbnailAlpha) != 0) {
- startFadeAnimation(taskViewThumbnailAlpha, 150, null);
- }
- }
- }
-
- /**
- * Prepares for the enter recents animation, this gets called before the the view
- * is first visible and will be followed by a startEnterRecentsAnimation() call.
- */
- void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask) {
- if (isTaskViewLaunchTargetTask) {
- mThumbnailAlpha = 1f;
- } else {
- mThumbnailAlpha = getResources().getFloat(
- R.dimen.recents_task_view_thumbnail_alpha);
- }
- updateThumbnailPaintFilter();
- }
-
- /** Animates this task thumbnail as it enters Recents. */
- void startEnterRecentsAnimation(Runnable postAnimRunnable) {
- float taskViewThumbnailAlpha = getResources().getFloat(
- R.dimen.recents_task_view_thumbnail_alpha);
- startFadeAnimation(taskViewThumbnailAlpha,
- getResources().getInteger(R.integer.recents_task_enter_from_app_duration),
- postAnimRunnable);
- }
-
- /** Animates this task thumbnail as it exits Recents. */
- void startLaunchTaskAnimation(Runnable postAnimRunnable) {
- int taskViewExitToAppDuration = mContext.getResources().getInteger(
- R.integer.recents_task_exit_to_app_duration);
- startFadeAnimation(1f, taskViewExitToAppDuration, postAnimRunnable);
- }
-
- /** Starts a new thumbnail alpha animation. */
- void startFadeAnimation(float finalAlpha, int duration, final Runnable postAnimRunnable) {
- Utilities.cancelAnimationWithoutCallbacks(mThumbnailAlphaAnimator);
- mThumbnailAlphaAnimator = ValueAnimator.ofFloat(mThumbnailAlpha, finalAlpha);
- mThumbnailAlphaAnimator.setDuration(duration);
- mThumbnailAlphaAnimator.setInterpolator(mFastOutSlowInInterpolator);
- mThumbnailAlphaAnimator.addUpdateListener(mThumbnailAlphaUpdateListener);
- if (postAnimRunnable != null) {
- mThumbnailAlphaAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- postAnimRunnable.run();
- }
- });
- }
- mThumbnailAlphaAnimator.start();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 50e010f..6ff7a3e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -17,10 +17,14 @@
package com.android.systemui.stackdivider;
import android.content.res.Configuration;
+import android.view.IDockDividerVisibilityListener;
import android.view.LayoutInflater;
+import android.view.View;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -33,6 +37,8 @@
private int mDividerWindowWidth;
private DividerWindowManager mWindowManager;
private DividerView mView;
+ private DockDividerVisibilityListener mDockDividerVisibilityListener;
+ private boolean mVisible = false;
@Override
public void start() {
@@ -41,6 +47,9 @@
com.android.internal.R.dimen.docked_stack_divider_thickness);
update(mContext.getResources().getConfiguration());
putComponent(Divider.class, this);
+ mDockDividerVisibilityListener = new DockDividerVisibilityListener();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.registerDockDividerVisibilityListener(mDockDividerVisibilityListener);
}
@Override
@@ -56,6 +65,7 @@
private void addDivider(Configuration configuration) {
mView = (DividerView)
LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
+ mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
final int width = landscape ? mDividerWindowWidth : MATCH_PARENT;
final int height = landscape ? MATCH_PARENT : mDividerWindowWidth;
@@ -71,4 +81,23 @@
removeDivider();
addDivider(configuration);
}
+
+ private void updateVisibility(final boolean visible) {
+ mView.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mVisible != visible) {
+ mVisible = visible;
+ mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+ });
+ }
+
+ class DockDividerVisibilityListener extends IDockDividerVisibilityListener.Stub {
+ @Override
+ public void onDockDividerVisibilityChanged(boolean visible) {
+ updateVisibility(visible);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 93a8fd8..0917528 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -110,7 +110,7 @@
// When enabling location, a user consent dialog will pop up, and the
// setting won't be fully enabled until the user accepts the agreement.
int mode = enabled
- ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY : Settings.Secure.LOCATION_MODE_OFF;
+ ? Settings.Secure.LOCATION_MODE_PREVIOUS : Settings.Secure.LOCATION_MODE_OFF;
// QuickSettings always runs as the owner, so specifically set the settings
// for the current foreground user.
return Settings.Secure
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3d358ec..11fdbb5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -433,7 +433,6 @@
AccessibilityEvent.obtain(event)).sendToTarget();
}
event.recycle();
- getUserStateLocked(resolvedUserId).mHandledFeedbackTypes = 0;
}
return (OWN_PROCESS_ID != Binder.getCallingPid());
}
@@ -1051,9 +1050,7 @@
Service service = state.mBoundServices.get(i);
if (service.mIsDefault == isDefault) {
- if (canDispatchEventToServiceLocked(service, event,
- state.mHandledFeedbackTypes)) {
- state.mHandledFeedbackTypes |= service.mFeedbackType;
+ if (canDispatchEventToServiceLocked(service, event)) {
service.notifyAccessibilityEvent(event);
}
}
@@ -1088,19 +1085,14 @@
/**
* Determines if given event can be dispatched to a service based on the package of the
- * event source and already notified services for that event type. Specifically, a
- * service is notified if it is interested in events from the package and no other service
- * providing the same feedback type has been notified. Exception are services the
- * provide generic feedback (feedback type left as a safety net for unforeseen feedback
- * types) which are always notified.
+ * event source. Specifically, a service is notified if it is interested in events from the
+ * package.
*
* @param service The potential receiver.
* @param event The event.
- * @param handledFeedbackTypes The feedback types for which services have been notified.
* @return True if the listener should be notified, false otherwise.
*/
- private boolean canDispatchEventToServiceLocked(Service service, AccessibilityEvent event,
- int handledFeedbackTypes) {
+ private boolean canDispatchEventToServiceLocked(Service service, AccessibilityEvent event) {
if (!service.canReceiveEventsLocked()) {
return false;
@@ -1121,15 +1113,7 @@
String packageName = (event.getPackageName() != null)
? event.getPackageName().toString() : null;
- if (packageNames.isEmpty() || packageNames.contains(packageName)) {
- int feedbackType = service.mFeedbackType;
- if ((handledFeedbackTypes & feedbackType) != feedbackType
- || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
- return true;
- }
- }
-
- return false;
+ return (packageNames.isEmpty() || packageNames.contains(packageName));
}
private void unbindAllServicesLocked(UserState userState) {
@@ -3886,8 +3870,6 @@
public final Set<ComponentName> mTouchExplorationGrantedServices =
new HashSet<>();
- public int mHandledFeedbackTypes = 0;
-
public int mLastSentClientState = -1;
public boolean mIsAccessibilityEnabled;
@@ -3950,7 +3932,6 @@
mBindingServices.clear();
// Clear event management state.
- mHandledFeedbackTypes = 0;
mLastSentClientState = -1;
// Clear state persisted in settings.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ae6874f..639753a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2533,7 +2533,7 @@
}
}
} else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
- if (transit == TRANSIT_ENTER) {
+ if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
return R.anim.fade_in;
} else if (transit == TRANSIT_EXIT) {
return R.anim.fade_out;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6b62467..df8d5d6 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -18,7 +18,11 @@
import android.content.Context;
import android.graphics.Rect;
+import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.IDockDividerVisibilityListener;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.view.WindowManager.DOCKED_BOTTOM;
@@ -40,6 +44,8 @@
private WindowState mWindow;
private final Rect mTmpRect = new Rect();
private final Rect mLastRect = new Rect();
+ private IDockDividerVisibilityListener mListener;
+ private boolean mLastVisibility = false;
DockedStackDividerController(Context context, DisplayContent displayContent) {
mDisplayContent = displayContent;
@@ -67,12 +73,21 @@
}
void reevaluateVisibility() {
- if (mWindow == null) return;
+ if (mWindow == null) {
+ return;
+ }
TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID);
- if (stack != null && stack.isVisibleLocked()) {
- mWindow.showLw(true /* doAnimation */);
- } else {
- mWindow.hideLw(true /* doAnimation */);
+ final boolean visible = stack != null && stack.isVisibleLocked();
+ if (mLastVisibility == visible) {
+ return;
+ }
+ mLastVisibility = visible;
+ if (mListener != null) {
+ try {
+ mListener.onDockDividerVisibilityChanged(visible);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "visibility call failed: " + e);
+ }
}
}
@@ -110,4 +125,11 @@
}
mLastRect.set(frame);
}
+
+ public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+ if (mListener != null && listener != null) {
+ throw new IllegalStateException("Dock divider visibility listener already set!");
+ }
+ mListener = listener;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ac90daf..395962c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -121,6 +121,7 @@
import android.view.Gravity;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.IDockDividerVisibilityListener;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
@@ -2689,8 +2690,7 @@
// need to see about starting one.
final boolean notExitingOrAnimating =
!win.mExiting && !win.isAnimatingWithSavedSurface();
- result |= notExitingOrAnimating
- ? RELAYOUT_RES_SURFACE_CHANGED : 0;
+ result |= notExitingOrAnimating ? RELAYOUT_RES_SURFACE_CHANGED : 0;
if (notExitingOrAnimating) {
focusMayChange = tryStartingAnimation(win, winAnimator, isDefaultDisplay,
focusMayChange);
@@ -3075,7 +3075,7 @@
// TODO:
}
- boolean checkCallingPermission(String permission, String func) {
+ private boolean checkCallingPermission(String permission, String func) {
// Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == Process.myPid()) {
return true;
@@ -10211,6 +10211,29 @@
mDestroySurface.add(win);
}
+ @Override
+ public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+ if (!checkCallingPermission(android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS,
+ "registerDockDividerVisibilityListener()")) {
+ return;
+ }
+ // TODO(multi-display): The listener is registered on the default display only.
+ final DockedStackDividerController controller =
+ getDefaultDisplayContentLocked().getDockedDividerController();
+ controller.registerDockDividerVisibilityListener(listener);
+ try {
+ listener.asBinder().linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ getDefaultDisplayContentLocked().getDockedDividerController()
+ .registerDockDividerVisibilityListener(null);
+ }
+ }, 0);
+ } catch (RemoteException e) {
+ controller.registerDockDividerVisibilityListener(null);
+ }
+ }
+
private final class LocalService extends WindowManagerInternal {
@Override
public void requestTraversalFromDisplayManager() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 064b412..29cadf3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -730,7 +730,7 @@
mVisibleFrame.set(mContentFrame);
mStableFrame.set(mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- if (isVisibleLw()) {
+ if (isVisibleLw() || mWinAnimator.isAnimating()) {
// We don't adjust the dock divider frame for reasons other than performance. The
// real reason is that if it gets adjusted before it is shown for the first time,
// it would get size (0, 0). This causes a problem when we finally show the dock
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index f1a8f1c..fb0fe38 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1537,19 +1537,20 @@
std::queue<CompileResourceWorkItem>& workQueue = table.getWorkQueue();
while (!workQueue.empty()) {
CompileResourceWorkItem& workItem = workQueue.front();
- if (workItem.xmlRoot != NULL) {
- err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.xmlRoot,
- workItem.file, &table, xmlFlags);
- } else {
- err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.file,
- &table, xmlFlags);
+ int xmlCompilationFlags = xmlFlags | XML_COMPILE_PARSE_VALUES
+ | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS;
+ if (!workItem.needsCompiling) {
+ xmlCompilationFlags &= ~XML_COMPILE_ASSIGN_ATTRIBUTE_IDS;
+ xmlCompilationFlags &= ~XML_COMPILE_PARSE_VALUES;
}
+ err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.xmlRoot,
+ workItem.file, &table, xmlCompilationFlags);
if (err == NO_ERROR) {
assets->addResource(workItem.resPath.getPathLeaf(),
- workItem.resPath,
- workItem.file,
- workItem.file->getResourceType());
+ workItem.resPath,
+ workItem.file,
+ workItem.file->getResourceType());
} else {
hasErrors = true;
}
@@ -1744,9 +1745,7 @@
manifestFile->getGroupEntry(),
manifestFile->getResourceType());
err = compileXmlFile(bundle, assets, String16(), manifestFile,
- outManifestFile, &table,
- XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
- | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
+ outManifestFile, &table, XML_COMPILE_STANDARD_RESOURCE & ~XML_COMPILE_STRIP_COMMENTS);
if (err < NO_ERROR) {
return err;
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 7d19828..0e470d9 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -100,9 +100,11 @@
}
}
- status_t err = root->parseValues(assets, table);
- if (err != NO_ERROR) {
- hasErrors = true;
+ if ((options&XML_COMPILE_PARSE_VALUES) != 0) {
+ status_t err = root->parseValues(assets, table);
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
}
if (hasErrors) {
@@ -117,7 +119,7 @@
printf("Input XML Resource:\n");
root->print();
}
- err = root->flatten(target,
+ status_t err = root->flatten(target,
(options&XML_COMPILE_STRIP_COMMENTS) != 0,
(options&XML_COMPILE_STRIP_RAW_VALUES) != 0);
if (err != NO_ERROR) {
@@ -4788,9 +4790,10 @@
item.resPath = resPath;
item.file = newFile;
item.xmlRoot = newRoot;
+ item.needsCompiling = false; // This step occurs after we parse/assign, so we don't need
+ // to do it again.
mWorkQueue.push(item);
}
-
return NO_ERROR;
}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index 8ef84f7..4b7b3cd 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -23,13 +23,14 @@
enum {
XML_COMPILE_STRIP_COMMENTS = 1<<0,
XML_COMPILE_ASSIGN_ATTRIBUTE_IDS = 1<<1,
- XML_COMPILE_COMPACT_WHITESPACE = 1<<2,
- XML_COMPILE_STRIP_WHITESPACE = 1<<3,
- XML_COMPILE_STRIP_RAW_VALUES = 1<<4,
- XML_COMPILE_UTF8 = 1<<5,
+ XML_COMPILE_PARSE_VALUES = 1 << 2,
+ XML_COMPILE_COMPACT_WHITESPACE = 1<<3,
+ XML_COMPILE_STRIP_WHITESPACE = 1<<4,
+ XML_COMPILE_STRIP_RAW_VALUES = 1<<5,
+ XML_COMPILE_UTF8 = 1<<6,
XML_COMPILE_STANDARD_RESOURCE =
- XML_COMPILE_STRIP_COMMENTS | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
+ XML_COMPILE_STRIP_COMMENTS | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS | XML_COMPILE_PARSE_VALUES
| XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES
};
@@ -84,6 +85,7 @@
String8 resPath;
sp<AaptFile> file;
sp<XMLNode> xmlRoot;
+ bool needsCompiling = true;
};
class ResourceTable : public ResTable::Accessor
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 6951ede..eea254b 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -541,4 +541,8 @@
@Override
public void endProlongedAnimations() {
}
+
+ @Override
+ public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+ }
}