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.AlertDialog; 20 import android.app.Dialog; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.os.Bundle; 24 import android.text.Editable; 25 import android.text.InputFilter; 26 import android.text.InputType; 27 import android.text.TextUtils; 28 import android.text.TextWatcher; 29 import android.view.KeyEvent; 30 import android.view.inputmethod.EditorInfo; 31 import android.view.inputmethod.InputMethodManager; 32 import android.widget.Button; 33 import android.widget.EditText; 34 import android.widget.TextView; 35 36 import androidx.annotation.NonNull; 37 import androidx.annotation.Nullable; 38 import androidx.annotation.StringRes; 39 40 import com.android.car.settings.R; 41 import com.android.car.ui.AlertDialogBuilder; 42 import com.android.car.ui.preference.CarUiDialogFragment; 43 44 import java.util.Objects; 45 46 /** Dialog fragment for renaming a Bluetooth device. */ 47 public abstract class BluetoothRenameDialogFragment extends CarUiDialogFragment implements 48 TextWatcher, TextView.OnEditorActionListener { 49 50 // Keys to save the edited name and edit status for restoring after configuration change. 51 private static final String KEY_NAME = "device_name"; 52 53 private static final int BLUETOOTH_NAME_MAX_LENGTH_BYTES = 248; 54 55 private AlertDialog mAlertDialog; 56 private String mDeviceName; 57 58 /** Returns the title to use for the dialog. */ 59 @StringRes getDialogTitle()60 protected abstract int getDialogTitle(); 61 62 /** Returns the current name used for this device or {@code null} if a name is not available. */ 63 @Nullable getDeviceName()64 protected abstract String getDeviceName(); 65 66 /** 67 * Set the device to the given name. 68 * 69 * @param deviceName the name to use. 70 */ setDeviceName(String deviceName)71 protected abstract void setDeviceName(String deviceName); 72 73 @Override 74 @NonNull onCreateDialog(Bundle savedInstanceState)75 public Dialog onCreateDialog(Bundle savedInstanceState) { 76 String deviceName = getDeviceName(); 77 if (savedInstanceState != null) { 78 deviceName = savedInstanceState.getString(KEY_NAME, deviceName); 79 } 80 81 createAlertDialog(deviceName); 82 83 return mAlertDialog; 84 } 85 createAlertDialog(String deviceName)86 private void createAlertDialog(String deviceName) { 87 InputFilter[] inputFilters = new InputFilter[]{new Utf8ByteLengthFilter( 88 BLUETOOTH_NAME_MAX_LENGTH_BYTES)}; 89 AlertDialogBuilder builder = new AlertDialogBuilder(requireActivity()) 90 .setTitle(getDialogTitle()) 91 .setPositiveButton(R.string.bluetooth_rename_button, 92 (dialog, which) -> setDeviceName(getEditedName())) 93 .setNegativeButton(android.R.string.cancel, /* listener= */ null) 94 .setEditBox(deviceName, /* textChangedListener= */ this, inputFilters, 95 InputType.TYPE_CLASS_TEXT); 96 97 mAlertDialog = builder.create(); 98 99 mAlertDialog.setOnShowListener(d -> { 100 101 EditText deviceNameView = getDeviceNameView(); 102 103 if (mDeviceName != null) { 104 deviceNameView.setSelection(mDeviceName.length()); 105 } 106 107 deviceNameView.setOnEditorActionListener(this); 108 deviceNameView.setImeOptions(EditorInfo.IME_ACTION_DONE); 109 110 if (deviceNameView.requestFocus()) { 111 InputMethodManager imm = (InputMethodManager) requireContext().getSystemService( 112 Context.INPUT_METHOD_SERVICE); 113 if (imm != null) { 114 imm.showSoftInput(deviceNameView, InputMethodManager.SHOW_IMPLICIT); 115 } 116 } 117 118 refreshRenameButton(); 119 }); 120 121 } 122 123 @Override onDialogClosed(boolean positiveResult)124 protected void onDialogClosed(boolean positiveResult) { 125 } 126 127 @Override onSaveInstanceState(@onNull Bundle outState)128 public void onSaveInstanceState(@NonNull Bundle outState) { 129 outState.putString(KEY_NAME, getEditedName()); 130 } 131 132 133 @Override onStart()134 public void onStart() { 135 super.onStart(); 136 refreshRenameButton(); 137 } 138 139 @Override onDestroy()140 public void onDestroy() { 141 super.onDestroy(); 142 mAlertDialog = null; 143 } 144 145 /** Refreshes the displayed device name with the latest value from {@link #getDeviceName()}. */ updateDeviceName()146 protected void updateDeviceName() { 147 String deviceName = getDeviceName(); 148 if (deviceName != null) { 149 mDeviceName = deviceName; 150 EditText deviceNameView = getDeviceNameView(); 151 if (deviceNameView != null) { 152 deviceNameView.setText(deviceName); 153 } 154 } 155 } 156 157 @Override beforeTextChanged(CharSequence s, int start, int count, int after)158 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 159 } 160 161 @Override onTextChanged(CharSequence s, int start, int before, int count)162 public void onTextChanged(CharSequence s, int start, int before, int count) { 163 mDeviceName = s.toString(); 164 } 165 166 @Override afterTextChanged(Editable s)167 public void afterTextChanged(Editable s) { 168 refreshRenameButton(); 169 } 170 171 @Override onEditorAction(TextView v, int actionId, KeyEvent event)172 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 173 if (actionId == EditorInfo.IME_ACTION_DONE) { 174 String editedName = getEditedName(); 175 if (TextUtils.isEmpty(editedName)) { 176 return false; 177 } 178 setDeviceName(editedName); 179 if (mAlertDialog != null && mAlertDialog.isShowing()) { 180 mAlertDialog.dismiss(); 181 } 182 return true; 183 } 184 return false; 185 } 186 refreshRenameButton()187 private void refreshRenameButton() { 188 if (mAlertDialog != null) { 189 Button renameButton = mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE); 190 if (renameButton != null) { 191 String editedName = getEditedName(); 192 renameButton.setEnabled( 193 !TextUtils.isEmpty(editedName) && !Objects.equals(editedName, 194 getDeviceName())); 195 } 196 } 197 } 198 getEditedName()199 private String getEditedName() { 200 return mDeviceName.trim(); 201 } 202 getDeviceNameView()203 private EditText getDeviceNameView() { 204 return mAlertDialog.getWindow().findViewById(R.id.textbox); 205 } 206 } 207