BluetoothPbapActivity.java revision eb7b90f5b93db1230a5b64caa3d8d05a642e33a6
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
33package com.android.bluetooth.pbap;
34
35import com.android.bluetooth.R;
36
37import android.content.BroadcastReceiver;
38import android.content.Context;
39import android.content.DialogInterface;
40import android.content.Intent;
41import android.content.IntentFilter;
42import android.os.Bundle;
43import android.os.Handler;
44import android.os.Message;
45import android.preference.Preference;
46import android.text.InputFilter;
47import android.text.TextWatcher;
48import android.text.InputFilter.LengthFilter;
49import android.util.Log;
50import android.view.View;
51import android.widget.CheckBox;
52import android.widget.EditText;
53import android.widget.TextView;
54import android.widget.Button;
55
56import com.android.internal.app.AlertActivity;
57import com.android.internal.app.AlertController;
58
59/**
60 * PbapActivity shows two dialogues: One for accepting incoming pbap request and
61 * the other prompts the user to enter a session key for authentication with a
62 * remote Bluetooth device.
63 */
64public class BluetoothPbapActivity extends AlertActivity implements
65        DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, TextWatcher {
66    private static final String TAG = "BluetoothPbapActivity";
67
68    private static final boolean V = BluetoothPbapService.VERBOSE;
69
70    private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16;
71
72    private static final int DIALOG_YES_NO_AUTH = 1;
73
74    private static final String KEY_USER_TIMEOUT = "user_timeout";
75
76    private View mView;
77
78    private EditText mKeyView;
79
80    private TextView messageView;
81
82    private String mSessionKey = "";
83
84    private int mCurrentDialog;
85
86    private Button mOkButton;
87
88    private CheckBox mAlwaysAllowed;
89
90    private boolean mTimeout = false;
91
92    private boolean mAlwaysAllowedValue = true;
93
94    private static final int DISMISS_TIMEOUT_DIALOG = 0;
95
96    private static final int DISMISS_TIMEOUT_DIALOG_VALUE = 2000;
97
98    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
99        @Override
100        public void onReceive(Context context, Intent intent) {
101            if (!BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) {
102                return;
103            }
104            onTimeout();
105        }
106    };
107
108    @Override
109    protected void onCreate(Bundle savedInstanceState) {
110        super.onCreate(savedInstanceState);
111        Intent i = getIntent();
112        String action = i.getAction();
113        if (action.equals(BluetoothPbapService.AUTH_CHALL_ACTION)) {
114            showPbapDialog(DIALOG_YES_NO_AUTH);
115            mCurrentDialog = DIALOG_YES_NO_AUTH;
116        } else {
117            Log.e(TAG, "Error: this activity may be started only with intent "
118                    + "PBAP_ACCESS_REQUEST or PBAP_AUTH_CHALL ");
119            finish();
120        }
121        registerReceiver(mReceiver, new IntentFilter(
122                BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION));
123    }
124
125    private void showPbapDialog(int id) {
126        final AlertController.AlertParams p = mAlertParams;
127        switch (id) {
128            case DIALOG_YES_NO_AUTH:
129                p.mTitle = getString(R.string.pbap_session_key_dialog_header);
130                p.mView = createView(DIALOG_YES_NO_AUTH);
131                p.mPositiveButtonText = getString(android.R.string.ok);
132                p.mPositiveButtonListener = this;
133                p.mNegativeButtonText = getString(android.R.string.cancel);
134                p.mNegativeButtonListener = this;
135                setupAlert();
136                mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
137                mOkButton.setEnabled(false);
138                break;
139            default:
140                break;
141        }
142    }
143
144    private String createDisplayText(final int id) {
145        String mRemoteName = BluetoothPbapService.getRemoteDeviceName();
146        switch (id) {
147            case DIALOG_YES_NO_AUTH:
148                String mMessage2 = getString(R.string.pbap_session_key_dialog_title, mRemoteName);
149                return mMessage2;
150            default:
151                return null;
152        }
153    }
154
155    private View createView(final int id) {
156        switch (id) {
157            case DIALOG_YES_NO_AUTH:
158                mView = getLayoutInflater().inflate(R.layout.auth, null);
159                messageView = (TextView)mView.findViewById(R.id.message);
160                messageView.setText(createDisplayText(id));
161                mKeyView = (EditText)mView.findViewById(R.id.text);
162                mKeyView.addTextChangedListener(this);
163                mKeyView.setFilters(new InputFilter[] {
164                    new LengthFilter(BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH)
165                });
166                return mView;
167            default:
168                return null;
169        }
170    }
171
172    private void onPositive() {
173        if (!mTimeout) {
174            if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
175                sendIntentToReceiver(BluetoothPbapService.AUTH_RESPONSE_ACTION,
176                        BluetoothPbapService.EXTRA_SESSION_KEY, mSessionKey);
177                mKeyView.removeTextChangedListener(this);
178            }
179        }
180        mTimeout = false;
181        finish();
182    }
183
184    private void onNegative() {
185        if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
186            sendIntentToReceiver(BluetoothPbapService.AUTH_CANCELLED_ACTION, null, null);
187            mKeyView.removeTextChangedListener(this);
188        }
189        finish();
190    }
191
192    private void sendIntentToReceiver(final String intentName, final String extraName,
193            final String extraValue) {
194        Intent intent = new Intent(intentName);
195        intent.setClassName(BluetoothPbapService.THIS_PACKAGE_NAME, BluetoothPbapReceiver.class
196                .getName());
197        if (extraName != null) {
198            intent.putExtra(extraName, extraValue);
199        }
200        sendBroadcast(intent);
201    }
202
203    private void sendIntentToReceiver(final String intentName, final String extraName,
204            final boolean extraValue) {
205        Intent intent = new Intent(intentName);
206        intent.setClassName(BluetoothPbapService.THIS_PACKAGE_NAME, BluetoothPbapReceiver.class
207                .getName());
208        if (extraName != null) {
209            intent.putExtra(extraName, extraValue);
210        }
211        sendBroadcast(intent);
212    }
213
214    public void onClick(DialogInterface dialog, int which) {
215        switch (which) {
216            case DialogInterface.BUTTON_POSITIVE:
217                if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
218                    mSessionKey = mKeyView.getText().toString();
219                }
220                onPositive();
221                break;
222
223            case DialogInterface.BUTTON_NEGATIVE:
224                onNegative();
225                break;
226            default:
227                break;
228        }
229    }
230
231    private void onTimeout() {
232        mTimeout = true;
233        if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
234            messageView.setText(getString(R.string.pbap_authentication_timeout_message,
235                    BluetoothPbapService.getRemoteDeviceName()));
236            mKeyView.setVisibility(View.GONE);
237            mKeyView.clearFocus();
238            mKeyView.removeTextChangedListener(this);
239            mOkButton.setEnabled(true);
240            mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
241        }
242
243        mTimeoutHandler.sendMessageDelayed(mTimeoutHandler.obtainMessage(DISMISS_TIMEOUT_DIALOG),
244                DISMISS_TIMEOUT_DIALOG_VALUE);
245    }
246
247    @Override
248    protected void onRestoreInstanceState(Bundle savedInstanceState) {
249        super.onRestoreInstanceState(savedInstanceState);
250        mTimeout = savedInstanceState.getBoolean(KEY_USER_TIMEOUT);
251        if (V) Log.v(TAG, "onRestoreInstanceState() mTimeout: " + mTimeout);
252        if (mTimeout) {
253            onTimeout();
254        }
255    }
256
257    @Override
258    protected void onSaveInstanceState(Bundle outState) {
259        super.onSaveInstanceState(outState);
260        outState.putBoolean(KEY_USER_TIMEOUT, mTimeout);
261    }
262
263    @Override
264    protected void onDestroy() {
265        super.onDestroy();
266        unregisterReceiver(mReceiver);
267    }
268
269    public boolean onPreferenceChange(Preference preference, Object newValue) {
270        return true;
271    }
272
273    public void beforeTextChanged(CharSequence s, int start, int before, int after) {
274    }
275
276    public void onTextChanged(CharSequence s, int start, int before, int count) {
277    }
278
279    public void afterTextChanged(android.text.Editable s) {
280        if (s.length() > 0) {
281            mOkButton.setEnabled(true);
282        }
283    }
284
285    private final Handler mTimeoutHandler = new Handler() {
286        @Override
287        public void handleMessage(Message msg) {
288            switch (msg.what) {
289                case DISMISS_TIMEOUT_DIALOG:
290                    if (V) Log.v(TAG, "Received DISMISS_TIMEOUT_DIALOG msg.");
291                    finish();
292                    break;
293                default:
294                    break;
295            }
296        }
297    };
298}
299