1 /*
2  * Copyright 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.car.settings.bluetooth;
18 
19 import android.app.Dialog;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.content.Context;
23 import android.os.Bundle;
24 import android.text.TextUtils;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 
29 import com.android.car.settings.R;
30 import com.android.car.ui.AlertDialogBuilder;
31 import com.android.car.ui.preference.CarUiDialogFragment;
32 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
33 import com.android.settingslib.bluetooth.LocalBluetoothManager;
34 
35 /**
36  * Displays a dialog which prompts the user to confirm disconnecting from a remote Bluetooth device.
37  */
38 public class BluetoothDisconnectConfirmDialogFragment extends CarUiDialogFragment {
39 
40     private static final String KEY_DEVICE_ADDRESS = "device_address";
41 
42     private final CachedBluetoothDevice.Callback mDeviceCallback = this::dismissIfNotConnected;
43     private CachedBluetoothDevice mCachedDevice;
44 
45     /**
46      * Returns a new {@link BluetoothDisconnectConfirmDialogFragment} for the given {@code device}.
47      */
newInstance( CachedBluetoothDevice device)48     public static BluetoothDisconnectConfirmDialogFragment newInstance(
49             CachedBluetoothDevice device) {
50         Bundle args = new Bundle();
51         args.putString(KEY_DEVICE_ADDRESS, device.getAddress());
52         BluetoothDisconnectConfirmDialogFragment fragment =
53                 new BluetoothDisconnectConfirmDialogFragment();
54         fragment.setArguments(args);
55         return fragment;
56     }
57 
58     @Override
onAttach(Context context)59     public void onAttach(Context context) {
60         super.onAttach(context);
61         String deviceAddress = getArguments().getString(KEY_DEVICE_ADDRESS);
62         LocalBluetoothManager manager = BluetoothUtils.getLocalBtManager(context);
63         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
64                 deviceAddress);
65         mCachedDevice = manager.getCachedDeviceManager().findDevice(device);
66     }
67 
68     @NonNull
69     @Override
onCreateDialog(@ullable Bundle savedInstanceState)70     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
71         Context context = requireContext();
72         String name = mCachedDevice.getName();
73         if (TextUtils.isEmpty(name)) {
74             name = context.getString(R.string.bluetooth_device);
75         }
76         String title = context.getString(R.string.bluetooth_disconnect_title);
77         String message = context.getString(R.string.bluetooth_disconnect_all_profiles, name);
78 
79         return new AlertDialogBuilder(context)
80                 .setTitle(title)
81                 .setMessage(message)
82                 .setPositiveButton(android.R.string.ok,
83                         (dialog, which) -> mCachedDevice.disconnect())
84                 .setNegativeButton(android.R.string.cancel, /* listener= */ null)
85                 .create();
86     }
87 
88     @Override
onDialogClosed(boolean positiveResult)89     protected void onDialogClosed(boolean positiveResult) {
90     }
91 
92     @Override
onStart()93     public void onStart() {
94         super.onStart();
95         mCachedDevice.registerCallback(mDeviceCallback);
96     }
97 
98     @Override
onStop()99     public void onStop() {
100         super.onStop();
101         mCachedDevice.unregisterCallback(mDeviceCallback);
102     }
103 
dismissIfNotConnected()104     private void dismissIfNotConnected() {
105         // This handles the case where the dialog is showing and the connection is broken via UI
106         // on the remote device. It does not cover the case of the device disconnecting while the
107         // fragment is starting because we cannot begin another transaction for dismiss while in
108         // a transaction to show. That case, however, should be extremely rare, and the action
109         // taken on the dialog will have no effect.
110         if (!mCachedDevice.isConnected() && getDialog().isShowing()) {
111             dismiss();
112         }
113     }
114 }
115