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.AlertDialog;
20 import android.app.Dialog;
21 import android.app.DialogFragment;
22 import android.bluetooth.BluetoothAdapter;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.res.Configuration;
29 import android.os.Bundle;
30 import android.text.Editable;
31 import android.text.InputFilter;
32 import android.text.TextWatcher;
33 import android.util.Log;
34 import android.view.KeyEvent;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.view.WindowManager;
38 import android.view.inputmethod.EditorInfo;
39 import android.widget.Button;
40 import android.widget.EditText;
41 import android.widget.TextView;
42 
43 import com.android.settings.R;
44 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
45 import com.android.settingslib.bluetooth.LocalBluetoothManager;
46 
47 /**
48  * Dialog fragment for renaming the local Bluetooth device.
49  */
50 public final class BluetoothNameDialogFragment extends DialogFragment implements TextWatcher {
51     private static final int BLUETOOTH_NAME_MAX_LENGTH_BYTES = 248;
52 
53     private AlertDialog mAlertDialog;
54     private Button mOkButton;
55 
56     // accessed from inner class (not private to avoid thunks)
57     static final String TAG = "BluetoothNameDialogFragment";
58     final LocalBluetoothAdapter mLocalAdapter;
59     EditText mDeviceNameView;
60 
61     // This flag is set when the name is updated by code, to distinguish from user changes
62     private boolean mDeviceNameUpdated;
63 
64     // This flag is set when the user edits the name (preserved on rotation)
65     private boolean mDeviceNameEdited;
66 
67     // Key to save the edited name and edit status for restoring after rotation
68     private static final String KEY_NAME = "device_name";
69     private static final String KEY_NAME_EDITED = "device_name_edited";
70 
71     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
72         @Override
73         public void onReceive(Context context, Intent intent) {
74             String action = intent.getAction();
75             if (action.equals(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)) {
76                 updateDeviceName();
77             } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) &&
78                     (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) ==
79                             BluetoothAdapter.STATE_ON)) {
80                 updateDeviceName();
81             }
82         }
83     };
84 
BluetoothNameDialogFragment()85     public BluetoothNameDialogFragment() {
86         LocalBluetoothManager localManager = Utils.getLocalBtManager(getActivity());
87         mLocalAdapter = localManager.getBluetoothAdapter();
88     }
89 
90     @Override
onCreateDialog(Bundle savedInstanceState)91     public Dialog onCreateDialog(Bundle savedInstanceState) {
92         String deviceName = mLocalAdapter.getName();
93         if (savedInstanceState != null) {
94             deviceName = savedInstanceState.getString(KEY_NAME, deviceName);
95             mDeviceNameEdited = savedInstanceState.getBoolean(KEY_NAME_EDITED, false);
96         }
97         mAlertDialog = new AlertDialog.Builder(getActivity())
98                 .setTitle(R.string.bluetooth_rename_device)
99                 .setView(createDialogView(deviceName))
100                 .setPositiveButton(R.string.bluetooth_rename_button,
101                         new DialogInterface.OnClickListener() {
102                             public void onClick(DialogInterface dialog, int which) {
103                                 String deviceName = mDeviceNameView.getText().toString();
104                                 setDeviceName(deviceName);
105                             }
106                         })
107                 .setNegativeButton(android.R.string.cancel, null)
108                 .create();
109         mAlertDialog.getWindow().setSoftInputMode(
110                 WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
111 
112         return mAlertDialog;
113     }
114 
setDeviceName(String deviceName)115     private void setDeviceName(String deviceName) {
116         Log.d(TAG, "Setting device name to " + deviceName);
117         mLocalAdapter.setName(deviceName);
118     }
119 
120     @Override
onSaveInstanceState(Bundle outState)121     public void onSaveInstanceState(Bundle outState) {
122         outState.putString(KEY_NAME, mDeviceNameView.getText().toString());
123         outState.putBoolean(KEY_NAME_EDITED, mDeviceNameEdited);
124     }
125 
createDialogView(String deviceName)126     private View createDialogView(String deviceName) {
127         final LayoutInflater layoutInflater = (LayoutInflater)getActivity()
128             .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
129         View view = layoutInflater.inflate(R.layout.dialog_edittext, null);
130         mDeviceNameView = (EditText) view.findViewById(R.id.edittext);
131         mDeviceNameView.setFilters(new InputFilter[] {
132                 new Utf8ByteLengthFilter(BLUETOOTH_NAME_MAX_LENGTH_BYTES)
133         });
134         mDeviceNameView.setText(deviceName);    // set initial value before adding listener
135         mDeviceNameView.addTextChangedListener(this);
136         mDeviceNameView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
137             @Override
138             public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
139                 if (actionId == EditorInfo.IME_ACTION_DONE) {
140                     setDeviceName(v.getText().toString());
141                     mAlertDialog.dismiss();
142                     return true;    // action handled
143                 } else {
144                     return false;   // not handled
145                 }
146             }
147         });
148         return view;
149     }
150 
151     @Override
onDestroy()152     public void onDestroy() {
153         super.onDestroy();
154         mAlertDialog = null;
155         mDeviceNameView = null;
156         mOkButton = null;
157     }
158 
159     @Override
onResume()160     public void onResume() {
161         super.onResume();
162         if (mOkButton == null) {
163             mOkButton = mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
164             mOkButton.setEnabled(mDeviceNameEdited);    // Ok button enabled after user edits
165         }
166         IntentFilter filter = new IntentFilter();
167         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
168         filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
169         getActivity().registerReceiver(mReceiver, filter);
170     }
171 
172     @Override
onPause()173     public void onPause() {
174         super.onPause();
175         getActivity().unregisterReceiver(mReceiver);
176     }
177 
updateDeviceName()178     void updateDeviceName() {
179         if (mLocalAdapter != null && mLocalAdapter.isEnabled()) {
180             mDeviceNameUpdated = true;
181             mDeviceNameEdited = false;
182             mDeviceNameView.setText(mLocalAdapter.getName());
183         }
184     }
185 
afterTextChanged(Editable s)186     public void afterTextChanged(Editable s) {
187         if (mDeviceNameUpdated) {
188             // Device name changed by code; disable Ok button until edited by user
189             mDeviceNameUpdated = false;
190             mOkButton.setEnabled(false);
191         } else {
192             mDeviceNameEdited = true;
193             if (mOkButton != null) {
194                 mOkButton.setEnabled(s.toString().trim().length() != 0);
195             }
196         }
197     }
198 
onConfigurationChanged(Configuration newConfig, CharSequence s)199     public void onConfigurationChanged(Configuration newConfig, CharSequence s) {
200         super.onConfigurationChanged(newConfig);
201         if (mOkButton != null) {
202             mOkButton.setEnabled(s.length() != 0 && !(s.toString().trim().isEmpty()));
203         }
204     }
205 
206     /* Not used */
beforeTextChanged(CharSequence s, int start, int count, int after)207     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
208     }
209 
210     /* Not used */
onTextChanged(CharSequence s, int start, int before, int count)211     public void onTextChanged(CharSequence s, int start, int before, int count) {
212     }
213 }
214