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