Gallery2: Add audio output switcher between headset/speaker
There is no audio output switcher between headset/speaker.
Add the audio output switcher button to menu list in order to
let user use this function.
CRs-Fixed: 650867
Change-Id: I16f5fdc74a76ee55557ba87b0145e51165f0408a
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 679ca04..6136da9 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,4 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
+ Not a Contribution.
+
+ 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.
+-->
<manifest android:versionCode="40030"
android:versionName="1.1.40030"
@@ -19,6 +36,7 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
diff --git a/res/values-zh-rCN/codeaurora_strings.xml b/res/values-zh-rCN/codeaurora_strings.xml
index b1b2dbe..d046f3f 100644
--- a/res/values-zh-rCN/codeaurora_strings.xml
+++ b/res/values-zh-rCN/codeaurora_strings.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (c) 2013, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -38,6 +38,9 @@
<string name="loop">"循环"</string>
<string name="single">"单次"</string>
<string name="stereo">"立体声"</string>
+ <string name="speaker_on">扬声器 开</string>
+ <string name="speaker_off">扬声器 关</string>
+ <string name="speaker_need_headset">请先插入耳机!</string>
<string name="single_track">"单声道"</string>
<string name="media_detail">"媒体详情"</string>
<string name="input_url">"输入 URL"</string>
diff --git a/res/values/codeaurora_strings.xml b/res/values/codeaurora_strings.xml
index 386d2b1..9413652 100644
--- a/res/values/codeaurora_strings.xml
+++ b/res/values/codeaurora_strings.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (c) 2013, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -38,6 +38,9 @@
<string name="loop">"Loop"</string>
<string name="single">"Single"</string>
<string name="stereo">"Stereo"</string>
+ <string name="speaker_on">Speaker ON</string>
+ <string name="speaker_off">Speaker OFF</string>
+ <string name="speaker_need_headset">Please plug in headset first!</string>
<string name="single_track">"Single track"</string>
<string name="media_detail">"Media details"</string>
<string name="input_url">"Input URL"</string>
diff --git a/src/org/codeaurora/gallery3d/video/ExtensionHelper.java b/src/org/codeaurora/gallery3d/video/ExtensionHelper.java
index 2ff61a4..b515610 100755
--- a/src/org/codeaurora/gallery3d/video/ExtensionHelper.java
+++ b/src/org/codeaurora/gallery3d/video/ExtensionHelper.java
@@ -1,3 +1,31 @@
+/*
+Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
package org.codeaurora.gallery3d.video;
import android.content.Context;
@@ -20,6 +48,7 @@
boolean stereo = context.getResources().getBoolean(R.bool.stereo);
boolean streaming = context.getResources().getBoolean(R.bool.streaming);
boolean playlist = context.getResources().getBoolean(R.bool.playlist);
+ boolean speaker = context.getResources().getBoolean(R.bool.speaker);
if (loop == true) {
group.addHooker(new LoopVideoHooker()); // add it for common feature.
@@ -35,6 +64,9 @@
group.addHooker(new MovieListHooker()); // add it for common feature.
group.addHooker(new StepOptionSettingsHooker());
}
+ if (speaker == true) {
+ group.addHooker(new SpeakerHooker());
+ }
return group;
}
}
diff --git a/src/org/codeaurora/gallery3d/video/SpeakerHooker.java b/src/org/codeaurora/gallery3d/video/SpeakerHooker.java
new file mode 100644
index 0000000..9bf5348
--- /dev/null
+++ b/src/org/codeaurora/gallery3d/video/SpeakerHooker.java
@@ -0,0 +1,210 @@
+/*
+Copyright (c) 2014, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+package org.codeaurora.gallery3d.video;
+
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.os.Bundle;
+
+import com.android.gallery3d.R;
+
+public class SpeakerHooker extends MovieHooker {
+
+ private static final int MENU_SPEAKER = 1;
+
+ private AudioManager mAudioManager;
+
+ private MenuItem mMenuSpeakerButton;
+
+ private boolean mIsHeadsetOn = false;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ initAudioManager();
+ }
+
+ private void initAudioManager() {
+ if (mAudioManager == null) {
+ mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ turnSpeakerOff();
+ super.onDestroy();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ registerHeadSetReceiver();
+ }
+
+ private void registerHeadSetReceiver() {
+ final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
+ intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+ intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ intentFilter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+ getContext().registerReceiver(mHeadSetReceiver, intentFilter);
+ }
+
+ @Override
+ public void onPause() {
+ unregisterHeadSetReceiver();
+ super.onPause();
+ }
+
+ private void unregisterHeadSetReceiver() {
+ try {
+ getContext().unregisterReceiver(mHeadSetReceiver);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private final BroadcastReceiver mHeadSetReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (mAudioManager == null) {
+ initAudioManager();
+ }
+ if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
+ mIsHeadsetOn = (intent.getIntExtra("state", 0) == 1)
+ || mAudioManager.isBluetoothA2dpOn();
+ } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)
+ || action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
+ final int deviceClass = ((BluetoothDevice)
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))
+ .getBluetoothClass().getDeviceClass();
+ if ((deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES)
+ || (deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) {
+ mIsHeadsetOn = action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)
+ || mAudioManager.isWiredHeadsetOn();
+ }
+ } else if (action.equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
+ mIsHeadsetOn = false;
+ }
+ updateSpeakerButton();
+ if (!mIsHeadsetOn) {
+ turnSpeakerOff();
+ }
+ }
+ };
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ mMenuSpeakerButton = menu.add(0, getMenuActivityId(MENU_SPEAKER), 0, R.string.speaker_on);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ super.onOptionsItemSelected(item);
+ switch (getMenuOriginalId(item.getItemId())) {
+ case MENU_SPEAKER:
+ changeSpeakerState();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void changeSpeakerState() {
+ if (isSpeakerOn()) {
+ turnSpeakerOff();
+ } else {
+ if (mIsHeadsetOn) {
+ turnSpeakerOn();
+ } else {
+ Toast.makeText(getContext(), getContext().getString(R.string.speaker_need_headset),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ updateSpeakerButton();
+ }
+
+ private void turnSpeakerOn() {
+ if (mAudioManager == null) {
+ initAudioManager();
+ }
+ mAudioManager.setSpeakerphoneOn(true);
+ AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
+ AudioSystem.FORCE_SPEAKER);
+ }
+
+ private void turnSpeakerOff() {
+ if (mAudioManager == null) {
+ initAudioManager();
+ }
+ AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
+ AudioSystem.FORCE_NONE);
+ mAudioManager.setSpeakerphoneOn(false);
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ updateSpeakerButton();
+ return true;
+ }
+
+ private void updateSpeakerButton() {
+ if (mMenuSpeakerButton != null) {
+ if (isSpeakerOn()) {
+ mMenuSpeakerButton.setTitle(R.string.speaker_off);
+ } else {
+ mMenuSpeakerButton.setTitle(R.string.speaker_on);
+ }
+ }
+ }
+
+ private boolean isSpeakerOn() {
+ boolean isSpeakerOn = false;
+ if (mAudioManager == null) {
+ initAudioManager();
+ }
+ isSpeakerOn = mAudioManager.isSpeakerphoneOn();
+ return isSpeakerOn;
+ }
+
+}