Loading res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -10090,4 +10090,7 @@ <string name="network_query_error">Couldn\u2019t find networks. Try again.</string> <!-- Text to show this network is forbidden [CHAR LIMIT=NONE] --> <string name="forbidden_network">(forbidden)</string> <!-- Message informs the user that has no SIM card in personalized Settings [CHAR LIMIT=30] --> <string name="no_sim_card">No SIM card</string> </resources> src/com/android/settings/homepage/deviceinfo/DataUsageSlice.java 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * 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.homepage.deviceinfo; import android.app.PendingIntent; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.format.Formatter; import android.text.style.TextAppearanceSpan; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.builders.ListBuilder; import androidx.slice.builders.SliceAction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.SubSettings; import com.android.settings.Utils; import com.android.settings.datausage.DataUsageSummary; import com.android.settings.datausage.DataUsageUtils; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SettingsSliceProvider; import com.android.settings.slices.SliceBuilderUtils; import com.android.settingslib.net.DataUsageController; import java.util.concurrent.TimeUnit; public class DataUsageSlice implements CustomSliceable { private static final String TAG = "DataUsageSlice"; private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1); /** * The path denotes the unique name of data usage slice. */ public static final String PATH_DATA_USAGE_CARD = "data_usage_card"; /** * Backing Uri for the Data usage Slice. */ public static final Uri DATA_USAGE_CARD_URI = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(SettingsSliceProvider.SLICE_AUTHORITY) .appendPath(PATH_DATA_USAGE_CARD) .build(); private final Context mContext; public DataUsageSlice(Context context) { mContext = context; } @Override public Uri getUri() { return DATA_USAGE_CARD_URI; } /** * Return a Data usage Slice bound to {@link #DATA_USAGE_CARD_URI} */ @Override public Slice getSlice() { final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_settings_data_usage); final String title = mContext.getString(R.string.data_usage_summary_title); final SliceAction primaryAction = new SliceAction(getPrimaryAction(), icon, title); final DataUsageController dataUsageController = new DataUsageController(mContext); final DataUsageController.DataUsageInfo info = dataUsageController.getDataUsageInfo(); final ListBuilder listBuilder = new ListBuilder(mContext, DATA_USAGE_CARD_URI, ListBuilder.INFINITY) .setAccentColor(Utils.getColorAccentDefaultColor(mContext)) .setHeader(new ListBuilder.HeaderBuilder().setTitle(title)); if (DataUsageUtils.hasSim(mContext)) { listBuilder.addRow(new ListBuilder.RowBuilder() .setTitle(getDataUsageText(info)) .setSubtitle(getCycleTime(info)) .setPrimaryAction(primaryAction)); } else { listBuilder.addRow(new ListBuilder.RowBuilder() .setTitle(mContext.getText(R.string.no_sim_card)) .setPrimaryAction(primaryAction)); } return listBuilder.build(); } @Override public Intent getIntent() { final String screenTitle = mContext.getText(R.string.data_usage_wifi_title).toString(); final Uri contentUri = new Uri.Builder().appendPath(PATH_DATA_USAGE_CARD).build(); return SliceBuilderUtils.buildSearchResultPageIntent(mContext, DataUsageSummary.class.getName(), PATH_DATA_USAGE_CARD, screenTitle, MetricsProto.MetricsEvent.SLICE) .setClassName(mContext.getPackageName(), SubSettings.class.getName()) .setData(contentUri); } private PendingIntent getPrimaryAction() { final Intent intent = getIntent(); return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); } @VisibleForTesting CharSequence getDataUsageText(DataUsageController.DataUsageInfo info) { final Formatter.BytesResult usedResult = Formatter.formatBytes(mContext.getResources(), info.usageLevel, Formatter.FLAG_CALCULATE_ROUNDED | Formatter.FLAG_IEC_UNITS); final SpannableString usageNumberText = new SpannableString(usedResult.value); usageNumberText.setSpan( new TextAppearanceSpan(mContext, android.R.style.TextAppearance_Large), 0, usageNumberText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return TextUtils.expandTemplate(mContext.getText(R.string.data_used_formatted), usageNumberText, usedResult.units); } @VisibleForTesting CharSequence getCycleTime(DataUsageController.DataUsageInfo info) { final long millisLeft = info.cycleEnd - System.currentTimeMillis(); if (millisLeft <= 0) { return mContext.getString(R.string.billing_cycle_none_left); } else { final int daysLeft = (int) (millisLeft / MILLIS_IN_A_DAY); return daysLeft < 1 ? mContext.getString(R.string.billing_cycle_less_than_one_day_left) : mContext.getResources().getQuantityString(R.plurals.billing_cycle_days_left, daysLeft, daysLeft); } } @Override public void onNotifyChange(Intent intent) { } } src/com/android/settings/slices/CustomSliceManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.content.Context; import android.net.Uri; import android.util.ArrayMap; import com.android.settings.homepage.deviceinfo.DataUsageSlice; import com.android.settings.wifi.WifiSlice; import java.util.Map; Loading Loading @@ -87,5 +88,6 @@ public class CustomSliceManager { private void addSlices() { mUriMap.put(WifiSlice.WIFI_URI, WifiSlice.class); mUriMap.put(DataUsageSlice.DATA_USAGE_CARD_URI, DataUsageSlice.class); } } No newline at end of file tests/robotests/src/com/android/settings/homepage/deviceinfo/DataUsageSliceTest.java 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * 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.homepage.deviceinfo; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import android.content.Context; import android.content.res.Resources; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.SliceItem; import androidx.slice.SliceMetadata; import androidx.slice.SliceProvider; import androidx.slice.core.SliceAction; import androidx.slice.widget.SliceLiveData; import com.android.settings.R; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SliceTester; import com.android.settings.testutils.shadow.ShadowDataUsageUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) @Config(shadows = ShadowDataUsageUtils.class) public class DataUsageSliceTest { private static final String DATA_USAGE_TITLE = "Data usage"; private static final String DATA_USAGE_SUMMARY = "test_summary"; private Context mContext; private DataUsageSlice mDataUsageSlice; @Before public void setUp() { mContext = spy(RuntimeEnvironment.application); // Prevent crash in SliceMetadata. Resources resources = spy(mContext.getResources()); doReturn(60).when(resources).getDimensionPixelSize(anyInt()); doReturn(resources).when(mContext).getResources(); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); mDataUsageSlice = spy(new DataUsageSlice(mContext)); } @Test public void getSlice_hasSim_shouldBeCorrectSliceContent() { ShadowDataUsageUtils.HAS_SIM = true; doReturn(DATA_USAGE_TITLE).when(mDataUsageSlice).getDataUsageText(any()); doReturn(DATA_USAGE_SUMMARY).when(mDataUsageSlice).getCycleTime(any()); final Slice slice = mDataUsageSlice.getSlice(); final SliceMetadata metadata = SliceMetadata.from(mContext, slice); final SliceAction primaryAction = metadata.getPrimaryAction(); final IconCompat expectedIcon = IconCompat.createWithResource(mContext, R.drawable.ic_settings_data_usage); assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedIcon.toString()); final List<SliceItem> sliceItems = slice.getItems(); SliceTester.assertTitle(sliceItems, mContext.getString(R.string.data_usage_summary_title)); } @Test public void getSlice_hasNoSim_shouldShowNoSimCard() { ShadowDataUsageUtils.HAS_SIM = false; final Slice slice = mDataUsageSlice.getSlice(); final List<SliceItem> sliceItems = slice.getItems(); SliceTester.assertTitle(sliceItems, mContext.getString(R.string.data_usage_summary_title)); SliceTester.assertTitle(sliceItems, mContext.getString(R.string.no_sim_card)); } } tests/robotests/src/com/android/settings/testutils/SliceTester.java +5 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat; import android.app.PendingIntent; import android.content.Context; import android.text.TextUtils; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; Loading Loading @@ -209,11 +210,13 @@ public class SliceTester { continue; } hasTitle = true; for (SliceItem subTitleItem : titleItems) { if (TextUtils.equals(subTitleItem.getText(), title)) { hasTitle = true; assertThat(subTitleItem.getText()).isEqualTo(title); } } } assertThat(hasTitle).isTrue(); } Loading Loading
res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -10090,4 +10090,7 @@ <string name="network_query_error">Couldn\u2019t find networks. Try again.</string> <!-- Text to show this network is forbidden [CHAR LIMIT=NONE] --> <string name="forbidden_network">(forbidden)</string> <!-- Message informs the user that has no SIM card in personalized Settings [CHAR LIMIT=30] --> <string name="no_sim_card">No SIM card</string> </resources>
src/com/android/settings/homepage/deviceinfo/DataUsageSlice.java 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * 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.homepage.deviceinfo; import android.app.PendingIntent; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.format.Formatter; import android.text.style.TextAppearanceSpan; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.builders.ListBuilder; import androidx.slice.builders.SliceAction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.SubSettings; import com.android.settings.Utils; import com.android.settings.datausage.DataUsageSummary; import com.android.settings.datausage.DataUsageUtils; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SettingsSliceProvider; import com.android.settings.slices.SliceBuilderUtils; import com.android.settingslib.net.DataUsageController; import java.util.concurrent.TimeUnit; public class DataUsageSlice implements CustomSliceable { private static final String TAG = "DataUsageSlice"; private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1); /** * The path denotes the unique name of data usage slice. */ public static final String PATH_DATA_USAGE_CARD = "data_usage_card"; /** * Backing Uri for the Data usage Slice. */ public static final Uri DATA_USAGE_CARD_URI = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(SettingsSliceProvider.SLICE_AUTHORITY) .appendPath(PATH_DATA_USAGE_CARD) .build(); private final Context mContext; public DataUsageSlice(Context context) { mContext = context; } @Override public Uri getUri() { return DATA_USAGE_CARD_URI; } /** * Return a Data usage Slice bound to {@link #DATA_USAGE_CARD_URI} */ @Override public Slice getSlice() { final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_settings_data_usage); final String title = mContext.getString(R.string.data_usage_summary_title); final SliceAction primaryAction = new SliceAction(getPrimaryAction(), icon, title); final DataUsageController dataUsageController = new DataUsageController(mContext); final DataUsageController.DataUsageInfo info = dataUsageController.getDataUsageInfo(); final ListBuilder listBuilder = new ListBuilder(mContext, DATA_USAGE_CARD_URI, ListBuilder.INFINITY) .setAccentColor(Utils.getColorAccentDefaultColor(mContext)) .setHeader(new ListBuilder.HeaderBuilder().setTitle(title)); if (DataUsageUtils.hasSim(mContext)) { listBuilder.addRow(new ListBuilder.RowBuilder() .setTitle(getDataUsageText(info)) .setSubtitle(getCycleTime(info)) .setPrimaryAction(primaryAction)); } else { listBuilder.addRow(new ListBuilder.RowBuilder() .setTitle(mContext.getText(R.string.no_sim_card)) .setPrimaryAction(primaryAction)); } return listBuilder.build(); } @Override public Intent getIntent() { final String screenTitle = mContext.getText(R.string.data_usage_wifi_title).toString(); final Uri contentUri = new Uri.Builder().appendPath(PATH_DATA_USAGE_CARD).build(); return SliceBuilderUtils.buildSearchResultPageIntent(mContext, DataUsageSummary.class.getName(), PATH_DATA_USAGE_CARD, screenTitle, MetricsProto.MetricsEvent.SLICE) .setClassName(mContext.getPackageName(), SubSettings.class.getName()) .setData(contentUri); } private PendingIntent getPrimaryAction() { final Intent intent = getIntent(); return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); } @VisibleForTesting CharSequence getDataUsageText(DataUsageController.DataUsageInfo info) { final Formatter.BytesResult usedResult = Formatter.formatBytes(mContext.getResources(), info.usageLevel, Formatter.FLAG_CALCULATE_ROUNDED | Formatter.FLAG_IEC_UNITS); final SpannableString usageNumberText = new SpannableString(usedResult.value); usageNumberText.setSpan( new TextAppearanceSpan(mContext, android.R.style.TextAppearance_Large), 0, usageNumberText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return TextUtils.expandTemplate(mContext.getText(R.string.data_used_formatted), usageNumberText, usedResult.units); } @VisibleForTesting CharSequence getCycleTime(DataUsageController.DataUsageInfo info) { final long millisLeft = info.cycleEnd - System.currentTimeMillis(); if (millisLeft <= 0) { return mContext.getString(R.string.billing_cycle_none_left); } else { final int daysLeft = (int) (millisLeft / MILLIS_IN_A_DAY); return daysLeft < 1 ? mContext.getString(R.string.billing_cycle_less_than_one_day_left) : mContext.getResources().getQuantityString(R.plurals.billing_cycle_days_left, daysLeft, daysLeft); } } @Override public void onNotifyChange(Intent intent) { } }
src/com/android/settings/slices/CustomSliceManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.content.Context; import android.net.Uri; import android.util.ArrayMap; import com.android.settings.homepage.deviceinfo.DataUsageSlice; import com.android.settings.wifi.WifiSlice; import java.util.Map; Loading Loading @@ -87,5 +88,6 @@ public class CustomSliceManager { private void addSlices() { mUriMap.put(WifiSlice.WIFI_URI, WifiSlice.class); mUriMap.put(DataUsageSlice.DATA_USAGE_CARD_URI, DataUsageSlice.class); } } No newline at end of file
tests/robotests/src/com/android/settings/homepage/deviceinfo/DataUsageSliceTest.java 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * 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.homepage.deviceinfo; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import android.content.Context; import android.content.res.Resources; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.SliceItem; import androidx.slice.SliceMetadata; import androidx.slice.SliceProvider; import androidx.slice.core.SliceAction; import androidx.slice.widget.SliceLiveData; import com.android.settings.R; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SliceTester; import com.android.settings.testutils.shadow.ShadowDataUsageUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) @Config(shadows = ShadowDataUsageUtils.class) public class DataUsageSliceTest { private static final String DATA_USAGE_TITLE = "Data usage"; private static final String DATA_USAGE_SUMMARY = "test_summary"; private Context mContext; private DataUsageSlice mDataUsageSlice; @Before public void setUp() { mContext = spy(RuntimeEnvironment.application); // Prevent crash in SliceMetadata. Resources resources = spy(mContext.getResources()); doReturn(60).when(resources).getDimensionPixelSize(anyInt()); doReturn(resources).when(mContext).getResources(); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); mDataUsageSlice = spy(new DataUsageSlice(mContext)); } @Test public void getSlice_hasSim_shouldBeCorrectSliceContent() { ShadowDataUsageUtils.HAS_SIM = true; doReturn(DATA_USAGE_TITLE).when(mDataUsageSlice).getDataUsageText(any()); doReturn(DATA_USAGE_SUMMARY).when(mDataUsageSlice).getCycleTime(any()); final Slice slice = mDataUsageSlice.getSlice(); final SliceMetadata metadata = SliceMetadata.from(mContext, slice); final SliceAction primaryAction = metadata.getPrimaryAction(); final IconCompat expectedIcon = IconCompat.createWithResource(mContext, R.drawable.ic_settings_data_usage); assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedIcon.toString()); final List<SliceItem> sliceItems = slice.getItems(); SliceTester.assertTitle(sliceItems, mContext.getString(R.string.data_usage_summary_title)); } @Test public void getSlice_hasNoSim_shouldShowNoSimCard() { ShadowDataUsageUtils.HAS_SIM = false; final Slice slice = mDataUsageSlice.getSlice(); final List<SliceItem> sliceItems = slice.getItems(); SliceTester.assertTitle(sliceItems, mContext.getString(R.string.data_usage_summary_title)); SliceTester.assertTitle(sliceItems, mContext.getString(R.string.no_sim_card)); } }
tests/robotests/src/com/android/settings/testutils/SliceTester.java +5 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat; import android.app.PendingIntent; import android.content.Context; import android.text.TextUtils; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; Loading Loading @@ -209,11 +210,13 @@ public class SliceTester { continue; } hasTitle = true; for (SliceItem subTitleItem : titleItems) { if (TextUtils.equals(subTitleItem.getText(), title)) { hasTitle = true; assertThat(subTitleItem.getText()).isEqualTo(title); } } } assertThat(hasTitle).isTrue(); } Loading