1 /* 2 * Copyright (C) 2018 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.panel; 18 19 import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; 20 import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; 21 22 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI; 23 import static com.android.settings.slices.CustomSliceRegistry.REMOTE_MEDIA_SLICE_URI; 24 import static com.android.settings.slices.CustomSliceRegistry.VOLUME_ALARM_URI; 25 import static com.android.settings.slices.CustomSliceRegistry.VOLUME_CALL_URI; 26 import static com.android.settings.slices.CustomSliceRegistry.VOLUME_MEDIA_URI; 27 import static com.android.settings.slices.CustomSliceRegistry.VOLUME_NOTIFICATION_URI; 28 import static com.android.settings.slices.CustomSliceRegistry.VOLUME_SEPARATE_RING_URI; 29 30 import android.app.Activity; 31 import android.app.settings.SettingsEnums; 32 import android.bluetooth.BluetoothDevice; 33 import android.content.BroadcastReceiver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.net.Uri; 38 import android.provider.Settings; 39 import android.text.TextUtils; 40 import android.util.Log; 41 42 import androidx.lifecycle.LifecycleObserver; 43 import androidx.lifecycle.OnLifecycleEvent; 44 45 import com.android.settings.R; 46 import com.android.settings.bluetooth.Utils; 47 import com.android.settingslib.bluetooth.A2dpProfile; 48 import com.android.settingslib.bluetooth.BluetoothUtils; 49 import com.android.settingslib.bluetooth.LocalBluetoothManager; 50 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; 51 import com.android.settingslib.media.MediaOutputConstants; 52 53 import java.util.ArrayList; 54 import java.util.List; 55 import java.util.concurrent.ExecutionException; 56 import java.util.concurrent.FutureTask; 57 58 /** 59 * Panel data class for Volume settings. 60 * 61 * @deprecated this is no longer used after V and will be removed. 62 */ 63 @Deprecated(forRemoval = true) 64 public class VolumePanel implements PanelContent, LifecycleObserver { 65 private static final String TAG = "VolumePanel"; 66 67 private final Context mContext; 68 69 private PanelContentCallback mCallback; 70 private LocalBluetoothProfileManager mProfileManager; 71 private int mControlSliceWidth; 72 73 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 74 @Override 75 public void onReceive(Context context, Intent intent) { 76 if (MediaOutputConstants.ACTION_CLOSE_PANEL.equals(intent.getAction())) { 77 mCallback.forceClose(); 78 } 79 } 80 }; 81 create(Context context)82 public static VolumePanel create(Context context) { 83 return new VolumePanel(context); 84 } 85 VolumePanel(Context context)86 private VolumePanel(Context context) { 87 mContext = context.getApplicationContext(); 88 if (context instanceof Activity) { 89 int panelWidth = 90 ((Activity) context).getWindowManager().getCurrentWindowMetrics().getBounds() 91 .width(); 92 // The control slice width = panel width - two left and right horizontal paddings 93 mControlSliceWidth = panelWidth - context.getResources().getDimensionPixelSize( 94 R.dimen.panel_slice_Horizontal_padding) * 2; 95 } 96 97 final FutureTask<LocalBluetoothManager> localBtManagerFutureTask = new FutureTask<>( 98 // Avoid StrictMode ThreadPolicy violation 99 () -> Utils.getLocalBtManager(mContext)); 100 LocalBluetoothManager localBluetoothManager; 101 try { 102 localBtManagerFutureTask.run(); 103 localBluetoothManager = localBtManagerFutureTask.get(); 104 } catch (InterruptedException | ExecutionException e) { 105 Log.w(TAG, "Error getting LocalBluetoothManager."); 106 return; 107 } 108 if (localBluetoothManager != null) { 109 mProfileManager = localBluetoothManager.getProfileManager(); 110 } 111 } 112 113 /** Invoked when the panel is resumed. */ 114 @OnLifecycleEvent(ON_RESUME) onResume()115 public void onResume() { 116 final IntentFilter filter = new IntentFilter(); 117 filter.addAction(MediaOutputConstants.ACTION_CLOSE_PANEL); 118 mContext.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED); 119 } 120 121 /** Invoked when the panel is paused. */ 122 @OnLifecycleEvent(ON_PAUSE) onPause()123 public void onPause() { 124 mContext.unregisterReceiver(mReceiver); 125 } 126 127 @Override getTitle()128 public CharSequence getTitle() { 129 return mContext.getText(R.string.sound_settings); 130 } 131 132 /** 133 * When considering ring and notification, we include all controllers unconditionally and rely 134 * on getAvailability to govern visibility 135 */ 136 @Override getSlices()137 public List<Uri> getSlices() { 138 final List<Uri> uris = new ArrayList<>(); 139 140 uris.add(REMOTE_MEDIA_SLICE_URI); 141 uris.add(VOLUME_MEDIA_URI); 142 Uri controlUri = getExtraControlUri(); 143 if (controlUri != null) { 144 Log.d(TAG, "add extra control slice"); 145 uris.add(controlUri); 146 } 147 uris.add(MEDIA_OUTPUT_INDICATOR_SLICE_URI); 148 uris.add(VOLUME_CALL_URI); 149 uris.add(VOLUME_SEPARATE_RING_URI); 150 uris.add(VOLUME_NOTIFICATION_URI); 151 uris.add(VOLUME_ALARM_URI); 152 return uris; 153 } 154 155 @Override getSeeMoreIntent()156 public Intent getSeeMoreIntent() { 157 return new Intent(Settings.ACTION_SOUND_SETTINGS) 158 .setPackage(mContext.getPackageName()) 159 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 160 } 161 162 @Override getMetricsCategory()163 public int getMetricsCategory() { 164 return SettingsEnums.PANEL_VOLUME; 165 } 166 167 @Override getViewType()168 public int getViewType() { 169 return PanelContent.VIEW_TYPE_SLIDER; 170 } 171 172 @Override registerCallback(PanelContentCallback callback)173 public void registerCallback(PanelContentCallback callback) { 174 mCallback = callback; 175 } 176 getExtraControlUri()177 private Uri getExtraControlUri() { 178 Uri controlUri = null; 179 final BluetoothDevice bluetoothDevice = findActiveDevice(); 180 if (bluetoothDevice != null) { 181 final String uri = BluetoothUtils.getControlUriMetaData(bluetoothDevice); 182 if (!TextUtils.isEmpty(uri)) { 183 try { 184 controlUri = Uri.parse(uri + mControlSliceWidth); 185 } catch (NullPointerException exception) { 186 Log.d(TAG, "unable to parse uri"); 187 controlUri = null; 188 } 189 } 190 } 191 return controlUri; 192 } 193 findActiveDevice()194 private BluetoothDevice findActiveDevice() { 195 if (mProfileManager != null) { 196 final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile(); 197 if (a2dpProfile != null) { 198 return a2dpProfile.getActiveDevice(); 199 } 200 } 201 return null; 202 } 203 } 204