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