1 /* 2 * Copyright (c) 2008-2009, Motorola, Inc. 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * - Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * - Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * - Neither the name of the Motorola, Inc. nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.android.bluetooth.pbap; 34 35 import android.bluetooth.AlertActivity; 36 import android.bluetooth.BluetoothDevice; 37 import android.content.BroadcastReceiver; 38 import android.content.Context; 39 import android.content.DialogInterface; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.os.Bundle; 43 import android.os.Handler; 44 import android.os.Message; 45 import android.preference.Preference; 46 import android.text.InputFilter; 47 import android.text.InputFilter.LengthFilter; 48 import android.text.TextWatcher; 49 import android.util.Log; 50 import android.view.View; 51 import android.widget.EditText; 52 import android.widget.TextView; 53 54 import com.android.bluetooth.R; 55 56 /** 57 * PbapActivity shows two dialogues: One for accepting incoming pbap request and 58 * the other prompts the user to enter a session key for authentication with a 59 * remote Bluetooth device. 60 */ 61 public class BluetoothPbapActivity extends AlertActivity 62 implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, 63 TextWatcher { 64 private static final String TAG = "BluetoothPbapActivity"; 65 66 private static final boolean V = BluetoothPbapService.VERBOSE; 67 68 private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16; 69 70 private static final int DIALOG_YES_NO_AUTH = 1; 71 72 private static final String KEY_USER_TIMEOUT = "user_timeout"; 73 74 private View mView; 75 76 private EditText mKeyView; 77 78 private TextView mMessageView; 79 80 private String mSessionKey = ""; 81 82 private int mCurrentDialog; 83 84 private boolean mTimeout = false; 85 86 private static final int DISMISS_TIMEOUT_DIALOG = 0; 87 88 private static final int DISMISS_TIMEOUT_DIALOG_VALUE = 2000; 89 90 private BluetoothDevice mDevice; 91 92 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 93 @Override 94 public void onReceive(Context context, Intent intent) { 95 if (!BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) { 96 return; 97 } 98 onTimeout(); 99 } 100 }; 101 102 @Override onCreate(Bundle savedInstanceState)103 protected void onCreate(Bundle savedInstanceState) { 104 super.onCreate(savedInstanceState); 105 Intent i = getIntent(); 106 String action = i.getAction(); 107 mDevice = i.getParcelableExtra(BluetoothPbapService.EXTRA_DEVICE); 108 if (action != null && action.equals(BluetoothPbapService.AUTH_CHALL_ACTION)) { 109 showPbapDialog(DIALOG_YES_NO_AUTH); 110 mCurrentDialog = DIALOG_YES_NO_AUTH; 111 } else { 112 Log.e(TAG, "Error: this activity may be started only with intent " 113 + "PBAP_ACCESS_REQUEST or PBAP_AUTH_CHALL "); 114 finish(); 115 } 116 registerReceiver(mReceiver, 117 new IntentFilter(BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION)); 118 } 119 showPbapDialog(int id)120 private void showPbapDialog(int id) { 121 switch (id) { 122 case DIALOG_YES_NO_AUTH: 123 mAlertBuilder.setTitle(getString(R.string.pbap_session_key_dialog_header)); 124 mAlertBuilder.setView(createView(DIALOG_YES_NO_AUTH)); 125 mAlertBuilder.setPositiveButton(android.R.string.ok, this); 126 mAlertBuilder.setNegativeButton(android.R.string.cancel, this); 127 setupAlert(); 128 changeButtonEnabled(DialogInterface.BUTTON_POSITIVE, false); 129 break; 130 default: 131 break; 132 } 133 } 134 createDisplayText(final int id)135 private String createDisplayText(final int id) { 136 switch (id) { 137 case DIALOG_YES_NO_AUTH: 138 String mMessage2 = getString(R.string.pbap_session_key_dialog_title, mDevice); 139 return mMessage2; 140 default: 141 return null; 142 } 143 } 144 createView(final int id)145 private View createView(final int id) { 146 switch (id) { 147 case DIALOG_YES_NO_AUTH: 148 mView = getLayoutInflater().inflate(R.layout.auth, null); 149 mMessageView = (TextView) mView.findViewById(R.id.message); 150 mMessageView.setText(createDisplayText(id)); 151 mKeyView = (EditText) mView.findViewById(R.id.text); 152 mKeyView.addTextChangedListener(this); 153 mKeyView.setFilters(new InputFilter[]{ 154 new LengthFilter(BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH) 155 }); 156 return mView; 157 default: 158 return null; 159 } 160 } 161 onPositive()162 private void onPositive() { 163 if (!mTimeout) { 164 if (mCurrentDialog == DIALOG_YES_NO_AUTH) { 165 sendIntentToReceiver(BluetoothPbapService.AUTH_RESPONSE_ACTION, 166 BluetoothPbapService.EXTRA_SESSION_KEY, mSessionKey); 167 mKeyView.removeTextChangedListener(this); 168 } 169 } 170 mTimeout = false; 171 finish(); 172 } 173 onNegative()174 private void onNegative() { 175 if (mCurrentDialog == DIALOG_YES_NO_AUTH) { 176 sendIntentToReceiver(BluetoothPbapService.AUTH_CANCELLED_ACTION, null, null); 177 mKeyView.removeTextChangedListener(this); 178 } 179 finish(); 180 } 181 sendIntentToReceiver(final String intentName, final String extraName, final String extraValue)182 private void sendIntentToReceiver(final String intentName, final String extraName, 183 final String extraValue) { 184 Intent intent = new Intent(intentName); 185 intent.setPackage(BluetoothPbapService.THIS_PACKAGE_NAME); 186 intent.putExtra(BluetoothPbapService.EXTRA_DEVICE, mDevice); 187 if (extraName != null) { 188 intent.putExtra(extraName, extraValue); 189 } 190 sendBroadcast(intent); 191 } 192 193 @Override onClick(DialogInterface dialog, int which)194 public void onClick(DialogInterface dialog, int which) { 195 switch (which) { 196 case DialogInterface.BUTTON_POSITIVE: 197 if (mCurrentDialog == DIALOG_YES_NO_AUTH) { 198 mSessionKey = mKeyView.getText().toString(); 199 } 200 onPositive(); 201 break; 202 203 case DialogInterface.BUTTON_NEGATIVE: 204 onNegative(); 205 break; 206 default: 207 break; 208 } 209 } 210 onTimeout()211 private void onTimeout() { 212 mTimeout = true; 213 if (mCurrentDialog == DIALOG_YES_NO_AUTH) { 214 mMessageView.setText(getString(R.string.pbap_authentication_timeout_message, mDevice)); 215 mKeyView.setVisibility(View.GONE); 216 mKeyView.clearFocus(); 217 mKeyView.removeTextChangedListener(this); 218 changeButtonEnabled(DialogInterface.BUTTON_POSITIVE, true); 219 changeButtonVisibility(DialogInterface.BUTTON_NEGATIVE, View.GONE); 220 } 221 222 mTimeoutHandler.sendMessageDelayed(mTimeoutHandler.obtainMessage(DISMISS_TIMEOUT_DIALOG), 223 DISMISS_TIMEOUT_DIALOG_VALUE); 224 } 225 226 @Override onRestoreInstanceState(Bundle savedInstanceState)227 protected void onRestoreInstanceState(Bundle savedInstanceState) { 228 super.onRestoreInstanceState(savedInstanceState); 229 mTimeout = savedInstanceState.getBoolean(KEY_USER_TIMEOUT); 230 if (V) { 231 Log.v(TAG, "onRestoreInstanceState() mTimeout: " + mTimeout); 232 } 233 if (mTimeout) { 234 onTimeout(); 235 } 236 } 237 238 @Override onSaveInstanceState(Bundle outState)239 protected void onSaveInstanceState(Bundle outState) { 240 super.onSaveInstanceState(outState); 241 outState.putBoolean(KEY_USER_TIMEOUT, mTimeout); 242 } 243 244 @Override onDestroy()245 protected void onDestroy() { 246 super.onDestroy(); 247 unregisterReceiver(mReceiver); 248 } 249 250 @Override onPreferenceChange(Preference preference, Object newValue)251 public boolean onPreferenceChange(Preference preference, Object newValue) { 252 return true; 253 } 254 255 @Override beforeTextChanged(CharSequence s, int start, int before, int after)256 public void beforeTextChanged(CharSequence s, int start, int before, int after) { 257 } 258 259 @Override onTextChanged(CharSequence s, int start, int before, int count)260 public void onTextChanged(CharSequence s, int start, int before, int count) { 261 } 262 263 @Override afterTextChanged(android.text.Editable s)264 public void afterTextChanged(android.text.Editable s) { 265 if (s.length() > 0) { 266 changeButtonEnabled(DialogInterface.BUTTON_POSITIVE, true); 267 } 268 } 269 270 private final Handler mTimeoutHandler = new Handler() { 271 @Override 272 public void handleMessage(Message msg) { 273 switch (msg.what) { 274 case DISMISS_TIMEOUT_DIALOG: 275 if (V) { 276 Log.v(TAG, "Received DISMISS_TIMEOUT_DIALOG msg."); 277 } 278 finish(); 279 break; 280 default: 281 break; 282 } 283 } 284 }; 285 } 286