From c7a35a8bc1aa2f5c5bf80641876d477c6eae94d9 Mon Sep 17 00:00:00 2001 From: cjybyjk Date: Mon, 5 Sep 2022 04:43:13 +0000 Subject: [PATCH] Settings: support per-app volume [3/3] Change-Id: I3e72a4b0fda68a5cd1e9a35fc161a1d7ddae6d7e Signed-off-by: AnierinB Signed-off-by: Pranav Vashi --- AndroidManifest.xml | 4 + res/values/evolution_strings.xml | 5 + res/xml/sound_settings.xml | 7 + .../settings/media/AppVolumeSlice.java | 150 ++++++++++++++++++ .../settings/panel/AppVolumePanel.java | 65 ++++++++ .../panel/PanelFeatureProviderImpl.java | 2 + .../settings/slices/CustomSliceRegistry.java | 12 ++ 7 files changed, 245 insertions(+) create mode 100644 src/com/android/settings/media/AppVolumeSlice.java create mode 100644 src/com/android/settings/panel/AppVolumePanel.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 1588cd9657e..3ed7e42fc4e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5085,6 +5085,10 @@ + + + + Quad9 Unsecured with ECS DNS dns12.quad9.net + + + App volume + Per-app volume control + Show per-app volume button into volume dialog diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml index 56968f127cf..e3939197685 100644 --- a/res/xml/sound_settings.xml +++ b/res/xml/sound_settings.xml @@ -168,6 +168,13 @@ android:ringtoneType="alarm" android:order="-60"/> + + appVols = new ArrayList<>(); + for (AppVolume vol : mAudioManager.listAppVolumes()) { + if (vol.isActive()) { + appVols.add(vol); + } + } + if (appVols.isEmpty()) { + Log.d(TAG, "No active tracks"); + return listBuilder.build(); + } + + for (AppVolume vol : appVols) { + final CharSequence appName = Utils.getApplicationLabel( + mContext, vol.getPackageName()); + IconCompat icon = getApplicationIcon(vol.getPackageName()); + final SliceAction primarySliceAction = SliceAction.create( + getBroadcastIntent(mContext), icon, ListBuilder.ICON_IMAGE, appName); + listBuilder.addInputRange(new InputRangeBuilder() + .setTitleItem(icon, ListBuilder.ICON_IMAGE) + .setTitle(appName) + .setInputAction(getSliderInputAction(vol.getPackageName())) + .setMax(100) + .setValue((int)(vol.getVolume() * 100)) + .setPrimaryAction(primarySliceAction)); + } + return listBuilder.build(); + } + + private IconCompat getApplicationIcon(String packageName) { + PackageManager pm = mContext.getPackageManager(); + try { + ApplicationInfo ai = pm.getApplicationInfo(packageName, 0); + Resources resources = pm.getResourcesForApplication(ai); + IconCompat icon = IconCompat.createWithResource(resources, packageName, ai.icon); + return icon; + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Failed to get icon of " + packageName, e); + } + + final Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + return IconCompat.createWithBitmap(bitmap); + } + + + private PendingIntent getSliderInputAction(String packageName) { + final int requestCode = packageName.hashCode(); + final Intent intent = new Intent(getUri().toString()) + .setData(getUri()) + .putExtra(PACKAGE_NAME, packageName) + .setClass(mContext, SliceBroadcastReceiver.class); + return PendingIntent.getBroadcast(mContext, requestCode, intent, + PendingIntent.FLAG_MUTABLE); + } + + @Override + public Uri getUri() { + return APP_VOLUME_SLICE_URI; + } + + @Override + public Intent getIntent() { + return null; + } + + @Override + public int getSliceHighlightMenuRes() { + return R.string.menu_key_sound; + } +} diff --git a/src/com/android/settings/panel/AppVolumePanel.java b/src/com/android/settings/panel/AppVolumePanel.java new file mode 100644 index 00000000000..876a15cf576 --- /dev/null +++ b/src/com/android/settings/panel/AppVolumePanel.java @@ -0,0 +1,65 @@ +/* + * 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 com.android.settings.panel; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.provider.Settings; + +import com.android.settings.R; +import com.android.settings.slices.CustomSliceRegistry; + +import java.util.ArrayList; +import java.util.List; + +public class AppVolumePanel implements PanelContent { + + private final Context mContext; + + public static AppVolumePanel create(Context context) { + return new AppVolumePanel(context); + } + + private AppVolumePanel(Context context) { + mContext = context.getApplicationContext(); + } + + @Override + public CharSequence getTitle() { + return mContext.getText(R.string.app_volume); + } + + @Override + public List getSlices() { + final List uris = new ArrayList<>(); + uris.add(CustomSliceRegistry.APP_VOLUME_SLICE_URI); + return uris; + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.PANEL_VOLUME; + } + + @Override + public Intent getSeeMoreIntent() { + return new Intent(Settings.ACTION_SOUND_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + +} diff --git a/src/com/android/settings/panel/PanelFeatureProviderImpl.java b/src/com/android/settings/panel/PanelFeatureProviderImpl.java index a0aeec60856..4d7acea49f3 100644 --- a/src/com/android/settings/panel/PanelFeatureProviderImpl.java +++ b/src/com/android/settings/panel/PanelFeatureProviderImpl.java @@ -87,6 +87,8 @@ public class PanelFeatureProviderImpl implements PanelFeatureProvider { return VolumePanel.create(context); } } + case Settings.Panel.ACTION_APP_VOLUME: + return AppVolumePanel.create(context); } throw new IllegalStateException("No matching panel for: " + panelType); diff --git a/src/com/android/settings/slices/CustomSliceRegistry.java b/src/com/android/settings/slices/CustomSliceRegistry.java index 38ca5cde95e..5100ceeadd3 100644 --- a/src/com/android/settings/slices/CustomSliceRegistry.java +++ b/src/com/android/settings/slices/CustomSliceRegistry.java @@ -33,6 +33,7 @@ import com.android.settings.homepage.contextualcards.slices.DarkThemeSlice; import com.android.settings.homepage.contextualcards.slices.FaceSetupSlice; import com.android.settings.homepage.contextualcards.slices.LowStorageSlice; import com.android.settings.location.LocationSlice; +import com.android.settings.media.AppVolumeSlice; import com.android.settings.media.MediaOutputIndicatorSlice; import com.android.settings.media.RemoteMediaSlice; import com.android.settings.network.ProviderModelSlice; @@ -291,6 +292,16 @@ public class CustomSliceRegistry { .appendPath("always_on_display") .build(); + /** + * Backing Uri for the App Volume Slice. + */ + public static Uri APP_VOLUME_SLICE_URI = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(SettingsSliceProvider.SLICE_AUTHORITY) + .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) + .appendPath("app_volume") + .build(); + @VisibleForTesting static final Map> sUriToSlice; @@ -305,6 +316,7 @@ public class CustomSliceRegistry { sUriToSlice.put(ALWAYS_ON_SLICE_URI, AlwaysOnDisplaySlice.class); sUriToSlice.put(MEDIA_OUTPUT_INDICATOR_SLICE_URI, MediaOutputIndicatorSlice.class); sUriToSlice.put(REMOTE_MEDIA_SLICE_URI, RemoteMediaSlice.class); + sUriToSlice.put(APP_VOLUME_SLICE_URI, AppVolumeSlice.class); // Slices for contextual card. sUriToSlice.put(FACE_ENROLL_SLICE_URI, FaceSetupSlice.class);