Adding settings for sound
Test: manually verified
Change-Id: Ia2433830eb5241a0a948e91ff22008ebd87ec728
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f799255..090712a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -167,5 +167,22 @@
android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true"/>
</activity>
+
+ <activity
+ android:name=".sound.SoundSettingsActivity"
+ android:label="@string/sound_settings"
+ android:theme="@style/CarSettingTheme"
+ android:icon="@drawable/ic_settings_sound">
+ <intent-filter android:priority="1">
+ <action android:name="com.android.settings.action.SETTINGS"/>
+ </intent-filter>
+
+ <meta-data
+ android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+ android:value="true"/>
+ <meta-data
+ android:name="com.android.settings.category"
+ android:value="com.android.settings.category.ia.homepage"/>
+ </activity>
</application>
</manifest>
diff --git a/res/drawable/ic_settings_sound.xml b/res/drawable/ic_settings_sound.xml
new file mode 100644
index 0000000..18c6aa1
--- /dev/null
+++ b/res/drawable/ic_settings_sound.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M3.0,9.0l0.0,6.0l4.0,0.0l5.0,5.0L12.0,4.0L7.0,9.0L3.0,9.0zm13.5,3.0c0.0,-1.77 -1.02,-3.29 -2.5,-4.03l0.0,8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14.0,3.23l0.0,2.06c2.8,0.86 5.0,3.54 5.0,6.71s-2.11,5.85 -5.0,6.71l0.0,2.06c4.01,-0.91 7.0,-4.49 7.0,-8.77s-2.99,-7.86 -7.0,-8.77z"/>
+</vector>
diff --git a/res/layout/list_item.xml b/res/layout/list_item.xml
index dd147ff..59a0142 100644
--- a/res/layout/list_item.xml
+++ b/res/layout/list_item.xml
@@ -20,12 +20,12 @@
android:layout_height="wrap_content">
<ImageView
android:id="@+id/icon"
- android:layout_width="@dimen/tile_icon_size"
- android:layout_height="@dimen/tile_icon_size"
+ android:layout_width="@dimen/car_list_item_small_icon_size"
+ android:layout_height="@dimen/car_list_item_small_icon_size"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
- android:layout_marginStart="@dimen/key_line1"
- android:layout_marginEnd="@dimen/key_line1" />
+ android:layout_marginStart="@dimen/car_list_item_icon_right_margin"
+ android:layout_marginEnd="@dimen/car_list_item_icon_right_margin" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -47,12 +47,12 @@
</LinearLayout>
<ImageButton
android:id="@+id/action"
- android:layout_width="@dimen/tile_icon_size"
- android:layout_height="@dimen/tile_icon_size"
+ android:layout_width="@dimen/car_list_item_right_icon_size"
+ android:layout_height="@dimen/car_list_item_right_icon_size"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_settings_gear"
android:background="@null"
android:visibility="gone"
- android:layout_marginEnd="@dimen/key_line1" />
+ android:layout_marginEnd="@dimen/car_list_item_right_icon_margin" />
</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/tile_item.xml b/res/layout/tile_item.xml
index 277be6e..afcf3b2 100644
--- a/res/layout/tile_item.xml
+++ b/res/layout/tile_item.xml
@@ -27,11 +27,11 @@
<ImageView
android:id="@+id/icon"
- android:layout_width="@dimen/dashboard_tile_image_size"
- android:layout_height="@dimen/dashboard_tile_image_size"
+ android:layout_width="@dimen/car_list_item_small_icon_size"
+ android:layout_height="@dimen/car_list_item_small_icon_size"
android:scaleType="fitCenter"
- android:layout_marginStart="@dimen/key_line1"
- android:layout_marginEnd="@dimen/key_line1" />
+ android:layout_marginStart="@dimen/car_list_item_icon_right_margin"
+ android:layout_marginEnd="@dimen/car_list_item_icon_right_margin" />
<LinearLayout
android:layout_width="match_parent"
@@ -56,6 +56,6 @@
android:gravity="center_vertical"
android:maxLines="1"
android:ellipsize="end"
- android:paddingEnd="@dimen/key_line1" />
+ android:paddingEnd="@dimen/car_list_item_right_icon_margin" />
</LinearLayout>
</LinearLayout>
diff --git a/res/layout/volume_controller_view.xml b/res/layout/volume_controller_view.xml
new file mode 100644
index 0000000..3b9b5d8
--- /dev/null
+++ b/res/layout/volume_controller_view.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/car_list_item_small_icon_size"
+ android:layout_height="@dimen/car_list_item_small_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="@dimen/car_list_item_icon_right_margin"
+ android:layout_marginEnd="@dimen/car_list_item_icon_right_margin" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="@dimen/tile_top_bottom_padding"
+ android:paddingBottom="@dimen/tile_top_bottom_padding"
+ android:layout_toEndOf="@+id/icon" >
+ <TextView
+ android:id="@+id/stream_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/medium_text_size" />
+ <SeekBar
+ android:id="@+id/seekbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/volume_list.xml b/res/layout/volume_list.xml
new file mode 100644
index 0000000..57c6e1d
--- /dev/null
+++ b/res/layout/volume_list.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+ <include
+ android:id="@+id/media_volume"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/volume_controller_view" />
+ <include
+ android:id="@+id/ring_volume"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/volume_controller_view" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6144915..0019e98 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -105,6 +105,22 @@
<!-- Bluetooth settings: The sub heading for available devices during and after scanning. [CHAR LIMIT=40] -->
<string name="bluetooth_preference_found_devices">Available devices</string>
+ <!-- sound settings -->
+ <!-- Sound settings screen heading -->
+ <string name="sound_settings">Sound</string>
+ <!-- Sound settings screen, setting option name -->
+ <string name="ring_volume_title">Ring volume</string>
+ <!-- Sound settings screen, the title of the volume bar to adjust the incoming call volume -->
+ <string name="incoming_call_volume_title">Ringtone</string>
+ <!-- Sound settings screen, the title of the volume bar to adjust the notification volume -->
+ <string name="notification_volume_title">Notification</string>
+ <!-- Sound settings screen, setting option name -->
+ <string name="media_volume_title">Media</string>
+ <!-- Sound settings screen, setting option summary text -->
+ <string name="media_volume_summary">Set volume for music and videos</string>
+ <!-- Sound settings screen, alarm volume slider title -->
+ <string name="alarm_volume_title">Alarm</string>
+
<!-- generic -->
<!-- Button label for generic forget action [CHAR LIMIT=20] -->
<string name="forget">Forget</string>
diff --git a/src/com/android/car/settings/sound/SoundSettingsActivity.java b/src/com/android/car/settings/sound/SoundSettingsActivity.java
new file mode 100644
index 0000000..5e13185
--- /dev/null
+++ b/src/com/android/car/settings/sound/SoundSettingsActivity.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 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.car.settings.sound;
+
+import android.car.Car;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.View;
+
+import com.android.car.settings.CarSettingActivity;
+import com.android.car.settings.R;
+
+/**
+ * Activity hosts sound related settings.
+ */
+public class SoundSettingsActivity extends CarSettingActivity {
+ private static final String TAG = "SoundSettingsActivity";
+ private Car mCar;
+
+ private VolumeControllerPresenter mMediaVolumeControllerPresenter;
+ private VolumeControllerPresenter mRingVolumeControllerPresenter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ showMenuIcon();
+ setContentView(R.layout.volume_list);
+
+ View mediaVolumeControllerView = findViewById(
+ R.id.media_volume);
+ View ringVolumeControllerView = findViewById(
+ R.id.ring_volume);
+ mMediaVolumeControllerPresenter = new VolumeControllerPresenter(
+ this /* context*/,
+ mediaVolumeControllerView,
+ AudioManager.STREAM_MUSIC,
+ null /* Uri sampleUri */,
+ R.string.media_volume_title,
+ com.android.internal.R.drawable.ic_audio_media);
+ mRingVolumeControllerPresenter = new VolumeControllerPresenter(
+ this /* context*/,
+ ringVolumeControllerView,
+ AudioManager.STREAM_RING,
+ null /* Uri sampleUri */,
+ R.string.ring_volume_title,
+ com.android.internal.R.drawable.ic_audio_ring_notif);
+ mCar = Car.createCar(this /* context */, mServiceConnection);
+ }
+
+ ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mMediaVolumeControllerPresenter.onServiceConnected(mCar);
+ mRingVolumeControllerPresenter.onServiceConnected(mCar);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mMediaVolumeControllerPresenter.onServiceDisconnected();
+ mRingVolumeControllerPresenter.onServiceDisconnected();
+ }
+ };
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mCar.connect();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mMediaVolumeControllerPresenter.stop();
+ mRingVolumeControllerPresenter.stop();
+ mCar.disconnect();
+ }
+}
diff --git a/src/com/android/car/settings/sound/VolumeControllerPresenter.java b/src/com/android/car/settings/sound/VolumeControllerPresenter.java
new file mode 100644
index 0000000..cd7a2a5
--- /dev/null
+++ b/src/com/android/car/settings/sound/VolumeControllerPresenter.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017 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.car.settings.sound;
+
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.media.CarAudioManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.media.AudioManager;
+import android.media.IVolumeController;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import com.android.car.settings.R;
+
+/**
+ * Contains logic about volume controller UI.
+ */
+public class VolumeControllerPresenter implements OnSeekBarChangeListener {
+
+ private static final String TAG = "SeekBarVolumizer";
+ private static final int AUDIO_FEEDBACK_DELAY_MS = 1500;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final SeekBar mSeekBar;
+ private final int mStreamType;
+ private final Ringtone mRingtone;
+ private final VolumnCallback mVolumeCallback = new VolumnCallback();
+
+ private CarAudioManager mCarAudioManager;
+
+ public void onServiceConnected(Car car) {
+ try {
+ mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE);
+ mCarAudioManager.setVolumeController(mVolumeCallback);
+ mSeekBar.setMax(mCarAudioManager.getStreamMaxVolume(mStreamType));
+ mSeekBar.setProgress(mCarAudioManager.getStreamVolume(mStreamType));
+ mSeekBar.setOnSeekBarChangeListener(VolumeControllerPresenter.this);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ }
+
+ public void onServiceDisconnected() {
+ mSeekBar.setOnSeekBarChangeListener(null);
+ mCarAudioManager = null;
+ }
+
+ public VolumeControllerPresenter(Context context, View volumeControllerView,
+ int streamType, Uri sampleUri, int titleStringResId, int iconResId) {
+ mSeekBar = (SeekBar) volumeControllerView.findViewById(R.id.seekbar);
+ mStreamType = streamType;
+ Uri ringtoneUri;
+
+ if (sampleUri == null) {
+ switch (mStreamType) {
+ case AudioManager.STREAM_RING:
+ ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
+ break;
+ case AudioManager.STREAM_NOTIFICATION:
+ ringtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+ break;
+ default:
+ ringtoneUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
+ }
+ } else {
+ ringtoneUri = sampleUri;
+ }
+ mRingtone = RingtoneManager.getRingtone(context, ringtoneUri);
+ if (mRingtone != null) {
+ mRingtone.setStreamType(mStreamType);
+ }
+ ((ImageView) volumeControllerView.findViewById(R.id.icon)).setImageResource(iconResId);
+ ((TextView) volumeControllerView.findViewById(R.id.stream_name)).setText(titleStringResId);
+ }
+
+ public void stop() {
+ mHandler.removeCallbacksAndMessages(null);
+ mRingtone.stop();
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ try {
+ if (mCarAudioManager == null) {
+ Log.w(TAG, "CarAudiomanager not available, Car is not connected!");
+ return;
+ }
+ mCarAudioManager.setStreamVolume(mStreamType, progress, AudioManager.FLAG_PLAY_SOUND);
+ playAudioFeedback();
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ playAudioFeedback();
+ }
+
+ private void playAudioFeedback() {
+ mHandler.removeCallbacksAndMessages(null);
+ mRingtone.play();
+ mHandler.postDelayed(() -> {
+ if (mRingtone.isPlaying()) {
+ mRingtone.stop();
+ }
+ }, AUDIO_FEEDBACK_DELAY_MS);
+ }
+
+ /**
+ * The interface has a terrible name, it is actually a callback, so here name it accordingly.
+ */
+ private final class VolumnCallback extends IVolumeController.Stub {
+
+ private final String TAG = VolumeControllerPresenter.TAG + ".cb";
+
+ @Override
+ public void displaySafeVolumeWarning(int flags) throws RemoteException {
+ }
+
+ @Override
+ public void volumeChanged(int streamType, int flags) throws RemoteException {
+ if (streamType != mStreamType) {
+ return;
+ }
+ try {
+ if (mCarAudioManager == null) {
+ Log.w(TAG, "CarAudiomanager not available, Car is not connected!");
+ return;
+ }
+ int volume = mCarAudioManager.getStreamVolume(mStreamType);
+ if (mSeekBar.getProgress() == volume) {
+ return;
+ }
+ mSeekBar.setProgress(volume);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ }
+
+ // this is not mute of this stream
+ @Override
+ public void masterMuteChanged(int flags) throws RemoteException {
+ }
+
+ @Override
+ public void setLayoutDirection(int layoutDirection) throws RemoteException {
+ }
+
+ @Override
+ public void dismiss() throws RemoteException {
+ }
+
+ @Override
+ public void setA11yMode(int mode) {
+ }
+ }
+}