1 /* 2 * Copyright (C) 2024 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 android.platform.systemui_tapl.controller 18 19 import android.Manifest.permission 20 import android.content.ContentResolver 21 import android.media.AudioManager 22 import android.os.VibratorManager 23 import android.platform.uiautomatorhelpers.DeviceHelpers.context 24 import android.platform.uiautomatorhelpers.ShellPrivilege 25 import android.platform.uiautomatorhelpers.WaitUtils.ensureThat 26 import android.provider.Settings 27 28 /** Controller for adjusting the device volume. */ 29 class VolumeController private constructor() { 30 31 private val audioManager: AudioManager = 32 context.getSystemService(AudioManager::class.java) 33 ?: error("Can't get the AudioManager for ${VolumeController::class}}") 34 35 /** Available ringer modes defined in [AudioManager] */ 36 enum class RingerMode(internal val mode: Int) { 37 NORMAL(AudioManager.RINGER_MODE_NORMAL), 38 SILENT(AudioManager.RINGER_MODE_SILENT), 39 VIBRATE(AudioManager.RINGER_MODE_VIBRATE); 40 41 /** Whether the ringer mode is available on the device under test. */ 42 val isAvailable: Boolean 43 get() { 44 return when (this) { 45 NORMAL, 46 SILENT -> true 47 VIBRATE -> hasVibrator 48 } 49 } 50 } 51 52 /** 53 * Adjusts the volume to ADJUST_SAME. 54 * 55 * This method can be used to show the volume dialog. 56 * 57 * Note: ADJUST_SAME here means [AudioManager.ADJUST_SAME]. It tells AudioManager the direction 58 * to change the volume, which is to keep the slider as is. 59 */ adjustVolumeSamenull60 fun adjustVolumeSame() = adjustVolume(AudioManager.ADJUST_SAME) 61 62 /** Adjusts the volume to ADJUST_LOWER. Shows the volume dialog. */ 63 fun adjustVolumeLower() = adjustVolume(AudioManager.ADJUST_LOWER) 64 65 /** Adjusts the volume to ADJUST_RAISE. Shows the volume dialog. */ 66 fun adjustVolumeRaise() = adjustVolume(AudioManager.ADJUST_RAISE) 67 68 private fun adjustVolume(direction: Int) { 69 audioManager.adjustSuggestedStreamVolume( 70 direction, 71 AudioManager.STREAM_MUSIC, 72 AudioManager.FLAG_SHOW_UI, 73 ) 74 } 75 76 /** 77 * The current Music stream's volume. This setter won't bring up volume dialog. Use 78 * [adjustVolumeSame] instead if you want to open the dialog. 79 */ 80 var volume: Int 81 get() = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) 82 set(volumeIndex) { 83 audioManager.setStreamVolume( 84 AudioManager.STREAM_MUSIC, 85 volumeIndex, 86 0, // Do nothing, prevent opening UI. 87 ) <lambda>null88 ensureThat("music volume == ${volumeIndex}") { volume == volumeIndex } 89 } 90 91 /** 92 * Set SysUI's internal ringer mode state. 93 * 94 * Notice that it requires STATUS_BAR_SERVICE permission to complete the setup. If the caller 95 * doesn't have the permission, the function will try to automatically grant the permission for 96 * the caller. However, it may throw [SecurityException] if the caller has called 97 * [android.app.UiAutomation.adoptShellPermissionIdentity] before. Therefore, use this function 98 * carefully. 99 * 100 * @param ringerMode[RingerMode] 101 */ setRingerModeInternalnull102 fun setRingerModeInternal(ringerMode: RingerMode) { 103 ShellPrivilege(permission.STATUS_BAR_SERVICE).use { 104 audioManager.ringerModeInternal = ringerMode.mode 105 } 106 } 107 108 /** 109 * Sets volume dialog timeout in ms. 110 * 111 * This method is called to set the timeout to a longer value to help the test to recognize its 112 * visibility. 113 * 114 * Use [SetVolumeDialogTimeoutRule] in order to control the timeout value during the test only. 115 * 116 * @param cr content resolver. 117 * @param timeout long press timeout. 118 */ setVolumeDialogTimeoutnull119 fun setVolumeDialogTimeout(cr: ContentResolver, timeout: Int) { 120 Settings.Secure.putInt(cr, Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, timeout) 121 } 122 123 /** ringerMode is the current available Audio ringer mode. */ 124 val ringerMode: RingerMode 125 get() { 126 val ringerMode = audioManager.ringerMode <lambda>null127 return RingerMode.values().firstOrNull { it.mode == ringerMode } 128 ?: error("Ringer mode $ringerMode isn't defined in ${RingerMode::class}") 129 } 130 131 /** Maximum volume of music stream. The value may be different based on the device's setting. */ 132 val maxVolume: Int 133 get() = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) 134 135 /** Minimum volume of music stream. The value may be different based on the device's setting. */ 136 val minVolume: Int 137 get() = audioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC) 138 139 companion object { 140 @JvmStatic 141 private val hasVibrator: Boolean 142 get() = 143 context 144 .getSystemService(VibratorManager::class.java)!! 145 .defaultVibrator 146 .hasVibrator() 147 148 /** Returns an instance of VolumeController. */ 149 @JvmStatic getnull150 fun get(): VolumeController { 151 return VolumeController() 152 } 153 } 154 } 155