From d2626522fedd36c51aaa28b67b543768aeb4a1ef Mon Sep 17 00:00:00 2001 From: cjybyjk Date: Mon, 5 Sep 2022 04:14:52 +0000 Subject: [PATCH 1/2] base: support per-app volume [2/3] [ghostrider-reborn] - Use app volume icon by amartya (@ yukiscape) Dhina17: - VolumeDialog: Fix per-app volume on secondary users nurkeinneid: - Convert app volume button to Compose/Kotlin neobuddy89: - Drop legacy bits Change-Id: I674cda1d7463b3603ccaac673178aeead4093619 Co-authored-by: yukiscape Signed-off-by: chrisw444 Signed-off-by: Mohammad Hasan Keramat J Signed-off-by: cjybyjk Signed-off-by: Adithya R Signed-off-by: Dhina17 Signed-off-by: Pranav Vashi --- core/java/android/provider/Settings.java | 12 +++ core/jni/android_media_AudioSystem.cpp | 98 +++++++++++++++++++ media/java/android/media/AppVolume.java | 53 ++++++++++ media/java/android/media/AudioManager.java | 26 +++++ media/java/android/media/AudioSystem.java | 7 ++ .../SystemUI/res/drawable/ic_app_volume.xml | 32 ++++++ .../layout/volume_dialog_bottom_section.xml | 13 +++ .../VolumeDialogAppVolumeButtonInteractor.kt | 77 +++++++++++++++ .../VolumeDialogAppVolumeButtonViewBinder.kt | 54 ++++++++++ .../VolumeDialogAppVolumeButtonViewModel.kt | 16 +++ .../dagger/module/VolumeDialogModule.kt | 3 + .../VolumePanelNavigationInteractor.kt | 4 + .../volume/domain/model/VolumePanelRoute.kt | 1 + .../volume/ui/navigation/VolumeNavigator.kt | 5 + 14 files changed, 401 insertions(+) create mode 100644 media/java/android/media/AppVolume.java create mode 100644 packages/SystemUI/res/drawable/ic_app_volume.xml create mode 100644 packages/SystemUI/src/com/android/systemui/volume/dialog/appvolume/domain/VolumeDialogAppVolumeButtonInteractor.kt create mode 100644 packages/SystemUI/src/com/android/systemui/volume/dialog/appvolume/ui/binder/VolumeDialogAppVolumeButtonViewBinder.kt create mode 100644 packages/SystemUI/src/com/android/systemui/volume/dialog/appvolume/ui/viewmodel/VolumeDialogAppVolumeButtonViewModel.kt diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b701d09aefaa2..44a3d63ef394f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6990,6 +6990,12 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean */ public static final String SCREENSHOT_SHUTTER_SOUND = "screenshot_shutter_sound"; + /** + * Show app volume rows in volume panel + * @hide + */ + public static final String SHOW_APP_VOLUME = "show_app_volume"; + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. @@ -22715,6 +22721,12 @@ private Panel() { @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_VOLUME = "android.settings.panel.action.VOLUME"; + + /** + * @hide + */ + public static final String ACTION_APP_VOLUME = + "android.settings.panel.action.APP_VOLUME"; } /** diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index a301195f15cff..6b2d0808b7017 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,9 @@ static jmethodID gIntegerCstor; static jclass gMapClass; static jmethodID gMapPut; +static jclass gAppVolumeClass; +static jmethodID gAppVolumeCstor; + static jclass gAudioHandleClass; static jmethodID gAudioHandleCstor; static struct { @@ -962,6 +966,88 @@ android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz) return balance; } +static jint +android_media_AudioSystem_setAppVolume(JNIEnv *env, jobject thiz, jstring packageName, jfloat value) +{ + const jchar* c_packageName = env->GetStringCritical(packageName, 0); + String8 package8 = String8(reinterpret_cast(c_packageName), env->GetStringLength(packageName)); + env->ReleaseStringCritical(packageName, c_packageName); + return (jint) check_AudioSystem_Command(AudioSystem::setAppVolume(package8, value)); +} + +static jint +android_media_AudioSystem_setAppMute(JNIEnv *env, jobject thiz, jstring packageName, jboolean mute) +{ + const jchar* c_packageName = env->GetStringCritical(packageName, 0); + String8 package8 = String8(reinterpret_cast(c_packageName), env->GetStringLength(packageName)); + env->ReleaseStringCritical(packageName, c_packageName); + return (jint) check_AudioSystem_Command(AudioSystem::setAppMute(package8, mute)); +} + +jint convertAppVolumeFromNative(JNIEnv *env, jobject *jAppVolume, const media::AppVolume *AppVolume) +{ + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + jstring jPackageName; + jfloat jVolume; + jboolean jMute; + jboolean jActive; + + if (AppVolume == NULL || jAppVolume == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + + jPackageName = env->NewStringUTF(AppVolume->packageName); + jVolume = AppVolume->volume; + jMute = AppVolume->muted; + jActive = AppVolume->active; + + *jAppVolume = env->NewObject(gAppVolumeClass, gAppVolumeCstor, + jPackageName, jMute, jVolume, jActive); + + env->DeleteLocalRef(jPackageName); +exit: + return jStatus; +} + +static jint +android_media_AudioSystem_listAppVolumes(JNIEnv *env, jobject clazz, jobject jVolumes) +{ + ALOGV("listAppVolumes"); + + if (jVolumes == NULL) { + ALOGE("listAppVolumes NULL AppVolume ArrayList"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jVolumes, gArrayListClass)) { + ALOGE("listAppVolumes not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + std::vector volumes; + + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + status_t status = AudioSystem::listAppVolumes(&volumes); + + if (status != NO_ERROR) { + ALOGE("AudioSystem::listAppVolumes error %d", status); + jStatus = nativeToJavaStatus(status); + return jStatus; + } + + for (size_t i = 0; i < volumes.size(); i++) { + jobject jAppVolume; + jStatus = convertAppVolumeFromNative(env, &jAppVolume, &volumes[i]); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + env->CallBooleanMethod(jVolumes, gArrayListMethods.add, jAppVolume); + env->DeleteLocalRef(jAppVolume); + } + + return jStatus; +} + static jint android_media_AudioSystem_getPrimaryOutputSamplingRate(JNIEnv *env, jobject clazz) { @@ -3697,6 +3783,13 @@ static const JNINativeMethod gMethods[] = { android_media_AudioSystem_listenForSystemPropertyChange), MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate", "(J)V", android_media_AudioSystem_triggerSystemPropertyUpdate), + + MAKE_JNI_NATIVE_METHOD("setAppVolume", "(Ljava/lang/String;F)I", + android_media_AudioSystem_setAppVolume), + MAKE_JNI_NATIVE_METHOD("setAppMute", "(Ljava/lang/String;Z)I", + android_media_AudioSystem_setAppMute), + MAKE_JNI_NATIVE_METHOD("listAppVolumes", "(Ljava/util/ArrayList;)I", + android_media_AudioSystem_listAppVolumes), MAKE_AUDIO_SYSTEM_METHOD(setSimulateDeviceConnections), }; @@ -3979,6 +4072,11 @@ int register_android_media_AudioSystem(JNIEnv *env) LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&gVm) != 0); + jclass AppVolumeClass = FindClassOrDie(env, "android/media/AppVolume"); + gAppVolumeClass = MakeGlobalRefOrDie(env, AppVolumeClass); + gAppVolumeCstor = GetMethodIDOrDie(env, AppVolumeClass, "", + "(Ljava/lang/String;ZFZ)V"); + AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback); RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); diff --git a/media/java/android/media/AppVolume.java b/media/java/android/media/AppVolume.java new file mode 100644 index 0000000000000..732b4802c69fd --- /dev/null +++ b/media/java/android/media/AppVolume.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 Project Kaleidoscope + * + * 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 android.media; + +import android.annotation.NonNull; + +/** + * @hide + */ +public class AppVolume { + private final String mPackageName; + private final float mVolume; + private final boolean mMute; + private final boolean mActive; + + AppVolume(String packageName, boolean mute, float volume, boolean active) { + mPackageName = packageName; + mMute = mute; + mVolume = volume; + mActive = active; + } + + @NonNull + public String getPackageName() { + return mPackageName; + } + + public float getVolume() { + return mVolume; + } + + public boolean isMuted() { + return mMute; + } + + public boolean isActive() { + return mActive; + } +} diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 24037d40115ff..616d4d49eea15 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1173,6 +1173,32 @@ public void setMasterMute(boolean mute, int flags) { } } + /** @hide */ + @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public int setAppVolume(String packageName, float volume) { + return AudioSystem.setAppVolume(packageName, volume); + } + + /** @hide */ + @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public int setAppMute(String packageName, boolean mute) { + return AudioSystem.setAppMute(packageName, mute); + } + + /** @hide */ + @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public ArrayList listAppVolumes() { + ArrayList volumes = new ArrayList(); + int status = AudioSystem.listAppVolumes(volumes); + if (status != AudioManager.SUCCESS) { + return new ArrayList(); + } + return volumes; + } + /** * Returns the current ringtone mode. * diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 966dcece50b20..442fc38d13d9c 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -2006,6 +2006,13 @@ public static int getDevicesForStream(int stream) { getDevicesForAttributes(attr, true /* forVolume */))); } + /** @hide */ + public static native int setAppVolume(@NonNull String packageName, float volume); + /** @hide */ + public static native int setAppMute(@NonNull String packageName, boolean mute); + /** @hide */ + public static native int listAppVolumes(ArrayList volumes); + /** @hide * Conversion from a device set to a bit mask. * diff --git a/packages/SystemUI/res/drawable/ic_app_volume.xml b/packages/SystemUI/res/drawable/ic_app_volume.xml new file mode 100644 index 0000000000000..7b2a88fc15395 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_app_volume.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/packages/SystemUI/res/layout/volume_dialog_bottom_section.xml b/packages/SystemUI/res/layout/volume_dialog_bottom_section.xml index d92be5f66ca2e..dd13b8d5d0a3c 100644 --- a/packages/SystemUI/res/layout/volume_dialog_bottom_section.xml +++ b/packages/SystemUI/res/layout/volume_dialog_bottom_section.xml @@ -27,6 +27,19 @@ android:soundEffectsEnabled="false" android:visibility="gone"/> + + = + callbackFlow { + val handler = Handler(Looper.getMainLooper()) + val observer = object : ContentObserver(handler) { + override fun onChange(selfChange: Boolean) { + trySend(shouldShowAppVolume()) + } + } + context.contentResolver.registerContentObserver( + Settings.System.getUriFor(Settings.System.SHOW_APP_VOLUME), + false, + observer, + UserHandle.USER_CURRENT + ) + trySend(shouldShowAppVolume()) + awaitClose { + context.contentResolver.unregisterContentObserver(observer) + } + } + .stateIn(coroutineScope, SharingStarted.Eagerly, shouldShowAppVolume()) + + fun onButtonClicked() { + volumeNavigator.openVolumePanel( + volumePanelNavigationInteractor.getAppVolumePanelRoute() + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/appvolume/ui/binder/VolumeDialogAppVolumeButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/appvolume/ui/binder/VolumeDialogAppVolumeButtonViewBinder.kt new file mode 100644 index 0000000000000..ebe4439105f48 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/appvolume/ui/binder/VolumeDialogAppVolumeButtonViewBinder.kt @@ -0,0 +1,54 @@ +package com.android.systemui.volume.dialog.appvolume.ui.binder + +import android.view.View +import com.android.app.tracing.coroutines.launchInTraced +import com.android.app.tracing.coroutines.launchTraced +import com.android.systemui.res.R +import com.android.systemui.volume.Events +import com.android.systemui.volume.dialog.appvolume.ui.viewmodel.VolumeDialogAppVolumeButtonViewModel +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope +import com.android.systemui.volume.dialog.ui.binder.ViewBinder +import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogViewModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.onEach + +/** Binds the app volume button view. */ +@VolumeDialogScope +class VolumeDialogAppVolumeButtonViewBinder +@Inject +constructor( + private val viewModel: VolumeDialogAppVolumeButtonViewModel, + private val dialogViewModel: VolumeDialogViewModel, +) : ViewBinder { + override fun CoroutineScope.bind(view: View) { + val appVolumeButton = view.requireViewById(R.id.app_volume_icon) + + launchTraced("VDAVBVB#addTouchableBounds") { + dialogViewModel.addTouchableBounds(appVolumeButton) + } + + viewModel.isVisible + .onEach { isVisible -> + appVolumeButton.visibility = + if (isVisible) { + View.VISIBLE + } else { + View.GONE + } + } + .launchInTraced("VDAVBVB#isVisible", this) + + // Set color filter to match captions button disabled state (theme-aware) + appVolumeButton.setColorFilter( + appVolumeButton.context.getColor( + com.android.internal.R.color.materialColorOnSurface + ) + ) + + appVolumeButton.setOnClickListener { + viewModel.onButtonClicked() + Events.writeEvent(Events.EVENT_SETTINGS_CLICK) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/appvolume/ui/viewmodel/VolumeDialogAppVolumeButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/appvolume/ui/viewmodel/VolumeDialogAppVolumeButtonViewModel.kt new file mode 100644 index 0000000000000..a5c753eb7da18 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/appvolume/ui/viewmodel/VolumeDialogAppVolumeButtonViewModel.kt @@ -0,0 +1,16 @@ +package com.android.systemui.volume.dialog.appvolume.ui.viewmodel + +import com.android.systemui.volume.dialog.appvolume.domain.VolumeDialogAppVolumeButtonInteractor +import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow + +/** ViewModel for managing the app volume button in the volume dialog. */ +class VolumeDialogAppVolumeButtonViewModel +@Inject +constructor(private val interactor: VolumeDialogAppVolumeButtonInteractor) { + val isVisible: StateFlow = interactor.isVisible + + fun onButtonClicked() { + interactor.onButtonClicked() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt index e776962a7ef33..f94e755173ad2 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt @@ -16,6 +16,7 @@ package com.android.systemui.volume.dialog.dagger.module +import com.android.systemui.volume.dialog.appvolume.ui.binder.VolumeDialogAppVolumeButtonViewBinder import com.android.systemui.volume.dialog.captions.ui.binder.VolumeDialogCaptionsButtonViewBinder import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepository @@ -47,12 +48,14 @@ interface VolumeDialogModule { ringerViewBinder: VolumeDialogRingerViewBinder, settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder, captionsButtonViewBinder: VolumeDialogCaptionsButtonViewBinder, + appVolumeButtonViewBinder: VolumeDialogAppVolumeButtonViewBinder, ): List = listOf( slidersViewBinder, ringerViewBinder, settingsButtonViewBinder, captionsButtonViewBinder, + appVolumeButtonViewBinder, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumePanelNavigationInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumePanelNavigationInteractor.kt index d64bb03f04ed4..de08cd9b7d2e7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumePanelNavigationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumePanelNavigationInteractor.kt @@ -41,4 +41,8 @@ constructor( else -> VolumePanelRoute.SETTINGS_VOLUME_PANEL } } + + fun getAppVolumePanelRoute(): VolumePanelRoute { + return VolumePanelRoute.APP_VOLUME_PANEL + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/model/VolumePanelRoute.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/model/VolumePanelRoute.kt index c85af152137b3..7b9ed8c9e9b89 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/domain/model/VolumePanelRoute.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/domain/model/VolumePanelRoute.kt @@ -20,4 +20,5 @@ enum class VolumePanelRoute { COMPOSE_VOLUME_PANEL, SETTINGS_VOLUME_PANEL, SYSTEM_UI_VOLUME_PANEL, + APP_VOLUME_PANEL, } diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt index fb9b44f126cfa..66edc9222fc7f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt @@ -94,6 +94,11 @@ constructor( ) VolumePanelRoute.SYSTEM_UI_VOLUME_PANEL -> volumePanelFactory.create(aboveStatusBar = true, view = null) + VolumePanelRoute.APP_VOLUME_PANEL -> + activityStarter.startActivity( + /* intent= */ Intent(Settings.Panel.ACTION_APP_VOLUME), + /* dismissShade= */ true + ) } } From a7eda615b8225ff15636c0dff2295fd6d1651350 Mon Sep 17 00:00:00 2001 From: rmp22 <195054967+rmp22@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:14:47 +0800 Subject: [PATCH 2/2] services: Fixing per-app volume ux Change-Id: If4bd9d643d8438f5ccac544fd98a960ff5f40298 Signed-off-by: Pranav Vashi --- .../android/server/audio/AudioService.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 9b69b93fb1c5b..a973e89103a80 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -139,6 +139,7 @@ import android.hardware.input.InputManager; import android.hardware.usb.UsbManager; import android.hidl.manager.V1_0.IServiceManager; +import android.media.AppVolume; import android.media.AudioAttributes; import android.media.AudioAttributes.AttributeSystemUsage; import android.media.AudioDescriptor; @@ -383,6 +384,7 @@ public class AudioService extends IAudioService.Stub final Context mContext; private final ContentResolver mContentResolver; + private int mShowAppVolume; private final AppOpsManager mAppOps; /** do not use directly, use getMediaSessionManager() which handles lazy initialization */ @@ -3435,6 +3437,9 @@ private void readPersistedSettings() { updateMasterBalance(cr); + mShowAppVolume = mSettings.getSystemIntForUser(cr, + Settings.System.SHOW_APP_VOLUME, 0, UserHandle.USER_CURRENT); + // Each stream will read its own persisted settings // Broadcast the sticky intents @@ -11382,6 +11387,8 @@ private class SettingsObserver extends ContentObserver { Settings.System.MASTER_MONO), false, this, UserHandle.USER_ALL); mContentResolver.registerContentObserver(Settings.System.getUriFor( Settings.System.MASTER_BALANCE), false, this, UserHandle.USER_ALL); + mContentResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SHOW_APP_VOLUME), false, this); mEncodedSurroundMode = mSettings.getGlobalInt( mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT, @@ -11415,6 +11422,7 @@ public void onChange(boolean selfChange) { readDockAudioSettings(mContentResolver); updateMasterMono(mContentResolver); updateMasterBalance(mContentResolver); + updateShowAppVolume(mContentResolver); } synchronized (mSurroundLock) { @@ -11427,6 +11435,17 @@ public void onChange(boolean selfChange) { } } + private void updateShowAppVolume(ContentResolver cr) { + int showAppVolume = mSettings.getSystemIntForUser(cr, + Settings.System.SHOW_APP_VOLUME, 0, UserHandle.USER_CURRENT); + if (mShowAppVolume != showAppVolume) { + mShowAppVolume = showAppVolume; + if (mShowAppVolume == 0) { + resetAppVolumes(); + } + } + } + @GuardedBy("mSurroundLock") private void updateEncodedSurroundOutput() { int newSurroundMode = mSettings.getGlobalInt( @@ -11445,6 +11464,17 @@ private void updateEncodedSurroundOutput() { } } + private void resetAppVolumes() { + ArrayList volumes = new ArrayList<>(); + int status = AudioSystem.listAppVolumes(volumes); + if (status == AudioSystem.SUCCESS) { + for (AppVolume vol : volumes) { + AudioSystem.setAppVolume(vol.getPackageName(), 1.0f); + AudioSystem.setAppMute(vol.getPackageName(), false); + } + } + } + private void avrcpSupportsAbsoluteVolume(String address, boolean support) { // address is not used for now, but may be used when multiple a2dp devices are supported sVolumeLogger.enqueue(new EventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="