Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9fcb921c authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I35bd5fd7,Ia4aa6132 into main

* changes:
  [SB][Screen Chips] Refactor OngoingCallRepo to use model instead of boolean.
  [SB][Screen Chips] If sharing a single app, show app name in stop dialog
parents 03903ab1 c4c7c0f6
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -330,6 +330,8 @@
    <string name="share_to_app_stop_dialog_title">Stop sharing screen?</string>
    <!-- Text telling a user that they will stop sharing their screen if they click the "Stop sharing" button [CHAR LIMIT=100] -->
    <string name="share_to_app_stop_dialog_message">You will stop sharing your screen</string>
    <!-- Text telling a user that they will stop sharing the contents of the specified [app_name] if they click the "Stop sharing" button. Note that the app name will appear in bold. [CHAR LIMIT=100] -->
    <string name="share_to_app_stop_dialog_message_specific_app">You will stop sharing &lt;b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g>&lt;/b></string>
    <!-- Button to stop screen sharing [CHAR LIMIT=35] -->
    <string name="share_to_app_stop_dialog_button">Stop sharing</string>

@@ -337,6 +339,8 @@
    <string name="cast_to_other_device_stop_dialog_title">Stop casting screen?</string>
    <!-- Text telling a user that they will stop casting their screen to a different device if they click the "Stop casting" button [CHAR LIMIT=100] -->
    <string name="cast_to_other_device_stop_dialog_message">You will stop casting your screen</string>
    <!-- Text telling a user that they will stop casting the contents of the specified [app_name] to a different device if they click the "Stop casting" button. Note that the app name will appear in bold.  [CHAR LIMIT=100] -->
    <string name="cast_to_other_device_stop_dialog_message_specific_app">You will stop casting &lt;b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g>&lt;/b></string>
    <!-- Button to stop screen casting to a different device [CHAR LIMIT=35] -->
    <string name="cast_to_other_device_stop_dialog_button">Stop casting</string>

+21 −12
Original line number Diff line number Diff line
@@ -30,8 +30,8 @@ import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChi
import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener
import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndCastToOtherDeviceDialogDelegate
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndShareToAppDialogDelegate
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.Utils
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -60,8 +60,8 @@ constructor(
    private val mediaProjectionRepository: MediaProjectionRepository,
    private val packageManager: PackageManager,
    private val systemClock: SystemClock,
    private val dialogFactory: SystemUIDialog.Factory,
    private val dialogTransitionAnimator: DialogTransitionAnimator,
    private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
) : OngoingActivityChipInteractor {
    override val chip: StateFlow<OngoingActivityChipModel> =
        mediaProjectionRepository.mediaProjectionState
@@ -70,9 +70,9 @@ constructor(
                    is MediaProjectionState.NotProjecting -> OngoingActivityChipModel.Hidden
                    is MediaProjectionState.Projecting -> {
                        if (isProjectionToOtherDevice(state.hostPackage)) {
                            createCastToOtherDeviceChip()
                            createCastToOtherDeviceChip(state)
                        } else {
                            createShareToAppChip()
                            createShareToAppChip(state)
                        }
                    }
                }
@@ -97,7 +97,9 @@ constructor(
        return Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName)
    }

    private fun createCastToOtherDeviceChip(): OngoingActivityChipModel.Shown {
    private fun createCastToOtherDeviceChip(
        state: MediaProjectionState.Projecting,
    ): OngoingActivityChipModel.Shown {
        return OngoingActivityChipModel.Shown(
            icon =
                Icon.Resource(
@@ -107,32 +109,39 @@ constructor(
            // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
            startTimeMs = systemClock.elapsedRealtime(),
            createDialogLaunchOnClickListener(
                castToOtherDeviceDialogDelegate,
                createCastToOtherDeviceDialogDelegate(state),
                dialogTransitionAnimator,
            ),
        )
    }

    private val castToOtherDeviceDialogDelegate =
    private fun createCastToOtherDeviceDialogDelegate(state: MediaProjectionState.Projecting) =
        EndCastToOtherDeviceDialogDelegate(
            dialogFactory,
            endMediaProjectionDialogHelper,
            this@MediaProjectionChipInteractor,
            state,
        )

    private fun createShareToAppChip(): OngoingActivityChipModel.Shown {
    private fun createShareToAppChip(
        state: MediaProjectionState.Projecting,
    ): OngoingActivityChipModel.Shown {
        return OngoingActivityChipModel.Shown(
            // TODO(b/332662551): Use the right content description.
            icon = Icon.Resource(SHARE_TO_APP_ICON, contentDescription = null),
            // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
            startTimeMs = systemClock.elapsedRealtime(),
            createDialogLaunchOnClickListener(shareToAppDialogDelegate, dialogTransitionAnimator),
            createDialogLaunchOnClickListener(
                createShareToAppDialogDelegate(state),
                dialogTransitionAnimator
            ),
        )
    }

    private val shareToAppDialogDelegate =
    private fun createShareToAppDialogDelegate(state: MediaProjectionState.Projecting) =
        EndShareToAppDialogDelegate(
            dialogFactory,
            endMediaProjectionDialogHelper,
            this@MediaProjectionChipInteractor,
            state,
        )

    companion object {
+12 −4
Original line number Diff line number Diff line
@@ -17,25 +17,33 @@
package com.android.systemui.statusbar.chips.mediaprojection.ui.view

import android.os.Bundle
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog

/** A dialog that lets the user stop an ongoing cast-screen-to-other-device event. */
class EndCastToOtherDeviceDialogDelegate(
    private val systemUIDialogFactory: SystemUIDialog.Factory,
    private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
    private val interactor: MediaProjectionChipInteractor,
    private val state: MediaProjectionState.Projecting,
) : SystemUIDialog.Delegate {
    override fun createDialog(): SystemUIDialog {
        return systemUIDialogFactory.create(this)
        return endMediaProjectionDialogHelper.createDialog(this)
    }

    override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
        with(dialog) {
            setIcon(MediaProjectionChipInteractor.CAST_TO_OTHER_DEVICE_ICON)
            setTitle(R.string.cast_to_other_device_stop_dialog_title)
            // TODO(b/332662551): Use a different message if they're sharing just a single app.
            setMessage(R.string.cast_to_other_device_stop_dialog_message)
            setMessage(
                endMediaProjectionDialogHelper.getDialogMessage(
                    state,
                    genericMessageResId = R.string.cast_to_other_device_stop_dialog_message,
                    specificAppMessageResId =
                        R.string.cast_to_other_device_stop_dialog_message_specific_app,
                )
            )
            // No custom on-click, because the dialog will automatically be dismissed when the
            // button is clicked anyway.
            setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
+84 −0
Original line number Diff line number Diff line
/*
 * 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.statusbar.chips.mediaprojection.ui.view

import android.annotation.StringRes
import android.content.Context
import android.content.pm.PackageManager
import android.text.Html
import android.text.Html.FROM_HTML_MODE_LEGACY
import android.text.TextUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject

/** Helper class for showing dialogs that let users end different types of media projections. */
@SysUISingleton
class EndMediaProjectionDialogHelper
@Inject
constructor(
    private val dialogFactory: SystemUIDialog.Factory,
    private val packageManager: PackageManager,
    private val context: Context
) {
    /** Creates a new [SystemUIDialog] using the given delegate. */
    fun createDialog(delegate: SystemUIDialog.Delegate): SystemUIDialog {
        return dialogFactory.create(delegate)
    }

    /**
     * Returns the message to show in the dialog based on the specific media projection state.
     *
     * @param genericMessageResId a res ID for a more generic "end projection" message
     * @param specificAppMessageResId a res ID for an "end projection" message that also lets us
     *   specify which app is currently being projected.
     */
    fun getDialogMessage(
        state: MediaProjectionState.Projecting,
        @StringRes genericMessageResId: Int,
        @StringRes specificAppMessageResId: Int,
    ): CharSequence {
        when (state) {
            is MediaProjectionState.Projecting.EntireScreen ->
                return context.getString(genericMessageResId)
            is MediaProjectionState.Projecting.SingleTask -> {
                val packageName =
                    state.task.baseIntent.component?.packageName
                        ?: return context.getString(genericMessageResId)
                try {
                    val appInfo = packageManager.getApplicationInfo(packageName, 0)
                    val appName = appInfo.loadLabel(packageManager)
                    return getSpecificAppMessageText(specificAppMessageResId, appName)
                } catch (e: PackageManager.NameNotFoundException) {
                    // TODO(b/332662551): Log this error.
                    return context.getString(genericMessageResId)
                }
            }
        }
    }

    private fun getSpecificAppMessageText(
        @StringRes specificAppMessageResId: Int,
        appName: CharSequence,
    ): CharSequence {
        // https://developer.android.com/guide/topics/resources/string-resource#StylingWithHTML
        val escapedAppName = TextUtils.htmlEncode(appName.toString())
        val text = context.getString(specificAppMessageResId, escapedAppName)
        return Html.fromHtml(text, FROM_HTML_MODE_LEGACY)
    }
}
+11 −4
Original line number Diff line number Diff line
@@ -17,25 +17,32 @@
package com.android.systemui.statusbar.chips.mediaprojection.ui.view

import android.os.Bundle
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog

/** A dialog that lets the user stop an ongoing share-screen-to-app event. */
class EndShareToAppDialogDelegate(
    private val systemUIDialogFactory: SystemUIDialog.Factory,
    private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
    private val interactor: MediaProjectionChipInteractor,
    private val state: MediaProjectionState.Projecting,
) : SystemUIDialog.Delegate {
    override fun createDialog(): SystemUIDialog {
        return systemUIDialogFactory.create(this)
        return endMediaProjectionDialogHelper.createDialog(this)
    }

    override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
        with(dialog) {
            setIcon(MediaProjectionChipInteractor.SHARE_TO_APP_ICON)
            setTitle(R.string.share_to_app_stop_dialog_title)
            // TODO(b/332662551): Use a different message if they're sharing just a single app.
            setMessage(R.string.share_to_app_stop_dialog_message)
            setMessage(
                endMediaProjectionDialogHelper.getDialogMessage(
                    state,
                    genericMessageResId = R.string.share_to_app_stop_dialog_message,
                    specificAppMessageResId = R.string.share_to_app_stop_dialog_message_specific_app
                )
            )
            // No custom on-click, because the dialog will automatically be dismissed when the
            // button is clicked anyway.
            setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
Loading