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

Commit dbd0419b authored by Caitlin Cassidy's avatar Caitlin Cassidy Committed by Android (Google) Code Review
Browse files

Merge changes Iceea7c10,Iba98b916,I912caa44,Id5aef5c4,Iddc05936, ...

* changes:
  [Media TTT] Add NoLongerCloseToReceiver callback.
  [Media TTT] Add TransferToThisDeviceSucceeded callback.
  [Media TTT] Add an undo callback to the interface for when a transfer has succeeded. The callback will be invoked when the user presses the undo button.
  [Media TTT] Add the #transferToReceiverSucceeded callback.
  [Media TTT] Add transferToThisDeviceTriggered callback.
  [Media TTT] Update chip states to not require otherDeviceName if they don't need it. Define a #getChipTextString method instead.
parents 8919e54d 180e9686
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -2161,7 +2161,9 @@
    <!-- Text to ask the user to move their device closer to a different device (deviceName) in order to transfer media from the different device and back onto the current device. [CHAR LIMIT=75] -->
    <string name="media_move_closer_to_end_cast">Move closer to <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g> to play here</string>
    <!-- Text informing the user that their media is now playing on a different device (deviceName). [CHAR LIMIT=50] -->
    <string name="media_transfer_playing">Playing on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
    <string name="media_transfer_playing_different_device">Playing on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
    <!-- Text informing the user that their media is now playing on this device. [CHAR LIMIT=50] -->
    <string name="media_transfer_playing_this_device">Playing on this phone</string>
    <!-- Text informing the user that the media transfer has failed because something went wrong. [CHAR LIMIT=50] -->
    <string name="media_transfer_failed">Something went wrong</string>

+53 −2
Original line number Diff line number Diff line
@@ -18,16 +18,17 @@ package com.android.systemui.shared.mediattt;

import android.media.MediaRoute2Info;
import com.android.systemui.shared.mediattt.DeviceInfo;
import com.android.systemui.shared.mediattt.IUndoTransferCallback;

/**
 * A callback interface that can be invoked to trigger media transfer events on System UI.
 * An interface that can be invoked to trigger media transfer events on System UI.
 *
 * This interface is for the *sender* device, which is the device currently playing media. This
 * sender device can transfer the media to a different device, called the receiver.
 *
 * System UI will implement this interface and other services will invoke it.
 */
interface IDeviceSenderCallback {
interface IDeviceSenderService {
    /**
     * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
     * the user can potentially *start* a cast to the receiver device if the user moves their device
@@ -72,6 +73,50 @@ interface IDeviceSenderCallback {
    oneway void transferToReceiverTriggered(
        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);

    /**
     * Invoke to notify System UI that a media transfer from the receiver and back to this device
     * (the sender) has been started.
     *
     * Important notes:
     *   - This callback is for *ending* a cast. It should be used when media is currently being
     *     played on the receiver device and the media has started being transferred to play locally
     *     instead.
     */
    oneway void transferToThisDeviceTriggered(
        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);

    /**
     * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver
     * device has finished successfully.
     *
     * Important notes:
     *   - This callback is for *starting* a cast. It should be used when this device had previously
     *     been playing media locally and the media has successfully been transferred to the
     *     receiver device instead.
     *
     * @param undoCallback will be invoked if the user chooses to undo this transfer.
     */
    oneway void transferToReceiverSucceeded(
        in MediaRoute2Info mediaInfo,
        in DeviceInfo otherDeviceInfo,
        in IUndoTransferCallback undoCallback);

    /**
     * Invoke to notify System UI that a media transfer from the receiver and back to this device
     * (the sender) has finished successfully.
     *
     * Important notes:
     *   - This callback is for *ending* a cast. It should be used when media was previously being
     *     played on the receiver device and has been successfully transferred to play locally on
     *     this device instead.
     *
     * @param undoCallback will be invoked if the user chooses to undo this transfer.
     */
    oneway void transferToThisDeviceSucceeded(
        in MediaRoute2Info mediaInfo,
        in DeviceInfo otherDeviceInfo,
        in IUndoTransferCallback undoCallback);

    /**
     * Invoke to notify System UI that the attempted transfer has failed.
     *
@@ -79,4 +124,10 @@ interface IDeviceSenderCallback {
     * on the receiver and the transfer that should've *ended* the playing on the receiver.
     */
    oneway void transferFailed(in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);

    /**
     * Invoke to notify System UI that this device is no longer close to the receiver device.
     */
    oneway void noLongerCloseToReceiver(
        in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
}
+32 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.shared.mediattt;

/**
 * An interface that will be invoked by System UI if the user choose to undo a transfer.
 *
 * Other services will implement this interface and System UI will invoke it.
 */
interface IUndoTransferCallback {

    /**
     * Invoked by SystemUI when the user requests to undo the media transfer that just occurred.
     *
     * Implementors of this method are repsonsible for actually undoing the transfer.
     */
    oneway void onUndoTriggered();
}
+0 −2
Original line number Diff line number Diff line
@@ -112,7 +112,6 @@ public interface MediaModule {
            MediaTttFlags mediaTttFlags,
            CommandRegistry commandRegistry,
            Context context,
            MediaTttChipControllerSender mediaTttChipControllerSender,
            MediaTttChipControllerReceiver mediaTttChipControllerReceiver) {
        if (!mediaTttFlags.isMediaTttEnabled()) {
            return Optional.empty();
@@ -121,7 +120,6 @@ public interface MediaModule {
                new MediaTttCommandLineHelper(
                        commandRegistry,
                        context,
                        mediaTttChipControllerSender,
                        mediaTttChipControllerReceiver));
    }

+92 −68
Original line number Diff line number Diff line
@@ -30,15 +30,17 @@ import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
import com.android.systemui.media.taptotransfer.sender.MoveCloserToEndCast
import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast
import com.android.systemui.media.taptotransfer.sender.TransferFailed
import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered
import com.android.systemui.media.taptotransfer.sender.TransferSucceeded
import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded
import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered
import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded
import com.android.systemui.shared.mediattt.DeviceInfo
import com.android.systemui.shared.mediattt.IDeviceSenderCallback
import com.android.systemui.shared.mediattt.IDeviceSenderService
import com.android.systemui.shared.mediattt.IUndoTransferCallback
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import java.io.PrintWriter
@@ -52,10 +54,9 @@ import javax.inject.Inject
class MediaTttCommandLineHelper @Inject constructor(
    commandRegistry: CommandRegistry,
    private val context: Context,
    private val mediaTttChipControllerSender: MediaTttChipControllerSender,
    private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
) {
    private var senderCallback: IDeviceSenderCallback? = null
    private var senderService: IDeviceSenderService? = null
    private val senderServiceConnection = SenderServiceConnection()

    private val appIconDrawable =
@@ -64,17 +65,15 @@ class MediaTttCommandLineHelper @Inject constructor(
        }

    init {
        commandRegistry.registerCommand(
            ADD_CHIP_COMMAND_SENDER_TAG) { AddChipCommandSender() }
        commandRegistry.registerCommand(
            REMOVE_CHIP_COMMAND_SENDER_TAG) { RemoveChipCommandSender() }
        commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() }
        commandRegistry.registerCommand(
            ADD_CHIP_COMMAND_RECEIVER_TAG) { AddChipCommandReceiver() }
        commandRegistry.registerCommand(
            REMOVE_CHIP_COMMAND_RECEIVER_TAG) { RemoveChipCommandReceiver() }
    }

    inner class AddChipCommandSender : Command {
    /** All commands for the sender device. */
    inner class SenderCommand : Command {
        override fun execute(pw: PrintWriter, args: List<String>) {
            val otherDeviceName = args[0]
            val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
@@ -84,65 +83,99 @@ class MediaTttCommandLineHelper @Inject constructor(

            when (args[1]) {
                MOVE_CLOSER_TO_START_CAST_COMMAND_NAME -> {
                    runOnService { senderCallback ->
                        senderCallback.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
                    runOnService { senderService ->
                        senderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
                    }
                }
                MOVE_CLOSER_TO_END_CAST_COMMAND_NAME -> {
                    runOnService { senderCallback ->
                        senderCallback.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
                    runOnService { senderService ->
                        senderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
                    }
                }
                TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME -> {
                    runOnService { senderCallback ->
                        senderCallback.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
                    }
                }
                // TODO(b/203800643): Migrate this command to invoke the service instead of the
                //   controller.
                TRANSFER_SUCCEEDED_COMMAND_NAME -> {
                    mediaTttChipControllerSender.displayChip(
                        TransferSucceeded(
                            appIconDrawable,
                            APP_ICON_CONTENT_DESCRIPTION,
                            otherDeviceName,
                            fakeUndoRunnable
                        )
                    )
                    runOnService { senderService ->
                        senderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
                    }
                }
                TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME -> {
                    runOnService { senderService ->
                        senderService.transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo)
                    }
                }
                TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME -> {
                    val undoCallback = object : IUndoTransferCallback.Stub() {
                        override fun onUndoTriggered() {
                            Log.i(TAG, "Undo transfer to receiver callback triggered")
                            // The external services that implement this callback would kick off a
                            // transfer back to this device, so mimic that here.
                            runOnService { senderService ->
                                senderService
                                    .transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo)
                            }
                        }
                    }
                    runOnService { senderService ->
                        senderService
                            .transferToReceiverSucceeded(mediaInfo, otherDeviceInfo, undoCallback)
                    }
                }
                TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME -> {
                    val undoCallback = object : IUndoTransferCallback.Stub() {
                        override fun onUndoTriggered() {
                            Log.i(TAG, "Undo transfer to this device callback triggered")
                            // The external services that implement this callback would kick off a
                            // transfer back to the receiver, so mimic that here.
                            runOnService { senderService ->
                                senderService
                                    .transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
                            }
                        }
                    }
                    runOnService { senderService ->
                        senderService
                            .transferToThisDeviceSucceeded(mediaInfo, otherDeviceInfo, undoCallback)
                    }
                }
                TRANSFER_FAILED_COMMAND_NAME -> {
                    runOnService { senderCallback ->
                        senderCallback.transferFailed(mediaInfo, otherDeviceInfo)
                    runOnService { senderService ->
                        senderService.transferFailed(mediaInfo, otherDeviceInfo)
                    }
                }
                NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME -> {
                    runOnService { senderService ->
                        senderService.noLongerCloseToReceiver(mediaInfo, otherDeviceInfo)
                        context.unbindService(senderServiceConnection)
                    }
                }
                else -> {
                    pw.println("Chip type must be one of " +
                    pw.println("Sender command must be one of " +
                            "$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " +
                            "$MOVE_CLOSER_TO_END_CAST_COMMAND_NAME, " +
                            "$TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME, " +
                            "$TRANSFER_SUCCEEDED_COMMAND_NAME, " +
                            TRANSFER_FAILED_COMMAND_NAME
                            "$TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME, " +
                            "$TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME, " +
                            "$TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME, " +
                            "$TRANSFER_FAILED_COMMAND_NAME, " +
                            NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME
                    )
                }
            }
        }

        override fun help(pw: PrintWriter) {
            pw.println("Usage: adb shell cmd statusbar " +
                    "$ADD_CHIP_COMMAND_SENDER_TAG <deviceName> <chipStatus>"
            )
            pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipStatus>")
        }

        private fun runOnService(command: SenderCallbackCommand) {
            val currentServiceCallback = senderCallback
            if (currentServiceCallback != null) {
                command.run(currentServiceCallback)
        private fun runOnService(command: SenderServiceCommand) {
            val currentService = senderService
            if (currentService != null) {
                command.run(currentService)
            } else {
                bindService(command)
            }
        }

        private fun bindService(command: SenderCallbackCommand) {
        private fun bindService(command: SenderServiceCommand) {
            senderServiceConnection.pendingCommand = command
            val binding = context.bindService(
                Intent(context, MediaTttSenderService::class.java),
@@ -153,19 +186,6 @@ class MediaTttCommandLineHelper @Inject constructor(
        }
    }

    /** A command to REMOVE the media ttt chip on the SENDER device. */
    inner class RemoveChipCommandSender : Command {
        override fun execute(pw: PrintWriter, args: List<String>) {
            mediaTttChipControllerSender.removeChip()
            if (senderCallback != null) {
                context.unbindService(senderServiceConnection)
            }
        }
        override fun help(pw: PrintWriter) {
            pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_SENDER_TAG")
        }
    }

    /** A command to DISPLAY the media ttt chip on the RECEIVER device. */
    inner class AddChipCommandReceiver : Command {
        override fun execute(pw: PrintWriter, args: List<String>) {
@@ -188,36 +208,32 @@ class MediaTttCommandLineHelper @Inject constructor(
        }
    }

    /** A service connection for [IDeviceSenderCallback]. */
    /** A service connection for [IDeviceSenderService]. */
    private inner class SenderServiceConnection : ServiceConnection {
        // A command that should be run when the service gets connected.
        var pendingCommand: SenderCallbackCommand? = null
        var pendingCommand: SenderServiceCommand? = null

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            val newCallback = IDeviceSenderCallback.Stub.asInterface(service)
            senderCallback = newCallback
            val newCallback = IDeviceSenderService.Stub.asInterface(service)
            senderService = newCallback
            pendingCommand?.run(newCallback)
            pendingCommand = null
        }

        override fun onServiceDisconnected(className: ComponentName) {
            senderCallback = null
        }
            senderService = null
        }

    /** An interface defining a command that should be run on the sender callback. */
    private fun interface SenderCallbackCommand {
        /** Runs the command on the provided [senderCallback]. */
        fun run(senderCallback: IDeviceSenderCallback)
    }

    private val fakeUndoRunnable = Runnable {
        Log.i(TAG, "Undo runnable triggered")
    /** An interface defining a command that should be run on the sender service. */
    private fun interface SenderServiceCommand {
        /** Runs the command on the provided [senderService]. */
        fun run(senderService: IDeviceSenderService)
    }
}

@VisibleForTesting
const val ADD_CHIP_COMMAND_SENDER_TAG = "media-ttt-chip-add-sender"
const val SENDER_COMMAND = "media-ttt-chip-sender"
@VisibleForTesting
const val REMOVE_CHIP_COMMAND_SENDER_TAG = "media-ttt-chip-remove-sender"
@VisibleForTesting
@@ -231,9 +247,17 @@ val MOVE_CLOSER_TO_END_CAST_COMMAND_NAME = MoveCloserToEndCast::class.simpleName
@VisibleForTesting
val TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME = TransferToReceiverTriggered::class.simpleName!!
@VisibleForTesting
val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
val TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME =
    TransferToThisDeviceTriggered::class.simpleName!!
@VisibleForTesting
val TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME = TransferToReceiverSucceeded::class.simpleName!!
@VisibleForTesting
val TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME =
    TransferToThisDeviceSucceeded::class.simpleName!!
@VisibleForTesting
val TRANSFER_FAILED_COMMAND_NAME = TransferFailed::class.simpleName!!
@VisibleForTesting
val NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME = "NoLongerCloseToReceiver"

private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"
private const val TAG = "MediaTapToTransferCli"
Loading