Home | History | Annotate | Download | only in bluetooth
      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.BluetoothDevice;
     23 import android.bluetooth.BluetoothProfile;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.os.Bundle;
     27 import android.preference.CheckBoxPreference;
     28 import android.preference.EditTextPreference;
     29 import android.text.Html;
     30 import android.text.TextUtils;
     31 import android.util.Log;
     32 import android.view.LayoutInflater;
     33 import android.view.View;
     34 import android.view.View.OnClickListener;
     35 import android.view.ViewGroup;
     36 import android.widget.CheckBox;
     37 import android.widget.EditText;
     38 import android.widget.TextView;
     39 
     40 import com.android.settings.R;
     41 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
     42 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
     43 import com.android.settingslib.bluetooth.LocalBluetoothManager;
     44 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
     45 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
     46 import com.android.settingslib.bluetooth.MapProfile;
     47 import com.android.settingslib.bluetooth.PanProfile;
     48 import com.android.settingslib.bluetooth.PbapServerProfile;
     49 
     50 import java.util.HashMap;
     51 
     52 public final class DeviceProfilesSettings extends DialogFragment implements
     53         CachedBluetoothDevice.Callback, DialogInterface.OnClickListener, OnClickListener {
     54     private static final String TAG = "DeviceProfilesSettings";
     55 
     56     public static final String ARG_DEVICE_ADDRESS = "device_address";
     57 
     58     private static final String KEY_PROFILE_CONTAINER = "profile_container";
     59     private static final String KEY_UNPAIR = "unpair";
     60     private static final String KEY_PBAP_SERVER = "PBAP Server";
     61 
     62     private CachedBluetoothDevice mCachedDevice;
     63     private LocalBluetoothManager mManager;
     64     private LocalBluetoothProfileManager mProfileManager;
     65 
     66     private ViewGroup mProfileContainer;
     67     private TextView mProfileLabel;
     68     private EditTextPreference mDeviceNamePref;
     69 
     70     private final HashMap<LocalBluetoothProfile, CheckBoxPreference> mAutoConnectPrefs
     71             = new HashMap<LocalBluetoothProfile, CheckBoxPreference>();
     72 
     73     private AlertDialog mDisconnectDialog;
     74     private boolean mProfileGroupIsRemoved;
     75 
     76     private View mRootView;
     77 
     78     @Override
     79     public void onCreate(Bundle savedInstanceState) {
     80         super.onCreate(savedInstanceState);
     81 
     82         mManager = Utils.getLocalBtManager(getActivity());
     83         CachedBluetoothDeviceManager deviceManager = mManager.getCachedDeviceManager();
     84 
     85         String address = getArguments().getString(ARG_DEVICE_ADDRESS);
     86         BluetoothDevice remoteDevice = mManager.getBluetoothAdapter().getRemoteDevice(address);
     87 
     88         mCachedDevice = deviceManager.findDevice(remoteDevice);
     89         if (mCachedDevice == null) {
     90             mCachedDevice = deviceManager.addDevice(mManager.getBluetoothAdapter(),
     91                     mManager.getProfileManager(), remoteDevice);
     92         }
     93         mProfileManager = mManager.getProfileManager();
     94     }
     95 
     96     @Override
     97     public Dialog onCreateDialog(Bundle savedInstanceState) {
     98         mRootView = LayoutInflater.from(getContext()).inflate(R.layout.device_profiles_settings,
     99                 null);
    100         mProfileContainer = (ViewGroup) mRootView.findViewById(R.id.profiles_section);
    101         mProfileLabel = (TextView) mRootView.findViewById(R.id.profiles_label);
    102         final EditText deviceName = (EditText) mRootView.findViewById(R.id.name);
    103         deviceName.setText(mCachedDevice.getName(), TextView.BufferType.EDITABLE);
    104         return new AlertDialog.Builder(getContext())
    105                 .setView(mRootView)
    106                 .setNegativeButton(R.string.forget, this)
    107                 .setPositiveButton(R.string.okay, this)
    108                 .setTitle(R.string.bluetooth_preference_paired_devices)
    109                 .create();
    110     }
    111 
    112     @Override
    113     public void onClick(DialogInterface dialog, int which) {
    114         switch (which) {
    115             case DialogInterface.BUTTON_POSITIVE:
    116                 EditText deviceName = (EditText) mRootView.findViewById(R.id.name);
    117                 mCachedDevice.setName(deviceName.getText().toString());
    118                 break;
    119             case DialogInterface.BUTTON_NEGATIVE:
    120                 mCachedDevice.unpair();
    121                 com.android.settings.bluetooth.Utils.updateSearchIndex(getContext(),
    122                         BluetoothSettings.class.getName(), mCachedDevice.getName(),
    123                         getString(R.string.bluetooth_settings),
    124                         R.drawable.ic_settings_bluetooth, false);
    125                 break;
    126         }
    127     }
    128 
    129     @Override
    130     public void onDestroy() {
    131         super.onDestroy();
    132         if (mDisconnectDialog != null) {
    133             mDisconnectDialog.dismiss();
    134             mDisconnectDialog = null;
    135         }
    136         if (mCachedDevice != null) {
    137             mCachedDevice.unregisterCallback(this);
    138         }
    139     }
    140 
    141     @Override
    142     public void onSaveInstanceState(Bundle outState) {
    143         super.onSaveInstanceState(outState);
    144     }
    145 
    146     @Override
    147     public void onResume() {
    148         super.onResume();
    149 
    150         mManager.setForegroundActivity(getActivity());
    151         if (mCachedDevice != null) {
    152             mCachedDevice.registerCallback(this);
    153             if (mCachedDevice.getBondState() == BluetoothDevice.BOND_NONE) {
    154                 dismiss();
    155                 return;
    156             }
    157             addPreferencesForProfiles();
    158             refresh();
    159         }
    160     }
    161 
    162     @Override
    163     public void onPause() {
    164         super.onPause();
    165 
    166         if (mCachedDevice != null) {
    167             mCachedDevice.unregisterCallback(this);
    168         }
    169 
    170         mManager.setForegroundActivity(null);
    171     }
    172 
    173     private void addPreferencesForProfiles() {
    174         mProfileContainer.removeAllViews();
    175         for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
    176             CheckBox pref = createProfilePreference(profile);
    177             mProfileContainer.addView(pref);
    178         }
    179 
    180         final int pbapPermission = mCachedDevice.getPhonebookPermissionChoice();
    181         // Only provide PBAP cabability if the client device has requested PBAP.
    182         if (pbapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
    183             final PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
    184             CheckBox pbapPref = createProfilePreference(psp);
    185             mProfileContainer.addView(pbapPref);
    186         }
    187 
    188         final MapProfile mapProfile = mManager.getProfileManager().getMapProfile();
    189         final int mapPermission = mCachedDevice.getMessagePermissionChoice();
    190         if (mapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
    191             CheckBox mapPreference = createProfilePreference(mapProfile);
    192             mProfileContainer.addView(mapPreference);
    193         }
    194 
    195         showOrHideProfileGroup();
    196     }
    197 
    198     private void showOrHideProfileGroup() {
    199         int numProfiles = mProfileContainer.getChildCount();
    200         if (!mProfileGroupIsRemoved && numProfiles == 0) {
    201             mProfileContainer.setVisibility(View.GONE);
    202             mProfileLabel.setVisibility(View.GONE);
    203             mProfileGroupIsRemoved = true;
    204         } else if (mProfileGroupIsRemoved && numProfiles != 0) {
    205             mProfileContainer.setVisibility(View.VISIBLE);
    206             mProfileLabel.setVisibility(View.VISIBLE);
    207             mProfileGroupIsRemoved = false;
    208         }
    209     }
    210 
    211     /**
    212      * Creates a checkbox preference for the particular profile. The key will be
    213      * the profile's name.
    214      *
    215      * @param profile The profile for which the preference controls.
    216      * @return A preference that allows the user to choose whether this profile
    217      *         will be connected to.
    218      */
    219     private CheckBox createProfilePreference(LocalBluetoothProfile profile) {
    220         CheckBox pref = new CheckBox(getActivity());
    221         pref.setTag(profile.toString());
    222         pref.setText(profile.getNameResource(mCachedDevice.getDevice()));
    223         pref.setOnClickListener(this);
    224 
    225         refreshProfilePreference(pref, profile);
    226 
    227         return pref;
    228     }
    229 
    230     @Override
    231     public void onClick(View v) {
    232         if (v instanceof CheckBox) {
    233             LocalBluetoothProfile prof = getProfileOf(v);
    234             onProfileClicked(prof, (CheckBox) v);
    235         }
    236     }
    237 
    238     private void onProfileClicked(LocalBluetoothProfile profile, CheckBox profilePref) {
    239         BluetoothDevice device = mCachedDevice.getDevice();
    240 
    241         if (KEY_PBAP_SERVER.equals(profilePref.getTag())) {
    242             final int newPermission = mCachedDevice.getPhonebookPermissionChoice()
    243                 == CachedBluetoothDevice.ACCESS_ALLOWED ? CachedBluetoothDevice.ACCESS_REJECTED
    244                 : CachedBluetoothDevice.ACCESS_ALLOWED;
    245             mCachedDevice.setPhonebookPermissionChoice(newPermission);
    246             profilePref.setChecked(newPermission == CachedBluetoothDevice.ACCESS_ALLOWED);
    247             return;
    248         }
    249 
    250         if (!profilePref.isChecked()) {
    251             // Recheck it, until the dialog is done.
    252             profilePref.setChecked(true);
    253             askDisconnect(mManager.getForegroundActivity(), profile);
    254         } else {
    255             if (profile instanceof MapProfile) {
    256                 mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_ALLOWED);
    257             }
    258             if (profile.isPreferred(device)) {
    259                 // profile is preferred but not connected: disable auto-connect
    260                 if (profile instanceof PanProfile) {
    261                     mCachedDevice.connectProfile(profile);
    262                 } else {
    263                     profile.setPreferred(device, false);
    264                 }
    265             } else {
    266                 profile.setPreferred(device, true);
    267                 mCachedDevice.connectProfile(profile);
    268             }
    269             refreshProfilePreference(profilePref, profile);
    270         }
    271     }
    272 
    273     private void askDisconnect(Context context,
    274             final LocalBluetoothProfile profile) {
    275         // local reference for callback
    276         final CachedBluetoothDevice device = mCachedDevice;
    277         String name = device.getName();
    278         if (TextUtils.isEmpty(name)) {
    279             name = context.getString(R.string.bluetooth_device);
    280         }
    281 
    282         String profileName = context.getString(profile.getNameResource(device.getDevice()));
    283 
    284         String title = context.getString(R.string.bluetooth_disable_profile_title);
    285         String message = context.getString(R.string.bluetooth_disable_profile_message,
    286                 profileName, name);
    287 
    288         DialogInterface.OnClickListener disconnectListener =
    289                 new DialogInterface.OnClickListener() {
    290             public void onClick(DialogInterface dialog, int which) {
    291                 device.disconnect(profile);
    292                 profile.setPreferred(device.getDevice(), false);
    293                 if (profile instanceof MapProfile) {
    294                     device.setMessagePermissionChoice(BluetoothDevice.ACCESS_REJECTED);
    295                 }
    296                 refreshProfilePreference(findProfile(profile.toString()), profile);
    297             }
    298         };
    299 
    300         mDisconnectDialog = Utils.showDisconnectDialog(context,
    301                 mDisconnectDialog, disconnectListener, title, Html.fromHtml(message));
    302     }
    303 
    304     @Override
    305     public void onDeviceAttributesChanged() {
    306         refresh();
    307     }
    308 
    309     private void refresh() {
    310         final EditText deviceNameField = (EditText) mRootView.findViewById(R.id.name);
    311         if (deviceNameField != null) {
    312             deviceNameField.setText(mCachedDevice.getName());
    313         }
    314 
    315         refreshProfiles();
    316     }
    317 
    318     private void refreshProfiles() {
    319         for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
    320             CheckBox profilePref = findProfile(profile.toString());
    321             if (profilePref == null) {
    322                 profilePref = createProfilePreference(profile);
    323                 mProfileContainer.addView(profilePref);
    324             } else {
    325                 refreshProfilePreference(profilePref, profile);
    326             }
    327         }
    328         for (LocalBluetoothProfile profile : mCachedDevice.getRemovedProfiles()) {
    329             CheckBox profilePref = findProfile(profile.toString());
    330             if (profilePref != null) {
    331                 Log.d(TAG, "Removing " + profile.toString() + " from profile list");
    332                 mProfileContainer.removeView(profilePref);
    333             }
    334         }
    335 
    336         showOrHideProfileGroup();
    337     }
    338 
    339     private CheckBox findProfile(String profile) {
    340         return (CheckBox) mProfileContainer.findViewWithTag(profile);
    341     }
    342 
    343     private void refreshProfilePreference(CheckBox profilePref,
    344             LocalBluetoothProfile profile) {
    345         BluetoothDevice device = mCachedDevice.getDevice();
    346 
    347         // Gray out checkbox while connecting and disconnecting.
    348         profilePref.setEnabled(!mCachedDevice.isBusy());
    349 
    350         if (profile instanceof MapProfile) {
    351             profilePref.setChecked(mCachedDevice.getMessagePermissionChoice()
    352                     == CachedBluetoothDevice.ACCESS_ALLOWED);
    353 
    354         } else if (profile instanceof PbapServerProfile) {
    355             profilePref.setChecked(mCachedDevice.getPhonebookPermissionChoice()
    356                     == CachedBluetoothDevice.ACCESS_ALLOWED);
    357 
    358         } else if (profile instanceof PanProfile) {
    359             profilePref.setChecked(profile.getConnectionStatus(device) ==
    360                     BluetoothProfile.STATE_CONNECTED);
    361 
    362         } else {
    363             profilePref.setChecked(profile.isPreferred(device));
    364         }
    365     }
    366 
    367     private LocalBluetoothProfile getProfileOf(View v) {
    368         if (!(v instanceof CheckBox)) {
    369             return null;
    370         }
    371         String key = (String) v.getTag();
    372         if (TextUtils.isEmpty(key)) return null;
    373 
    374         try {
    375             return mProfileManager.getProfileByName(key);
    376         } catch (IllegalArgumentException ignored) {
    377             return null;
    378         }
    379     }
    380 }
    381