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.app.Dialog;
20 import android.app.settings.SettingsEnums;
21 import android.os.Bundle;
22 import android.util.Log;
23 import android.util.Pair;
24 
25 import androidx.annotation.NonNull;
26 import androidx.annotation.Nullable;
27 import androidx.annotation.VisibleForTesting;
28 import androidx.appcompat.app.AlertDialog;
29 import androidx.fragment.app.Fragment;
30 import androidx.fragment.app.FragmentManager;
31 
32 import com.android.settings.R;
33 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
34 import com.android.settings.overlay.FeatureFactory;
35 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
36 import com.android.settingslib.utils.ThreadUtils;
37 
38 import com.google.common.collect.Iterables;
39 
40 import java.util.List;
41 import java.util.Locale;
42 
43 public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment {
44     private static final String TAG = "AudioSharingStopDialog";
45 
46     private static final String BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS =
47             "bundle_key_device_to_disconnect_items";
48     private static final String BUNDLE_KEY_NEW_DEVICE_NAME = "bundle_key_new_device_name";
49 
50     // The host creates an instance of this dialog fragment must implement this interface to receive
51     // event callbacks.
52     public interface DialogEventListener {
53         /** Called when users click the stop sharing button in the dialog. */
onStopSharingClick()54         void onStopSharingClick();
55     }
56 
57     @Nullable private static DialogEventListener sListener;
58     @Nullable private static CachedBluetoothDevice sCachedDevice;
59     private static Pair<Integer, Object>[] sEventData = new Pair[0];
60 
61     @Override
getMetricsCategory()62     public int getMetricsCategory() {
63         return SettingsEnums.DIALOG_STOP_AUDIO_SHARING;
64     }
65 
66     /**
67      * Display the {@link AudioSharingStopDialogFragment} dialog.
68      *
69      * <p>If the dialog is showing, update the dialog message and event listener.
70      *
71      * @param host The Fragment this dialog will be hosted.
72      * @param deviceItems The existing connected device items in audio sharing session.
73      * @param newDevice The latest connected device triggered this dialog.
74      * @param listener The callback to handle the user action on this dialog.
75      * @param eventData The eventData to log with for dialog onClick events.
76      */
show( @onNull Fragment host, @NonNull List<AudioSharingDeviceItem> deviceItems, @NonNull CachedBluetoothDevice newDevice, @NonNull DialogEventListener listener, @NonNull Pair<Integer, Object>[] eventData)77     public static void show(
78             @NonNull Fragment host,
79             @NonNull List<AudioSharingDeviceItem> deviceItems,
80             @NonNull CachedBluetoothDevice newDevice,
81             @NonNull DialogEventListener listener,
82             @NonNull Pair<Integer, Object>[] eventData) {
83         if (!AudioSharingUtils.isFeatureEnabled()) return;
84         final FragmentManager manager = host.getChildFragmentManager();
85         AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
86         if (dialog != null) {
87             int newGroupId = AudioSharingUtils.getGroupId(newDevice);
88             if (sCachedDevice != null
89                     && newGroupId == AudioSharingUtils.getGroupId(sCachedDevice)) {
90                 Log.d(
91                         TAG,
92                         String.format(
93                                 Locale.US,
94                                 "Dialog is showing for the same device group %d, return.",
95                                 newGroupId));
96                 sListener = listener;
97                 sCachedDevice = newDevice;
98                 sEventData = eventData;
99                 return;
100             } else {
101                 Log.d(
102                         TAG,
103                         String.format(
104                                 Locale.US,
105                                 "Dialog is showing for new device group %d, "
106                                         + "dismiss current dialog.",
107                                 newGroupId));
108                 dialog.dismiss();
109                 var unused =
110                         ThreadUtils.postOnBackgroundThread(
111                                 () ->
112                                         FeatureFactory.getFeatureFactory()
113                                                 .getMetricsFeatureProvider()
114                                                 .action(
115                                                         dialog.getContext(),
116                                                         SettingsEnums
117                                                         .ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS,
118                                                         SettingsEnums.DIALOG_STOP_AUDIO_SHARING));
119             }
120         }
121         sListener = listener;
122         sCachedDevice = newDevice;
123         sEventData = eventData;
124         Log.d(TAG, "Show up the dialog.");
125         final Bundle bundle = new Bundle();
126         bundle.putParcelableList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems);
127         bundle.putString(BUNDLE_KEY_NEW_DEVICE_NAME, newDevice.getName());
128         AudioSharingStopDialogFragment dialogFrag = new AudioSharingStopDialogFragment();
129         dialogFrag.setArguments(bundle);
130         dialogFrag.show(manager, TAG);
131     }
132 
133     /** Return the tag of {@link AudioSharingStopDialogFragment} dialog. */
tag()134     public static @NonNull String tag() {
135         return TAG;
136     }
137 
138     /** Get the latest connected device which triggers the dialog. */
getDevice()139     public @Nullable CachedBluetoothDevice getDevice() {
140         return sCachedDevice;
141     }
142 
143     /** Test only: get the event data passed to the dialog. */
144     @VisibleForTesting
145     @NonNull
getEventData()146     Pair<Integer, Object>[] getEventData() {
147         return sEventData;
148     }
149 
150     @Override
151     @NonNull
onCreateDialog(@ullable Bundle savedInstanceState)152     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
153         Bundle arguments = requireArguments();
154         List<AudioSharingDeviceItem> deviceItems =
155                 arguments.getParcelable(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, List.class);
156         String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME);
157         String customMessage = "";
158         if (deviceItems != null) {
159             customMessage =
160                     deviceItems.size() == 1
161                             ? getString(
162                                     R.string.audio_sharing_stop_dialog_content,
163                                     Iterables.getOnlyElement(deviceItems).getName())
164                             : (deviceItems.size() == 2
165                                     ? getString(
166                                             R.string.audio_sharing_stop_dialog_with_two_content,
167                                             deviceItems.get(0).getName(),
168                                             deviceItems.get(1).getName())
169                                     : getString(
170                                             R.string.audio_sharing_stop_dialog_with_more_content));
171         }
172         AlertDialog dialog =
173                 AudioSharingDialogFactory.newBuilder(getActivity())
174                         .setTitle(
175                                 getString(R.string.audio_sharing_stop_dialog_title, newDeviceName))
176                         .setTitleIcon(com.android.settings.R.drawable.ic_warning_24dp)
177                         .setIsCustomBodyEnabled(true)
178                         .setCustomMessage(customMessage)
179                         .setPositiveButton(
180                                 R.string.audio_sharing_connect_button_label,
181                                 (dlg, which) -> {
182                                     if (sListener != null) {
183                                         sListener.onStopSharingClick();
184                                         mMetricsFeatureProvider.action(
185                                                 getContext(),
186                                                 SettingsEnums
187                                                 .ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED,
188                                                 sEventData);
189                                     }
190                                 })
191                         .setNegativeButton(
192                                 com.android.settings.R.string.cancel,
193                                 (dlg, which) ->
194                                         mMetricsFeatureProvider.action(
195                                                 getContext(),
196                                                 SettingsEnums
197                                                 .ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED,
198                                                 sEventData))
199                         .build();
200         dialog.show();
201         AudioSharingDialogHelper.updateMessageStyle(dialog);
202         return dialog;
203     }
204 }
205