1 /*
2  * Copyright (C) 2023 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.connecteddevice.audiosharing;
18 
19 import android.bluetooth.BluetoothLeBroadcast;
20 import android.bluetooth.BluetoothLeBroadcastMetadata;
21 import android.content.Context;
22 import android.util.Log;
23 
24 import androidx.annotation.NonNull;
25 import androidx.annotation.Nullable;
26 import androidx.annotation.VisibleForTesting;
27 import androidx.lifecycle.DefaultLifecycleObserver;
28 import androidx.lifecycle.LifecycleOwner;
29 import androidx.preference.Preference;
30 import androidx.preference.PreferenceScreen;
31 
32 import com.android.settings.R;
33 import com.android.settings.bluetooth.Utils;
34 import com.android.settings.core.BasePreferenceController;
35 import com.android.settingslib.bluetooth.BluetoothCallback;
36 import com.android.settingslib.bluetooth.BluetoothEventManager;
37 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
38 import com.android.settingslib.bluetooth.LocalBluetoothManager;
39 import com.android.settingslib.utils.ThreadUtils;
40 
41 import java.util.concurrent.Executor;
42 import java.util.concurrent.Executors;
43 
44 public class AudioSharingPreferenceController extends BasePreferenceController
45         implements DefaultLifecycleObserver, BluetoothCallback {
46     private static final String TAG = "AudioSharingPreferenceController";
47 
48     @Nullable private final LocalBluetoothManager mBtManager;
49     @Nullable private final BluetoothEventManager mEventManager;
50     @Nullable private final LocalBluetoothLeBroadcast mBroadcast;
51     @Nullable private Preference mPreference;
52     private final Executor mExecutor;
53 
54     @VisibleForTesting
55     final BluetoothLeBroadcast.Callback mBroadcastCallback =
56             new BluetoothLeBroadcast.Callback() {
57                 @Override
58                 public void onBroadcastStarted(int reason, int broadcastId) {
59                     refreshSummary();
60                 }
61 
62                 @Override
63                 public void onBroadcastStartFailed(int reason) {}
64 
65                 @Override
66                 public void onBroadcastMetadataChanged(
67                         int broadcastId, @NonNull BluetoothLeBroadcastMetadata metadata) {}
68 
69                 @Override
70                 public void onBroadcastStopped(int reason, int broadcastId) {
71                     refreshSummary();
72                 }
73 
74                 @Override
75                 public void onBroadcastStopFailed(int reason) {}
76 
77                 @Override
78                 public void onBroadcastUpdated(int reason, int broadcastId) {}
79 
80                 @Override
81                 public void onBroadcastUpdateFailed(int reason, int broadcastId) {}
82 
83                 @Override
84                 public void onPlaybackStarted(int reason, int broadcastId) {}
85 
86                 @Override
87                 public void onPlaybackStopped(int reason, int broadcastId) {}
88             };
89 
AudioSharingPreferenceController(Context context, String preferenceKey)90     public AudioSharingPreferenceController(Context context, String preferenceKey) {
91         super(context, preferenceKey);
92         mBtManager = Utils.getLocalBtManager(context);
93         mEventManager = mBtManager == null ? null : mBtManager.getEventManager();
94         mBroadcast =
95                 mBtManager == null
96                         ? null
97                         : mBtManager.getProfileManager().getLeAudioBroadcastProfile();
98         mExecutor = Executors.newSingleThreadExecutor();
99     }
100 
101     @Override
onStart(@onNull LifecycleOwner owner)102     public void onStart(@NonNull LifecycleOwner owner) {
103         if (!isAvailable()) {
104             Log.d(TAG, "Skip register callbacks, feature not support");
105             return;
106         }
107         if (mEventManager == null || mBroadcast == null) {
108             Log.d(TAG, "Skip register callbacks, profile not ready");
109             return;
110         }
111         mEventManager.registerCallback(this);
112         mBroadcast.registerServiceCallBack(mExecutor, mBroadcastCallback);
113     }
114 
115     @Override
onStop(@onNull LifecycleOwner owner)116     public void onStop(@NonNull LifecycleOwner owner) {
117         if (!isAvailable()) {
118             Log.d(TAG, "Skip unregister callbacks, feature not support");
119             return;
120         }
121         if (mEventManager == null || mBroadcast == null) {
122             Log.d(TAG, "Skip register callbacks, profile not ready");
123             return;
124         }
125         mEventManager.unregisterCallback(this);
126         mBroadcast.unregisterServiceCallBack(mBroadcastCallback);
127     }
128 
129     @Override
displayPreference(@onNull PreferenceScreen screen)130     public void displayPreference(@NonNull PreferenceScreen screen) {
131         super.displayPreference(screen);
132         mPreference = screen.findPreference(getPreferenceKey());
133     }
134 
135     @Override
getAvailabilityStatus()136     public int getAvailabilityStatus() {
137         return AudioSharingUtils.isFeatureEnabled() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
138     }
139 
140     @Override
getSummary()141     public CharSequence getSummary() {
142         return AudioSharingUtils.isBroadcasting(mBtManager)
143                 ? mContext.getString(R.string.audio_sharing_summary_on)
144                 : mContext.getString(R.string.audio_sharing_summary_off);
145     }
146 
147     @Override
onBluetoothStateChanged(@dapterState int bluetoothState)148     public void onBluetoothStateChanged(@AdapterState int bluetoothState) {
149         refreshSummary();
150     }
151 
refreshSummary()152     private void refreshSummary() {
153         if (mPreference == null) {
154             return;
155         }
156         var unused =
157                 ThreadUtils.postOnBackgroundThread(
158                         () -> {
159                             final CharSequence summary = getSummary();
160                             AudioSharingUtils.postOnMainThread(
161                                     mContext,
162                                     () -> {
163                                         // Check nullability to pass NullAway check
164                                         if (mPreference != null) {
165                                             mPreference.setSummary(summary);
166                                         }
167                                     });
168                         });
169     }
170 }
171