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