Merge "Set entitlement status refresh days as 7" into 24D1-dev
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 51585af..03fa841 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11078,21 +11078,12 @@
"assist_long_press_home_enabled";
/**
- * Whether press and hold on nav handle can trigger search.
+ * Whether all entrypoints can trigger search. Replaces individual settings.
*
* @hide
*/
- public static final String SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED =
- "search_press_hold_nav_handle_enabled";
-
- /**
- * Whether long-pressing on the home button can trigger search.
- *
- * @hide
- */
- public static final String SEARCH_LONG_PRESS_HOME_ENABLED =
- "search_long_press_home_enabled";
-
+ public static final String SEARCH_ALL_ENTRYPOINTS_ENABLED =
+ "search_all_entrypoints_enabled";
/**
* Whether or not the accessibility data streaming is enbled for the
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 14fb17c..65bf241 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -38,6 +38,17 @@
}
flag {
+ name: "skip_sleeping_when_switching_display"
+ namespace: "windowing_frontend"
+ description: "Reduce unnecessary visibility or lifecycle changes when changing fold state"
+ bug: "303241079"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "introduce_smoother_dimmer"
namespace: "windowing_frontend"
description: "Refactor dim to fix flickers"
diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
index 5e89d06..e1aff2a 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
@@ -3281,6 +3281,11 @@
}
public PackageImpl(Parcel in) {
+ this(in, /* callback */ null);
+ }
+
+ public PackageImpl(@NonNull Parcel in, @Nullable ParsingPackageUtils.Callback callback) {
+ mCallback = callback;
// We use the boot classloader for all classes that we load.
final ClassLoader boot = Object.class.getClassLoader();
this.supportsSmallScreens = sForBoolean.unparcel(in);
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 763d9ce..6b0c2d2 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -143,9 +143,11 @@
optional SettingProto gesture_setup_complete = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto touch_gesture_enabled = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto long_press_home_enabled = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto search_press_hold_nav_handle_enabled = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto search_long_press_home_enabled = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // Deprecated - use search_all_entrypoints_enabled instead
+ optional SettingProto search_press_hold_nav_handle_enabled = 12 [ (android.privacy).dest = DEST_AUTOMATIC, deprecated = true ];
+ optional SettingProto search_long_press_home_enabled = 13 [ (android.privacy).dest = DEST_AUTOMATIC, deprecated = true ];
optional SettingProto visual_query_accessibility_detection_enabled = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto search_all_entrypoints_enabled = 15 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Assist assist = 7;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8edf42a..6f605e9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6422,10 +6422,8 @@
<!-- Default value for Settings.ASSIST_TOUCH_GESTURE_ENABLED -->
<bool name="config_assistTouchGestureEnabledDefault">true</bool>
- <!-- Default value for Settings.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED -->
- <bool name="config_searchPressHoldNavHandleEnabledDefault">true</bool>
- <!-- Default value for Settings.ASSIST_LONG_PRESS_HOME_ENABLED for search overlay -->
- <bool name="config_searchLongPressHomeEnabledDefault">true</bool>
+ <!-- Default value for Settings.SEARCH_ALL_ENTRYPOINTS_ENABLED -->
+ <bool name="config_searchAllEntrypointsEnabledDefault">true</bool>
<!-- The maximum byte size of the information contained in the bundle of
HotwordDetectedResult. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a180467..e9331bf 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5018,8 +5018,7 @@
<java-symbol type="bool" name="config_assistLongPressHomeEnabledDefault" />
<java-symbol type="bool" name="config_assistTouchGestureEnabledDefault" />
- <java-symbol type="bool" name="config_searchPressHoldNavHandleEnabledDefault" />
- <java-symbol type="bool" name="config_searchLongPressHomeEnabledDefault" />
+ <java-symbol type="bool" name="config_searchAllEntrypointsEnabledDefault" />
<java-symbol type="integer" name="config_hotwordDetectedResultMaxBundleSize" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 87e372c..bd186ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1368,7 +1368,7 @@
* Handles all changes to the PictureInPictureParams.
*/
protected void applyNewPictureInPictureParams(@NonNull PictureInPictureParams params) {
- if (mDeferredTaskInfo != null || PipUtils.aspectRatioChanged(params.getAspectRatioFloat(),
+ if (PipUtils.aspectRatioChanged(params.getAspectRatioFloat(),
mPictureInPictureParams.getAspectRatioFloat())) {
if (mPipBoundsAlgorithm.isValidPictureInPictureAspectRatio(
params.getAspectRatioFloat())) {
@@ -1381,8 +1381,7 @@
TAG, params.hasSetAspectRatio(), params.getAspectRatioFloat());
}
}
- if (mDeferredTaskInfo != null
- || PipUtils.remoteActionsChanged(params.getActions(),
+ if (PipUtils.remoteActionsChanged(params.getActions(),
mPictureInPictureParams.getActions())
|| !PipUtils.remoteActionsMatch(params.getCloseAction(),
mPictureInPictureParams.getCloseAction())) {
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2fa1c6e..2d4a4a1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -255,8 +255,7 @@
Settings.Secure.HEARING_AID_MEDIA_ROUTING,
Settings.Secure.HEARING_AID_NOTIFICATION_ROUTING,
Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
- Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED,
- Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED,
+ Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED,
Settings.Secure.HUB_MODE_TUTORIAL_STATE,
Settings.Secure.STYLUS_BUTTONS_ENABLED,
Settings.Secure.STYLUS_HANDWRITING_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 2535fdb..24c8c3d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -208,8 +208,7 @@
VALIDATORS.put(Secure.ASSIST_TOUCH_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.SEARCH_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"}));
VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 02d212c..dba3bac 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1950,11 +1950,8 @@
Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED,
SecureSettingsProto.Assist.LONG_PRESS_HOME_ENABLED);
dumpSetting(s, p,
- Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED,
- SecureSettingsProto.Assist.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED);
- dumpSetting(s, p,
- Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED,
- SecureSettingsProto.Assist.SEARCH_LONG_PRESS_HOME_ENABLED);
+ Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED,
+ SecureSettingsProto.Assist.SEARCH_ALL_ENTRYPOINTS_ENABLED);
dumpSetting(s, p,
Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED,
SecureSettingsProto.Assist.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 12e8f57..41d9a23 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -430,7 +430,7 @@
</intent-filter>
</receiver>
- <activity android:name=".screenshot.LongScreenshotActivity"
+ <activity android:name=".screenshot.scroll.LongScreenshotActivity"
android:theme="@style/LongScreenshotActivity"
android:process=":screenshot"
android:exported="false"
@@ -525,15 +525,6 @@
</intent-filter>
</activity-alias>
- <!-- Springboard for launching the share and edit activity. This needs to be in the main
- system ui process since we need to notify the status bar to dismiss the keyguard -->
- <receiver android:name=".screenshot.ActionProxyReceiver"
- android:exported="false" />
-
- <!-- Callback for deleting screenshot notification -->
- <receiver android:name=".screenshot.DeleteScreenshotReceiver"
- android:exported="false" />
-
<!-- Callback for invoking a smart action from the screenshot notification. -->
<receiver android:name=".screenshot.SmartActionsReceiver"
android:exported="false"/>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 8c8975f..dd1fc23 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -405,6 +405,13 @@
}
flag {
+ name: "screenshot_shelf_ui"
+ namespace: "systemui"
+ description: "Use new shelf UI flow for screenshots"
+ bug: "329659738"
+}
+
+flag {
name: "run_fingerprint_detect_on_dismissible_keyguard"
namespace: "systemui"
description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
diff --git a/packages/SystemUI/res/layout/app_clips_screenshot.xml b/packages/SystemUI/res/layout/app_clips_screenshot.xml
index cb638ee..bcc7bca 100644
--- a/packages/SystemUI/res/layout/app_clips_screenshot.xml
+++ b/packages/SystemUI/res/layout/app_clips_screenshot.xml
@@ -69,7 +69,7 @@
tools:minHeight="100dp"
tools:minWidth="100dp" />
- <com.android.systemui.screenshot.CropView
+ <com.android.systemui.screenshot.scroll.CropView
android:id="@+id/crop_view"
android:layout_width="0px"
android:layout_height="0px"
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 8a19c2e..4d207da 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -100,7 +100,7 @@
app:layout_constraintStart_toStartOf="parent"
android:transitionName="screenshot_preview_image"/>
- <com.android.systemui.screenshot.CropView
+ <com.android.systemui.screenshot.scroll.CropView
android:id="@+id/crop_view"
android:layout_width="0px"
android:layout_height="0px"
@@ -122,7 +122,7 @@
tools:minHeight="100dp"
tools:minWidth="100dp" />
- <com.android.systemui.screenshot.MagnifierView
+ <com.android.systemui.screenshot.scroll.MagnifierView
android:id="@+id/magnifier"
android:visibility="invisible"
android:layout_width="200dp"
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml
new file mode 100644
index 0000000..ef1a21f
--- /dev/null
+++ b/packages/SystemUI/res/layout/screenshot_shelf.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+<com.android.systemui.screenshot.ui.ScreenshotShelfView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:id="@+id/actions_container_background"
+ android:visibility="gone"
+ android:layout_height="0dp"
+ android:layout_width="0dp"
+ android:elevation="4dp"
+ android:background="@drawable/action_chip_container_background"
+ android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+ android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/actions_container"
+ app:layout_constraintEnd_toEndOf="@+id/actions_container"
+ app:layout_constraintBottom_toTopOf="@id/guideline"/>
+ <HorizontalScrollView
+ android:id="@+id/actions_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
+ android:paddingEnd="@dimen/overlay_action_container_padding_end"
+ android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
+ android:elevation="4dp"
+ android:scrollbars="none"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintWidth_percent="1.0"
+ app:layout_constraintWidth_max="wrap"
+ app:layout_constraintStart_toEndOf="@+id/screenshot_preview_border"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="@id/actions_container_background">
+ <LinearLayout
+ android:id="@+id/screenshot_actions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <include layout="@layout/overlay_action_chip"
+ android:id="@+id/screenshot_share_chip"/>
+ <include layout="@layout/overlay_action_chip"
+ android:id="@+id/screenshot_edit_chip"/>
+ <include layout="@layout/overlay_action_chip"
+ android:id="@+id/screenshot_scroll_chip"
+ android:visibility="gone" />
+ </LinearLayout>
+ </HorizontalScrollView>
+ <View
+ android:id="@+id/screenshot_preview_border"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="@dimen/overlay_border_width_neg"
+ android:layout_marginEnd="@dimen/overlay_border_width_neg"
+ android:layout_marginBottom="14dp"
+ android:elevation="8dp"
+ android:background="@drawable/overlay_border"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@id/screenshot_preview"
+ app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+ <ImageView
+ android:id="@+id/screenshot_preview"
+ android:layout_width="@dimen/overlay_x_scale"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/overlay_border_width"
+ android:layout_marginBottom="@dimen/overlay_border_width"
+ android:layout_gravity="center"
+ android:elevation="8dp"
+ android:contentDescription="@string/screenshot_edit_description"
+ android:scaleType="fitEnd"
+ android:background="@drawable/overlay_preview_background"
+ android:adjustViewBounds="true"
+ android:clickable="true"
+ app:layout_constraintStart_toStartOf="@id/screenshot_preview_border"
+ app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"/>
+ <ImageView
+ android:id="@+id/screenshot_badge"
+ android:layout_width="56dp"
+ android:layout_height="56dp"
+ android:visibility="gone"
+ android:elevation="9dp"
+ app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"
+ app:layout_constraintEnd_toEndOf="@id/screenshot_preview_border"/>
+ <FrameLayout
+ android:id="@+id/screenshot_dismiss_button"
+ android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
+ android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
+ android:elevation="11dp"
+ android:visibility="gone"
+ app:layout_constraintStart_toEndOf="@id/screenshot_preview"
+ app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
+ app:layout_constraintTop_toTopOf="@id/screenshot_preview"
+ app:layout_constraintBottom_toTopOf="@id/screenshot_preview"
+ android:contentDescription="@string/screenshot_dismiss_description">
+ <ImageView
+ android:id="@+id/screenshot_dismiss_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/overlay_dismiss_button_margin"
+ android:background="@drawable/circular_background"
+ android:backgroundTint="?androidprv:attr/materialColorPrimary"
+ android:tint="?androidprv:attr/materialColorOnPrimary"
+ android:padding="4dp"
+ android:src="@drawable/ic_close"/>
+ </FrameLayout>
+ <ImageView
+ android:id="@+id/screenshot_scrollable_preview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleType="matrix"
+ android:visibility="gone"
+ app:layout_constraintStart_toStartOf="@id/screenshot_preview"
+ app:layout_constraintTop_toTopOf="@id/screenshot_preview"
+ android:elevation="7dp"/>
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_end="0dp" />
+
+ <FrameLayout
+ android:id="@+id/screenshot_message_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
+ android:paddingHorizontal="@dimen/overlay_action_container_padding_end"
+ android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
+ android:elevation="4dp"
+ android:background="@drawable/action_chip_container_background"
+ android:visibility="gone"
+ app:layout_constraintTop_toBottomOf="@id/guideline"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintWidth_max="450dp"
+ app:layout_constraintHorizontal_bias="0">
+ <include layout="@layout/screenshot_work_profile_first_run" />
+ <include layout="@layout/screenshot_detection_notice" />
+ </FrameLayout>
+</com.android.systemui.screenshot.ui.ScreenshotShelfView>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b713417..ef9235b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -230,6 +230,8 @@
<string name="screenshot_edit_label">Edit</string>
<!-- Content description indicating that tapping the element will allow editing the screenshot [CHAR LIMIT=NONE] -->
<string name="screenshot_edit_description">Edit screenshot</string>
+ <!-- Label for UI element which allows sharing the screenshot [CHAR LIMIT=30] -->
+ <string name="screenshot_share_label">Share</string>
<!-- Content description indicating that tapping the element will allow sharing the screenshot [CHAR LIMIT=NONE] -->
<string name="screenshot_share_description">Share screenshot</string>
<!-- Label for UI element which allows the user to capture additional off-screen content in a screenshot. [CHAR LIMIT=30] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index f28d405..8a2245d 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -404,7 +404,9 @@
if (nextAlarmMillis > 0) nextAlarmMillis else null,
SysuiR.string::status_bar_alarm.name
)
- .also { data -> clock?.run { events.onAlarmDataChanged(data) } }
+ .also { data ->
+ mainExecutor.execute { clock?.run { events.onAlarmDataChanged(data) } }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 8c87b0c..893887f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -93,6 +93,8 @@
val keyguardAuthenticatedPrimaryAuth: Flow<Int> = repository.keyguardAuthenticatedPrimaryAuth
val keyguardAuthenticatedBiometrics: Flow<Boolean> =
repository.keyguardAuthenticatedBiometrics.filterNotNull()
+ val keyguardAuthenticatedBiometricsHandled: Flow<Unit> =
+ repository.keyguardAuthenticatedBiometrics.filter { it == null }.map {}
val userRequestedBouncerWhenAlreadyAuthenticated: Flow<Int> =
repository.userRequestedBouncerWhenAlreadyAuthenticated.filterNotNull()
val isShowing: StateFlow<Boolean> = repository.primaryBouncerShow
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
index 1c9d1f0..e1fea5f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -23,12 +23,14 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.bouncer.ui.BouncerViewDelegate
import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
/** Models UI state for the lock screen bouncer; handles user input. */
+@ExperimentalCoroutinesApi
class KeyguardBouncerViewModel
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 9afd5ed..d2df276 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -24,9 +24,9 @@
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.people.widget.LaunchConversationActivity;
-import com.android.systemui.screenshot.LongScreenshotActivity;
import com.android.systemui.screenshot.appclips.AppClipsActivity;
import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity;
+import com.android.systemui.screenshot.scroll.LongScreenshotActivity;
import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.telephony.ui.activity.SwitchToManagedProfileForCallActivity;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 3d8e4cb..6aa5e8b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -22,8 +22,6 @@
import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
-import com.android.systemui.screenshot.ActionProxyReceiver;
-import com.android.systemui.screenshot.DeleteScreenshotReceiver;
import com.android.systemui.screenshot.SmartActionsReceiver;
import com.android.systemui.volume.VolumePanelDialogReceiver;
@@ -42,24 +40,6 @@
*/
@Binds
@IntoMap
- @ClassKey(ActionProxyReceiver.class)
- public abstract BroadcastReceiver bindActionProxyReceiver(
- ActionProxyReceiver broadcastReceiver);
-
- /**
- *
- */
- @Binds
- @IntoMap
- @ClassKey(DeleteScreenshotReceiver.class)
- public abstract BroadcastReceiver bindDeleteScreenshotReceiver(
- DeleteScreenshotReceiver broadcastReceiver);
-
- /**
- *
- */
- @Binds
- @IntoMap
@ClassKey(SmartActionsReceiver.class)
public abstract BroadcastReceiver bindSmartActionsReceiver(
SmartActionsReceiver broadcastReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index d400210..3a2781c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -102,9 +102,16 @@
launch {
viewModel.scrimAlpha.collect {
+ val wasVisible = alternateBouncerViewContainer.visibility == View.VISIBLE
alternateBouncerViewContainer.visibility =
if (it < .1f) View.INVISIBLE else View.VISIBLE
scrim.viewAlpha = it
+ if (
+ wasVisible && alternateBouncerViewContainer.visibility == View.INVISIBLE
+ ) {
+ // view is no longer visible
+ viewModel.hideAlternateBouncer()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index 2526f0a..10a9e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -89,4 +89,8 @@
fun showPrimaryBouncer() {
statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true)
}
+
+ fun hideAlternateBouncer() {
+ statusBarKeyguardViewManager.hideAlternateBouncer(false)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index dfe41eb..d49a513 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -243,7 +243,7 @@
Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
false, mAssistContentObserver, UserHandle.USER_ALL);
mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Secure.SEARCH_LONG_PRESS_HOME_ENABLED),
+ Settings.Secure.getUriFor(Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED),
false, mAssistContentObserver, UserHandle.USER_ALL);
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
@@ -443,10 +443,10 @@
boolean overrideLongPressHome = mAssistManagerLazy.get()
.shouldOverrideAssist(AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
boolean longPressDefault = mContext.getResources().getBoolean(overrideLongPressHome
- ? com.android.internal.R.bool.config_searchLongPressHomeEnabledDefault
+ ? com.android.internal.R.bool.config_searchAllEntrypointsEnabledDefault
: com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
- overrideLongPressHome ? Secure.SEARCH_LONG_PRESS_HOME_ENABLED
+ overrideLongPressHome ? Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED
: Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
mUserTracker.getUserId()) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
deleted file mode 100644
index 7e234ae..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2020 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.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_EDIT;
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_DISALLOW_ENTER_PIP;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT;
-
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-import android.view.RemoteAnimationAdapter;
-import android.view.WindowManagerGlobal;
-
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import javax.inject.Inject;
-
-/**
- * Receiver to proxy the share or edit intent, used to clean up the notification and send
- * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
- */
-public class ActionProxyReceiver extends BroadcastReceiver {
- private static final String TAG = "ActionProxyReceiver";
-
- private final ActivityManagerWrapper mActivityManagerWrapper;
- private final ScreenshotSmartActions mScreenshotSmartActions;
- private final DisplayTracker mDisplayTracker;
- private final ActivityStarter mActivityStarter;
-
- @Inject
- public ActionProxyReceiver(ActivityManagerWrapper activityManagerWrapper,
- ScreenshotSmartActions screenshotSmartActions,
- DisplayTracker displayTracker,
- ActivityStarter activityStarter) {
- mActivityManagerWrapper = activityManagerWrapper;
- mScreenshotSmartActions = screenshotSmartActions;
- mDisplayTracker = displayTracker;
- mActivityStarter = activityStarter;
- }
-
- @Override
- public void onReceive(Context context, final Intent intent) {
- Runnable startActivityRunnable = () -> {
- mActivityManagerWrapper.closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
-
- PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
- ActivityOptions opts = ActivityOptions.makeBasic();
- opts.setDisallowEnterPictureInPictureWhileLaunching(
- intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
- opts.setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- try {
- actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
- if (intent.getBooleanExtra(ScreenshotController.EXTRA_OVERRIDE_TRANSITION, false)) {
- RemoteAnimationAdapter runner = new RemoteAnimationAdapter(
- ScreenshotController.SCREENSHOT_REMOTE_RUNNER, 0, 0);
- try {
- WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionRemote(runner,
- mDisplayTracker.getDefaultDisplayId());
- } catch (Exception e) {
- Log.e(TAG, "Error overriding screenshot app transition", e);
- }
- }
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent canceled", e);
- }
-
- };
-
- mActivityStarter.executeRunnableDismissingKeyguard(startActivityRunnable, null,
- true /* dismissShade */, true /* afterKeyguardGone */,
- true /* deferred */);
-
- if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
- String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
- ? ACTION_TYPE_EDIT
- : ACTION_TYPE_SHARE;
- mScreenshotSmartActions.notifyScreenshotAction(
- intent.getStringExtra(EXTRA_ID), actionType, false, null);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
deleted file mode 100644
index e0346f2..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2020 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.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_DELETE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_URI_ID;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-
-import com.android.systemui.dagger.qualifiers.Background;
-
-import java.util.concurrent.Executor;
-
-import javax.inject.Inject;
-
-/**
- * Removes the file at a provided URI.
- */
-public class DeleteScreenshotReceiver extends BroadcastReceiver {
-
- private final ScreenshotSmartActions mScreenshotSmartActions;
- private final Executor mBackgroundExecutor;
-
- @Inject
- public DeleteScreenshotReceiver(ScreenshotSmartActions screenshotSmartActions,
- @Background Executor backgroundExecutor) {
- mScreenshotSmartActions = screenshotSmartActions;
- mBackgroundExecutor = backgroundExecutor;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
- return;
- }
-
- // And delete the image from the media store
- final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
- mBackgroundExecutor.execute(() -> {
- ContentResolver resolver = context.getContentResolver();
- resolver.delete(uri, null, null);
- });
- if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
- mScreenshotSmartActions.notifyScreenshotAction(
- intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false, null);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index b42cc98..1c39f71 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -123,7 +123,7 @@
/**
* Writes the given Bitmap to outputFile.
*/
- ListenableFuture<File> exportToRawFile(Executor executor, Bitmap bitmap,
+ public ListenableFuture<File> exportToRawFile(Executor executor, Bitmap bitmap,
final File outputFile) {
return CallbackToFutureAdapter.getFuture(
(completer) -> {
@@ -165,7 +165,7 @@
*
* @return a listenable future result
*/
- ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
+ public ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
ZonedDateTime captureTime, UserHandle owner, int displayId) {
final Task task = new Task(mResolver, requestId, bitmap, captureTime, mCompressFormat,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
new file mode 100644
index 0000000..a1481f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2024 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.screenshot
+
+import android.animation.Animator
+import android.app.Notification
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.util.Log
+import android.view.KeyEvent
+import android.view.LayoutInflater
+import android.view.ScrollCaptureResponse
+import android.view.View
+import android.view.ViewTreeObserver
+import android.view.WindowInsets
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import androidx.appcompat.content.res.AppCompatResources
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.res.R
+import com.android.systemui.screenshot.scroll.ScrollCaptureController
+import com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * Legacy implementation of screenshot view methods. Just proxies the calls down into the original
+ * ScreenshotView.
+ */
+class LegacyScreenshotViewProxy
+@AssistedInject
+constructor(
+ private val logger: UiEventLogger,
+ flags: FeatureFlags,
+ @Assisted private val context: Context,
+ @Assisted private val displayId: Int
+) : ScreenshotViewProxy {
+ override val view: ScreenshotView =
+ LayoutInflater.from(context).inflate(R.layout.screenshot, null) as ScreenshotView
+ override val screenshotPreview: View
+ override var packageName: String = ""
+ set(value) {
+ field = value
+ view.setPackageName(value)
+ }
+ override var callbacks: ScreenshotView.ScreenshotViewCallback? = null
+ set(value) {
+ field = value
+ view.setCallbacks(value)
+ }
+ override var screenshot: ScreenshotData? = null
+ set(value) {
+ field = value
+ value?.let {
+ val badgeBg =
+ AppCompatResources.getDrawable(context, R.drawable.overlay_badge_background)
+ val user = it.userHandle
+ if (badgeBg != null && user != null) {
+ view.badgeScreenshot(context.packageManager.getUserBadgedIcon(badgeBg, user))
+ }
+ view.setScreenshot(it)
+ }
+ }
+
+ override val isAttachedToWindow
+ get() = view.isAttachedToWindow
+ override val isDismissing
+ get() = view.isDismissing
+ override val isPendingSharedTransition
+ get() = view.isPendingSharedTransition
+
+ init {
+ view.setUiEventLogger(logger)
+ view.setDefaultDisplay(displayId)
+ view.setFlags(flags)
+ addPredictiveBackListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
+ setOnKeyListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
+ if (LogConfig.DEBUG_WINDOW) {
+ Log.d(TAG, "adding OnComputeInternalInsetsListener")
+ }
+ view.viewTreeObserver.addOnComputeInternalInsetsListener(view)
+ screenshotPreview = view.screenshotPreview
+ }
+
+ override fun reset() = view.reset()
+ override fun updateInsets(insets: WindowInsets) = view.updateInsets(insets)
+ override fun updateOrientation(insets: WindowInsets) = view.updateOrientation(insets)
+
+ override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator =
+ view.createScreenshotDropInAnimation(screenRect, showFlash)
+
+ override fun addQuickShareChip(quickShareAction: Notification.Action) =
+ view.addQuickShareChip(quickShareAction)
+
+ override fun setChipIntents(imageData: ScreenshotController.SavedImageData) =
+ view.setChipIntents(imageData)
+
+ override fun requestDismissal(event: ScreenshotEvent) {
+ if (DEBUG_DISMISS) {
+ Log.d(TAG, "screenshot dismissal requested")
+ }
+ // If we're already animating out, don't restart the animation
+ if (view.isDismissing) {
+ if (DEBUG_DISMISS) {
+ Log.v(TAG, "Already dismissing, ignoring duplicate command $event")
+ }
+ return
+ }
+ logger.log(event, 0, packageName)
+ view.animateDismissal()
+ }
+
+ override fun showScrollChip(packageName: String, onClick: Runnable) =
+ view.showScrollChip(packageName, onClick)
+
+ override fun hideScrollChip() = view.hideScrollChip()
+
+ override fun prepareScrollingTransition(
+ response: ScrollCaptureResponse,
+ screenBitmap: Bitmap,
+ newScreenshot: Bitmap,
+ screenshotTakenInPortrait: Boolean,
+ onTransitionPrepared: Runnable,
+ ) {
+ view.prepareScrollingTransition(
+ response,
+ screenBitmap,
+ newScreenshot,
+ screenshotTakenInPortrait
+ )
+ view.post { onTransitionPrepared.run() }
+ }
+
+ override fun startLongScreenshotTransition(
+ transitionDestination: Rect,
+ onTransitionEnd: Runnable,
+ longScreenshot: ScrollCaptureController.LongScreenshot
+ ) = view.startLongScreenshotTransition(transitionDestination, onTransitionEnd, longScreenshot)
+
+ override fun restoreNonScrollingUi() = view.restoreNonScrollingUi()
+
+ override fun stopInputListening() = view.stopInputListening()
+
+ override fun requestFocus() {
+ view.requestFocus()
+ }
+
+ override fun announceForAccessibility(string: String) = view.announceForAccessibility(string)
+
+ override fun prepareEntranceAnimation(runnable: Runnable) {
+ view.viewTreeObserver.addOnPreDrawListener(
+ object : ViewTreeObserver.OnPreDrawListener {
+ override fun onPreDraw(): Boolean {
+ if (LogConfig.DEBUG_WINDOW) {
+ Log.d(TAG, "onPreDraw: startAnimation")
+ }
+ view.viewTreeObserver.removeOnPreDrawListener(this)
+ runnable.run()
+ return true
+ }
+ }
+ )
+ }
+
+ private fun addPredictiveBackListener(onDismissRequested: (ScreenshotEvent) -> Unit) {
+ val onBackInvokedCallback = OnBackInvokedCallback {
+ if (LogConfig.DEBUG_INPUT) {
+ Log.d(TAG, "Predictive Back callback dispatched")
+ }
+ onDismissRequested.invoke(SCREENSHOT_DISMISSED_OTHER)
+ }
+ view.addOnAttachStateChangeListener(
+ object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {
+ if (LogConfig.DEBUG_INPUT) {
+ Log.d(TAG, "Registering Predictive Back callback")
+ }
+ view
+ .findOnBackInvokedDispatcher()
+ ?.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ onBackInvokedCallback
+ )
+ }
+
+ override fun onViewDetachedFromWindow(view: View) {
+ if (LogConfig.DEBUG_INPUT) {
+ Log.d(TAG, "Unregistering Predictive Back callback")
+ }
+ view
+ .findOnBackInvokedDispatcher()
+ ?.unregisterOnBackInvokedCallback(onBackInvokedCallback)
+ }
+ }
+ )
+ }
+ private fun setOnKeyListener(onDismissRequested: (ScreenshotEvent) -> Unit) {
+ view.setOnKeyListener(
+ object : View.OnKeyListener {
+ override fun onKey(view: View, keyCode: Int, event: KeyEvent): Boolean {
+ if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
+ if (LogConfig.DEBUG_INPUT) {
+ Log.d(TAG, "onKeyEvent: $keyCode")
+ }
+ onDismissRequested.invoke(SCREENSHOT_DISMISSED_OTHER)
+ return true
+ }
+ return false
+ }
+ }
+ )
+ }
+
+ @AssistedFactory
+ interface Factory : ScreenshotViewProxy.Factory {
+ override fun getProxy(context: Context, displayId: Int): LegacyScreenshotViewProxy
+ }
+
+ companion object {
+ private const val TAG = "LegacyScreenshotViewProxy"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
index 6050c2b..440cf1c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
@@ -16,8 +16,9 @@
package com.android.systemui.screenshot;
+/** Stores debug log configuration for screenshots. */
@SuppressWarnings("PointlessBooleanExpression")
-class LogConfig {
+public class LogConfig {
/** Log ALL the things... */
private static final boolean DEBUG_ALL = false;
@@ -29,36 +30,37 @@
private static final boolean TAG_WITH_CLASS_NAME = false;
/** Action creation and user selection: Share, Save, Edit, Delete, Smart action, etc */
- static final boolean DEBUG_ACTIONS = DEBUG_ALL || false;
+ public static final boolean DEBUG_ACTIONS = DEBUG_ALL || false;
/** Debug info about animations such as start, complete and cancel */
- static final boolean DEBUG_ANIM = DEBUG_ALL || false;
+ public static final boolean DEBUG_ANIM = DEBUG_ALL || false;
/** Whenever Uri is supplied to consumer, or onComplete runnable is run() */
- static final boolean DEBUG_CALLBACK = DEBUG_ALL || false;
+ public static final boolean DEBUG_CALLBACK = DEBUG_ALL || false;
/** Logs information about dismissing the screenshot tool */
- static final boolean DEBUG_DISMISS = DEBUG_ALL || false;
+ public static final boolean DEBUG_DISMISS = DEBUG_ALL || false;
/** Touch or key event driven action or side effects */
- static final boolean DEBUG_INPUT = DEBUG_ALL || false;
+ public static final boolean DEBUG_INPUT = DEBUG_ALL || false;
/** Scroll capture usage */
- static final boolean DEBUG_SCROLL = DEBUG_ALL || false;
+ public static final boolean DEBUG_SCROLL = DEBUG_ALL || false;
/** Service lifecycle events and callbacks */
- static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
+ public static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
/** Storage related actions, Bitmap.compress, ContentManager, etc */
- static final boolean DEBUG_STORAGE = DEBUG_ALL || false;
+ public static final boolean DEBUG_STORAGE = DEBUG_ALL || false;
/** High level logical UI actions: timeout, onConfigChanged, insets, show actions, reset */
- static final boolean DEBUG_UI = DEBUG_ALL || false;
+ public static final boolean DEBUG_UI = DEBUG_ALL || false;
/** Interactions with Window and WindowManager */
- static final boolean DEBUG_WINDOW = DEBUG_ALL || false;
+ public static final boolean DEBUG_WINDOW = DEBUG_ALL || false;
- static String logTag(Class<?> cls) {
+ /** Get the appropriate class name */
+ public static String logTag(Class<?> cls) {
return TAG_WITH_CLASS_NAME ? cls.getSimpleName() : TAG_SS;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
new file mode 100644
index 0000000..abdbd68
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 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.screenshot
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.os.UserHandle
+import androidx.appcompat.content.res.AppCompatResources
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/**
+ * Provides static actions for screenshots. This class can be overridden by a vendor-specific SysUI
+ * implementation.
+ */
+interface ScreenshotActionsProvider {
+ data class ScreenshotAction(
+ val icon: Drawable?,
+ val text: String?,
+ val overrideTransition: Boolean,
+ val retrieveIntent: (Uri) -> Intent
+ )
+
+ fun getPreviewAction(context: Context, uri: Uri, user: UserHandle): Intent
+ fun getActions(context: Context, user: UserHandle): List<ScreenshotAction>
+}
+
+class DefaultScreenshotActionsProvider @Inject constructor() : ScreenshotActionsProvider {
+ override fun getPreviewAction(context: Context, uri: Uri, user: UserHandle): Intent {
+ return ActionIntentCreator.createEdit(uri, context)
+ }
+
+ override fun getActions(
+ context: Context,
+ user: UserHandle
+ ): List<ScreenshotActionsProvider.ScreenshotAction> {
+ val editAction =
+ ScreenshotActionsProvider.ScreenshotAction(
+ AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_edit),
+ context.resources.getString(R.string.screenshot_edit_label),
+ true
+ ) { uri ->
+ ActionIntentCreator.createEdit(uri, context)
+ }
+ val shareAction =
+ ScreenshotActionsProvider.ScreenshotAction(
+ AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_share),
+ context.resources.getString(R.string.screenshot_share_label),
+ false
+ ) { uri ->
+ ActionIntentCreator.createShare(uri)
+ }
+ return listOf(editAction, shareAction)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index ee3e7ba..c8e13bb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -21,7 +21,6 @@
import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
-import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT;
import static com.android.systemui.screenshot.LogConfig.DEBUG_UI;
import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW;
@@ -64,8 +63,6 @@
import android.view.Display;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
@@ -77,8 +74,6 @@
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
-import android.window.OnBackInvokedCallback;
-import android.window.OnBackInvokedDispatcher;
import android.window.WindowContext;
import com.android.internal.app.ChooserActivity;
@@ -92,6 +87,10 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
+import com.android.systemui.screenshot.scroll.LongScreenshotActivity;
+import com.android.systemui.screenshot.scroll.LongScreenshotData;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient;
+import com.android.systemui.screenshot.scroll.ScrollCaptureController;
import com.android.systemui.util.Assert;
import com.google.common.util.concurrent.ListenableFuture;
@@ -165,7 +164,7 @@
/**
* Structure returned by the SaveImageInBackgroundTask
*/
- static class SavedImageData {
+ public static class SavedImageData {
public Uri uri;
public List<Notification.Action> smartActions;
public Notification.Action quickShareAction;
@@ -205,7 +204,7 @@
void onActionsReady(ScreenshotController.QuickShareData quickShareData);
}
- interface TransitionDestination {
+ public interface TransitionDestination {
/**
* Allows the long screenshot activity to call back with a destination location (the bounds
* on screen of the destination for the transitioning view) and a Runnable to be run once
@@ -233,10 +232,11 @@
// From WizardManagerHelper.java
private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
- private static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
+ static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
private final WindowContext mContext;
private final FeatureFlags mFlags;
+ private final ScreenshotViewProxy mViewProxy;
private final ScreenshotNotificationsController mNotificationsController;
private final ScreenshotSmartActions mScreenshotSmartActions;
private final UiEventLogger mUiEventLogger;
@@ -265,14 +265,6 @@
private final UserManager mUserManager;
private final AssistContentRequester mAssistContentRequester;
- private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
- if (DEBUG_INPUT) {
- Log.d(TAG, "Predictive Back callback dispatched");
- }
- respondToKeyDismissal();
- };
-
- private ScreenshotView mScreenshotView;
private final MessageContainerController mMessageContainerController;
private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
@@ -305,6 +297,7 @@
ScreenshotController(
Context context,
FeatureFlags flags,
+ ScreenshotViewProxy.Factory viewProxyFactory,
ScreenshotSmartActions screenshotSmartActions,
ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory,
ScrollCaptureClient scrollCaptureClient,
@@ -342,12 +335,7 @@
mScreenshotHandler = timeoutHandler;
mScreenshotHandler.setDefaultTimeoutMillis(SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS);
- mScreenshotHandler.setOnTimeoutRunnable(() -> {
- if (DEBUG_UI) {
- Log.d(TAG, "Corner timeout hit");
- }
- dismissScreenshot(SCREENSHOT_INTERACTION_TIMEOUT);
- });
+
mDisplayId = displayId;
mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
@@ -360,6 +348,15 @@
mMessageContainerController = messageContainerController;
mAssistContentRequester = assistContentRequester;
+ mViewProxy = viewProxyFactory.getProxy(mContext, mDisplayId);
+
+ mScreenshotHandler.setOnTimeoutRunnable(() -> {
+ if (DEBUG_UI) {
+ Log.d(TAG, "Corner timeout hit");
+ }
+ mViewProxy.requestDismissal(SCREENSHOT_INTERACTION_TIMEOUT);
+ });
+
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
// Setup the window that we are going to use
@@ -383,7 +380,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (ClipboardOverlayController.COPY_OVERLAY_ACTION.equals(intent.getAction())) {
- dismissScreenshot(SCREENSHOT_DISMISSED_OTHER);
+ mViewProxy.requestDismissal(SCREENSHOT_DISMISSED_OTHER);
}
}
};
@@ -461,13 +458,13 @@
// The window is focusable by default
setWindowFocusable(true);
- mScreenshotView.requestFocus();
+ mViewProxy.requestFocus();
enqueueScrollCaptureRequest(screenshot.getUserHandle());
attachWindow();
- boolean showFlash = true;
+ boolean showFlash;
if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
if (screenshot.getScreenBounds() != null
&& aspectRatiosMatch(screenshot.getBitmap(), screenshot.getInsets(),
@@ -479,16 +476,15 @@
screenshot.setScreenBounds(new Rect(0, 0, screenshot.getBitmap().getWidth(),
screenshot.getBitmap().getHeight()));
}
+ } else {
+ showFlash = true;
}
- prepareAnimation(screenshot.getScreenBounds(), showFlash, () -> {
- mMessageContainerController.onScreenshotTaken(screenshot);
- });
+ mViewProxy.prepareEntranceAnimation(
+ () -> startAnimation(screenshot.getScreenBounds(), showFlash,
+ () -> mMessageContainerController.onScreenshotTaken(screenshot)));
- mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
- mContext.getDrawable(R.drawable.overlay_badge_background),
- screenshot.getUserHandle()));
- mScreenshotView.setScreenshot(screenshot);
+ mViewProxy.setScreenshot(screenshot);
// ignore system bar insets for the purpose of window layout
mWindow.getDecorView().setOnApplyWindowInsetsListener(
@@ -503,55 +499,44 @@
void prepareViewForNewScreenshot(ScreenshotData screenshot, String oldPackageName) {
withWindowAttached(() -> {
if (mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
- mScreenshotView.announceForAccessibility(mContext.getResources().getString(
+ mViewProxy.announceForAccessibility(mContext.getResources().getString(
R.string.screenshot_saving_work_profile_title));
} else {
- mScreenshotView.announceForAccessibility(
+ mViewProxy.announceForAccessibility(
mContext.getResources().getString(R.string.screenshot_saving_title));
}
});
- mScreenshotView.reset();
+ mViewProxy.reset();
- if (mScreenshotView.isAttachedToWindow()) {
+ if (mViewProxy.isAttachedToWindow()) {
// if we didn't already dismiss for another reason
- if (!mScreenshotView.isDismissing()) {
+ if (!mViewProxy.isDismissing()) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0,
oldPackageName);
}
if (DEBUG_WINDOW) {
Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
- + "(dismissing=" + mScreenshotView.isDismissing() + ")");
+ + "(dismissing=" + mViewProxy.isDismissing() + ")");
}
}
- mScreenshotView.setPackageName(mPackageName);
+ mViewProxy.setPackageName(mPackageName);
- mScreenshotView.updateOrientation(
+ mViewProxy.updateOrientation(
mWindowManager.getCurrentWindowMetrics().getWindowInsets());
}
/**
- * Clears current screenshot
+ * Requests the view to dismiss the current screenshot (may be ignored, if screenshot is already
+ * being dismissed)
*/
- void dismissScreenshot(ScreenshotEvent event) {
- if (DEBUG_DISMISS) {
- Log.d(TAG, "dismissScreenshot");
- }
- // If we're already animating out, don't restart the animation
- if (mScreenshotView.isDismissing()) {
- if (DEBUG_DISMISS) {
- Log.v(TAG, "Already dismissing, ignoring duplicate command");
- }
- return;
- }
- mUiEventLogger.log(event, 0, mPackageName);
- mScreenshotHandler.cancelTimeout();
- mScreenshotView.animateDismissal();
+ void requestDismissal(ScreenshotEvent event) {
+ mViewProxy.requestDismissal(event);
}
boolean isPendingSharedTransition() {
- return mScreenshotView.isPendingSharedTransition();
+ return mViewProxy.isPendingSharedTransition();
}
// Any cleanup needed when the service is being destroyed.
@@ -576,11 +561,7 @@
private void releaseMediaPlayer() {
if (mScreenshotSoundController == null) return;
- mScreenshotSoundController.releaseScreenshotSound();
- }
-
- private void respondToKeyDismissal() {
- dismissScreenshot(SCREENSHOT_DISMISSED_OTHER);
+ mScreenshotSoundController.releaseScreenshotSoundAsync();
}
/**
@@ -591,31 +572,8 @@
Log.d(TAG, "reloadAssets()");
}
- // Inflate the screenshot layout
- mScreenshotView = (ScreenshotView)
- LayoutInflater.from(mContext).inflate(R.layout.screenshot, null);
- mMessageContainerController.setView(mScreenshotView);
- mScreenshotView.addOnAttachStateChangeListener(
- new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(@NonNull View v) {
- if (DEBUG_INPUT) {
- Log.d(TAG, "Registering Predictive Back callback");
- }
- mScreenshotView.findOnBackInvokedDispatcher().registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback);
- }
-
- @Override
- public void onViewDetachedFromWindow(@NonNull View v) {
- if (DEBUG_INPUT) {
- Log.d(TAG, "Unregistering Predictive Back callback");
- }
- mScreenshotView.findOnBackInvokedDispatcher()
- .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
- }
- });
- mScreenshotView.init(mUiEventLogger, new ScreenshotView.ScreenshotViewCallback() {
+ mMessageContainerController.setView(mViewProxy.getView());
+ mViewProxy.setCallbacks(new ScreenshotView.ScreenshotViewCallback() {
@Override
public void onUserInteraction() {
if (DEBUG_INPUT) {
@@ -640,45 +598,12 @@
// TODO(159460485): Remove this when focus is handled properly in the system
setWindowFocusable(false);
}
- }, mFlags);
- mScreenshotView.setDefaultDisplay(mDisplayId);
- mScreenshotView.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis());
-
- mScreenshotView.setOnKeyListener((v, keyCode, event) -> {
- if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
- if (DEBUG_INPUT) {
- Log.d(TAG, "onKeyEvent: " + keyCode);
- }
- respondToKeyDismissal();
- return true;
- }
- return false;
});
if (DEBUG_WINDOW) {
- Log.d(TAG, "adding OnComputeInternalInsetsListener");
+ Log.d(TAG, "setContentView: " + mViewProxy.getView());
}
- mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(mScreenshotView);
- if (DEBUG_WINDOW) {
- Log.d(TAG, "setContentView: " + mScreenshotView);
- }
- setContentView(mScreenshotView);
- }
-
- private void prepareAnimation(Rect screenRect, boolean showFlash,
- Runnable onAnimationComplete) {
- mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- if (DEBUG_WINDOW) {
- Log.d(TAG, "onPreDraw: startAnimation");
- }
- mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
- startAnimation(screenRect, showFlash, onAnimationComplete);
- return true;
- }
- });
+ setContentView(mViewProxy.getView());
}
private void enqueueScrollCaptureRequest(UserHandle owner) {
@@ -694,13 +619,13 @@
if (mConfigChanges.applyNewConfig(mContext.getResources())) {
// Hide the scroll chip until we know it's available in this
// orientation
- mScreenshotView.hideScrollChip();
+ mViewProxy.hideScrollChip();
// Delay scroll capture eval a bit to allow the underlying activity
// to set up in the new orientation.
mScreenshotHandler.postDelayed(() -> {
requestScrollCapture(owner);
}, 150);
- mScreenshotView.updateInsets(
+ mViewProxy.updateInsets(
mWindowManager.getCurrentWindowMetrics().getWindowInsets());
// Screenshot animation calculations won't be valid anymore,
// so just end
@@ -759,16 +684,20 @@
+ mLastScrollCaptureResponse.getWindowTitle() + "]");
final ScrollCaptureResponse response = mLastScrollCaptureResponse;
- mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
+ mViewProxy.showScrollChip(response.getPackageName(), /* onClick */ () -> {
DisplayMetrics displayMetrics = new DisplayMetrics();
getDisplay().getRealMetrics(displayMetrics);
Bitmap newScreenshot = mImageCapture.captureDisplay(mDisplayId,
new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
- mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
- mScreenshotTakenInPortrait);
- // delay starting scroll capture to make sure the scrim is up before the app moves
- mScreenshotView.post(() -> runBatchScrollCapture(response, owner));
+ if (newScreenshot != null) {
+ // delay starting scroll capture to make sure scrim is up before the app moves
+ mViewProxy.prepareScrollingTransition(
+ response, mScreenBitmap, newScreenshot, mScreenshotTakenInPortrait,
+ () -> runBatchScrollCapture(response, owner));
+ } else {
+ Log.wtf(TAG, "failed to capture current screenshot for scroll transition");
+ }
});
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "requestScrollCapture failed", e);
@@ -794,19 +723,19 @@
return;
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "Exception", e);
- mScreenshotView.restoreNonScrollingUi();
+ mViewProxy.restoreNonScrollingUi();
return;
}
if (longScreenshot.getHeight() == 0) {
- mScreenshotView.restoreNonScrollingUi();
+ mViewProxy.restoreNonScrollingUi();
return;
}
mLongScreenshotHolder.setLongScreenshot(longScreenshot);
mLongScreenshotHolder.setTransitionDestinationCallback(
(transitionDestination, onTransitionEnd) -> {
- mScreenshotView.startLongScreenshotTransition(
+ mViewProxy.startLongScreenshotTransition(
transitionDestination, onTransitionEnd,
longScreenshot);
// TODO: Do this via ActionIntentExecutor instead.
@@ -882,16 +811,14 @@
}
mWindowManager.removeViewImmediate(decorView);
}
- // Ensure that we remove the input monitor
- if (mScreenshotView != null) {
- mScreenshotView.stopInputListening();
- }
+
+ mViewProxy.stopInputListening();
}
private void playCameraSoundIfNeeded() {
if (mScreenshotSoundController == null) return;
// the controller is not-null only on the default display controller
- mScreenshotSoundController.playCameraSound();
+ mScreenshotSoundController.playScreenshotSoundAsync();
}
/**
@@ -932,7 +859,7 @@
}
mScreenshotAnimation =
- mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
+ mViewProxy.createScreenshotDropInAnimation(screenRect, showFlash);
if (onAnimationComplete != null) {
mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@@ -975,7 +902,7 @@
};
Pair<ActivityOptions, ExitTransitionCoordinator> transition =
ActivityOptions.startSharedElementAnimation(mWindow, callbacks, null,
- Pair.create(mScreenshotView.getScreenshotPreview(),
+ Pair.create(mViewProxy.getScreenshotPreview(),
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
return transition;
@@ -999,7 +926,7 @@
mCurrentRequestCallback.onFinish();
mCurrentRequestCallback = null;
}
- mScreenshotView.reset();
+ mViewProxy.reset();
removeWindow();
mScreenshotHandler.cancelTimeout();
}
@@ -1067,7 +994,7 @@
}
private void doPostAnimation(ScreenshotController.SavedImageData imageData) {
- mScreenshotView.setChipIntents(imageData);
+ mViewProxy.setChipIntents(imageData);
}
/**
@@ -1084,11 +1011,11 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mScreenshotView.addQuickShareChip(quickShareData.quickShareAction);
+ mViewProxy.addQuickShareChip(quickShareData.quickShareAction);
}
});
} else {
- mScreenshotView.addQuickShareChip(quickShareData.quickShareAction);
+ mViewProxy.addQuickShareChip(quickShareData.quickShareAction);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
new file mode 100644
index 0000000..9354fd2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2024 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.screenshot
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.app.Notification
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.view.KeyEvent
+import android.view.LayoutInflater
+import android.view.ScrollCaptureResponse
+import android.view.View
+import android.view.ViewTreeObserver
+import android.view.WindowInsets
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.log.DebugLogger.debugLog
+import com.android.systemui.res.R
+import com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS
+import com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS
+import com.android.systemui.screenshot.LogConfig.DEBUG_INPUT
+import com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER
+import com.android.systemui.screenshot.scroll.ScrollCaptureController
+import com.android.systemui.screenshot.ui.ScreenshotAnimationController
+import com.android.systemui.screenshot.ui.ScreenshotShelfView
+import com.android.systemui.screenshot.ui.binder.ScreenshotShelfViewBinder
+import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel
+import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** Controls the screenshot view and viewModel. */
+class ScreenshotShelfViewProxy
+@AssistedInject
+constructor(
+ private val logger: UiEventLogger,
+ private val viewModel: ScreenshotViewModel,
+ private val staticActionsProvider: ScreenshotActionsProvider,
+ @Assisted private val context: Context,
+ @Assisted private val displayId: Int
+) : ScreenshotViewProxy {
+ override val view: ScreenshotShelfView =
+ LayoutInflater.from(context).inflate(R.layout.screenshot_shelf, null) as ScreenshotShelfView
+ override val screenshotPreview: View
+ override var packageName: String = ""
+ override var callbacks: ScreenshotView.ScreenshotViewCallback? = null
+ override var screenshot: ScreenshotData? = null
+ set(value) {
+ viewModel.setScreenshotBitmap(value?.bitmap)
+ field = value
+ }
+
+ override val isAttachedToWindow
+ get() = view.isAttachedToWindow
+ override var isDismissing = false
+ override var isPendingSharedTransition = false
+
+ private val animationController = ScreenshotAnimationController(view)
+
+ init {
+ ScreenshotShelfViewBinder.bind(view, viewModel, LayoutInflater.from(context))
+ addPredictiveBackListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
+ setOnKeyListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
+ debugLog(DEBUG_WINDOW) { "adding OnComputeInternalInsetsListener" }
+ screenshotPreview = view.screenshotPreview
+ }
+
+ override fun reset() {
+ animationController.cancel()
+ isPendingSharedTransition = false
+ viewModel.setScreenshotBitmap(null)
+ viewModel.setActions(listOf())
+ }
+ override fun updateInsets(insets: WindowInsets) {}
+ override fun updateOrientation(insets: WindowInsets) {}
+
+ override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator {
+ return animationController.getEntranceAnimation()
+ }
+
+ override fun addQuickShareChip(quickShareAction: Notification.Action) {}
+
+ override fun setChipIntents(imageData: ScreenshotController.SavedImageData) {
+ val staticActions =
+ staticActionsProvider.getActions(context, imageData.owner).map {
+ ActionButtonViewModel(it.icon, it.text) {
+ val intent = it.retrieveIntent(imageData.uri)
+ debugLog(DEBUG_ACTIONS) { "Action tapped: $intent" }
+ isPendingSharedTransition = true
+ callbacks?.onAction(intent, imageData.owner, it.overrideTransition)
+ }
+ }
+
+ viewModel.setActions(staticActions)
+ }
+
+ override fun requestDismissal(event: ScreenshotEvent) {
+ debugLog(DEBUG_DISMISS) { "screenshot dismissal requested: $event" }
+
+ // If we're already animating out, don't restart the animation
+ if (isDismissing) {
+ debugLog(DEBUG_DISMISS) { "Already dismissing, ignoring duplicate command $event" }
+ return
+ }
+ logger.log(event, 0, packageName)
+ val animator = animationController.getExitAnimation()
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animator: Animator) {
+ isDismissing = true
+ }
+ override fun onAnimationEnd(animator: Animator) {
+ isDismissing = false
+ callbacks?.onDismiss()
+ }
+ }
+ )
+ animator.start()
+ }
+
+ override fun showScrollChip(packageName: String, onClick: Runnable) {}
+
+ override fun hideScrollChip() {}
+
+ override fun prepareScrollingTransition(
+ response: ScrollCaptureResponse,
+ screenBitmap: Bitmap,
+ newScreenshot: Bitmap,
+ screenshotTakenInPortrait: Boolean,
+ onTransitionPrepared: Runnable,
+ ) {}
+
+ override fun startLongScreenshotTransition(
+ transitionDestination: Rect,
+ onTransitionEnd: Runnable,
+ longScreenshot: ScrollCaptureController.LongScreenshot
+ ) {}
+
+ override fun restoreNonScrollingUi() {}
+
+ override fun stopInputListening() {}
+
+ override fun requestFocus() {
+ view.requestFocus()
+ }
+
+ override fun announceForAccessibility(string: String) = view.announceForAccessibility(string)
+
+ override fun prepareEntranceAnimation(runnable: Runnable) {
+ view.viewTreeObserver.addOnPreDrawListener(
+ object : ViewTreeObserver.OnPreDrawListener {
+ override fun onPreDraw(): Boolean {
+ debugLog(DEBUG_WINDOW) { "onPreDraw: startAnimation" }
+ view.viewTreeObserver.removeOnPreDrawListener(this)
+ runnable.run()
+ return true
+ }
+ }
+ )
+ }
+
+ private fun addPredictiveBackListener(onDismissRequested: (ScreenshotEvent) -> Unit) {
+ val onBackInvokedCallback = OnBackInvokedCallback {
+ debugLog(DEBUG_INPUT) { "Predictive Back callback dispatched" }
+ onDismissRequested.invoke(SCREENSHOT_DISMISSED_OTHER)
+ }
+ view.addOnAttachStateChangeListener(
+ object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {
+ debugLog(DEBUG_INPUT) { "Registering Predictive Back callback" }
+ view
+ .findOnBackInvokedDispatcher()
+ ?.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ onBackInvokedCallback
+ )
+ }
+
+ override fun onViewDetachedFromWindow(view: View) {
+ debugLog(DEBUG_INPUT) { "Unregistering Predictive Back callback" }
+ view
+ .findOnBackInvokedDispatcher()
+ ?.unregisterOnBackInvokedCallback(onBackInvokedCallback)
+ }
+ }
+ )
+ }
+ private fun setOnKeyListener(onDismissRequested: (ScreenshotEvent) -> Unit) {
+ view.setOnKeyListener(
+ object : View.OnKeyListener {
+ override fun onKey(view: View, keyCode: Int, event: KeyEvent): Boolean {
+ if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
+ debugLog(DEBUG_INPUT) { "onKeyEvent: $keyCode" }
+ onDismissRequested.invoke(SCREENSHOT_DISMISSED_OTHER)
+ return true
+ }
+ return false
+ }
+ }
+ )
+ }
+
+ @AssistedFactory
+ interface Factory : ScreenshotViewProxy.Factory {
+ override fun getProxy(context: Context, displayId: Int): ScreenshotShelfViewProxy
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
index 2c0bdde..d3a7fc4a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -21,22 +21,34 @@
import com.android.app.tracing.coroutines.async
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.google.errorprone.annotations.CanIgnoreReturnValue
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
/** Controls sound reproduction after a screenshot is taken. */
interface ScreenshotSoundController {
/** Reproduces the camera sound. */
- @CanIgnoreReturnValue fun playCameraSound(): Deferred<Unit>
+ suspend fun playScreenshotSound()
- /** Releases the sound. [playCameraSound] behaviour is undefined after this has been called. */
- @CanIgnoreReturnValue fun releaseScreenshotSound(): Deferred<Unit>
+ /**
+ * Releases the sound. [playScreenshotSound] behaviour is undefined after this has been called.
+ */
+ suspend fun releaseScreenshotSound()
+
+ /** Reproduces the camera sound. Used for compatibility with Java code. */
+ fun playScreenshotSoundAsync()
+
+ /**
+ * Releases the sound. [playScreenshotSound] behaviour is undefined after this has been called.
+ * Used for compatibility with Java code.
+ */
+ fun releaseScreenshotSoundAsync()
}
class ScreenshotSoundControllerImpl
@@ -47,8 +59,8 @@
@Background private val bgDispatcher: CoroutineDispatcher
) : ScreenshotSoundController {
- val player: Deferred<MediaPlayer?> =
- coroutineScope.async("loadCameraSound", bgDispatcher) {
+ private val player: Deferred<MediaPlayer?> =
+ coroutineScope.async("loadScreenshotSound", bgDispatcher) {
try {
soundProvider.getScreenshotSound()
} catch (e: IllegalStateException) {
@@ -57,8 +69,8 @@
}
}
- override fun playCameraSound(): Deferred<Unit> {
- return coroutineScope.async("playCameraSound", bgDispatcher) {
+ override suspend fun playScreenshotSound() {
+ withContext(bgDispatcher) {
try {
player.await()?.start()
} catch (e: IllegalStateException) {
@@ -68,8 +80,8 @@
}
}
- override fun releaseScreenshotSound(): Deferred<Unit> {
- return coroutineScope.async("releaseScreenshotSound", bgDispatcher) {
+ override suspend fun releaseScreenshotSound() {
+ withContext(bgDispatcher) {
try {
withTimeout(1.seconds) { player.await()?.release() }
} catch (e: TimeoutCancellationException) {
@@ -79,6 +91,14 @@
}
}
+ override fun playScreenshotSoundAsync() {
+ coroutineScope.launch { playScreenshotSound() }
+ }
+
+ override fun releaseScreenshotSoundAsync() {
+ coroutineScope.launch { releaseScreenshotSound() }
+ }
+
private companion object {
const val TAG = "ScreenshotSoundControllerImpl"
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index be30a15..cb2dba0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -26,6 +26,7 @@
import static com.android.systemui.screenshot.LogConfig.DEBUG_UI;
import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW;
import static com.android.systemui.screenshot.LogConfig.logTag;
+import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS;
import static java.util.Objects.requireNonNull;
@@ -33,6 +34,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.BroadcastOptions;
import android.app.Notification;
@@ -90,6 +92,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
+import com.android.systemui.screenshot.scroll.ScrollCaptureController;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.QuickStepContract;
@@ -102,7 +105,7 @@
public class ScreenshotView extends FrameLayout implements
ViewTreeObserver.OnComputeInternalInsetsListener {
- interface ScreenshotViewCallback {
+ public interface ScreenshotViewCallback {
void onUserInteraction();
void onAction(Intent intent, UserHandle owner, boolean overrideTransition);
@@ -168,7 +171,6 @@
private ScreenshotData mScreenshotData;
private final InteractionJankMonitor mInteractionJankMonitor;
- private long mDefaultTimeoutOfTimeoutHandler;
private FeatureFlags mFlags;
private final Bundle mInteractiveBroadcastOption;
@@ -244,10 +246,6 @@
return InteractionJankMonitor.getInstance();
}
- void setDefaultTimeoutMillis(long timeout) {
- mDefaultTimeoutOfTimeoutHandler = timeout;
- }
-
public void hideScrollChip() {
mScrollChip.setVisibility(View.GONE);
}
@@ -426,15 +424,15 @@
return mScreenshotPreview;
}
- /**
- * Set up the logger and callback on dismissal.
- *
- * Note: must be called before any other (non-constructor) method or null pointer exceptions
- * may occur.
- */
- void init(UiEventLogger uiEventLogger, ScreenshotViewCallback callbacks, FeatureFlags flags) {
+ void setUiEventLogger(UiEventLogger uiEventLogger) {
mUiEventLogger = uiEventLogger;
+ }
+
+ void setCallbacks(ScreenshotViewCallback callbacks) {
mCallbacks = callbacks;
+ }
+
+ void setFlags(FeatureFlags flags) {
mFlags = flags;
}
@@ -755,7 +753,7 @@
InteractionJankMonitor.Configuration.Builder.withView(
CUJ_TAKE_SCREENSHOT, mScreenshotStatic)
.setTag("Actions")
- .setTimeout(mDefaultTimeoutOfTimeoutHandler);
+ .setTimeout(SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS);
mInteractionJankMonitor.begin(builder);
}
});
@@ -781,7 +779,7 @@
return animator;
}
- void badgeScreenshot(Drawable badge) {
+ void badgeScreenshot(@Nullable Drawable badge) {
mScreenshotBadge.setImageDrawable(badge);
mScreenshotBadge.setVisibility(badge != null ? View.VISIBLE : View.GONE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
new file mode 100644
index 0000000..6be32a9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 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.screenshot
+
+import android.animation.Animator
+import android.app.Notification
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.view.ScrollCaptureResponse
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import com.android.systemui.screenshot.scroll.ScrollCaptureController
+
+/** Abstraction of the surface between ScreenshotController and ScreenshotView */
+interface ScreenshotViewProxy {
+ val view: ViewGroup
+ val screenshotPreview: View
+
+ var packageName: String
+ var callbacks: ScreenshotView.ScreenshotViewCallback?
+ var screenshot: ScreenshotData?
+
+ val isAttachedToWindow: Boolean
+ val isDismissing: Boolean
+ val isPendingSharedTransition: Boolean
+
+ fun reset()
+ fun updateInsets(insets: WindowInsets)
+ fun updateOrientation(insets: WindowInsets)
+ fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator
+ fun addQuickShareChip(quickShareAction: Notification.Action)
+ fun setChipIntents(imageData: ScreenshotController.SavedImageData)
+ fun requestDismissal(event: ScreenshotEvent)
+
+ fun showScrollChip(packageName: String, onClick: Runnable)
+ fun hideScrollChip()
+ fun prepareScrollingTransition(
+ response: ScrollCaptureResponse,
+ screenBitmap: Bitmap,
+ newScreenshot: Bitmap,
+ screenshotTakenInPortrait: Boolean,
+ onTransitionPrepared: Runnable,
+ )
+ fun startLongScreenshotTransition(
+ transitionDestination: Rect,
+ onTransitionEnd: Runnable,
+ longScreenshot: ScrollCaptureController.LongScreenshot
+ )
+ fun restoreNonScrollingUi()
+
+ fun stopInputListening()
+ fun requestFocus()
+ fun announceForAccessibility(string: String)
+ fun prepareEntranceAnimation(runnable: Runnable)
+
+ interface Factory {
+ fun getProxy(context: Context, displayId: Int): ScreenshotViewProxy
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index e464fd0..bc33755 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -136,7 +136,7 @@
fun onCloseSystemDialogsReceived() {
screenshotControllers.forEach { (_, screenshotController) ->
if (!screenshotController.isPendingSharedTransition) {
- screenshotController.dismissScreenshot(ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER)
+ screenshotController.requestDismissal(ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 0991c9a..9cf347b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -53,9 +53,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ScreenshotRequest;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -89,7 +89,7 @@
// TODO(b/295143676): move receiver inside executor when the flag is enabled.
mTakeScreenshotExecutor.get().onCloseSystemDialogsReceived();
} else if (!mScreenshot.isPendingSharedTransition()) {
- mScreenshot.dismissScreenshot(SCREENSHOT_DISMISSED_OTHER);
+ mScreenshot.requestDismissal(SCREENSHOT_DISMISSED_OTHER);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TimeoutHandler.java b/packages/SystemUI/src/com/android/systemui/screenshot/TimeoutHandler.java
index 71c2cb4..5df6c45 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TimeoutHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TimeoutHandler.java
@@ -40,7 +40,7 @@
private final Context mContext;
private Runnable mOnTimeout;
- private int mDefaultTimeout = DEFAULT_TIMEOUT_MILLIS;
+ int mDefaultTimeout = DEFAULT_TIMEOUT_MILLIS;
@Inject
public TimeoutHandler(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index aa23d6b..d87d85b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -52,7 +52,7 @@
import com.android.internal.logging.UiEventLogger.UiEventEnum;
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
-import com.android.systemui.screenshot.CropView;
+import com.android.systemui.screenshot.scroll.CropView;
import com.android.systemui.settings.UserTracker;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 3797b8b..9118ee1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -16,25 +16,36 @@
package com.android.systemui.screenshot.dagger;
-import android.app.Service;
+import static com.android.systemui.Flags.screenshotShelfUi;
+import android.app.Service;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.screenshot.DefaultScreenshotActionsProvider;
import com.android.systemui.screenshot.ImageCapture;
import com.android.systemui.screenshot.ImageCaptureImpl;
+import com.android.systemui.screenshot.LegacyScreenshotViewProxy;
import com.android.systemui.screenshot.RequestProcessor;
+import com.android.systemui.screenshot.ScreenshotActionsProvider;
import com.android.systemui.screenshot.ScreenshotPolicy;
import com.android.systemui.screenshot.ScreenshotPolicyImpl;
import com.android.systemui.screenshot.ScreenshotProxyService;
import com.android.systemui.screenshot.ScreenshotRequestProcessor;
+import com.android.systemui.screenshot.ScreenshotShelfViewProxy;
import com.android.systemui.screenshot.ScreenshotSoundController;
import com.android.systemui.screenshot.ScreenshotSoundControllerImpl;
import com.android.systemui.screenshot.ScreenshotSoundProvider;
import com.android.systemui.screenshot.ScreenshotSoundProviderImpl;
+import com.android.systemui.screenshot.ScreenshotViewProxy;
import com.android.systemui.screenshot.TakeScreenshotService;
import com.android.systemui.screenshot.appclips.AppClipsScreenshotHelperService;
import com.android.systemui.screenshot.appclips.AppClipsService;
+import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel;
import dagger.Binds;
import dagger.Module;
+import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
@@ -81,4 +92,26 @@
@Binds
abstract ScreenshotSoundController bindScreenshotSoundController(
ScreenshotSoundControllerImpl screenshotSoundProviderImpl);
+
+ @Binds
+ abstract ScreenshotActionsProvider bindScreenshotActionsProvider(
+ DefaultScreenshotActionsProvider defaultScreenshotActionsProvider);
+
+ @Provides
+ @SysUISingleton
+ static ScreenshotViewModel providesScreenshotViewModel(
+ AccessibilityManager accessibilityManager) {
+ return new ScreenshotViewModel(accessibilityManager);
+ }
+
+ @Provides
+ static ScreenshotViewProxy.Factory providesScreenshotViewProxyFactory(
+ ScreenshotShelfViewProxy.Factory shelfScreenshotViewProxyFactory,
+ LegacyScreenshotViewProxy.Factory legacyScreenshotViewProxyFactory) {
+ if (screenshotShelfUi()) {
+ return shelfScreenshotViewProxyFactory;
+ } else {
+ return legacyScreenshotViewProxyFactory;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
index 2f411ea..5e561cf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -265,7 +265,7 @@
Log.w(TAG, "No boundary selected");
break;
}
- Log.i(TAG, "Updated mCrop: " + mCrop);
+ Log.i(TAG, "Updated mCrop: " + mCrop);
invalidate();
}
@@ -385,7 +385,7 @@
/**
* @param action either ACTION_DOWN, ACTION_UP or ACTION_MOVE.
- * @param x coordinate of the relevant pointer.
+ * @param x x-coordinate of the relevant pointer.
*/
private void updateListener(int action, float x) {
if (mCropInteractionListener != null && isVertical(mCurrentDraggingBoundary)) {
@@ -643,11 +643,13 @@
/**
* Listen for crop motion events and state.
*/
- public interface CropInteractionListener {
+ interface CropInteractionListener {
void onCropDragStarted(CropBoundary boundary, float boundaryPosition,
int boundaryPositionPx, float horizontalCenter, float x);
+
void onCropDragMoved(CropBoundary boundary, float boundaryPosition,
int boundaryPositionPx, float horizontalCenter, float x);
+
void onCropDragComplete();
}
@@ -675,8 +677,7 @@
out.writeParcelable(mCrop, 0);
}
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
+ public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageLoader.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageLoader.java
index 7ee7c31..df86d69 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -35,20 +35,20 @@
import javax.inject.Inject;
/** Loads images. */
-public class ImageLoader {
+class ImageLoader {
private final ContentResolver mResolver;
static class Result {
- @Nullable Uri uri;
- @Nullable File fileName;
- @Nullable Bitmap bitmap;
+ @Nullable Uri mUri;
+ @Nullable File mFilename;
+ @Nullable Bitmap mBitmap;
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Result{");
- sb.append("uri=").append(uri);
- sb.append(", fileName=").append(fileName);
- sb.append(", bitmap=").append(bitmap);
+ sb.append("uri=").append(mUri);
+ sb.append(", fileName=").append(mFilename);
+ sb.append(", bitmap=").append(mBitmap);
sb.append('}');
return sb.toString();
}
@@ -69,11 +69,10 @@
return CallbackToFutureAdapter.getFuture(completer -> {
Result result = new Result();
try (InputStream in = mResolver.openInputStream(uri)) {
- result.uri = uri;
- result.bitmap = BitmapFactory.decodeStream(in);
+ result.mUri = uri;
+ result.mBitmap = BitmapFactory.decodeStream(in);
completer.set(result);
- }
- catch (IOException e) {
+ } catch (IOException e) {
completer.setException(e);
}
return "BitmapFactory#decodeStream";
@@ -91,8 +90,8 @@
return CallbackToFutureAdapter.getFuture(completer -> {
try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
Result result = new Result();
- result.fileName = file;
- result.bitmap = BitmapFactory.decodeStream(in);
+ result.mFilename = file;
+ result.mBitmap = BitmapFactory.decodeStream(in);
completer.set(result);
} catch (IOException e) {
completer.setException(e);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTile.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTile.java
index a95c91b..c9c297e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static android.graphics.ColorSpace.Named.SRGB;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTileSet.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTileSet.java
index 356f67e..76a72f7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTileSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.annotation.AnyThread;
import android.graphics.Bitmap;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
index 00d480a..1e1a577 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -47,11 +47,14 @@
import com.android.internal.view.OneShotPreDrawListener;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
-import com.android.systemui.screenshot.CropView.CropBoundary;
-import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot;
-import com.android.systemui.settings.UserTracker;
+import com.android.systemui.screenshot.ActionIntentCreator;
+import com.android.systemui.screenshot.ActionIntentExecutor;
+import com.android.systemui.screenshot.ImageExporter;
+import com.android.systemui.screenshot.LogConfig;
+import com.android.systemui.screenshot.ScreenshotEvent;
+import com.android.systemui.screenshot.scroll.CropView.CropBoundary;
+import com.android.systemui.screenshot.scroll.ScrollCaptureController.LongScreenshot;
import com.google.common.util.concurrent.ListenableFuture;
@@ -81,8 +84,6 @@
private final ImageExporter mImageExporter;
private final LongScreenshotData mLongScreenshotHolder;
private final ActionIntentExecutor mActionExecutor;
- private final FeatureFlags mFeatureFlags;
- private final UserTracker mUserTracker;
private ImageView mPreview;
private ImageView mTransitionView;
@@ -113,16 +114,13 @@
@Inject
public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
@Main Executor mainExecutor, @Background Executor bgExecutor,
- LongScreenshotData longScreenshotHolder, ActionIntentExecutor actionExecutor,
- FeatureFlags featureFlags, UserTracker userTracker) {
+ LongScreenshotData longScreenshotHolder, ActionIntentExecutor actionExecutor) {
mUiEventLogger = uiEventLogger;
mUiExecutor = mainExecutor;
mBackgroundExecutor = bgExecutor;
mImageExporter = imageExporter;
mLongScreenshotHolder = longScreenshotHolder;
mActionExecutor = actionExecutor;
- mFeatureFlags = featureFlags;
- mUserTracker = userTracker;
}
@@ -265,13 +263,13 @@
private void onCachedImageLoaded(ImageLoader.Result imageResult) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_CACHED_IMAGE_LOADED);
- BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap);
+ BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.mBitmap);
mPreview.setImageDrawable(drawable);
mPreview.setAlpha(1f);
- mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(),
- imageResult.bitmap.getHeight());
+ mMagnifierView.setDrawable(drawable, imageResult.mBitmap.getWidth(),
+ imageResult.mBitmap.getHeight());
mCropView.setVisibility(View.VISIBLE);
- mSavedImagePath = imageResult.fileName;
+ mSavedImagePath = imageResult.mFilename;
setButtonsEnabled(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
index f549faf..ebac5bf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.screenshot.ScreenshotController;
import java.util.concurrent.atomic.AtomicReference;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/MagnifierView.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/MagnifierView.java
index 0c543cd..0a1a747 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/MagnifierView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -64,16 +64,16 @@
private ViewPropertyAnimator mTranslationAnimator;
private final Animator.AnimatorListener mTranslationAnimatorListener =
new AnimatorListenerAdapter() {
- @Override
- public void onAnimationCancel(Animator animation) {
- mTranslationAnimator = null;
- }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mTranslationAnimator = null;
+ }
- @Override
- public void onAnimationEnd(Animator animation) {
- mTranslationAnimator = null;
- }
- };
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mTranslationAnimator = null;
+ }
+ };
public MagnifierView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureClient.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureClient.java
index e93f737..0e43343 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL;
@@ -46,6 +46,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.screenshot.LogConfig;
import com.google.common.util.concurrent.ListenableFuture;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureController.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureController.java
index bb34ede..f4c77da 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.content.Context;
import android.graphics.Bitmap;
@@ -30,8 +30,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
-import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.android.systemui.screenshot.LogConfig;
+import com.android.systemui.screenshot.ScreenshotEvent;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.CaptureResult;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.Session;
import com.google.common.util.concurrent.ListenableFuture;
@@ -75,7 +77,7 @@
private String mWindowOwner;
private volatile boolean mCancelled;
- static class LongScreenshot {
+ public static class LongScreenshot {
private final ImageTileSet mImageTileSet;
private final Session mSession;
// TODO: Add UserHandle so LongScreenshots can adhere to work profile screenshot policy
@@ -85,7 +87,7 @@
mImageTileSet = imageTileSet;
}
- /** Returns a bitmap containing the combinded result. */
+ /** Returns a bitmap containing the combined result. */
public Bitmap toBitmap() {
return mImageTileSet.toBitmap();
}
@@ -167,7 +169,7 @@
* {@link ScrollCaptureResponse#isConnected() connected}.
* @return a future ImageTile set containing the result
*/
- ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
+ public ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
mCancelled = false;
return CallbackToFutureAdapter.getFuture(completer -> {
mCaptureCompleter = completer;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/TiledImageDrawable.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/TiledImageDrawable.java
index 71df369..00455bc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/TiledImageDrawable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.annotation.Nullable;
import android.graphics.Canvas;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
new file mode 100644
index 0000000..2c17873
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 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.screenshot.ui
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.view.View
+
+class ScreenshotAnimationController(private val view: View) {
+ private var animator: Animator? = null
+
+ fun getEntranceAnimation(): Animator {
+ val animator = ValueAnimator.ofFloat(0f, 1f)
+ animator.addUpdateListener { view.alpha = it.animatedFraction }
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animator: Animator) {
+ view.alpha = 0f
+ }
+ override fun onAnimationEnd(animator: Animator) {
+ view.alpha = 1f
+ }
+ }
+ )
+ this.animator = animator
+ return animator
+ }
+
+ fun getExitAnimation(): Animator {
+ val animator = ValueAnimator.ofFloat(1f, 0f)
+ animator.addUpdateListener { view.alpha = it.animatedValue as Float }
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animator: Animator) {
+ view.alpha = 1f
+ }
+ override fun onAnimationEnd(animator: Animator) {
+ view.alpha = 0f
+ }
+ }
+ )
+ this.animator = animator
+ return animator
+ }
+
+ fun cancel() {
+ animator?.cancel()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
new file mode 100644
index 0000000..747ad4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 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.screenshot.ui
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.ImageView
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.android.systemui.res.R
+
+class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
+ ConstraintLayout(context, attrs) {
+ lateinit var screenshotPreview: ImageView
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ screenshotPreview = requireViewById(R.id.screenshot_preview)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
new file mode 100644
index 0000000..a5825b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 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.screenshot.ui.binder
+
+import android.view.View
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.res.R
+import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel
+
+object ActionButtonViewBinder {
+ /** Binds the given view to the given view-model */
+ fun bind(view: View, viewModel: ActionButtonViewModel) {
+ val iconView = view.requireViewById<ImageView>(R.id.overlay_action_chip_icon)
+ val textView = view.requireViewById<TextView>(R.id.overlay_action_chip_text)
+ iconView.setImageDrawable(viewModel.icon)
+ textView.text = viewModel.name
+ setMargins(iconView, textView, viewModel.name?.isNotEmpty() ?: false)
+ if (viewModel.onClicked != null) {
+ view.setOnClickListener { viewModel.onClicked.invoke() }
+ } else {
+ view.setOnClickListener(null)
+ }
+ view.visibility = View.VISIBLE
+ view.alpha = 1f
+ }
+
+ private fun setMargins(iconView: View, textView: View, hasText: Boolean) {
+ val iconParams = iconView.layoutParams as LinearLayout.LayoutParams
+ val textParams = textView.layoutParams as LinearLayout.LayoutParams
+ if (hasText) {
+ iconParams.marginStart = iconView.dpToPx(R.dimen.overlay_action_chip_padding_start)
+ iconParams.marginEnd = iconView.dpToPx(R.dimen.overlay_action_chip_spacing)
+ textParams.marginStart = 0
+ textParams.marginEnd = textView.dpToPx(R.dimen.overlay_action_chip_padding_end)
+ } else {
+ val paddingHorizontal =
+ iconView.dpToPx(R.dimen.overlay_action_chip_icon_only_padding_horizontal)
+ iconParams.marginStart = paddingHorizontal
+ iconParams.marginEnd = paddingHorizontal
+ }
+ iconView.layoutParams = iconParams
+ textView.layoutParams = textParams
+ }
+
+ private fun View.dpToPx(dimenId: Int): Int {
+ return this.resources.getDimensionPixelSize(dimenId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
new file mode 100644
index 0000000..3bcd52c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 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.screenshot.ui.binder
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
+import com.android.systemui.util.children
+import kotlinx.coroutines.launch
+
+object ScreenshotShelfViewBinder {
+ fun bind(
+ view: ViewGroup,
+ viewModel: ScreenshotViewModel,
+ layoutInflater: LayoutInflater,
+ ) {
+ val previewView: ImageView = view.requireViewById(R.id.screenshot_preview)
+ val previewBorder = view.requireViewById<View>(R.id.screenshot_preview_border)
+ previewView.clipToOutline = true
+ val actionsContainer: LinearLayout = view.requireViewById(R.id.screenshot_actions)
+ view.requireViewById<View>(R.id.screenshot_dismiss_button).visibility =
+ if (viewModel.showDismissButton) View.VISIBLE else View.GONE
+
+ view.repeatWhenAttached {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.preview.collect { bitmap ->
+ if (bitmap != null) {
+ previewView.setImageBitmap(bitmap)
+ previewView.visibility = View.VISIBLE
+ previewBorder.visibility = View.VISIBLE
+ } else {
+ previewView.visibility = View.GONE
+ previewBorder.visibility = View.GONE
+ }
+ }
+ }
+ launch {
+ viewModel.actions.collect { actions ->
+ if (actions.isNotEmpty()) {
+ view
+ .requireViewById<View>(R.id.actions_container_background)
+ .visibility = View.VISIBLE
+ }
+ val viewPool = actionsContainer.children.toList()
+ actionsContainer.removeAllViews()
+ val actionButtons =
+ List(actions.size) {
+ viewPool.getOrElse(it) {
+ layoutInflater.inflate(
+ R.layout.overlay_action_chip,
+ actionsContainer,
+ false
+ )
+ }
+ }
+ actionButtons.zip(actions).forEach {
+ actionsContainer.addView(it.first)
+ ActionButtonViewBinder.bind(it.first, it.second)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt
new file mode 100644
index 0000000..6ee9705
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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.screenshot.ui.viewmodel
+
+import android.graphics.drawable.Drawable
+
+data class ActionButtonViewModel(
+ val icon: Drawable?,
+ val name: String?,
+ val onClicked: (() -> Unit)?
+)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
new file mode 100644
index 0000000..3a652d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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.screenshot.ui.viewmodel
+
+import android.graphics.Bitmap
+import android.view.accessibility.AccessibilityManager
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager) {
+ private val _preview = MutableStateFlow<Bitmap?>(null)
+ val preview: StateFlow<Bitmap?> = _preview
+ private val _actions = MutableStateFlow(emptyList<ActionButtonViewModel>())
+ val actions: StateFlow<List<ActionButtonViewModel>> = _actions
+ val showDismissButton: Boolean
+ get() = accessibilityManager.isEnabled
+
+ fun setScreenshotBitmap(bitmap: Bitmap?) {
+ _preview.value = bitmap
+ }
+
+ fun setActions(actions: List<ActionButtonViewModel>) {
+ _actions.value = actions
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index d1055c7..9cfedaab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -76,6 +76,8 @@
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
import com.android.systemui.keyguard.shared.model.DismissAction;
import com.android.systemui.keyguard.shared.model.KeyguardDone;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.TaskbarDelegate;
@@ -101,6 +103,8 @@
import dagger.Lazy;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -112,6 +116,7 @@
import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
+import kotlinx.coroutines.Job;
/**
* Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
@@ -158,6 +163,9 @@
private final BouncerView mPrimaryBouncerView;
private final Lazy<ShadeController> mShadeController;
+ private Job mListenForAlternateBouncerTransitionSteps = null;
+ private Job mListenForKeyguardAuthenticatedBiometricsHandled = null;
+
// Local cache of expansion events, to avoid duplicates
private float mFraction = -1f;
private boolean mTracking = false;
@@ -482,6 +490,26 @@
mDockManager.addListener(mDockEventListener);
mIsDocked = mDockManager.isDocked();
}
+ if (mListenForAlternateBouncerTransitionSteps != null) {
+ mListenForAlternateBouncerTransitionSteps.cancel(null);
+ }
+ mListenForAlternateBouncerTransitionSteps = null;
+ if (mListenForKeyguardAuthenticatedBiometricsHandled != null) {
+ mListenForKeyguardAuthenticatedBiometricsHandled.cancel(null);
+ }
+ mListenForKeyguardAuthenticatedBiometricsHandled = null;
+ if (!DeviceEntryUdfpsRefactor.isEnabled()) {
+ mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow(
+ mKeyguardTransitionInteractor.transitionStepsFromState(
+ KeyguardState.ALTERNATE_BOUNCER),
+ this::consumeFromAlternateBouncerTransitionSteps
+ );
+
+ mListenForKeyguardAuthenticatedBiometricsHandled = mJavaAdapter.alwaysCollectFlow(
+ mPrimaryBouncerInteractor.getKeyguardAuthenticatedBiometricsHandled(),
+ this::consumeKeyguardAuthenticatedBiometricsHandled
+ );
+ }
if (KeyguardWmStateRefactor.isEnabled()) {
// Show the keyguard views whenever we've told WM that the lockscreen is visible.
@@ -500,6 +528,22 @@
}
}
+ @VisibleForTesting
+ void consumeFromAlternateBouncerTransitionSteps(TransitionStep step) {
+ hideAlternateBouncer(false);
+ }
+
+ /**
+ * Required without fix for b/328643370: missing AlternateBouncer (when occluded) => Gone
+ * transition.
+ */
+ @VisibleForTesting
+ void consumeKeyguardAuthenticatedBiometricsHandled(Unit handled) {
+ if (mAlternateBouncerInteractor.isVisibleState()) {
+ hideAlternateBouncer(false);
+ }
+ }
+
private void consumeShowStatusBarKeyguardView(boolean show) {
if (show != mLastShowing) {
if (show) {
@@ -1441,7 +1485,6 @@
mPrimaryBouncerInteractor.notifyKeyguardAuthenticatedBiometrics(strongAuth);
if (mAlternateBouncerInteractor.isVisibleState()) {
- hideAlternateBouncer(false);
executeAfterKeyguardGoneAction();
}
}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 5882b56..572a6c1 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -113,7 +113,7 @@
android:excludeFromRecents="true"
/>
- <activity android:name="com.android.systemui.screenshot.ScrollViewActivity"
+ <activity android:name="com.android.systemui.screenshot.scroll.ScrollViewActivity"
android:exported="false" />
<activity android:name="com.android.systemui.screenshot.RecyclerViewActivity"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index 87391cc..d410dac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -36,6 +37,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.Mockito.verify
@ExperimentalCoroutinesApi
@RunWith(JUnit4::class)
@@ -48,6 +50,20 @@
private val underTest = kosmos.alternateBouncerViewModel
@Test
+ fun showPrimaryBouncer() =
+ testScope.runTest {
+ underTest.showPrimaryBouncer()
+ verify(statusBarKeyguardViewManager).showPrimaryBouncer(any())
+ }
+
+ @Test
+ fun hideAlternateBouncer() =
+ testScope.runTest {
+ underTest.hideAlternateBouncer()
+ verify(statusBarKeyguardViewManager).hideAlternateBouncer(any())
+ }
+
+ @Test
fun transitionToAlternateBouncer_scrimAlphaUpdate() =
testScope.runTest {
val scrimAlphas by collectValues(underTest.scrimAlpha)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
deleted file mode 100644
index 9ea30d6..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2020 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.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.os.Bundle;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class ActionProxyReceiverTest extends SysuiTestCase {
- @Mock
- private ActivityManagerWrapper mMockActivityManagerWrapper;
- @Mock
- private ScreenshotSmartActions mMockScreenshotSmartActions;
- @Mock
- private PendingIntent mMockPendingIntent;
- @Mock
- private ActivityStarter mActivityStarter;
-
- private Intent mIntent;
- private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-
- @Before
- public void setup() throws InterruptedException, ExecutionException, TimeoutException {
- MockitoAnnotations.initMocks(this);
- mIntent = new Intent(mContext, ActionProxyReceiver.class)
- .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, mMockPendingIntent);
- }
-
- @Test
- public void testPendingIntentSentWithStatusBar() throws PendingIntent.CanceledException {
- ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver();
- // ensure that the pending intent call is passed through
- doAnswer((Answer<Object>) invocation -> {
- ((Runnable) invocation.getArgument(0)).run();
- return null;
- }).when(mActivityStarter).executeRunnableDismissingKeyguard(
- any(Runnable.class), isNull(), anyBoolean(), anyBoolean(), anyBoolean());
-
- actionProxyReceiver.onReceive(mContext, mIntent);
-
- verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
- verify(mActivityStarter).executeRunnableDismissingKeyguard(
- any(Runnable.class), isNull(), eq(true), eq(true), eq(true));
- verify(mMockPendingIntent).send(
- eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
- }
-
- @Test
- public void testSmartActionsNotNotifiedByDefault() {
- ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver();
-
- actionProxyReceiver.onReceive(mContext, mIntent);
-
- verify(mMockScreenshotSmartActions, never())
- .notifyScreenshotAction(anyString(), anyString(), anyBoolean(),
- any(Intent.class));
- }
-
- @Test
- public void testSmartActionsNotifiedIfEnabled() {
- ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver();
- mIntent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
- String testId = "testID";
- mIntent.putExtra(EXTRA_ID, testId);
-
- actionProxyReceiver.onReceive(mContext, mIntent);
-
- verify(mMockScreenshotSmartActions).notifyScreenshotAction(
- testId, ACTION_TYPE_SHARE, false, null);
- }
-
- private ActionProxyReceiver constructActionProxyReceiver() {
- return new ActionProxyReceiver(
- mMockActivityManagerWrapper,
- mMockScreenshotSmartActions,
- mDisplayTracker,
- mActivityStarter
- );
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
deleted file mode 100644
index d58f47a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2020 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.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_DELETE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_URI_ID;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Environment;
-import android.provider.MediaStore;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.util.concurrent.Executor;
-
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class DeleteScreenshotReceiverTest extends SysuiTestCase {
-
- @Mock
- private ScreenshotSmartActions mMockScreenshotSmartActions;
- @Mock
- private Executor mMockExecutor;
-
- private DeleteScreenshotReceiver mDeleteScreenshotReceiver;
- private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mDeleteScreenshotReceiver =
- new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mMockExecutor);
- }
-
- @Test
- public void testNoUriProvided() {
- Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
-
- mDeleteScreenshotReceiver.onReceive(mContext, intent);
-
- verify(mMockExecutor, never()).execute(any(Runnable.class));
- verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
- any(String.class), any(String.class), anyBoolean(),
- any(Intent.class));
- }
-
- @Test
- public void testFileDeleted() {
- DeleteScreenshotReceiver deleteScreenshotReceiver =
- new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mFakeExecutor);
- ContentResolver contentResolver = mContext.getContentResolver();
- final Uri testUri = contentResolver.insert(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI, getFakeContentValues());
- assertNotNull(testUri);
-
- try {
- Cursor cursor =
- contentResolver.query(testUri, null, null, null, null);
- assertEquals(1, cursor.getCount());
- Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class)
- .putExtra(SCREENSHOT_URI_ID, testUri.toString());
-
- deleteScreenshotReceiver.onReceive(mContext, intent);
- int runCount = mFakeExecutor.runAllReady();
-
- assertEquals(1, runCount);
- cursor =
- contentResolver.query(testUri, null, null, null, null);
- assertEquals(0, cursor.getCount());
- } finally {
- contentResolver.delete(testUri, null, null);
- }
-
- // ensure smart actions not called by default
- verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
- any(String.class), any(String.class), anyBoolean(), any(Intent.class));
- }
-
- @Test
- public void testNotifyScreenshotAction() {
- Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
- String uriString = "testUri";
- String testId = "testID";
- intent.putExtra(SCREENSHOT_URI_ID, uriString);
- intent.putExtra(EXTRA_ID, testId);
- intent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
-
- mDeleteScreenshotReceiver.onReceive(mContext, intent);
-
- verify(mMockExecutor).execute(any(Runnable.class));
- verify(mMockScreenshotSmartActions).notifyScreenshotAction(testId,
- ACTION_TYPE_DELETE, false, null);
- }
-
- private static ContentValues getFakeContentValues() {
- final ContentValues values = new ContentValues();
- values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
- + File.separator + Environment.DIRECTORY_SCREENSHOTS);
- values.put(MediaStore.MediaColumns.DISPLAY_NAME, "test_screenshot");
- values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
- values.put(MediaStore.MediaColumns.DATE_ADDED, 0);
- values.put(MediaStore.MediaColumns.DATE_MODIFIED, 0);
- return values;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
index 2f911fff..92c2404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
@@ -22,8 +22,10 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import java.lang.IllegalStateException
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -31,12 +33,14 @@
import org.mockito.Mockito.verify
@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
class ScreenshotSoundControllerTest : SysuiTestCase() {
private val soundProvider = mock<ScreenshotSoundProvider>()
private val mediaPlayer = mock<MediaPlayer>()
private val bgDispatcher = UnconfinedTestDispatcher()
private val scope = TestScope(bgDispatcher)
+
@Before
fun setup() {
whenever(soundProvider.getScreenshotSound()).thenReturn(mediaPlayer)
@@ -45,52 +49,59 @@
@Test
fun init_soundLoading() {
createController()
- bgDispatcher.scheduler.runCurrent()
+ scope.advanceUntilIdle()
verify(soundProvider).getScreenshotSound()
}
@Test
- fun init_soundLoadingException_playAndReleaseDoNotThrow() = runTest {
- whenever(soundProvider.getScreenshotSound()).thenThrow(IllegalStateException())
+ fun init_soundLoadingException_playAndReleaseDoNotThrow() =
+ scope.runTest {
+ whenever(soundProvider.getScreenshotSound()).thenThrow(IllegalStateException())
- val controller = createController()
+ val controller = createController()
- controller.playCameraSound().await()
- controller.releaseScreenshotSound().await()
+ controller.playScreenshotSound()
+ advanceUntilIdle()
- verify(mediaPlayer, never()).start()
- verify(mediaPlayer, never()).release()
- }
+ verify(mediaPlayer, never()).start()
+ verify(mediaPlayer, never()).release()
+ }
@Test
- fun playCameraSound_soundLoadingSuccessful_mediaPlayerPlays() = runTest {
- val controller = createController()
+ fun playCameraSound_soundLoadingSuccessful_mediaPlayerPlays() =
+ scope.runTest {
+ val controller = createController()
- controller.playCameraSound().await()
+ controller.playScreenshotSound()
+ advanceUntilIdle()
- verify(mediaPlayer).start()
- }
+ verify(mediaPlayer).start()
+ }
@Test
- fun playCameraSound_illegalStateException_doesNotThrow() = runTest {
- whenever(mediaPlayer.start()).thenThrow(IllegalStateException())
+ fun playCameraSound_illegalStateException_doesNotThrow() =
+ scope.runTest {
+ whenever(mediaPlayer.start()).thenThrow(IllegalStateException())
- val controller = createController()
- controller.playCameraSound().await()
+ val controller = createController()
+ controller.playScreenshotSound()
+ advanceUntilIdle()
- verify(mediaPlayer).start()
- verify(mediaPlayer).release()
- }
+ verify(mediaPlayer).start()
+ verify(mediaPlayer).release()
+ }
@Test
- fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() = runTest {
- val controller = createController()
+ fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() =
+ scope.runTest {
+ val controller = createController()
- controller.releaseScreenshotSound().await()
+ controller.releaseScreenshotSound()
+ advanceUntilIdle()
- verify(mediaPlayer).release()
- }
+ verify(mediaPlayer).release()
+ }
private fun createController() =
ScreenshotSoundControllerImpl(soundProvider, scope, bgDispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index 3dc9037..0baee5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -274,8 +274,8 @@
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
screenshotExecutor.onCloseSystemDialogsReceived()
- verify(controller0).dismissScreenshot(any())
- verify(controller1).dismissScreenshot(any())
+ verify(controller0).requestDismissal(any())
+ verify(controller1).requestDismissal(any())
screenshotExecutor.onDestroy()
}
@@ -290,8 +290,8 @@
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
screenshotExecutor.onCloseSystemDialogsReceived()
- verify(controller0, never()).dismissScreenshot(any())
- verify(controller1).dismissScreenshot(any())
+ verify(controller0, never()).requestDismissal(any())
+ verify(controller1).requestDismissal(any())
screenshotExecutor.onDestroy()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
index 4c8a4b0..aad46139 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static com.google.common.util.concurrent.Futures.getUnchecked;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureClientTest.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureClientTest.java
index 670a130..1023260 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureClientTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static org.junit.Assert.assertEquals;
@@ -37,8 +37,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
-import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.CaptureResult;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.Session;
import com.google.common.util.concurrent.ListenableFuture;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
index 6f081c7..f39f543 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static com.google.common.util.concurrent.Futures.getUnchecked;
import static com.google.common.util.concurrent.Futures.immediateFuture;
@@ -36,7 +36,7 @@
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.Session;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
index de97bc3..5699cfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
index 4c84df2..04aba11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.app.Activity;
import android.os.Bundle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 3666248..a715556 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -82,6 +82,9 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
+import com.android.systemui.keyguard.shared.model.TransitionState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.TaskbarDelegate;
import com.android.systemui.plugins.ActivityStarter;
@@ -101,6 +104,8 @@
import com.google.common.truth.Truth;
+import kotlin.Unit;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -1039,4 +1044,35 @@
verify(mCentralSurfaces, never()).hideKeyguard();
verify(mPrimaryBouncerInteractor, never()).show(true);
}
+
+ @Test
+ public void altBouncerNotVisible_keyguardAuthenticatedBiometricsHandled() {
+ clearInvocations(mAlternateBouncerInteractor);
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
+ mStatusBarKeyguardViewManager.consumeKeyguardAuthenticatedBiometricsHandled(Unit.INSTANCE);
+ verify(mAlternateBouncerInteractor, never()).hide();
+ }
+
+ @Test
+ public void altBouncerVisible_keyguardAuthenticatedBiometricsHandled() {
+ clearInvocations(mAlternateBouncerInteractor);
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+ mStatusBarKeyguardViewManager.consumeKeyguardAuthenticatedBiometricsHandled(Unit.INSTANCE);
+ verify(mAlternateBouncerInteractor).hide();
+ }
+
+ @Test
+ public void fromAlternateBouncerTransitionStep() {
+ clearInvocations(mAlternateBouncerInteractor);
+ mStatusBarKeyguardViewManager.consumeFromAlternateBouncerTransitionSteps(
+ new TransitionStep(
+ /* from */ KeyguardState.ALTERNATE_BOUNCER,
+ /* to */ KeyguardState.GONE,
+ /* value */ 1f,
+ TransitionState.FINISHED,
+ "StatusBarKeyguardViewManagerTest"
+ )
+ );
+ verify(mAlternateBouncerInteractor).hide();
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeScrollCaptureConnection.java
similarity index 97%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeScrollCaptureConnection.java
index 63f7c97..ea59c0a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeScrollCaptureConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeSession.java b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeSession.java
similarity index 97%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeSession.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeSession.java
index 478658e..3b7b158 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeSession.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static android.util.MathUtils.constrain;
@@ -32,6 +32,8 @@
import android.media.Image;
import android.util.Log;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient;
+
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 21a02ed2..dedc96e 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -105,6 +105,7 @@
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pair;
+import android.util.SizeF;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -159,6 +160,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider,
OnCrossProfileWidgetProvidersChangeListener {
@@ -172,6 +174,9 @@
private static final String STATE_FILENAME = "appwidgets.xml";
+ private static final String KEY_SIZES = "sizes";
+ private static final String SIZE_SEPARATOR = ",";
+
private static final int MIN_UPDATE_PERIOD = DEBUG ? 0 : 30 * 60 * 1000; // 30 minutes
private static final int TAG_UNDEFINED = -1;
@@ -2677,6 +2682,13 @@
out.attributeIntHex(null, "max_height", (maxHeight > 0) ? maxHeight : 0);
out.attributeIntHex(null, "host_category", widget.options.getInt(
AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY));
+ List<SizeF> sizes = widget.options.getParcelableArrayList(
+ AppWidgetManager.OPTION_APPWIDGET_SIZES, SizeF.class);
+ if (sizes != null) {
+ String sizeStr = sizes.stream().map(SizeF::toString)
+ .collect(Collectors.joining(SIZE_SEPARATOR));
+ out.attribute(null, KEY_SIZES, sizeStr);
+ }
if (saveRestoreCompleted) {
boolean restoreCompleted = widget.options.getBoolean(
AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED);
@@ -2708,6 +2720,17 @@
if (maxHeight != -1) {
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight);
}
+ String sizesStr = parser.getAttributeValue(null, KEY_SIZES);
+ if (sizesStr != null) {
+ try {
+ ArrayList<SizeF> sizes = Arrays.stream(sizesStr.split(SIZE_SEPARATOR))
+ .map(SizeF::parseSizeF)
+ .collect(Collectors.toCollection(ArrayList::new));
+ options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, sizes);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Error parsing widget sizes", e);
+ }
+ }
int category = parser.getAttributeIntHex(null, "host_category",
AppWidgetProviderInfo.WIDGET_CATEGORY_UNKNOWN);
if (category != AppWidgetProviderInfo.WIDGET_CATEGORY_UNKNOWN) {
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 40b2f5a..10030b3 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -21,6 +21,7 @@
import android.os.IBinder;
import android.os.PowerManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.brightness.clamper.HdrClamper;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -30,8 +31,7 @@
class BrightnessRangeController {
private final HighBrightnessModeController mHbmController;
- private final NormalBrightnessModeController mNormalBrightnessModeController =
- new NormalBrightnessModeController();
+ private final NormalBrightnessModeController mNormalBrightnessModeController;
private final HdrClamper mHdrClamper;
@@ -45,17 +45,21 @@
Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler,
DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
this(hbmController, modeChangeCallback, displayDeviceConfig,
+ new NormalBrightnessModeController(),
new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags,
displayToken, info);
}
+ @VisibleForTesting
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
+ NormalBrightnessModeController normalBrightnessModeController,
HdrClamper hdrClamper, DisplayManagerFlags flags, IBinder displayToken,
DisplayDeviceInfo info) {
mHbmController = hbmController;
mModeChangeCallback = modeChangeCallback;
mHdrClamper = hdrClamper;
+ mNormalBrightnessModeController = normalBrightnessModeController;
mUseHdrClamper = flags.isHdrClamperEnabled();
mUseNbmController = flags.isNbmControllerEnabled();
if (mUseNbmController) {
@@ -126,8 +130,11 @@
float getCurrentBrightnessMax() {
- if (mUseNbmController && mHbmController.getHighBrightnessMode()
- == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF) {
+ // nbmController might adjust maxBrightness only if device does not support HBM or
+ // hbm is currently not allowed
+ if (mUseNbmController
+ && (!mHbmController.deviceSupportsHbm()
+ || !mHbmController.isHbmCurrentlyAllowed())) {
return Math.min(mHbmController.getCurrentBrightnessMax(),
mNormalBrightnessModeController.getCurrentBrightnessMax());
}
diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
index ab7c503..a12d248 100644
--- a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
+++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
@@ -42,6 +42,9 @@
import com.android.server.display.notifications.DisplayNotificationManager;
import com.android.server.display.utils.DebugUtils;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* Listens for Skin thermal sensor events, disables external displays if thermal status becomes
* equal or above {@link android.os.Temperature#THROTTLING_CRITICAL}, enables external displays if
@@ -106,6 +109,10 @@
private final ExternalDisplayStatsService mExternalDisplayStatsService;
@ThrottlingStatus
private volatile int mStatus = THROTTLING_NONE;
+ //@GuardedBy("mSyncRoot")
+ private boolean mIsBootCompleted;
+ //@GuardedBy("mSyncRoot")
+ private final Set<Integer> mDisplayIdsWaitingForBootCompletion = new HashSet<>();
ExternalDisplayPolicy(@NonNull final Injector injector) {
mInjector = injector;
@@ -121,6 +128,17 @@
* Starts listening for temperature changes.
*/
void onBootCompleted() {
+ synchronized (mSyncRoot) {
+ mIsBootCompleted = true;
+ for (var displayId : mDisplayIdsWaitingForBootCompletion) {
+ var logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (logicalDisplay != null) {
+ handleExternalDisplayConnectedLocked(logicalDisplay);
+ }
+ }
+ mDisplayIdsWaitingForBootCompletion.clear();
+ }
+
if (!mFlags.isConnectedDisplayManagementEnabled()) {
if (DEBUG) {
Slog.d(TAG, "External display management is not enabled on your device:"
@@ -189,6 +207,11 @@
return;
}
+ if (!mIsBootCompleted) {
+ mDisplayIdsWaitingForBootCompletion.add(logicalDisplay.getDisplayIdLocked());
+ return;
+ }
+
mExternalDisplayStatsService.onDisplayConnected(logicalDisplay);
if ((Build.IS_ENG || Build.IS_USERDEBUG)
@@ -227,7 +250,12 @@
return;
}
- mExternalDisplayStatsService.onDisplayDisconnected(logicalDisplay.getDisplayIdLocked());
+ var displayId = logicalDisplay.getDisplayIdLocked();
+ if (mDisplayIdsWaitingForBootCompletion.remove(displayId)) {
+ return;
+ }
+
+ mExternalDisplayStatsService.onDisplayDisconnected(displayId);
}
/**
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index a9f78fd..47176fe 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -168,7 +168,7 @@
}
float getCurrentBrightnessMax() {
- if (!deviceSupportsHbm() || isCurrentlyAllowed()) {
+ if (!deviceSupportsHbm() || isHbmCurrentlyAllowed()) {
// Either the device doesn't support HBM, or HBM range is currently allowed (device
// it in a high-lux environment). In either case, return the highest brightness
// level supported by the device.
@@ -356,7 +356,7 @@
return event.getStartTimeMillis();
}
- private boolean isCurrentlyAllowed() {
+ boolean isHbmCurrentlyAllowed() {
// Returns true if HBM is allowed (above the ambient lux threshold) and there's still
// time within the current window for additional HBM usage. We return false if there is an
// HDR layer because we don't want the brightness MAX to change for HDR, which has its
@@ -369,7 +369,7 @@
&& !mIsBlockedByLowPowerMode);
}
- private boolean deviceSupportsHbm() {
+ boolean deviceSupportsHbm() {
return mHbmData != null && mHighBrightnessModeMetadata != null;
}
@@ -462,7 +462,7 @@
+ ", isOnlyAllowedToStayOn: " + isOnlyAllowedToStayOn
+ ", remainingAllowedTime: " + remainingTime
+ ", isLuxHigh: " + mIsInAllowedAmbientRange
- + ", isHBMCurrentlyAllowed: " + isCurrentlyAllowed()
+ + ", isHBMCurrentlyAllowed: " + isHbmCurrentlyAllowed()
+ ", isHdrLayerPresent: " + mIsHdrLayerPresent
+ ", mMaxDesiredHdrSdrRatio: " + mMaxDesiredHdrSdrRatio
+ ", isAutoBrightnessEnabled: " + mIsAutoBrightnessEnabled
@@ -575,7 +575,7 @@
return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
} else if (mIsHdrLayerPresent) {
return BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR;
- } else if (isCurrentlyAllowed()) {
+ } else if (isHbmCurrentlyAllowed()) {
return BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 186cf5e..ef9acc4 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3598,7 +3598,8 @@
continue;
}
if ((scanFlags & SCAN_DROP_CACHE) != 0) {
- final PackageCacher cacher = new PackageCacher(mPm.getCacheDir());
+ final PackageCacher cacher = new PackageCacher(mPm.getCacheDir(),
+ mPm.mPackageParserCallback);
Log.w(TAG, "Dropping cache of " + file.getAbsolutePath());
cacher.cleanCachedResult(file);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fe8030b..c814a1e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1741,7 +1741,7 @@
() -> LocalServices.getService(UserManagerInternal.class)),
(i, pm) -> new DisplayMetrics(),
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(),
- new PackageCacher(pm.mCacheDir),
+ new PackageCacher(pm.mCacheDir, pm.mPackageParserCallback),
pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(), null,
pm.mPackageParserCallback) /* scanningPackageParserProducer */,
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index b6267c4..2db454a 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -17,6 +17,7 @@
package com.android.server.pm.parsing;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.pm.PackageParserCacheHelper;
import android.os.Environment;
import android.os.FileUtils;
@@ -29,8 +30,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.pm.parsing.IPackageCacher;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.ApexManager;
import libcore.io.IoUtils;
@@ -51,9 +54,16 @@
@NonNull
private final File mCacheDir;
+ @Nullable
+ private final PackageParser2.Callback mCallback;
- public PackageCacher(@NonNull File cacheDir) {
+ public PackageCacher(File cacheDir) {
+ this(cacheDir, null);
+ }
+
+ public PackageCacher(File cacheDir, @Nullable PackageParser2.Callback callback) {
this.mCacheDir = cacheDir;
+ this.mCallback = callback;
}
/**
@@ -71,12 +81,17 @@
@VisibleForTesting
protected ParsedPackage fromCacheEntry(byte[] bytes) {
- return fromCacheEntryStatic(bytes);
+ return fromCacheEntryStatic(bytes, mCallback);
}
/** static version of {@link #fromCacheEntry} for unit tests. */
@VisibleForTesting
public static ParsedPackage fromCacheEntryStatic(byte[] bytes) {
+ return fromCacheEntryStatic(bytes, null);
+ }
+
+ private static ParsedPackage fromCacheEntryStatic(byte[] bytes,
+ @Nullable ParsingPackageUtils.Callback callback) {
final Parcel p = Parcel.obtain();
p.unmarshall(bytes, 0, bytes.length);
p.setDataPosition(0);
@@ -85,7 +100,7 @@
new PackageParserCacheHelper.ReadHelper(p);
helper.startAndInstall();
- ParsedPackage pkg = new PackageImpl(p);
+ ParsedPackage pkg = new PackageImpl(p, callback);
p.recycle();
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index b18503d..1c70af0 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -596,7 +596,7 @@
ai.requiredDisplayCategory = a.getRequiredDisplayCategory();
ai.requireContentUriPermissionFromCaller = a.getRequireContentUriPermissionFromCaller();
ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
- assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, state, userId);
+ assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, userId);
return ai;
}
@@ -659,7 +659,7 @@
// Backwards compatibility, coerce to null if empty
si.metaData = metaData.isEmpty() ? null : metaData;
}
- assignFieldsComponentInfoParsedMainComponent(si, s, pkgSetting, state, userId);
+ assignFieldsComponentInfoParsedMainComponent(si, s, pkgSetting, userId);
return si;
}
@@ -710,7 +710,7 @@
pi.metaData = metaData.isEmpty() ? null : metaData;
}
pi.applicationInfo = applicationInfo;
- assignFieldsComponentInfoParsedMainComponent(pi, p, pkgSetting, state, userId);
+ assignFieldsComponentInfoParsedMainComponent(pi, p, pkgSetting, userId);
return pi;
}
@@ -903,13 +903,8 @@
private static void assignFieldsComponentInfoParsedMainComponent(
@NonNull ComponentInfo info, @NonNull ParsedMainComponent component,
- @NonNull PackageStateInternal pkgSetting, @NonNull PackageUserStateInternal state,
- @UserIdInt int userId) {
+ @NonNull PackageStateInternal pkgSetting, @UserIdInt int userId) {
assignFieldsComponentInfoParsedMainComponent(info, component);
- // overwrite the enabled state with the current user state
- info.enabled = PackageUserStateUtils.isEnabled(state, info.applicationInfo.enabled,
- info.enabled, info.name, /* flags */ 0);
-
Pair<CharSequence, Integer> labelAndIcon =
ParsedComponentStateUtils.getNonLocalizedLabelAndIcon(component, pkgSetting,
userId);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ec4b38b..994f50c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -530,6 +530,14 @@
// TODO(b/178103325): Track sleep/requested sleep for every display.
volatile boolean mRequestedOrSleepingDefaultDisplay;
+ /**
+ * This is used to check whether to invoke {@link #updateScreenOffSleepToken} when screen is
+ * turned off. E.g. if it is false when screen is turned off and the display is swapping, it
+ * is expected that the screen will be on in a short time. Then it is unnecessary to acquire
+ * screen-off-sleep-token, so it can avoid intermediate visibility or lifecycle changes.
+ */
+ volatile boolean mIsGoingToSleepDefaultDisplay;
+
volatile boolean mRecentsVisible;
volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
volatile boolean mPictureInPictureVisible;
@@ -5464,6 +5472,15 @@
}
mRequestedOrSleepingDefaultDisplay = true;
+ mIsGoingToSleepDefaultDisplay = true;
+
+ // In case startedGoingToSleep is called after screenTurnedOff (the source caller is in
+ // order but the methods run on different threads) and updateScreenOffSleepToken was
+ // skipped. Then acquire sleep token if screen was off.
+ if (!mDefaultDisplayPolicy.isScreenOnFully() && !mDefaultDisplayPolicy.isScreenOnEarly()
+ && com.android.window.flags.Flags.skipSleepingWhenSwitchingDisplay()) {
+ updateScreenOffSleepToken(true /* acquire */, false /* isSwappingDisplay */);
+ }
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason);
@@ -5487,6 +5504,7 @@
MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
mRequestedOrSleepingDefaultDisplay = false;
+ mIsGoingToSleepDefaultDisplay = false;
mDefaultDisplayPolicy.setAwake(false);
// We must get this work done here because the power manager will drop
@@ -5522,7 +5540,7 @@
}
EventLogTags.writeScreenToggled(1);
-
+ mIsGoingToSleepDefaultDisplay = false;
mDefaultDisplayPolicy.setAwake(true);
// Since goToSleep performs these functions synchronously, we must
@@ -5624,7 +5642,10 @@
if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");
if (displayId == DEFAULT_DISPLAY) {
- updateScreenOffSleepToken(true, isSwappingDisplay);
+ if (!isSwappingDisplay || mIsGoingToSleepDefaultDisplay
+ || !com.android.window.flags.Flags.skipSleepingWhenSwitchingDisplay()) {
+ updateScreenOffSleepToken(true /* acquire */, isSwappingDisplay);
+ }
mRequestedOrSleepingDefaultDisplay = false;
mDefaultDisplayPolicy.screenTurnedOff();
synchronized (mLock) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
new file mode 100644
index 0000000..1f3184d
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display
+
+import android.os.IBinder
+import androidx.test.filters.SmallTest
+import com.android.server.display.brightness.clamper.HdrClamper
+import com.android.server.display.feature.DisplayManagerFlags
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+private const val MAX_BRIGHTNESS = 1.0f
+private const val TRANSITION_POINT = 0.7f
+private const val NORMAL_BRIGHTNESS_HIGH = 0.8f
+private const val NORMAL_BRIGHTNESS_LOW = 0.6f
+
+@SmallTest
+class BrightnessRangeControllerTest {
+
+ private val mockHbmController = mock<HighBrightnessModeController>()
+ private val mockCallback = mock<Runnable>()
+ private val mockConfig = mock<DisplayDeviceConfig>()
+ private val mockNormalBrightnessController = mock<NormalBrightnessModeController>()
+ private val mockHdrClamper = mock<HdrClamper>()
+ private val mockFlags = mock<DisplayManagerFlags>()
+ private val mockToken = mock<IBinder>()
+
+ @Test
+ fun `returns HBC max brightness if HBM supported and ON`() {
+ val controller = createController()
+ assertThat(controller.currentBrightnessMax).isEqualTo(MAX_BRIGHTNESS)
+ }
+
+ @Test
+ fun `returns NBC max brightness if device does not support HBM`() {
+ val controller = createController(hbmSupported = false)
+ assertThat(controller.currentBrightnessMax).isEqualTo(NORMAL_BRIGHTNESS_LOW)
+ }
+
+ @Test
+ fun `returns NBC max brightness if HBM not allowed`() {
+ val controller = createController(hbmAllowed = false)
+ assertThat(controller.currentBrightnessMax).isEqualTo(NORMAL_BRIGHTNESS_LOW)
+ }
+
+ @Test
+ fun `returns HBC max brightness if NBM is disabled`() {
+ val controller = createController(nbmEnabled = false, hbmAllowed = false)
+ assertThat(controller.currentBrightnessMax).isEqualTo(MAX_BRIGHTNESS)
+ }
+
+ @Test
+ fun `returns HBC max brightness if lower than NBC max brightness`() {
+ val controller = createController(
+ hbmAllowed = false,
+ hbmMaxBrightness = TRANSITION_POINT,
+ nbmMaxBrightness = NORMAL_BRIGHTNESS_HIGH
+ )
+ assertThat(controller.currentBrightnessMax).isEqualTo(TRANSITION_POINT)
+ }
+
+ private fun createController(
+ nbmEnabled: Boolean = true,
+ hbmSupported: Boolean = true,
+ hbmAllowed: Boolean = true,
+ hbmMaxBrightness: Float = MAX_BRIGHTNESS,
+ nbmMaxBrightness: Float = NORMAL_BRIGHTNESS_LOW
+ ): BrightnessRangeController {
+ whenever(mockFlags.isNbmControllerEnabled).thenReturn(nbmEnabled)
+ whenever(mockHbmController.deviceSupportsHbm()).thenReturn(hbmSupported)
+ whenever(mockHbmController.isHbmCurrentlyAllowed).thenReturn(hbmAllowed)
+ whenever(mockHbmController.currentBrightnessMax).thenReturn(hbmMaxBrightness)
+ whenever(mockNormalBrightnessController.currentBrightnessMax).thenReturn(nbmMaxBrightness)
+
+ return BrightnessRangeController(mockHbmController, mockCallback, mockConfig,
+ mockNormalBrightnessController, mockHdrClamper, mockFlags, mockToken,
+ DisplayDeviceInfo())
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index b142334..18f0311 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -2408,6 +2408,7 @@
when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
manageDisplaysPermission(/* granted= */ true);
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
DisplayManagerInternal localService = displayManager.new LocalService();
DisplayManagerService.BinderService bs = displayManager.new BinderService();
LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
@@ -2440,6 +2441,7 @@
.when(() -> SystemProperties.getBoolean(ENABLE_ON_CONNECT, false));
manageDisplaysPermission(/* granted= */ true);
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
DisplayManagerInternal localService = displayManager.new LocalService();
DisplayManagerService.BinderService bs = displayManager.new BinderService();
LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
@@ -2487,6 +2489,7 @@
when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
manageDisplaysPermission(/* granted= */ true);
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
DisplayManagerService.BinderService bs = displayManager.new BinderService();
LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
@@ -2652,6 +2655,7 @@
when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
manageDisplaysPermission(/* granted= */ true);
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
DisplayManagerService.BinderService bs = displayManager.new BinderService();
DisplayManagerInternal localService = displayManager.new LocalService();
LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
@@ -2699,6 +2703,7 @@
when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
manageDisplaysPermission(/* granted= */ true);
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
DisplayManagerService.BinderService bs = displayManager.new BinderService();
LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index e9315c8..ba41d31 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1850,6 +1850,8 @@
mock(ScreenOffBrightnessSensorController.class);
final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class);
final HdrClamper hdrClamper = mock(HdrClamper.class);
+ final NormalBrightnessModeController normalBrightnessModeController = mock(
+ NormalBrightnessModeController.class);
BrightnessClamperController clamperController = mock(BrightnessClamperController.class);
when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
@@ -1862,7 +1864,8 @@
TestInjector injector = spy(new TestInjector(displayPowerState, animator,
automaticBrightnessController, wakelockController, brightnessMappingStrategy,
- hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper,
+ hysteresisLevels, screenOffBrightnessSensorController,
+ hbmController, normalBrightnessModeController, hdrClamper,
clamperController, mDisplayManagerFlagsMock));
final LogicalDisplay display = mock(LogicalDisplay.class);
@@ -1950,6 +1953,8 @@
private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
private final HighBrightnessModeController mHighBrightnessModeController;
+ private final NormalBrightnessModeController mNormalBrightnessModeController;
+
private final HdrClamper mHdrClamper;
private final BrightnessClamperController mClamperController;
@@ -1963,6 +1968,7 @@
HysteresisLevels hysteresisLevels,
ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
HighBrightnessModeController highBrightnessModeController,
+ NormalBrightnessModeController normalBrightnessModeController,
HdrClamper hdrClamper,
BrightnessClamperController clamperController,
DisplayManagerFlags flags) {
@@ -1974,6 +1980,7 @@
mHysteresisLevels = hysteresisLevels;
mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
mHighBrightnessModeController = highBrightnessModeController;
+ mNormalBrightnessModeController = normalBrightnessModeController;
mHdrClamper = hdrClamper;
mClamperController = clamperController;
mFlags = flags;
@@ -2086,7 +2093,8 @@
DisplayDeviceConfig displayDeviceConfig, Handler handler,
DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
return new BrightnessRangeController(hbmController, modeChangeCallback,
- displayDeviceConfig, mHdrClamper, mFlags, displayToken, info);
+ displayDeviceConfig, mNormalBrightnessModeController, mHdrClamper,
+ mFlags, displayToken, info);
}
@Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
index 1529a08..1a71e77 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
@@ -228,13 +228,27 @@
@Test
public void testOnExternalDisplayAvailable() {
- when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(false);
+
mExternalDisplayPolicy.handleExternalDisplayConnectedLocked(mMockedLogicalDisplay);
+ assertNotAskedToEnableDisplay();
+ verify(mMockedExternalDisplayStatsService, never()).onDisplayConnected(any());
+
+ mExternalDisplayPolicy.onBootCompleted();
assertAskedToEnableDisplay();
verify(mMockedExternalDisplayStatsService).onDisplayConnected(eq(mMockedLogicalDisplay));
}
@Test
+ public void testOnExternalDisplayUnpluggedBeforeBootCompletes() {
+ mExternalDisplayPolicy.handleExternalDisplayConnectedLocked(mMockedLogicalDisplay);
+ mExternalDisplayPolicy.handleLogicalDisplayDisconnectedLocked(mMockedLogicalDisplay);
+ mExternalDisplayPolicy.onBootCompleted();
+ assertNotAskedToEnableDisplay();
+ verify(mMockedExternalDisplayStatsService, never()).onDisplayConnected(any());
+ verify(mMockedExternalDisplayStatsService, never()).onDisplayDisconnected(anyInt());
+ }
+
+ @Test
public void testOnExternalDisplayAvailable_criticalThermalCondition()
throws RemoteException {
// Disallow external displays due to thermals.
@@ -303,8 +317,14 @@
mDisplayEventCaptor.capture());
assertThat(mLogicalDisplayCaptor.getValue()).isEqualTo(mMockedLogicalDisplay);
assertThat(mDisplayEventCaptor.getValue()).isEqualTo(EVENT_DISPLAY_CONNECTED);
+ verify(mMockedLogicalDisplay).setEnabledLocked(false);
clearInvocations(mMockedLogicalDisplayMapper);
- when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(true);
+ clearInvocations(mMockedLogicalDisplay);
+ }
+
+ private void assertNotAskedToEnableDisplay() {
+ verify(mMockedInjector, never()).sendExternalDisplayEventLocked(any(), anyInt());
+ verify(mMockedLogicalDisplay, never()).setEnabledLocked(anyBoolean());
}
private void assertIsExternalDisplayAllowed(final boolean enabled) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index 29467f2..a80e2f8 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -16,10 +16,14 @@
package com.android.server.policy;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManagerGlobal.ADD_OKAY;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -33,18 +37,27 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.PowerManager;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
+import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.DisplayPolicy;
+import com.android.server.wm.DisplayRotation;
+import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
@@ -64,16 +77,27 @@
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
PhoneWindowManager mPhoneWindowManager;
+ private ActivityTaskManagerInternal mAtmInternal;
+ private Context mContext;
@Before
public void setUp() {
mPhoneWindowManager = spy(new PhoneWindowManager());
spyOn(ActivityManager.getService());
+ mContext = getInstrumentation().getTargetContext();
+ spyOn(mContext);
+ mAtmInternal = mock(ActivityTaskManagerInternal.class);
+ LocalServices.addService(ActivityTaskManagerInternal.class, mAtmInternal);
+ mPhoneWindowManager.mActivityTaskManagerInternal = mAtmInternal;
+ LocalServices.addService(WindowManagerInternal.class, mock(WindowManagerInternal.class));
}
@After
public void tearDown() {
reset(ActivityManager.getService());
+ reset(mContext);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
}
@Test
@@ -99,6 +123,60 @@
}
@Test
+ public void testScreenTurnedOff() {
+ mSetFlagsRule.enableFlags(com.android.window.flags.Flags
+ .FLAG_SKIP_SLEEPING_WHEN_SWITCHING_DISPLAY);
+ doNothing().when(mPhoneWindowManager).updateSettings(any());
+ doNothing().when(mPhoneWindowManager).initializeHdmiState();
+ final boolean[] isScreenTurnedOff = { false };
+ final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
+ doAnswer(invocation -> isScreenTurnedOff[0] = true).when(displayPolicy).screenTurnedOff();
+ doAnswer(invocation -> !isScreenTurnedOff[0]).when(displayPolicy).isScreenOnEarly();
+ doAnswer(invocation -> !isScreenTurnedOff[0]).when(displayPolicy).isScreenOnFully();
+
+ mPhoneWindowManager.mDefaultDisplayPolicy = displayPolicy;
+ mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
+ final ActivityTaskManagerInternal.SleepTokenAcquirer tokenAcquirer =
+ mock(ActivityTaskManagerInternal.SleepTokenAcquirer.class);
+ doReturn(tokenAcquirer).when(mAtmInternal).createSleepTokenAcquirer(anyString());
+ final PowerManager pm = mock(PowerManager.class);
+ doReturn(true).when(pm).isInteractive();
+ doReturn(pm).when(mContext).getSystemService(eq(Context.POWER_SERVICE));
+
+ mContext.getMainThreadHandler().runWithScissors(() -> mPhoneWindowManager.init(
+ new PhoneWindowManager.Injector(mContext,
+ mock(WindowManagerPolicy.WindowManagerFuncs.class))), 0);
+ assertThat(isScreenTurnedOff[0]).isFalse();
+ assertThat(mPhoneWindowManager.mIsGoingToSleepDefaultDisplay).isFalse();
+
+ // Skip sleep-token for non-sleep-screen-off.
+ clearInvocations(tokenAcquirer);
+ mPhoneWindowManager.screenTurnedOff(DEFAULT_DISPLAY, true /* isSwappingDisplay */);
+ verify(tokenAcquirer, never()).acquire(anyInt(), anyBoolean());
+ assertThat(isScreenTurnedOff[0]).isTrue();
+
+ // Apply sleep-token for sleep-screen-off.
+ mPhoneWindowManager.startedGoingToSleep(DEFAULT_DISPLAY, 0 /* reason */);
+ assertThat(mPhoneWindowManager.mIsGoingToSleepDefaultDisplay).isTrue();
+ mPhoneWindowManager.screenTurnedOff(DEFAULT_DISPLAY, true /* isSwappingDisplay */);
+ verify(tokenAcquirer).acquire(eq(DEFAULT_DISPLAY), eq(true));
+
+ mPhoneWindowManager.finishedGoingToSleep(DEFAULT_DISPLAY, 0 /* reason */);
+ assertThat(mPhoneWindowManager.mIsGoingToSleepDefaultDisplay).isFalse();
+
+ // Simulate unexpected reversed order: screenTurnedOff -> startedGoingToSleep. The sleep
+ // token can still be acquired.
+ isScreenTurnedOff[0] = false;
+ clearInvocations(tokenAcquirer);
+ mPhoneWindowManager.screenTurnedOff(DEFAULT_DISPLAY, true /* isSwappingDisplay */);
+ verify(tokenAcquirer, never()).acquire(anyInt(), anyBoolean());
+ assertThat(displayPolicy.isScreenOnEarly()).isFalse();
+ assertThat(displayPolicy.isScreenOnFully()).isFalse();
+ mPhoneWindowManager.startedGoingToSleep(DEFAULT_DISPLAY, 0 /* reason */);
+ verify(tokenAcquirer).acquire(eq(DEFAULT_DISPLAY), eq(false));
+ }
+
+ @Test
public void testCheckAddPermission_withoutAccessibilityOverlay_noAccessibilityAppOpLogged() {
mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
@@ -130,11 +208,8 @@
private void mockStartDockOrHome() throws Exception {
doNothing().when(ActivityManager.getService()).stopAppSwitches();
- ActivityTaskManagerInternal mMockActivityTaskManagerInternal =
- mock(ActivityTaskManagerInternal.class);
- when(mMockActivityTaskManagerInternal.startHomeOnDisplay(
+ when(mAtmInternal.startHomeOnDisplay(
anyInt(), anyString(), anyInt(), anyBoolean(), anyBoolean())).thenReturn(false);
- mPhoneWindowManager.mActivityTaskManagerInternal = mMockActivityTaskManagerInternal;
mPhoneWindowManager.mUserManagerInternal = mock(UserManagerInternal.class);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 5a52968..ae4faa8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.voiceinteraction;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
@@ -1072,8 +1073,10 @@
// If visEnabledKey is set to true (or absent), we try following VIS path.
String csPkgName = mContext.getResources()
.getString(R.string.config_defaultContextualSearchPackageName);
- if (!csPkgName.equals(getCurInteractor(
- Binder.getCallingUserHandle().getIdentifier()).getPackageName())) {
+ ComponentName currInteractor =
+ getCurInteractor(Binder.getCallingUserHandle().getIdentifier());
+ if (currInteractor == null
+ || !csPkgName.equals(currInteractor.getPackageName())) {
// Check if the interactor can handle Contextual Search.
// If not, return failure.
Slog.w(TAG, "Contextual Search not supported yet. Returning failure.");
@@ -2718,7 +2721,7 @@
}
launchIntent.setComponent(resolveInfo.getComponentInfo().getComponentName());
launchIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION
- | FLAG_ACTIVITY_NO_USER_ACTION);
+ | FLAG_ACTIVITY_NO_USER_ACTION | FLAG_ACTIVITY_CLEAR_TASK);
launchIntent.putExtras(args);
boolean isAssistDataAllowed = mAtmInternal.isAssistDataAllowed();
final List<ActivityAssistInfo> records = mAtmInternal.getTopVisibleActivities();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8f7619a..cb56cc2 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -7987,6 +7987,27 @@
KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL =
KEY_PREFIX + "scan_limited_service_after_volte_failure_bool";
+ /**
+ * This config defines {@link ImsReasonInfo} code with which the emergency call
+ * shall be retried.
+ *
+ * <p>
+ * If the reason code is one of the following, the emergency call shall be retried
+ * regardless of this configuration.
+ * <ul>
+ * <li>{@link ImsReasonInfo#CODE_LOCAL_CALL_CS_RETRY_REQUIRED}</li>
+ * <li>{@link ImsReasonInfo#CODE_LOCAL_NOT_REGISTERED}</li>
+ * <li>{@link ImsReasonInfo#CODE_SIP_ALTERNATE_EMERGENCY_CALL}</li>
+ * </ul>
+ * <p>
+ *
+ * This config is empty by default.
+ *
+ * @hide
+ */
+ public static final String KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY =
+ KEY_PREFIX + "ims_reasoninfo_code_to_retry_emergency_int_array";
+
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putBoolean(KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL, false);
@@ -8059,6 +8080,8 @@
defaults.putBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL,
true);
defaults.putBoolean(KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL, false);
+ defaults.putIntArray(KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY,
+ new int[0]);
return defaults;
}
@@ -9828,6 +9851,19 @@
"satellite_entitlement_supported_bool";
/**
+ * Indicates the appName that is used when querying the entitlement server for satellite.
+ *
+ * The default value is androidSatmode.
+ *
+ * Reference: GSMA TS.43-v11, 2.8.5 Fast Authentication and Token Management.
+ * `app_name` is an optional attribute in the request and may vary depending on the carrier
+ * requirement.
+ * @hide
+ */
+ public static final String KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING =
+ "satellite_entitlement_app_name_string";
+
+ /**
* Indicating whether DUN APN should be disabled when the device is roaming. In that case,
* the default APN (i.e. internet) will be used for tethering.
*
@@ -10945,6 +10981,7 @@
sDefaults.putBoolean(KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, true);
sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 7);
sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
+ sDefaults.putString(KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING, "androidSatmode");
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);