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