1 /*
2  * Copyright (C) 2016 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.settings.notification;
18 
19 import android.app.PendingIntent;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.graphics.drawable.Drawable;
23 import android.media.AudioManager;
24 import android.net.Uri;
25 import android.text.TextUtils;
26 import android.util.Log;
27 
28 import androidx.core.graphics.drawable.IconCompat;
29 import androidx.slice.builders.ListBuilder;
30 import androidx.slice.builders.SliceAction;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.settings.R;
34 import com.android.settings.Utils;
35 import com.android.settings.bluetooth.BluetoothBroadcastDialog;
36 import com.android.settings.media.MediaOutputIndicatorWorker;
37 import com.android.settings.slices.CustomSliceRegistry;
38 import com.android.settings.slices.SliceBackgroundWorker;
39 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
40 import com.android.settingslib.flags.Flags;
41 import com.android.settingslib.media.BluetoothMediaDevice;
42 import com.android.settingslib.media.MediaDevice;
43 import com.android.settingslib.media.MediaOutputConstants;
44 
45 public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceController {
46     private static final String TAG = "MediaVolumePreCtrl";
47     private static final String KEY_MEDIA_VOLUME = "media_volume";
48 
49     private MediaOutputIndicatorWorker mWorker;
50     private MediaDevice mMediaDevice;
51     private static final String ACTION_LAUNCH_BROADCAST_DIALOG =
52             "android.settings.MEDIA_BROADCAST_DIALOG";
53 
MediaVolumePreferenceController(Context context)54     public MediaVolumePreferenceController(Context context) {
55         super(context, KEY_MEDIA_VOLUME);
56         mVolumePreferenceListener = this::updateContentDescription;
57     }
58 
59     @Override
getAvailabilityStatus()60     public int getAvailabilityStatus() {
61         return mContext.getResources().getBoolean(R.bool.config_show_media_volume)
62                 ? AVAILABLE
63                 : UNSUPPORTED_ON_DEVICE;
64     }
65 
66     @Override
isSliceable()67     public boolean isSliceable() {
68         return TextUtils.equals(getPreferenceKey(), KEY_MEDIA_VOLUME);
69     }
70 
71     @Override
isPublicSlice()72     public boolean isPublicSlice() {
73         return true;
74     }
75 
76     @Override
useDynamicSliceSummary()77     public boolean useDynamicSliceSummary() {
78         return true;
79     }
80 
81     @Override
getPreferenceKey()82     public String getPreferenceKey() {
83         return KEY_MEDIA_VOLUME;
84     }
85 
86     @Override
getAudioStream()87     public int getAudioStream() {
88         return AudioManager.STREAM_MUSIC;
89     }
90 
91     @Override
getMuteIcon()92     public int getMuteIcon() {
93         return R.drawable.ic_media_stream_off;
94     }
95 
96     @VisibleForTesting
isSupportEndItem()97     boolean isSupportEndItem() {
98         return Flags.legacyLeAudioSharing()
99                 && getWorker() != null
100                 && getWorker().isBroadcastSupported()
101                 && (getWorker().isDeviceBroadcasting() || isConnectedBLEDevice());
102     }
103 
isConnectedBLEDevice()104     private boolean isConnectedBLEDevice() {
105         if (getWorker() == null) {
106             Log.d(TAG, "The Worker is null");
107             return false;
108         }
109         mMediaDevice = getWorker().getCurrentConnectedMediaDevice();
110         if (mMediaDevice != null) {
111             return mMediaDevice.isBLEDevice();
112         }
113         return false;
114     }
115 
updateContentDescription()116     private void updateContentDescription() {
117         if (mPreference != null) {
118             if (mPreference.isMuted()) {
119                 mPreference.updateContentDescription(
120                         mContext.getString(
121                                 R.string.volume_content_description_silent_mode,
122                                 mPreference.getTitle()));
123             } else {
124                 mPreference.updateContentDescription(mPreference.getTitle());
125             }
126         }
127     }
128 
129     @Override
getSliceEndItem(Context context)130     public SliceAction getSliceEndItem(Context context) {
131         if (!isSupportEndItem()) {
132             Log.d(TAG, "The slice doesn't support end item");
133             return null;
134         }
135 
136         final Intent intent = new Intent();
137         PendingIntent pi = null;
138         if (getWorker().isDeviceBroadcasting()) {
139             intent.setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME);
140             intent.setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
141             intent.putExtra(
142                     MediaOutputConstants.EXTRA_PACKAGE_NAME,
143                     getWorker().getActiveLocalMediaController().getPackageName());
144 
145             pi =
146                     PendingIntent.getBroadcast(
147                             context,
148                             0 /* requestCode */,
149                             intent,
150                             PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
151         } else {
152             final CachedBluetoothDevice bluetoothDevice =
153                     ((BluetoothMediaDevice) mMediaDevice).getCachedDevice();
154             if (bluetoothDevice == null) {
155                 Log.d(TAG, "The bluetooth device is null");
156                 return null;
157             }
158             intent.setAction(ACTION_LAUNCH_BROADCAST_DIALOG);
159             intent.putExtra(
160                     BluetoothBroadcastDialog.KEY_APP_LABEL,
161                     Utils.getApplicationLabel(mContext, getWorker().getPackageName()));
162             intent.putExtra(
163                     BluetoothBroadcastDialog.KEY_DEVICE_ADDRESS, bluetoothDevice.getAddress());
164             intent.putExtra(
165                     BluetoothBroadcastDialog.KEY_MEDIA_STREAMING,
166                     getWorker() != null && getWorker().getActiveLocalMediaController() != null);
167 
168             pi =
169                     PendingIntent.getActivity(
170                             context,
171                             0 /* requestCode */,
172                             intent,
173                             PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
174         }
175 
176         final IconCompat icon = getBroadcastIcon(context);
177 
178         return SliceAction.createDeeplink(pi, icon, ListBuilder.ICON_IMAGE, getPreferenceKey());
179     }
180 
getBroadcastIcon(Context context)181     private IconCompat getBroadcastIcon(Context context) {
182         final Drawable drawable =
183                 context.getDrawable(com.android.settingslib.R.drawable.settings_input_antenna);
184         if (drawable != null) {
185             drawable.setTint(Utils.getColorAccentDefaultColor(context));
186             return Utils.createIconWithDrawable(drawable);
187         }
188         return null;
189     }
190 
getWorker()191     private MediaOutputIndicatorWorker getWorker() {
192         if (mWorker == null) {
193             mWorker = SliceBackgroundWorker.getInstance(getUri());
194         }
195         return mWorker;
196     }
197 
getUri()198     private Uri getUri() {
199         return CustomSliceRegistry.VOLUME_MEDIA_URI;
200     }
201 
202     @Override
getBackgroundWorkerClass()203     public Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() {
204         return MediaOutputIndicatorWorker.class;
205     }
206 }
207