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