1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.media.taptotransfer 18 19 import android.annotation.SuppressLint 20 import android.app.StatusBarManager 21 import android.content.Context 22 import android.media.MediaRoute2Info 23 import android.util.Log 24 import androidx.annotation.VisibleForTesting 25 import com.android.systemui.CoreStartable 26 import com.android.systemui.dagger.SysUISingleton 27 import com.android.systemui.dagger.qualifiers.Main 28 import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver 29 import com.android.systemui.media.taptotransfer.sender.ChipStateSender 30 import com.android.systemui.statusbar.commandline.Command 31 import com.android.systemui.statusbar.commandline.CommandRegistry 32 import java.io.PrintWriter 33 import java.lang.IllegalArgumentException 34 import java.util.concurrent.Executor 35 import javax.inject.Inject 36 37 /** 38 * A helper class to test the media tap-to-transfer chip via the command line. See inner classes for 39 * command usages. 40 */ 41 @SysUISingleton 42 class MediaTttCommandLineHelper @Inject constructor( 43 private val commandRegistry: CommandRegistry, 44 private val context: Context, 45 @Main private val mainExecutor: Executor 46 ) : CoreStartable { 47 48 /** All commands for the sender device. */ 49 inner class SenderCommand : Command { executenull50 override fun execute(pw: PrintWriter, args: List<String>) { 51 if (args.size < 2) { 52 help(pw) 53 return 54 } 55 56 val senderArgs = processArgs(args) 57 58 @StatusBarManager.MediaTransferSenderState 59 val displayState: Int? 60 try { 61 displayState = ChipStateSender.getSenderStateIdFromName(senderArgs.commandName) 62 } catch (ex: IllegalArgumentException) { 63 pw.println("Invalid command name ${senderArgs.commandName}") 64 return 65 } 66 67 @SuppressLint("WrongConstant") // sysui allowed to call STATUS_BAR_SERVICE 68 val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE) 69 as StatusBarManager 70 val routeInfo = MediaRoute2Info.Builder(senderArgs.id, senderArgs.deviceName) 71 .addFeature("feature") 72 if (senderArgs.useAppIcon) { 73 routeInfo.setClientPackageName(TEST_PACKAGE_NAME) 74 } 75 76 var undoExecutor: Executor? = null 77 var undoRunnable: Runnable? = null 78 if (isSucceededState(displayState) && senderArgs.showUndo) { 79 undoExecutor = mainExecutor 80 undoRunnable = Runnable { Log.i(CLI_TAG, "Undo triggered for $displayState") } 81 } 82 83 statusBarManager.updateMediaTapToTransferSenderDisplay( 84 displayState, 85 routeInfo.build(), 86 undoExecutor, 87 undoRunnable, 88 ) 89 } 90 processArgsnull91 private fun processArgs(args: List<String>): SenderArgs { 92 val senderArgs = SenderArgs( 93 deviceName = args[0], 94 commandName = args[1], 95 ) 96 97 if (args.size == 2) { 98 return senderArgs 99 } 100 101 // Process any optional arguments 102 args.subList(2, args.size).forEach { 103 when { 104 it == "useAppIcon=false" -> senderArgs.useAppIcon = false 105 it == "showUndo=false" -> senderArgs.showUndo = false 106 it.substring(0, 3) == "id=" -> senderArgs.id = it.substring(3) 107 } 108 } 109 110 return senderArgs 111 } 112 isSucceededStatenull113 private fun isSucceededState( 114 @StatusBarManager.MediaTransferSenderState displayState: Int 115 ): Boolean { 116 return displayState == 117 StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED || 118 displayState == 119 StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED 120 } 121 helpnull122 override fun help(pw: PrintWriter) { 123 pw.println( 124 "Usage: adb shell cmd statusbar $SENDER_COMMAND " + 125 "<deviceName> <chipState> " + 126 "useAppIcon=[true|false] id=<id> showUndo=[true|false]" 127 ) 128 pw.println("Note: useAppIcon, id, and showUndo are optional additional commands.") 129 } 130 } 131 132 private data class SenderArgs( 133 val deviceName: String, 134 val commandName: String, 135 var id: String = "id", 136 var useAppIcon: Boolean = true, 137 var showUndo: Boolean = true, 138 ) 139 140 /** All commands for the receiver device. */ 141 inner class ReceiverCommand : Command { executenull142 override fun execute(pw: PrintWriter, args: List<String>) { 143 if (args.isEmpty()) { 144 help(pw) 145 return 146 } 147 148 val commandName = args[0] 149 @StatusBarManager.MediaTransferReceiverState 150 val displayState: Int? 151 try { 152 displayState = ChipStateReceiver.getReceiverStateIdFromName(commandName) 153 } catch (ex: IllegalArgumentException) { 154 pw.println("Invalid command name $commandName") 155 return 156 } 157 158 @SuppressLint("WrongConstant") // sysui is allowed to call STATUS_BAR_SERVICE 159 val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE) 160 as StatusBarManager 161 val routeInfo = MediaRoute2Info.Builder( 162 if (args.size >= 3) args[2] else "id", 163 "Test Name" 164 ).addFeature("feature") 165 val useAppIcon = !(args.size >= 2 && args[1] == "useAppIcon=false") 166 if (useAppIcon) { 167 routeInfo.setClientPackageName(TEST_PACKAGE_NAME) 168 } 169 170 statusBarManager.updateMediaTapToTransferReceiverDisplay( 171 displayState, 172 routeInfo.build(), 173 null, 174 null 175 ) 176 } 177 helpnull178 override fun help(pw: PrintWriter) { 179 pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND " + 180 "<chipState> useAppIcon=[true|false] <id>") 181 } 182 } 183 startnull184 override fun start() { 185 commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() } 186 commandRegistry.registerCommand(RECEIVER_COMMAND) { ReceiverCommand() } 187 } 188 } 189 190 @VisibleForTesting 191 const val SENDER_COMMAND = "media-ttt-chip-sender" 192 @VisibleForTesting 193 const val RECEIVER_COMMAND = "media-ttt-chip-receiver" 194 private const val CLI_TAG = "MediaTransferCli" 195 private const val TEST_PACKAGE_NAME = "com.android.systemui" 196