OSDN Git Service

Implement Bluetooth device picker
authorYue Lixin <a5206c@motorola.com>
Thu, 9 Jul 2009 08:48:52 +0000 (16:48 +0800)
committerNick Pelly <npelly@google.com>
Thu, 20 Aug 2009 03:45:18 +0000 (20:45 -0700)
Add Bluetooth device picker in Settings
- add resource to support device picker
- show different UI accroding to start Intent
- add OPP profile manager

AndroidManifest.xml
res/values/strings.xml
res/xml/device_picker.xml [new file with mode: 0644]
src/com/android/settings/bluetooth/BluetoothDevicePreference.java
src/com/android/settings/bluetooth/BluetoothSettings.java
src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java
src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java

index 2565914..9f6ac0e 100644 (file)
                 <category android:name="com.android.settings.SHORTCUT" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.bluetooth.intent.action.DEVICE_PICKER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
         </activity>
 
         <activity android:name=".vpn.VpnSettings"
index 2382fe9..97015cb 100644 (file)
     <!-- Notification message when a Bluetooth device wants to pair with us -->
     <string name="bluetooth_notif_message">Select to pair with\u0020</string>
 
+    <!-- Strings for BluetoothDevicePicker -->
+    <string name="device_picker">Bluetooth device picker</string>
 
     <!-- Do not translate. Used for diagnostic screens, precise translation is not necessary -->
     <string name="bluetooth_scan_text">Empty button\u2026</string>
     <string name="bluetooth_profile_a2dp">Media</string>
     <!-- Bluetooth settings.  The user-visible string that is used whenever referring to the headset or handsfree profile. -->
     <string name="bluetooth_profile_headset">Phone</string>
+    <!-- Bluetooth settings.  The user-visible string that is used whenever referring to the OPP profile. -->
+    <string name="bluetooth_profile_opp">Transfer</string>
 
     <!-- Bluetooth settings.  The summary string when a device is connected to the A2DP profile. -->
     <string name="bluetooth_summary_connected_to_a2dp">Connected to media audio</string>
     <string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the headset checkbox preference when headset is connected. -->
     <string name="bluetooth_headset_profile_summary_connected">Connected to phone audio</string>
+    <!-- Bluetooth settings.  Connection options screen.  The summary for the OPP checkbox preference when OPP is connected. -->
+    <string name="bluetooth_opp_profile_summary_connected">Connected to file transfer server</string>
+    <!-- Bluetooth settings.  Connection options screen.  The summary for the OPP checkbox preference when OPP is not connected. -->
+    <string name="bluetooth_opp_profile_summary_not_connected">Not connected to file transfer server</string>
 
     <!-- Bluetooth settings.  Connection options screen.  The summary for the A2DP checkbox preference that describes how checking it will set the A2DP profile as preferred. -->
     <string name="bluetooth_a2dp_profile_summary_use_for">Use for media audio</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the headset checkbox preference that describes how checking it will set the headset profile as preferred. -->
     <string name="bluetooth_headset_profile_summary_use_for">Use for phone audio</string>
+    <!-- Bluetooth settings.  Connection options screen.  The summary for the OPP checkbox preference that describes how checking it will set the OPP profile as preferred. -->
+    <string name="bluetooth_opp_profile_summary_use_for">Use for file transfer</string>
 
     <!-- Wi-Fi settings -->
     <!-- Used in the 2nd-level settings screen to turn on Wi-Fi -->
diff --git a/res/xml/device_picker.xml b/res/xml/device_picker.xml
new file mode 100644 (file)
index 0000000..7dd5b68
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <Preference
+        android:key="bt_scan"
+        android:title="@string/bluetooth_preference_scan_title" />
+
+    <com.android.settings.ProgressCategory
+        android:key="bt_device_list"
+        android:title="@string/bluetooth_devices"
+        android:orderingFromXml="false" />
+
+</PreferenceScreen>
+
index b4daa88..237e4ab 100644 (file)
@@ -105,7 +105,9 @@ public class BluetoothDevicePreference extends Preference implements CachedBluet
     @Override
     protected void onBindView(View view) {
         // Disable this view if the bluetooth enable/disable preference view is off
-        setDependency("bt_checkbox");
+        if (null != findPreferenceInHierarchy("bt_checkbox")){
+            setDependency("bt_checkbox");
+        }
 
         super.onBindView(view);
 
index 29b6f9e..0525ec7 100644 (file)
@@ -18,12 +18,15 @@ package com.android.settings.bluetooth;
 
 import com.android.settings.ProgressCategory;
 import com.android.settings.R;
+import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
 
 import java.util.List;
 import java.util.WeakHashMap;
 
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothIntent;
+import android.bluetooth.BluetoothError;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -39,6 +42,7 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.util.Log;
 
 /**
  * BluetoothSettings is the Settings screen for Bluetooth configuration and
@@ -57,6 +61,15 @@ public class BluetoothSettings extends PreferenceActivity
     private static final String KEY_BT_NAME = "bt_name";
     private static final String KEY_BT_SCAN = "bt_scan";
 
+    private static final int SCREEN_TYPE_SETTINGS = 0;
+    private static final int SCREEN_TYPE_DEVICEPICKER = 1;
+
+    private int mScreenType;
+    private int mFilterType;
+    private boolean mNeedAuth;
+    private String mLaunchPackage;
+    private String mLaunchClass;
+
     private LocalBluetoothManager mLocalManager;
 
     private BluetoothEnabler mEnabler;
@@ -73,7 +86,19 @@ public class BluetoothSettings extends PreferenceActivity
         @Override
         public void onReceive(Context context, Intent intent) {
             // TODO: put this in callback instead of receiving
-            onBluetoothStateChanged(mLocalManager.getBluetoothState());
+
+            if (intent.getAction().equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION)) {
+                onBluetoothStateChanged(mLocalManager.getBluetoothState());
+            } else if (intent.getAction().equals(BluetoothIntent.BOND_STATE_CHANGED_ACTION)
+                    && mScreenType == SCREEN_TYPE_DEVICEPICKER) {
+                int bondState = intent
+                        .getIntExtra(BluetoothIntent.BOND_STATE, BluetoothError.ERROR);
+                if (bondState == BluetoothDevice.BOND_BONDED) {
+                    BluetoothDevice device = intent.getParcelableExtra(BluetoothIntent.DEVICE);
+                    sendDevicePickedIntent(device);
+                    finish();
+                }
+            }
         }
     };
 
@@ -84,17 +109,44 @@ public class BluetoothSettings extends PreferenceActivity
         mLocalManager = LocalBluetoothManager.getInstance(this);
         if (mLocalManager == null) finish();
 
-        addPreferencesFromResource(R.xml.bluetooth_settings);
-
-        mEnabler = new BluetoothEnabler(
-                this,
-                (CheckBoxPreference) findPreference(KEY_BT_CHECKBOX));
-
-        mDiscoverableEnabler = new BluetoothDiscoverableEnabler(
-                this,
-                (CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE));
-
-        mNamePreference = (BluetoothNamePreference) findPreference(KEY_BT_NAME);
+        // Note:
+        // If an application wish to show the BT device list, it can send an
+        // intent to Settings application with below extra data:
+        // -DEVICE_PICKER_FILTER_TYPE: the type of BT devices that want to show.
+        // -DEVICE_PICKER_LAUNCH_PACKAGE: the package which the application belongs to.
+        // -DEVICE_PICKER_LAUNCH_CLASS: the class which will receive user's selected
+        // result from the BT list.
+        // -DEVICE_PICKER_NEED_AUTH: to show if bonding procedure needed.
+
+        mFilterType = BluetoothDevice.DEVICE_PICKER_FILTER_TYPE_ALL;
+        Intent intent = getIntent();
+        String action = intent.getAction();
+
+        if (action.equals(BluetoothIntent.DEVICE_PICKER_DEVICE_PICKER)) {
+            mScreenType = SCREEN_TYPE_DEVICEPICKER;
+            mNeedAuth = intent.getBooleanExtra(BluetoothIntent.DEVICE_PICKER_NEED_AUTH, false);
+            mFilterType = intent.getIntExtra(BluetoothIntent.DEVICE_PICKER_FILTER_TYPE,
+                    BluetoothDevice.DEVICE_PICKER_FILTER_TYPE_ALL);
+            mLaunchPackage = intent.getStringExtra(BluetoothIntent.DEVICE_PICKER_LAUNCH_PACKAGE);
+            mLaunchClass = intent.getStringExtra(BluetoothIntent.DEVICE_PICKER_LAUNCH_CLASS);
+
+            setTitle(getString(R.string.device_picker));
+            addPreferencesFromResource(R.xml.device_picker);
+        } else {
+            addPreferencesFromResource(R.xml.bluetooth_settings);
+
+            mEnabler = new BluetoothEnabler(
+                    this,
+                    (CheckBoxPreference) findPreference(KEY_BT_CHECKBOX));
+
+            mDiscoverableEnabler = new BluetoothDiscoverableEnabler(
+                    this,
+                    (CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE));
+
+            mNamePreference = (BluetoothNamePreference) findPreference(KEY_BT_NAME);
+
+            mDeviceList = (ProgressCategory) findPreference(KEY_BT_DEVICE_LIST);
+        }
 
         mDeviceList = (ProgressCategory) findPreference(KEY_BT_DEVICE_LIST);
 
@@ -111,17 +163,21 @@ public class BluetoothSettings extends PreferenceActivity
         mDeviceList.removeAll();
         addDevices();
 
-        mEnabler.resume();
-        mDiscoverableEnabler.resume();
-        mNamePreference.resume();
+        if (mScreenType == SCREEN_TYPE_SETTINGS) {
+            mEnabler.resume();
+            mDiscoverableEnabler.resume();
+            mNamePreference.resume();
+        }
+
         mLocalManager.registerCallback(this);
 
         mDeviceList.setProgress(mLocalManager.getBluetoothAdapter().isDiscovering());
         mLocalManager.startScanning(false);
 
-        registerReceiver(mReceiver,
-                new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION));
-
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
+        intentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
+        registerReceiver(mReceiver, intentFilter);
         mLocalManager.setForegroundActivity(this);
     }
 
@@ -134,9 +190,11 @@ public class BluetoothSettings extends PreferenceActivity
         unregisterReceiver(mReceiver);
 
         mLocalManager.unregisterCallback(this);
-        mNamePreference.pause();
-        mDiscoverableEnabler.pause();
-        mEnabler.pause();
+        if (mScreenType == SCREEN_TYPE_SETTINGS) {
+            mNamePreference.pause();
+            mDiscoverableEnabler.pause();
+            mEnabler.pause();
+        }
     }
 
     private void addDevices() {
@@ -184,8 +242,20 @@ public class BluetoothSettings extends PreferenceActivity
         }
 
         if (preference instanceof BluetoothDevicePreference) {
-            BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference;
-            btPreference.getCachedDevice().onClicked();
+            BluetoothDevicePreference btPreference = (BluetoothDevicePreference)preference;
+            if (mScreenType == SCREEN_TYPE_SETTINGS) {
+                btPreference.getCachedDevice().onClicked();
+            } else if (mScreenType == SCREEN_TYPE_DEVICEPICKER) {
+                CachedBluetoothDevice device = btPreference.getCachedDevice();
+
+                if ((device.getBondState() == BluetoothDevice.BOND_BONDED) || (mNeedAuth == false)) {
+                    BluetoothDevice btAddress = btPreference.getCachedDevice().getDevice();
+                    sendDevicePickedIntent(btAddress);
+                    finish();
+                } else {
+                    btPreference.getCachedDevice().onClicked();
+                }
+            }
             return true;
         }
 
@@ -195,6 +265,10 @@ public class BluetoothSettings extends PreferenceActivity
     @Override
     public void onCreateContextMenu(ContextMenu menu, View v,
             ContextMenuInfo menuInfo) {
+        //For device picker, disable Context Menu
+        if (mScreenType != SCREEN_TYPE_SETTINGS) {
+            return;
+        }
         CachedBluetoothDevice cachedDevice = getDeviceFromMenuInfo(menuInfo);
         if (cachedDevice == null) return;
 
@@ -231,8 +305,19 @@ public class BluetoothSettings extends PreferenceActivity
             throw new IllegalStateException("Got onDeviceAdded, but cachedDevice already exists");
         }
 
-        createDevicePreference(cachedDevice);
-    }
+        List<Profile> profiles = cachedDevice.getProfiles();
+        if (mFilterType == BluetoothDevice.DEVICE_PICKER_FILTER_TYPE_TRANSFER){
+            if(profiles.contains(Profile.OPP)){
+                createDevicePreference(cachedDevice);
+            }
+        } else if (mFilterType == BluetoothDevice.DEVICE_PICKER_FILTER_TYPE_AUDIO) {
+            if((profiles.contains(Profile.A2DP)) || (profiles.contains(Profile.HEADSET))){
+                createDevicePreference(cachedDevice);
+            }
+        } else {
+            createDevicePreference(cachedDevice);
+        }
+     }
 
     private void createDevicePreference(CachedBluetoothDevice cachedDevice) {
         BluetoothDevicePreference preference = new BluetoothDevicePreference(this, cachedDevice);
@@ -260,4 +345,13 @@ public class BluetoothSettings extends PreferenceActivity
             mDeviceList.setProgress(false);
         }
     }
+
+    private void sendDevicePickedIntent(BluetoothDevice device) {
+        Intent intent = new Intent(BluetoothIntent.DEVICE_PICKER_DEVICE_SELECTED);
+        if (mLaunchPackage != null && mLaunchClass != null) {
+            intent.setClassName(mLaunchPackage, mLaunchClass);
+        }
+        intent.putExtra(BluetoothIntent.DEVICE, device);
+        sendBroadcast(intent);
+    }
 }
index bfc3caf..4f14390 100644 (file)
@@ -29,6 +29,8 @@ import android.preference.PreferenceGroup;
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.util.List;
+
 /**
  * ConnectSpecificProfilesActivity presents the user with all of the profiles
  * for a particular device, and allows him to choose which should be connected
@@ -43,7 +45,10 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity
     private static final String KEY_PROFILE_CONTAINER = "profile_container";
 
     public static final String EXTRA_DEVICE = "device";
-    
+
+    public static final String CLASS_NAME_OPP_PROFILE_MANAGER =
+        "com.android.settings.bluetooth.LocalBluetoothProfileManager$OppProfileManager";
+
     private LocalBluetoothManager mManager;
     private CachedBluetoothDevice mCachedDevice;
     
@@ -145,6 +150,19 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity
         pref.setPersistent(false);
         pref.setOnPreferenceChangeListener(this);
 
+        LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
+                .getProfileManager(mManager, profile);
+
+        /**
+         * Gray out checkbox while connecting and disconnecting or this is OPP
+         * profile
+         */
+        if (profileManager.getClass().getName().equals(CLASS_NAME_OPP_PROFILE_MANAGER)) {
+            pref.setEnabled(false);
+        } else {
+            pref.setEnabled(!mCachedDevice.isBusy());
+        }
+
         refreshProfilePreference(pref, profile);
         
         return pref;
@@ -221,6 +239,12 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity
         /* Gray out checkbox while connecting and disconnecting */
         mOnlineModePreference.setEnabled(!mCachedDevice.isBusy());
 
+        List<Profile> profiles = mCachedDevice.getProfiles();
+        if ((profiles.size() == 1) && (profiles.get(0).name().equals("OPP"))) {
+            Log.w(TAG, "there is only one profile: Opp, disable the connect button.");
+            mOnlineModePreference.setEnabled(false);
+        }
+
         /**
          * If the device is online, show status. Otherwise, show a summary that
          * describes what the checkbox does.
@@ -249,9 +273,16 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity
         
         int connectionStatus = profileManager.getConnectionStatus(device);
 
-        /* Gray out checkbox while connecting and disconnecting */
-        profilePref.setEnabled(!mCachedDevice.isBusy());
-
+        /*
+         * Gray out checkbox while connecting and disconnecting or this is OPP
+         * profile
+         */
+        if (profileManager.getClass().getName().equals(CLASS_NAME_OPP_PROFILE_MANAGER)) {
+            Log.w(TAG, "this is Opp profile");
+            profilePref.setEnabled(false);
+        } else {
+            profilePref.setEnabled(!mCachedDevice.isBusy());
+        }
         profilePref.setSummary(getProfileSummary(profileManager, profile, device,
                 connectionStatus, mOnlineMode));
         
@@ -291,6 +322,8 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity
                 return R.string.bluetooth_a2dp_profile_summary_use_for;
             case HEADSET:
                 return R.string.bluetooth_headset_profile_summary_use_for;
+            case OPP:
+                return R.string.bluetooth_opp_profile_summary_use_for;
             default:
                 return 0;
         }
index 49c2eea..2a52a8b 100644 (file)
@@ -18,6 +18,7 @@ package com.android.settings.bluetooth;
 
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothError;
 import android.bluetooth.BluetoothHeadset;
 import android.os.Handler;
@@ -59,6 +60,10 @@ public abstract class LocalBluetoothProfileManager {
                 case HEADSET:
                     profileManager = new HeadsetProfileManager(localManager);
                     break;
+
+                case OPP:
+                    profileManager = new OppProfileManager(localManager);
+                    break;
                 }
                 
                 sProfileMap.put(profile, profileManager);    
@@ -80,13 +85,17 @@ public abstract class LocalBluetoothProfileManager {
     public static void fill(int btClass, List<Profile> profiles) {
         profiles.clear();
 
-        if (BluetoothHeadset.doesClassMatch(btClass)) {
+        if (BluetoothClass.doesClassMatch(btClass, BluetoothClass.PROFILE_HEADSET)) {
             profiles.add(Profile.HEADSET);
         }
-        
-        if (BluetoothA2dp.doesClassMatchSink(btClass)) {
+
+        if (BluetoothClass.doesClassMatch(btClass, BluetoothClass.PROFILE_A2DP)) {
             profiles.add(Profile.A2DP);
         }
+
+        if (BluetoothClass.doesClassMatch(btClass, BluetoothClass.PROFILE_OPP)) {
+            profiles.add(Profile.OPP);
+        }
     }
 
     protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) {
@@ -114,7 +123,8 @@ public abstract class LocalBluetoothProfileManager {
     // TODO: int instead of enum
     public enum Profile {
         HEADSET(R.string.bluetooth_profile_headset),
-        A2DP(R.string.bluetooth_profile_a2dp);
+        A2DP(R.string.bluetooth_profile_a2dp),
+        OPP(R.string.bluetooth_profile_opp);
         
         public final int localizedString;
         
@@ -291,4 +301,63 @@ public abstract class LocalBluetoothProfileManager {
             }
         }
     }
+
+    /**
+     * OppProfileManager
+     */
+    private static class OppProfileManager extends LocalBluetoothProfileManager {
+
+        public OppProfileManager(LocalBluetoothManager localManager) {
+            super(localManager);
+        }
+
+        @Override
+        public int connect(BluetoothDevice device) {
+            return -1;
+        }
+
+        @Override
+        public int disconnect(BluetoothDevice device) {
+            return -1;
+        }
+
+        @Override
+        public int getConnectionStatus(BluetoothDevice device) {
+            return -1;
+        }
+
+        @Override
+        public int getSummary(BluetoothDevice device) {
+            int connectionStatus = getConnectionStatus(device);
+
+            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
+                return R.string.bluetooth_opp_profile_summary_connected;
+            } else {
+                return R.string.bluetooth_opp_profile_summary_not_connected;
+            }
+        }
+
+        @Override
+        public boolean isPreferred(BluetoothDevice device) {
+            return false;
+        }
+
+        @Override
+        public void setPreferred(BluetoothDevice device, boolean preferred) {
+        }
+
+        @Override
+        public int convertState(int oppState) {
+            switch (oppState) {
+            case 0:
+                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
+            case 1:
+                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
+            case 2:
+                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
+            default:
+                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
+            }
+        }
+    }
 }