Add Volume panel

Volume panel hosts each volume stream, including:
- Media volume
- Call Volume
- Ring volume
- Alarm volume

Change-Id: I1801d10d2304c57615e9499386c638c74ffcd7c3
Screenshot: https://screenshot.googleplex.com/m764j65ECto
Bug: 117804161
Test: Manual app
Test: robolectric
This commit is contained in:
Matthew Fritze
2018-11-30 14:29:45 -08:00
parent c14316c4a9
commit c999b088d2
12 changed files with 197 additions and 12 deletions

View File

@@ -10233,6 +10233,9 @@
<!-- Title for the Internet Connectivity dialog (settings panel) with Internet related settings [CHAR LIMIT=50] -->
<string name="internet_connectivity_panel_title">Internet Connectivity</string>
<!-- Title for the Volume dialog (settings panel) with all volume streams[CHAR LIMIT=50] -->
<string name="volume_connectivity_panel_title">Volume</string>
<!-- UI debug setting: force desktop mode [CHAR LIMIT=50] -->
<string name="force_desktop_mode">Force desktop mode</string>
<!-- UI debug setting: force desktop mode summary [CHAR LIMIT=NONE] -->

View File

@@ -28,6 +28,7 @@
android:icon="@drawable/ic_media_stream"
android:title="@string/media_volume_option_title"
android:order="-180"
settings:allowDynamicSummaryInSlice="true"
settings:controller="com.android.settings.notification.MediaVolumePreferenceController"/>
<!-- Media output switcher -->
@@ -44,6 +45,7 @@
android:icon="@drawable/ic_local_phone_24_lib"
android:title="@string/call_volume_option_title"
android:order="-170"
settings:allowDynamicSummaryInSlice="true"
settings:controller="com.android.settings.notification.CallVolumePreferenceController"/>
<!-- Hands free profile output switcher -->
@@ -60,6 +62,7 @@
android:icon="@drawable/ic_notifications"
android:title="@string/ring_volume_option_title"
android:order="-160"
settings:allowDynamicSummaryInSlice="true"
settings:controller="com.android.settings.notification.RingVolumePreferenceController"/>
@@ -69,6 +72,7 @@
android:icon="@*android:drawable/ic_audio_alarm"
android:title="@string/alarm_volume_option_title"
android:order="-150"
settings:allowDynamicSummaryInSlice="true"
settings:controller="com.android.settings.notification.AlarmVolumePreferenceController"/>
<!-- Notification volume -->

View File

@@ -47,8 +47,8 @@ public class InternetConnectivityPanel implements PanelContent {
}
@Override
public String getTitle() {
return (String) mContext.getText(R.string.internet_connectivity_panel_title);
public CharSequence getTitle() {
return mContext.getText(R.string.internet_connectivity_panel_title);
}
@Override

View File

@@ -25,6 +25,8 @@ public class PanelFeatureProviderImpl implements PanelFeatureProvider {
switch (panelType) {
case SettingsPanelActivity.PANEL_TYPE_WIFI:
return InternetConnectivityPanel.create(context);
case SettingsPanelActivity.PANEL_TYPE_VOLUME:
return VolumePanel.create(context);
}
throw new IllegalStateException("No matching panel for: " + panelType);

View File

@@ -54,6 +54,9 @@ public class SettingsPanelActivity extends FragmentActivity {
// TODO (b/117804442) move to framework
public static final String PANEL_TYPE_WIFI = "wifi_panel";
// TODO (b/117804442) move to framework
public static final String PANEL_TYPE_VOLUME = "volume_panel";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 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.panel;
import static com.android.settings.slices.CustomSliceRegistry.VOLUME_ALARM_URI;
import static com.android.settings.slices.CustomSliceRegistry.VOLUME_CALL_URI;
import static com.android.settings.slices.CustomSliceRegistry.VOLUME_MEDIA_URI;
import static com.android.settings.slices.CustomSliceRegistry.VOLUME_RINGER_URI;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.Settings;
import com.android.settings.R;
import java.util.ArrayList;
import java.util.List;
public class VolumePanel implements PanelContent {
private final Context mContext;
public static VolumePanel create(Context context) {
return new VolumePanel(context);
}
private VolumePanel(Context context) {
mContext = context.getApplicationContext();
}
@Override
public CharSequence getTitle() {
return mContext.getText(R.string.volume_connectivity_panel_title);
}
@Override
public List<Uri> getSlices() {
final List<Uri> uris = new ArrayList<>();
uris.add(VOLUME_MEDIA_URI);
uris.add(VOLUME_CALL_URI);
uris.add(VOLUME_RINGER_URI);
uris.add(VOLUME_ALARM_URI);
return uris;
}
@Override
public Intent getSeeMoreIntent() {
return new Intent(Settings.ACTION_SOUND_SETTINGS);
}
}

View File

@@ -25,6 +25,7 @@ import android.content.ContentResolver;
import android.net.Uri;
import android.provider.SettingsSlicesContract;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
import com.android.settings.wifi.calling.WifiCallingSliceHelper;
@@ -147,6 +148,43 @@ public class CustomSliceRegistry {
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath("storage_card")
.build();
/**
* Full {@link Uri} for the Alarm volume Slice.
*/
public static final Uri VOLUME_ALARM_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("alarm_volume")
.build();
/**
* Full {@link Uri} for the Call Volume Slice.
*/
public static final Uri VOLUME_CALL_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("call_volume")
.build();
/**
* Full {@link Uri} for the Media Volume Slice.
*/
public static final Uri VOLUME_MEDIA_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("media_volume")
.build();
/**
* Full {@link Uri} for the Ringer volume Slice.
*/
public static final Uri VOLUME_RINGER_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("ring_volume")
.build();
/**
* Full {@link Uri} for the Wifi Calling Slice.
*/

View File

@@ -193,8 +193,9 @@ public class SliceBuilderUtils {
CharSequence summaryText = controller.getSummary();
// Priority 1 : User prefers showing the dynamic summary in slice view rather than static
// summary.
if (isDynamicSummaryAllowed && isValidSummary(context, summaryText)) {
// summary. Note it doesn't require a valid summary - so we can force some slices to have
// empty summaries (ex: volume).
if (isDynamicSummaryAllowed) {
return summaryText;
}

View File

@@ -61,7 +61,6 @@ public class PanelFragmentTest {
mFakePanelContent = new FakePanelContent();
doReturn(mFakePanelContent).when(mPanelFeatureProvider).getPanel(any(), any());
ActivityController<FakeSettingsPanelActivity> activityController =
Robolectric.buildActivity(FakeSettingsPanelActivity.class);
activityController.setup();

View File

@@ -54,7 +54,13 @@ public class PanelSlicesAdapterTest {
public void setUp() {
mContext = RuntimeEnvironment.application;
final ActivityController<FakeSettingsPanelActivity> activityController =
mPanelFeatureProvider = spy(new PanelFeatureProviderImpl());
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mFakeFeatureFactory.panelFeatureProvider = mPanelFeatureProvider;
mFakePanelContent = new FakePanelContent();
doReturn(mFakePanelContent).when(mPanelFeatureProvider).getPanel(any(), any());
ActivityController<FakeSettingsPanelActivity> activityController =
Robolectric.buildActivity(FakeSettingsPanelActivity.class);
activityController.setup();
@@ -65,12 +71,6 @@ public class PanelSlicesAdapterTest {
.getSupportFragmentManager()
.findFragmentById(R.id.main_content));
mPanelFeatureProvider = spy(new PanelFeatureProviderImpl());
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mFakeFeatureFactory.panelFeatureProvider = mPanelFeatureProvider;
mFakePanelContent = new FakePanelContent();
doReturn(mFakePanelContent).when(mPanelFeatureProvider).getPanel(any(), any());
mAdapter = new PanelSlicesAdapter(mPanelFragment, mFakePanelContent.getSlices());
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2018 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.panel;
import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
public class VolumePanelTest {
private VolumePanel mPanel;
@Before
public void setUp() {
mPanel = VolumePanel.create(RuntimeEnvironment.application);
}
@Test
public void getSlices_containsNecessarySlices() {
final List<Uri> uris = mPanel.getSlices();
assertThat(uris).containsExactly(
CustomSliceRegistry.VOLUME_CALL_URI,
CustomSliceRegistry.VOLUME_MEDIA_URI,
CustomSliceRegistry.VOLUME_RINGER_URI,
CustomSliceRegistry.VOLUME_ALARM_URI);
}
@Test
public void getSeeMoreIntent_notNull() {
assertThat(mPanel.getSeeMoreIntent()).isNotNull();
}
}

View File

@@ -217,6 +217,19 @@ public class SliceBuilderUtilsTest {
assertThat(summary).isEqualTo(controllerSummary);
}
@Test
public void getDynamicSummary_allowDynamicSummary_nullSummary_returnsNull() {
final SliceData data = getDummyData(true /*isDynamicSummaryAllowed*/);
final FakePreferenceController controller = spy(
new FakePreferenceController(mContext, KEY));
final String controllerSummary = null;
doReturn(controllerSummary).when(controller).getSummary();
final CharSequence summary = SliceBuilderUtils.getSubtitleText(mContext, controller, data);
assertThat(summary).isNull();
}
@Test
public void getDynamicSummary_returnsScreenTitle() {
final SliceData data = getDummyData();