1 /* 2 * Copyright (C) 2011 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.bluetooth; 18 19 import android.app.Dialog; 20 import android.content.Context; 21 import android.content.DialogInterface; 22 import android.content.res.Configuration; 23 import android.os.Bundle; 24 import android.text.Editable; 25 import android.text.InputFilter; 26 import android.text.TextUtils; 27 import android.text.TextWatcher; 28 import android.view.KeyEvent; 29 import android.view.LayoutInflater; 30 import android.view.View; 31 import android.view.inputmethod.EditorInfo; 32 import android.view.inputmethod.InputMethodManager; 33 import android.widget.Button; 34 import android.widget.EditText; 35 import android.widget.TextView; 36 37 import androidx.annotation.VisibleForTesting; 38 import androidx.appcompat.app.AlertDialog; 39 40 import com.android.settings.R; 41 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 42 43 /** 44 * Dialog fragment for renaming a Bluetooth device. 45 */ 46 abstract class BluetoothNameDialogFragment extends InstrumentedDialogFragment 47 implements TextWatcher, TextView.OnEditorActionListener { 48 49 // Key to save the edited name and edit status for restoring after rotation 50 private static final String KEY_NAME = "device_name"; 51 private static final String KEY_NAME_EDITED = "device_name_edited"; 52 53 @VisibleForTesting 54 AlertDialog mAlertDialog; 55 private Button mOkButton; 56 57 EditText mDeviceNameView; 58 59 // This flag is set when the name is updated by code, to distinguish from user changes 60 private boolean mDeviceNameUpdated; 61 62 // This flag is set when the user edits the name (preserved on rotation) 63 private boolean mDeviceNameEdited; 64 65 /** 66 * @return the title to use for the dialog. 67 */ getDialogTitle()68 abstract protected int getDialogTitle(); 69 70 /** 71 * @return the current name used for this device. 72 */ getDeviceName()73 abstract protected String getDeviceName(); 74 75 /** 76 * Set the device to the given name. 77 * @param deviceName the name to use 78 */ setDeviceName(String deviceName)79 abstract protected void setDeviceName(String deviceName); 80 81 @Override onCreateDialog(Bundle savedInstanceState)82 public Dialog onCreateDialog(Bundle savedInstanceState) { 83 String deviceName = getDeviceName(); 84 if (savedInstanceState != null) { 85 deviceName = savedInstanceState.getString(KEY_NAME, deviceName); 86 mDeviceNameEdited = savedInstanceState.getBoolean(KEY_NAME_EDITED, false); 87 } 88 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) 89 .setTitle(getDialogTitle()) 90 .setView(createDialogView(deviceName)) 91 .setPositiveButton(R.string.bluetooth_rename_button, (dialog, which) -> { 92 setDeviceName(mDeviceNameView.getText().toString()); 93 }) 94 .setNegativeButton(android.R.string.cancel, null); 95 mAlertDialog = builder.create(); 96 mAlertDialog.setOnShowListener(d -> { 97 if (mDeviceNameView != null && mDeviceNameView.requestFocus()) { 98 InputMethodManager imm = (InputMethodManager) getContext().getSystemService( 99 Context.INPUT_METHOD_SERVICE); 100 if (imm != null) { 101 imm.showSoftInput(mDeviceNameView, InputMethodManager.SHOW_IMPLICIT); 102 } 103 } 104 }); 105 106 return mAlertDialog; 107 } 108 109 @Override onSaveInstanceState(Bundle outState)110 public void onSaveInstanceState(Bundle outState) { 111 outState.putString(KEY_NAME, mDeviceNameView.getText().toString()); 112 outState.putBoolean(KEY_NAME_EDITED, mDeviceNameEdited); 113 } 114 createDialogView(String deviceName)115 private View createDialogView(String deviceName) { 116 final LayoutInflater layoutInflater = (LayoutInflater)getActivity() 117 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 118 View view = layoutInflater.inflate(R.layout.dialog_edittext, null); 119 mDeviceNameView = (EditText) view.findViewById(R.id.edittext); 120 mDeviceNameView.setFilters(new InputFilter[] { 121 new BluetoothLengthDeviceNameFilter() 122 }); 123 mDeviceNameView.setText(deviceName); // set initial value before adding listener 124 if (!TextUtils.isEmpty(deviceName)) { 125 mDeviceNameView.setSelection(deviceName.length()); 126 } 127 mDeviceNameView.addTextChangedListener(this); 128 com.android.settings.Utils.setEditTextCursorPosition(mDeviceNameView); 129 mDeviceNameView.setOnEditorActionListener(this); 130 return view; 131 } 132 133 @Override onEditorAction(TextView v, int actionId, KeyEvent event)134 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 135 if (actionId == EditorInfo.IME_ACTION_DONE) { 136 setDeviceName(v.getText().toString()); 137 if (mAlertDialog != null && mAlertDialog.isShowing()) { 138 mAlertDialog.dismiss(); 139 } 140 return true; // action handled 141 } else { 142 return false; // not handled 143 } 144 } 145 146 @Override onDestroy()147 public void onDestroy() { 148 super.onDestroy(); 149 mAlertDialog = null; 150 mDeviceNameView = null; 151 mOkButton = null; 152 } 153 154 @Override onResume()155 public void onResume() { 156 super.onResume(); 157 if (mOkButton == null) { 158 mOkButton = mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE); 159 mOkButton.setEnabled(mDeviceNameEdited); // Ok button enabled after user edits 160 } 161 } 162 updateDeviceName()163 void updateDeviceName() { 164 String name = getDeviceName(); 165 if (name != null) { 166 mDeviceNameUpdated = true; 167 mDeviceNameEdited = false; 168 mDeviceNameView.setText(name); 169 } 170 } 171 afterTextChanged(Editable s)172 public void afterTextChanged(Editable s) { 173 if (mDeviceNameUpdated) { 174 // Device name changed by code; disable Ok button until edited by user 175 mDeviceNameUpdated = false; 176 mOkButton.setEnabled(false); 177 } else { 178 mDeviceNameEdited = true; 179 if (mOkButton != null) { 180 mOkButton.setEnabled(s.toString().trim().length() != 0); 181 } 182 } 183 } 184 onConfigurationChanged(Configuration newConfig, CharSequence s)185 public void onConfigurationChanged(Configuration newConfig, CharSequence s) { 186 super.onConfigurationChanged(newConfig); 187 if (mOkButton != null) { 188 mOkButton.setEnabled(s.length() != 0 && !(s.toString().trim().isEmpty())); 189 } 190 } 191 192 /* Not used */ beforeTextChanged(CharSequence s, int start, int count, int after)193 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 194 } 195 196 /* Not used */ onTextChanged(CharSequence s, int start, int before, int count)197 public void onTextChanged(CharSequence s, int start, int before, int count) { 198 } 199 } 200