Announce private profile screenshot saving.

Extract announcement logic behind a flag (not much to extract, but it's
one less thing in the controller and now can be tested).

Test: atest com.android.systemui.screenshot
Flag: com.android.systemui.screenshot_private_profile_accessibility_announcement
Bug: 326941376
Change-Id: Iec464e4487e14a5a44298521e18d5b895eb27e90
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index f3e2272..f937c09 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -523,6 +523,16 @@
 }
 
 flag {
+    name: "screenshot_private_profile_accessibility_announcement_fix"
+    namespace: "systemui"
+    description: "Modified a11y announcement for private space screenshots"
+    bug: "326941376"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "screenshot_private_profile_behavior_fix"
     namespace: "systemui"
     description: "Private profile support for screenshots"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6df48a0..abfdc2a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -214,6 +214,8 @@
     <string name="screenshot_saving_title">Saving screenshot\u2026</string>
     <!-- Informs the user that a screenshot is being saved. [CHAR LIMIT=50] -->
     <string name="screenshot_saving_work_profile_title">Saving screenshot to work profile\u2026</string>
+    <!-- Informs the user that a screenshot is being saved to the private profile. [CHAR LIMIT=100] -->
+    <string name="screenshot_saving_private_profile">Saving screenshot to private</string>
     <!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
     <string name="screenshot_saved_title">Screenshot saved</string>
     <!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/AnnouncementResolver.kt b/packages/SystemUI/src/com/android/systemui/screenshot/AnnouncementResolver.kt
new file mode 100644
index 0000000..746f0f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/AnnouncementResolver.kt
@@ -0,0 +1,47 @@
+/*
+ * 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 com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.screenshot.data.model.ProfileType
+import com.android.systemui.screenshot.data.repository.ProfileTypeRepository
+import com.android.systemui.screenshot.resources.Messages
+import java.util.function.Consumer
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/** Logic for determining the announcement that a screenshot has been taken (for accessibility). */
+class AnnouncementResolver
+@Inject
+constructor(
+    private val messages: Messages,
+    private val profileTypes: ProfileTypeRepository,
+    @Application private val mainScope: CoroutineScope,
+) {
+
+    suspend fun getScreenshotAnnouncement(userId: Int): String =
+        when (profileTypes.getProfileType(userId)) {
+            ProfileType.PRIVATE -> messages.savingToPrivateProfileAnnouncement
+            ProfileType.WORK -> messages.savingToWorkProfileAnnouncement
+            else -> messages.savingScreenshotAnnouncement
+        }
+
+    fun getScreenshotAnnouncement(userId: Int, announceCallback: Consumer<String>) {
+        mainScope.launch { announceCallback.accept(getScreenshotAnnouncement(userId)) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 9ad6d0f..1a11e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -19,6 +19,7 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 
+import static com.android.systemui.Flags.screenshotPrivateProfileAccessibilityAnnouncementFix;
 import static com.android.systemui.Flags.screenshotShelfUi2;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
@@ -217,6 +218,7 @@
     private final AssistContentRequester mAssistContentRequester;
 
     private final MessageContainerController mMessageContainerController;
+    private final AnnouncementResolver mAnnouncementResolver;
     private Bitmap mScreenBitmap;
     private SaveImageInBackgroundTask mSaveInBgTask;
     private boolean mScreenshotTakenInPortrait;
@@ -268,6 +270,7 @@
             AssistContentRequester assistContentRequester,
             MessageContainerController messageContainerController,
             Provider<ScreenshotSoundController> screenshotSoundController,
+            AnnouncementResolver announcementResolver,
             @Assisted Display display,
             @Assisted boolean showUIOnExternalDisplay
     ) {
@@ -297,6 +300,7 @@
         mUserManager = userManager;
         mMessageContainerController = messageContainerController;
         mAssistContentRequester = assistContentRequester;
+        mAnnouncementResolver = announcementResolver;
 
         mViewProxy = viewProxyFactory.getProxy(mContext, mDisplay.getDisplayId());
 
@@ -460,12 +464,20 @@
 
     void prepareViewForNewScreenshot(@NonNull ScreenshotData screenshot, String oldPackageName) {
         withWindowAttached(() -> {
-            if (mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
-                mViewProxy.announceForAccessibility(mContext.getResources().getString(
-                        R.string.screenshot_saving_work_profile_title));
+            if (screenshotPrivateProfileAccessibilityAnnouncementFix()) {
+                mAnnouncementResolver.getScreenshotAnnouncement(
+                        screenshot.getUserHandle().getIdentifier(),
+                        announcement -> {
+                            mViewProxy.announceForAccessibility(announcement);
+                        });
             } else {
-                mViewProxy.announceForAccessibility(
-                        mContext.getResources().getString(R.string.screenshot_saving_title));
+                if (mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
+                    mViewProxy.announceForAccessibility(mContext.getResources().getString(
+                            R.string.screenshot_saving_work_profile_title));
+                } else {
+                    mViewProxy.announceForAccessibility(
+                            mContext.getResources().getString(R.string.screenshot_saving_title));
+                }
             }
         });
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/resources/Messages.kt b/packages/SystemUI/src/com/android/systemui/screenshot/resources/Messages.kt
new file mode 100644
index 0000000..fb10168
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/resources/Messages.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.resources
+
+import android.content.Context
+import androidx.annotation.OpenForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** String values from resources, for easy injection. */
+@OpenForTesting
+@SysUISingleton
+open class Messages @Inject constructor(private val context: Context) {
+    open val savingScreenshotAnnouncement by lazy {
+        requireNotNull(context.resources.getString(R.string.screenshot_saving_title))
+    }
+
+    open val savingToWorkProfileAnnouncement by lazy {
+        requireNotNull(context.resources.getString(R.string.screenshot_saving_work_profile_title))
+    }
+
+    open val savingToPrivateProfileAnnouncement by lazy {
+        requireNotNull(context.resources.getString(R.string.screenshot_saving_private_profile))
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt
new file mode 100644
index 0000000..2e8498a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.testing.AndroidTestingRunner
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.screenshot.data.repository.profileTypeRepository
+import com.android.systemui.screenshot.policy.TestUserIds
+import com.android.systemui.screenshot.resources.Messages
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@RunWith(AndroidTestingRunner::class)
+class AnnouncementResolverTest {
+    private val kosmos = Kosmos()
+
+    private val screenshotMessage = "Saving screenshot"
+    private val workMessage = "Saving to work profile"
+    private val privateMessage = "Saving to private profile"
+
+    private val messages =
+        mock(Messages::class.java).also {
+            whenever(it.savingScreenshotAnnouncement).thenReturn(screenshotMessage)
+            whenever(it.savingToWorkProfileAnnouncement).thenReturn(workMessage)
+            whenever(it.savingToPrivateProfileAnnouncement).thenReturn(privateMessage)
+        }
+
+    private val announcementResolver =
+        AnnouncementResolver(
+            messages,
+            kosmos.profileTypeRepository,
+            TestScope(UnconfinedTestDispatcher())
+        )
+
+    @Test
+    fun personalProfile() = runTest {
+        assertThat(announcementResolver.getScreenshotAnnouncement(TestUserIds.PERSONAL))
+            .isEqualTo(screenshotMessage)
+    }
+
+    @Test
+    fun workProfile() = runTest {
+        assertThat(announcementResolver.getScreenshotAnnouncement(TestUserIds.WORK))
+            .isEqualTo(workMessage)
+    }
+
+    @Test
+    fun privateProfile() = runTest {
+        assertThat(announcementResolver.getScreenshotAnnouncement(TestUserIds.PRIVATE))
+            .isEqualTo(privateMessage)
+    }
+}