diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fd53b958adb..974950dd7da 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -488,6 +488,22 @@ + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 85ca4d40830..42905fe2029 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -643,6 +643,19 @@ Use for file transfer + + Dock Settings + + Use dock for audio + + As speaker phone + + For music and media + + Remember settings + + "Change with Settings under Sound & display > Dock audio + Wi-Fi @@ -895,7 +908,9 @@ Set volume for music and videos - Dock settings + Dock audio + + Audio settings for the attached dock Audible touch tones diff --git a/res/xml/sound_and_display_settings.xml b/res/xml/sound_and_display_settings.xml index 151b0fb7556..1ffbdbf8d36 100644 --- a/res/xml/sound_and_display_settings.xml +++ b/res/xml/sound_and_display_settings.xml @@ -4,9 +4,9 @@ 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. @@ -16,7 +16,7 @@ - + @@ -50,12 +50,9 @@ - - + android:title="@string/dock_settings_title" + android:summary="@string/dock_settings_summary" + android:widgetLayout="@*android:layout/preference_dialog" /> - + - + - - - @@ -158,7 +155,7 @@ android:summary="@string/brightness_summary" android:dialogIcon="@drawable/ic_popup_brightness" android:dialogTitle="@string/brightness" /> - + - + diff --git a/src/com/android/settings/SoundAndDisplaySettings.java b/src/com/android/settings/SoundAndDisplaySettings.java index 67fc533b61a..edcd4da090d 100644 --- a/src/com/android/settings/SoundAndDisplaySettings.java +++ b/src/com/android/settings/SoundAndDisplaySettings.java @@ -17,6 +17,9 @@ package com.android.settings; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; + +import com.android.settings.bluetooth.DockSettingsActivity; + import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -104,6 +107,8 @@ public class SoundAndDisplaySettings extends PreferenceActivity implements private PreferenceGroup mSoundSettings; + private Intent mDockIntent; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -217,6 +222,9 @@ public class SoundAndDisplaySettings extends PreferenceActivity implements if (dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { // Show dock settings item mSoundSettings.addPreference(mDockSettings); + + // Save the intent to send to the activity + mDockIntent = intent; } else { // Remove dock settings item mSoundSettings.removePreference(mDockSettings); @@ -346,6 +354,10 @@ public class SoundAndDisplaySettings extends PreferenceActivity implements boolean value = mNotificationPulse.isChecked(); Settings.System.putInt(getContentResolver(), Settings.System.NOTIFICATION_LIGHT_PULSE, value ? 1 : 0); + } else if (preference == mDockSettings) { + Intent i = new Intent(mDockIntent); + i.setClass(this, DockSettingsActivity.class); + startActivity(i); } return true; diff --git a/src/com/android/settings/bluetooth/DockAudioStateChangeReceiver.java b/src/com/android/settings/bluetooth/DockAudioStateChangeReceiver.java new file mode 100644 index 00000000000..d32074291e0 --- /dev/null +++ b/src/com/android/settings/bluetooth/DockAudioStateChangeReceiver.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009 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. + */ + +package com.android.settings.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +public class DockAudioStateChangeReceiver extends BroadcastReceiver { + + private static final boolean DBG = true; + private static final String TAG = "DockAudioStateChangeReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null) + return; + + if (DBG) { + Log.e(TAG, "Action:" + intent.getAction() + + " State:" + intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED)); + } + + if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (device == null) { + if (DBG) Log.e(TAG, "Device is missing"); + return; + } + + LocalBluetoothManager localManager = LocalBluetoothManager.getInstance(context); + + int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + + switch (state) { + case Intent.EXTRA_DOCK_STATE_UNDOCKED: + DockSettingsActivity.handleUndocked(context, localManager, device); + break; + case Intent.EXTRA_DOCK_STATE_CAR: + case Intent.EXTRA_DOCK_STATE_DESK: + if (DockSettingsActivity.getAutoConnectSetting(localManager)) { + // Auto connect + DockSettingsActivity.handleDocked(context, localManager, device, state); + } else { + // Don't auto connect. Show dialog. + Intent i = new Intent(intent); + i.setClass(context, DockSettingsActivity.class); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(i); + } + break; + default: + Log.e(TAG, "Unknown state"); + break; + } + } + } +} diff --git a/src/com/android/settings/bluetooth/DockSettingsActivity.java b/src/com/android/settings/bluetooth/DockSettingsActivity.java new file mode 100644 index 00000000000..f5e005564d5 --- /dev/null +++ b/src/com/android/settings/bluetooth/DockSettingsActivity.java @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2009 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. + */ + +package com.android.settings.bluetooth; + +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; +import com.android.settings.R; +import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile; + +import android.app.AlertDialog; +import android.app.Notification; +import android.app.NotificationManager; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.WindowManager; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; + +/** + * RequestPermissionActivity asks the user whether to enable discovery. This is + * usually started by an application wanted to start bluetooth and or discovery + */ +public class DockSettingsActivity extends AlertActivity implements DialogInterface.OnClickListener, + AlertDialog.OnMultiChoiceClickListener, OnCheckedChangeListener { + + private static final String TAG = "DockSettingsActivity"; + + private static final boolean DEBUG = true; + + private static final String SHARED_PREFERENCES_KEY_AUTO_CONNECT_TO_DOCK = "auto_connect_to_dock"; + + private BluetoothDevice mDevice; + + private int mState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + + private CachedBluetoothDevice mCachedDevice; + + private LocalBluetoothManager mLocalManager; + + private LocalBluetoothProfileManager mA2dpMgr; + + private LocalBluetoothProfileManager mHeadsetMgr; + + private LocalBluetoothProfileManager[] mProfileManagers; + + private boolean[] mCheckedItems; + + private CheckBox mRememberCheck; + + private BroadcastReceiver mReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + if (!parseIntent(intent)) { + finish(); + return; + } + + if (DEBUG) Log.d(TAG, "Action: " + intent.getAction() + " State: " + mState); + } + }; + + private Profile[] mProfiles; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (!parseIntent(getIntent())) { + finish(); + return; + } + + if (mState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { + handleUndocked(this, mLocalManager, mDevice); + dismiss(); + return; + } + getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); + createDialog(); + getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); + } + + @Override + protected void onNewIntent(Intent intent) { + if (!parseIntent(getIntent())) { + finish(); + return; + } + } + + @Override + protected void onResume() { + super.onResume(); + + IntentFilter filter = new IntentFilter(Intent.ACTION_DOCK_EVENT); + registerReceiver(mReceiver, filter); + } + + @Override + protected void onPause() { + super.onPause(); + + unregisterReceiver(mReceiver); + } + + private void createDialog() { + // TODO Avoid hardcoding dock and profiles. Read from system properties + int numOfProfiles; + switch (mState) { + case Intent.EXTRA_DOCK_STATE_CAR: + numOfProfiles = 2; + break; + case Intent.EXTRA_DOCK_STATE_DESK: + numOfProfiles = 1; + break; + default: + return; + } + + CharSequence[] items = new CharSequence[numOfProfiles]; + mCheckedItems = new boolean[numOfProfiles]; + mProfileManagers = new LocalBluetoothProfileManager[numOfProfiles]; + mProfiles = new Profile[numOfProfiles]; + + int i = 0; + switch (mState) { + case Intent.EXTRA_DOCK_STATE_CAR: + mProfileManagers[i] = mHeadsetMgr; + mProfiles[i] = Profile.HEADSET; + mCheckedItems[i] = mHeadsetMgr.isPreferred(mDevice); + items[i] = getString(R.string.bluetooth_dock_settings_headset); + ++i; + // fall through + case Intent.EXTRA_DOCK_STATE_DESK: + mProfileManagers[i] = mA2dpMgr; + mProfiles[i] = Profile.A2DP; + mCheckedItems[i] = mA2dpMgr.isPreferred(mDevice); + items[i] = getString(R.string.bluetooth_dock_settings_a2dp); + break; + } + + final AlertController.AlertParams p = mAlertParams; + p.mTitle = getString(R.string.bluetooth_dock_settings_title); + + // Profiles + p.mIsMultiChoice = true; + p.mItems = items; + p.mCheckedItems = mCheckedItems; + p.mOnCheckboxClickListener = this; + + // Remember this settings + LayoutInflater inflater = (LayoutInflater) getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + p.mView = inflater.inflate(R.layout.remember_dock_setting, null); + p.mViewSpacingSpecified = true; + float pixelScaleFactor = getResources().getDisplayMetrics().density; + p.mViewSpacingLeft = (int) (14 * pixelScaleFactor); + p.mViewSpacingRight = (int) (14 * pixelScaleFactor); + mRememberCheck = (CheckBox)p.mView.findViewById(R.id.remember); + if (DEBUG) Log.d(TAG, "Auto Check? = " + getAutoConnectSetting(mLocalManager)); + mRememberCheck.setChecked(getAutoConnectSetting(mLocalManager)); + mRememberCheck.setOnCheckedChangeListener(this); + + // Ok Button + p.mPositiveButtonText = getString(android.R.string.ok); + p.mPositiveButtonListener = this; + + setupAlert(); + } + + // Called when the individual items are clicked. + public void onClick(DialogInterface dialog, int which, boolean isChecked) { + if (DEBUG) Log.d(TAG, "Item " + which + " changed to " + isChecked); + mCheckedItems[which] = isChecked; + } + + // Called when the "Remember" Checkbox is clicked + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (DEBUG) Log.d(TAG, "onCheckedChanged: Remember Settings = " + isChecked); + saveAutoConnectSetting(mLocalManager, isChecked); + } + + // Called when clicked on the OK button + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + switch (mLocalManager.getBluetoothState()) { + case BluetoothAdapter.STATE_OFF: + case BluetoothAdapter.STATE_TURNING_OFF: + mLocalManager.getBluetoothAdapter().enable(); + // TODO can I call connect right away? probably not. + break; + case BluetoothAdapter.STATE_TURNING_ON: + // TODO wait? probably + break; + case BluetoothAdapter.STATE_ON: + break; + } + + for(int i = 0; i < mProfileManagers.length; i++) { + mProfileManagers[i].setPreferred(mDevice, mCheckedItems[i]); + + if (DEBUG) Log.d(TAG, mProfileManagers[i].toString() + " = " + mCheckedItems[i]); + boolean isConnected = mProfileManagers[i].isConnected(mDevice); + if (mCheckedItems[i] && !isConnected) { + if (DEBUG) Log.d(TAG, "Connecting "); + mCachedDevice.connect(mProfiles[i]); + } else if (isConnected){ + if (DEBUG) Log.d(TAG, "Disconnecting"); + mProfileManagers[i].disconnect(mDevice); + } + } + } + } + + private boolean parseIntent(Intent intent) { + if (intent == null) { + return false; + } + + mLocalManager = LocalBluetoothManager.getInstance(this); + if (mLocalManager == null) { + if (DEBUG) Log.d(TAG, "Error: there's a problem starting bluetooth"); + return false; + } + + mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (mDevice == null) { + if (DEBUG) Log.d(TAG, "device == null"); + return false; + } + + mState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + if (mState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { + handleUndocked(this, mLocalManager, mDevice); + return false; + } + + mCachedDevice = getCachedBluetoothDevice(this, mLocalManager, mDevice); + mA2dpMgr = LocalBluetoothProfileManager.getProfileManager(mLocalManager, Profile.A2DP); + mHeadsetMgr = LocalBluetoothProfileManager.getProfileManager(mLocalManager, + Profile.HEADSET); + + return true; + } + + public static void handleUndocked(Context context, LocalBluetoothManager localManager, + BluetoothDevice device) { + CachedBluetoothDevice cachedBluetoothDevice = getCachedBluetoothDevice(context, + localManager, device); + cachedBluetoothDevice.disconnect(); + } + + public static void handleDocked(Context context, LocalBluetoothManager localManager, + BluetoothDevice device, int state) { + CachedBluetoothDevice cachedBluetoothDevice = getCachedBluetoothDevice(context, + localManager, device); + cachedBluetoothDevice.connect(); + } + + private static CachedBluetoothDevice getCachedBluetoothDevice(Context context, + LocalBluetoothManager localManager, BluetoothDevice device) { + CachedBluetoothDeviceManager cachedDeviceManager = localManager.getCachedDeviceManager(); + CachedBluetoothDevice cachedBluetoothDevice = cachedDeviceManager.findDevice(device); + if (cachedBluetoothDevice == null) { + cachedBluetoothDevice = new CachedBluetoothDevice(context, device); + } + return cachedBluetoothDevice; + } + + public static boolean hasAutoConnectSetting(LocalBluetoothManager localManager) { + return localManager.getSharedPreferences().contains( + SHARED_PREFERENCES_KEY_AUTO_CONNECT_TO_DOCK); + } + + public static boolean getAutoConnectSetting(LocalBluetoothManager localManager) { + return localManager.getSharedPreferences().getBoolean( + SHARED_PREFERENCES_KEY_AUTO_CONNECT_TO_DOCK, false); + } + + public static void saveAutoConnectSetting(LocalBluetoothManager localManager, + boolean autoConnect) { + SharedPreferences.Editor editor = localManager.getSharedPreferences().edit(); + editor.putBoolean(SHARED_PREFERENCES_KEY_AUTO_CONNECT_TO_DOCK, autoConnect); + editor.commit(); + } +} diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java index da3c69ff9c1..40781319e98 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java @@ -185,7 +185,9 @@ public abstract class LocalBluetoothProfileManager { @Override public boolean disconnect(BluetoothDevice device) { // Downgrade priority as user is disconnecting the sink. - mService.setSinkPriority(device, BluetoothA2dp.PRIORITY_ON); + if (mService.getSinkPriority(device) > BluetoothA2dp.PRIORITY_ON) { + mService.setSinkPriority(device, BluetoothA2dp.PRIORITY_ON); + } return mService.disconnectSink(device); } @@ -291,7 +293,9 @@ public abstract class LocalBluetoothProfileManager { public boolean disconnect(BluetoothDevice device) { if (mService.getCurrentHeadset().equals(device)) { // Downgrade prority as user is disconnecting the headset. - mService.setPriority(device, BluetoothHeadset.PRIORITY_ON); + if (mService.getPriority(device) > BluetoothHeadset.PRIORITY_ON) { + mService.setPriority(device, BluetoothHeadset.PRIORITY_ON); + } return mService.disconnectHeadset(); } else { return false;