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
17package com.android.settings.bluetooth;
18
19import android.bluetooth.BluetoothDevice;
20import android.content.Intent;
21import android.os.Bundle;
22import android.preference.CheckBoxPreference;
23import android.preference.Preference;
24import android.preference.PreferenceActivity;
25import android.preference.PreferenceGroup;
26import android.text.TextUtils;
27import android.util.Log;
28
29import com.android.settings.R;
30import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
31
32/**
33 * ConnectSpecificProfilesActivity presents the user with all of the profiles
34 * for a particular device, and allows him to choose which should be connected
35 * (or disconnected).
36 */
37public class ConnectSpecificProfilesActivity extends PreferenceActivity
38        implements CachedBluetoothDevice.Callback, Preference.OnPreferenceChangeListener {
39    private static final String TAG = "ConnectSpecificProfilesActivity";
40
41    private static final String KEY_ONLINE_MODE = "online_mode";
42    private static final String KEY_TITLE = "title";
43    private static final String KEY_PROFILE_CONTAINER = "profile_container";
44
45    public static final String EXTRA_DEVICE = "device";
46
47    private LocalBluetoothManager mManager;
48    private CachedBluetoothDevice mCachedDevice;
49
50    private PreferenceGroup mProfileContainer;
51    private CheckBoxPreference mOnlineModePreference;
52
53    /**
54     * The current mode of this activity and its checkboxes (either online mode
55     * or offline mode). In online mode, user interactions with the profile
56     * checkboxes will also toggle the profile's connectivity. In offline mode,
57     * they will not, and only the preferred state will be saved for the
58     * profile.
59     */
60    private boolean mOnlineMode;
61
62    @Override
63    protected void onCreate(Bundle savedInstanceState) {
64        super.onCreate(savedInstanceState);
65
66        BluetoothDevice device;
67        if (savedInstanceState != null) {
68            device = savedInstanceState.getParcelable(EXTRA_DEVICE);
69        } else {
70            Intent intent = getIntent();
71            device = intent.getParcelableExtra(EXTRA_DEVICE);
72        }
73
74        if (device == null) {
75            Log.w(TAG, "Activity started without a remote Bluetooth device");
76            finish();
77        }
78
79        mManager = LocalBluetoothManager.getInstance(this);
80        mCachedDevice = mManager.getCachedDeviceManager().findDevice(device);
81        if (mCachedDevice == null) {
82            Log.w(TAG, "Device not found, cannot connect to it");
83            finish();
84        }
85
86        addPreferencesFromResource(R.xml.bluetooth_device_advanced);
87        mProfileContainer = (PreferenceGroup) findPreference(KEY_PROFILE_CONTAINER);
88
89        // Set the title of the screen
90        findPreference(KEY_TITLE).setTitle(
91                getString(R.string.bluetooth_device_advanced_title, mCachedDevice.getName()));
92
93        // Listen for check/uncheck of the online mode checkbox
94        mOnlineModePreference = (CheckBoxPreference) findPreference(KEY_ONLINE_MODE);
95        mOnlineModePreference.setOnPreferenceChangeListener(this);
96
97        // Add a preference for each profile
98        addPreferencesForProfiles();
99    }
100
101    @Override
102    protected void onSaveInstanceState(Bundle outState) {
103        super.onSaveInstanceState(outState);
104
105        outState.putParcelable(EXTRA_DEVICE, mCachedDevice.getDevice());
106    }
107
108    @Override
109    protected void onResume() {
110        super.onResume();
111
112        mManager.setForegroundActivity(this);
113        mCachedDevice.registerCallback(this);
114
115        refresh();
116    }
117
118    @Override
119    protected void onPause() {
120        super.onPause();
121
122        mCachedDevice.unregisterCallback(this);
123        mManager.setForegroundActivity(null);
124    }
125
126    private void addPreferencesForProfiles() {
127        for (Profile profile : mCachedDevice.getConnectableProfiles()) {
128            Preference pref = createProfilePreference(profile);
129            mProfileContainer.addPreference(pref);
130        }
131    }
132
133    /**
134     * Creates a checkbox preference for the particular profile. The key will be
135     * the profile's name.
136     *
137     * @param profile The profile for which the preference controls.
138     * @return A preference that allows the user to choose whether this profile
139     *         will be connected to.
140     */
141    private CheckBoxPreference createProfilePreference(Profile profile) {
142        CheckBoxPreference pref = new CheckBoxPreference(this);
143        pref.setKey(profile.toString());
144        pref.setTitle(profile.localizedString);
145        pref.setPersistent(false);
146        pref.setOnPreferenceChangeListener(this);
147
148        LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
149                .getProfileManager(mManager, profile);
150
151        /**
152         * Gray out checkbox while connecting and disconnecting
153         */
154        pref.setEnabled(!mCachedDevice.isBusy());
155
156        refreshProfilePreference(pref, profile);
157
158        return pref;
159    }
160
161    public boolean onPreferenceChange(Preference preference, Object newValue) {
162        String key = preference.getKey();
163        if (TextUtils.isEmpty(key) || newValue == null) return true;
164
165        if (key.equals(KEY_ONLINE_MODE)) {
166            onOnlineModeCheckedStateChanged((Boolean) newValue);
167
168        } else {
169            Profile profile = getProfileOf(preference);
170            if (profile == null) return false;
171            onProfileCheckedStateChanged(profile, (Boolean) newValue);
172        }
173
174        return true;
175    }
176
177    private void onOnlineModeCheckedStateChanged(boolean checked) {
178        setOnlineMode(checked, true);
179    }
180
181    private void onProfileCheckedStateChanged(Profile profile, boolean checked) {
182        LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
183                .getProfileManager(mManager, profile);
184        profileManager.setPreferred(mCachedDevice.getDevice(), checked);
185        if (mOnlineMode) {
186            if (checked) {
187                mCachedDevice.connect(profile);
188            } else {
189                mCachedDevice.disconnect(profile);
190            }
191        }
192    }
193
194    public void onDeviceAttributesChanged(CachedBluetoothDevice cachedDevice) {
195        refresh();
196    }
197
198    private void refresh() {
199        // We are in 'online mode' if we are connected, connecting, or disconnecting
200        setOnlineMode(mCachedDevice.isConnected() || mCachedDevice.isBusy(), false);
201        refreshProfiles();
202    }
203
204    /**
205     * Switches between online/offline mode.
206     *
207     * @param onlineMode Whether to be in online mode, or offline mode.
208     * @param takeAction Whether to take action (i.e., connect or disconnect)
209     *            based on the new online mode.
210     */
211    private void setOnlineMode(boolean onlineMode, boolean takeAction) {
212        mOnlineMode = onlineMode;
213
214        if (takeAction) {
215            if (onlineMode) {
216                mCachedDevice.connect();
217            } else {
218                mCachedDevice.disconnect();
219            }
220        }
221
222        refreshOnlineModePreference();
223    }
224
225    private void refreshOnlineModePreference() {
226        mOnlineModePreference.setChecked(mOnlineMode);
227
228        /* Gray out checkbox while connecting and disconnecting */
229        mOnlineModePreference.setEnabled(!mCachedDevice.isBusy());
230
231        /**
232         * If the device is online, show status. Otherwise, show a summary that
233         * describes what the checkbox does.
234         */
235        mOnlineModePreference.setSummary(mOnlineMode ? mCachedDevice.getSummary()
236                : R.string.bluetooth_device_advanced_online_mode_summary);
237    }
238
239    private void refreshProfiles() {
240        for (Profile profile : mCachedDevice.getConnectableProfiles()) {
241            CheckBoxPreference profilePref =
242                    (CheckBoxPreference) findPreference(profile.toString());
243            if (profilePref == null) {
244                profilePref = createProfilePreference(profile);
245                mProfileContainer.addPreference(profilePref);
246            } else {
247                refreshProfilePreference(profilePref, profile);
248            }
249        }
250    }
251
252    private void refreshProfilePreference(CheckBoxPreference profilePref, Profile profile) {
253        BluetoothDevice device = mCachedDevice.getDevice();
254        LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
255                .getProfileManager(mManager, profile);
256
257        int connectionStatus = profileManager.getConnectionStatus(device);
258
259        /*
260         * Gray out checkbox while connecting and disconnecting
261         */
262        profilePref.setEnabled(!mCachedDevice.isBusy());
263        profilePref.setSummary(getProfileSummary(profileManager, profile, device,
264                connectionStatus, mOnlineMode));
265
266        profilePref.setChecked(profileManager.isPreferred(device));
267    }
268
269    private Profile getProfileOf(Preference pref) {
270        if (!(pref instanceof CheckBoxPreference)) return null;
271        String key = pref.getKey();
272        if (TextUtils.isEmpty(key)) return null;
273
274        try {
275            return Profile.valueOf(pref.getKey());
276        } catch (IllegalArgumentException e) {
277            return null;
278        }
279    }
280
281    private static int getProfileSummary(LocalBluetoothProfileManager profileManager,
282            Profile profile, BluetoothDevice device, int connectionStatus, boolean onlineMode) {
283        if (!onlineMode || connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) {
284            return getProfileSummaryForSettingPreference(profile);
285        } else {
286            return profileManager.getSummary(device);
287        }
288    }
289
290    /**
291     * Gets the summary that describes when checked, it will become a preferred profile.
292     *
293     * @param profile The profile to get the summary for.
294     * @return The summary.
295     */
296    private static final int getProfileSummaryForSettingPreference(Profile profile) {
297        switch (profile) {
298            case A2DP:
299                return R.string.bluetooth_a2dp_profile_summary_use_for;
300            case HEADSET:
301                return R.string.bluetooth_headset_profile_summary_use_for;
302            default:
303                return 0;
304        }
305    }
306
307}
308