OSDN Git Service

Import translations. DO NOT MERGE
[android-x86/packages-apps-Settings.git] / src / com / android / settings / bluetooth / BluetoothPairingDialog.java
1 /*
2  * Copyright (C) 2008 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.bluetooth.BluetoothDevice;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.os.Bundle;
26 import android.text.Editable;
27 import android.text.Html;
28 import android.text.InputFilter;
29 import android.text.InputType;
30 import android.text.Spanned;
31 import android.text.TextWatcher;
32 import android.text.InputFilter.LengthFilter;
33 import android.util.Log;
34 import android.view.View;
35 import android.widget.Button;
36 import android.widget.CheckBox;
37 import android.widget.CompoundButton;
38 import android.widget.EditText;
39 import android.widget.TextView;
40
41 import com.android.internal.app.AlertActivity;
42 import com.android.internal.app.AlertController;
43 import com.android.settings.R;
44 import android.view.KeyEvent;
45
46 import java.util.Locale;
47
48 /**
49  * BluetoothPairingDialog asks the user to enter a PIN / Passkey / simple confirmation
50  * for pairing with a remote Bluetooth device. It is an activity that appears as a dialog.
51  */
52 public final class BluetoothPairingDialog extends AlertActivity implements
53         CompoundButton.OnCheckedChangeListener, DialogInterface.OnClickListener, TextWatcher {
54     private static final String TAG = "BluetoothPairingDialog";
55
56     private static final int BLUETOOTH_PIN_MAX_LENGTH = 16;
57     private static final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6;
58
59     private LocalBluetoothManager mBluetoothManager;
60     private CachedBluetoothDeviceManager mCachedDeviceManager;
61     private BluetoothDevice mDevice;
62     private int mType;
63     private String mPairingKey;
64     private EditText mPairingView;
65     private Button mOkButton;
66
67     /**
68      * Dismiss the dialog if the bond state changes to bonded or none,
69      * or if pairing was canceled for {@link #mDevice}.
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 (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
76                 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
77                                                    BluetoothDevice.ERROR);
78                 if (bondState == BluetoothDevice.BOND_BONDED ||
79                         bondState == BluetoothDevice.BOND_NONE) {
80                     dismiss();
81                 }
82             } else if (BluetoothDevice.ACTION_PAIRING_CANCEL.equals(action)) {
83                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
84                 if (device == null || device.equals(mDevice)) {
85                     dismiss();
86                 }
87             }
88         }
89     };
90
91     @Override
92     protected void onCreate(Bundle savedInstanceState) {
93         super.onCreate(savedInstanceState);
94
95         Intent intent = getIntent();
96         if (!intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST))
97         {
98             Log.e(TAG, "Error: this activity may be started only with intent " +
99                   BluetoothDevice.ACTION_PAIRING_REQUEST);
100             finish();
101             return;
102         }
103
104         mBluetoothManager = LocalBluetoothManager.getInstance(this);
105         if (mBluetoothManager == null) {
106             Log.e(TAG, "Error: BluetoothAdapter not supported by system");
107             finish();
108             return;
109         }
110         mCachedDeviceManager = mBluetoothManager.getCachedDeviceManager();
111
112         mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
113         mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
114
115         switch (mType) {
116             case BluetoothDevice.PAIRING_VARIANT_PIN:
117             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
118                 createUserEntryDialog();
119                 break;
120
121             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
122                 int passkey =
123                     intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
124                 if (passkey == BluetoothDevice.ERROR) {
125                     Log.e(TAG, "Invalid Confirmation Passkey received, not showing any dialog");
126                     return;
127                 }
128                 mPairingKey = String.format(Locale.US, "%06d", passkey);
129                 createConfirmationDialog();
130                 break;
131
132             case BluetoothDevice.PAIRING_VARIANT_CONSENT:
133             case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
134                 createConsentDialog();
135                 break;
136
137             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
138             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
139                 int pairingKey =
140                     intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
141                 if (pairingKey == BluetoothDevice.ERROR) {
142                     Log.e(TAG, "Invalid Confirmation Passkey or PIN received, not showing any dialog");
143                     return;
144                 }
145                 if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
146                     mPairingKey = String.format("%06d", pairingKey);
147                 } else {
148                     mPairingKey = String.format("%04d", pairingKey);
149                 }
150                 createDisplayPasskeyOrPinDialog();
151                 break;
152
153             default:
154                 Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
155         }
156
157         /*
158          * Leave this registered through pause/resume since we still want to
159          * finish the activity in the background if pairing is canceled.
160          */
161         registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_CANCEL));
162         registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
163     }
164
165     private void createUserEntryDialog() {
166         final AlertController.AlertParams p = mAlertParams;
167         p.mTitle = getString(R.string.bluetooth_pairing_request);
168         p.mView = createPinEntryView();
169         p.mPositiveButtonText = getString(android.R.string.ok);
170         p.mPositiveButtonListener = this;
171         p.mNegativeButtonText = getString(android.R.string.cancel);
172         p.mNegativeButtonListener = this;
173         setupAlert();
174
175         mOkButton = mAlert.getButton(BUTTON_POSITIVE);
176         mOkButton.setEnabled(false);
177     }
178
179     private View createPinEntryView() {
180         View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null);
181         TextView messageViewCaption = (TextView) view.findViewById(R.id.message_caption);
182         TextView messageViewContent = (TextView) view.findViewById(R.id.message_subhead);
183         TextView messageView2 = (TextView) view.findViewById(R.id.message_below_pin);
184         CheckBox alphanumericPin = (CheckBox) view.findViewById(R.id.alphanumeric_pin);
185         mPairingView = (EditText) view.findViewById(R.id.text);
186         mPairingView.addTextChangedListener(this);
187         alphanumericPin.setOnCheckedChangeListener(this);
188
189         int messageId1;
190         int messageId2;
191         int maxLength;
192         switch (mType) {
193             case BluetoothDevice.PAIRING_VARIANT_PIN:
194                 messageId1 = R.string.bluetooth_enter_pin_msg;
195                 messageId2 = R.string.bluetooth_enter_pin_other_device;
196                 // Maximum of 16 characters in a PIN
197                 maxLength = BLUETOOTH_PIN_MAX_LENGTH;
198                 break;
199
200             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
201                 messageId1 = R.string.bluetooth_enter_pin_msg;
202                 messageId2 = R.string.bluetooth_enter_passkey_other_device;
203                 // Maximum of 6 digits for passkey
204                 maxLength = BLUETOOTH_PASSKEY_MAX_LENGTH;
205                 alphanumericPin.setVisibility(View.GONE);
206                 break;
207
208             default:
209                 Log.e(TAG, "Incorrect pairing type for createPinEntryView: " + mType);
210                 return null;
211         }
212
213         messageViewCaption.setText(messageId1);
214         messageViewContent.setText(mCachedDeviceManager.getName(mDevice));
215         messageView2.setText(messageId2);
216         mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
217         mPairingView.setFilters(new InputFilter[] {
218                 new LengthFilter(maxLength) });
219
220         return view;
221     }
222
223     private View createView() {
224         View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_confirm, null);
225         // Escape device name to avoid HTML injection.
226         String name = Html.escapeHtml(mCachedDeviceManager.getName(mDevice));
227         TextView messageViewCaption = (TextView) view.findViewById(R.id.message_caption);
228         TextView messageViewContent = (TextView) view.findViewById(R.id.message_subhead);
229         TextView pairingViewCaption = (TextView) view.findViewById(R.id.pairing_caption);
230         TextView pairingViewContent = (TextView) view.findViewById(R.id.pairing_subhead);
231         TextView messagePairing = (TextView) view.findViewById(R.id.pairing_code_message);
232
233         String messageCaption = null;
234         String pairingContent = null;
235         switch (mType) {
236             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
237             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
238                 messagePairing.setVisibility(View.VISIBLE);
239             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
240                 messageCaption = getString(R.string.bluetooth_enter_pin_msg);
241                 pairingContent = mPairingKey;
242                 break;
243
244             case BluetoothDevice.PAIRING_VARIANT_CONSENT:
245             case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
246                 messagePairing.setVisibility(View.VISIBLE);
247                 messageCaption = getString(R.string.bluetooth_enter_pin_msg);
248                 break;
249
250             default:
251                 Log.e(TAG, "Incorrect pairing type received, not creating view");
252                 return null;
253         }
254
255         if (messageViewCaption != null) {
256             messageViewCaption.setText(messageCaption);
257             messageViewContent.setText(name);
258         }
259
260         if (pairingContent != null) {
261             pairingViewCaption.setVisibility(View.VISIBLE);
262             pairingViewContent.setVisibility(View.VISIBLE);
263             pairingViewContent.setText(pairingContent);
264         }
265
266         return view;
267     }
268
269     private void createConfirmationDialog() {
270         final AlertController.AlertParams p = mAlertParams;
271         p.mTitle = getString(R.string.bluetooth_pairing_request);
272         p.mView = createView();
273         p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
274         p.mPositiveButtonListener = this;
275         p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
276         p.mNegativeButtonListener = this;
277         setupAlert();
278     }
279
280     private void createConsentDialog() {
281         final AlertController.AlertParams p = mAlertParams;
282         p.mTitle = getString(R.string.bluetooth_pairing_request);
283         p.mView = createView();
284         p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
285         p.mPositiveButtonListener = this;
286         p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
287         p.mNegativeButtonListener = this;
288         setupAlert();
289     }
290
291     private void createDisplayPasskeyOrPinDialog() {
292         final AlertController.AlertParams p = mAlertParams;
293         p.mTitle = getString(R.string.bluetooth_pairing_request);
294         p.mView = createView();
295         p.mNegativeButtonText = getString(android.R.string.cancel);
296         p.mNegativeButtonListener = this;
297         setupAlert();
298
299         // Since its only a notification, send an OK to the framework,
300         // indicating that the dialog has been displayed.
301         if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
302             mDevice.setPairingConfirmation(true);
303         } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
304             byte[] pinBytes = BluetoothDevice.convertPinToBytes(mPairingKey);
305             mDevice.setPin(pinBytes);
306         }
307     }
308
309     @Override
310     protected void onDestroy() {
311         super.onDestroy();
312         unregisterReceiver(mReceiver);
313     }
314
315     public void afterTextChanged(Editable s) {
316         if (mOkButton != null) {
317             mOkButton.setEnabled(s.length() > 0);
318         }
319     }
320
321     private void allowPhonebookAccess() {
322         CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(mDevice);
323         if (cachedDevice == null) {
324             cachedDevice = mCachedDeviceManager.addDevice(
325                     mBluetoothManager.getBluetoothAdapter(),
326                     mBluetoothManager.getProfileManager(),
327                     mDevice);
328         }
329         cachedDevice.setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
330     }
331
332     private void onPair(String value) {
333         allowPhonebookAccess();
334
335         switch (mType) {
336             case BluetoothDevice.PAIRING_VARIANT_PIN:
337                 byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
338                 if (pinBytes == null) {
339                     return;
340                 }
341                 mDevice.setPin(pinBytes);
342                 break;
343
344             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
345                 int passkey = Integer.parseInt(value);
346                 mDevice.setPasskey(passkey);
347                 break;
348
349             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
350             case BluetoothDevice.PAIRING_VARIANT_CONSENT:
351                 mDevice.setPairingConfirmation(true);
352                 break;
353
354             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
355             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
356                 // Do nothing.
357                 break;
358
359             case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
360                 mDevice.setRemoteOutOfBandData();
361                 break;
362
363             default:
364                 Log.e(TAG, "Incorrect pairing type received");
365         }
366     }
367
368     private void onCancel() {
369         mDevice.cancelPairingUserInput();
370     }
371
372     public boolean onKeyDown(int keyCode, KeyEvent event) {
373         if (keyCode == KeyEvent.KEYCODE_BACK) {
374             onCancel();
375         }
376         return super.onKeyDown(keyCode,event);
377     }
378
379     public void onClick(DialogInterface dialog, int which) {
380         switch (which) {
381             case BUTTON_POSITIVE:
382                 if (mPairingView != null) {
383                     onPair(mPairingView.getText().toString());
384                 } else {
385                     onPair(null);
386                 }
387                 break;
388
389             case BUTTON_NEGATIVE:
390             default:
391                 onCancel();
392                 break;
393         }
394     }
395
396     /* Not used */
397     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
398     }
399
400     /* Not used */
401     public void onTextChanged(CharSequence s, int start, int before, int count) {
402     }
403
404     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
405         // change input type for soft keyboard to numeric or alphanumeric
406         if (isChecked) {
407             mPairingView.setInputType(InputType.TYPE_CLASS_TEXT);
408         } else {
409             mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
410         }
411     }
412 }