Loading src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceController.java +17 −5 Original line number Diff line number Diff line Loading @@ -43,6 +43,20 @@ public class StorageSummaryDonutPreferenceController extends AbstractPreferenceC super(context); } /** * Converts a used storage amount to a formatted text. * * @param context Context * @param usedBytes used bytes of storage * @return a formatted text. */ public static CharSequence convertUsedBytesToFormattedText(Context context, long usedBytes) { final Formatter.BytesResult result = Formatter.formatBytes(context.getResources(), usedBytes, 0); return TextUtils.expandTemplate(context.getText(R.string.storage_size_large_alternate), result.value, result.units); } @Override public void displayPreference(PreferenceScreen screen) { mSummary = (StorageSummaryDonutPreference) screen.findPreference("pref_summary"); Loading @@ -53,11 +67,7 @@ public class StorageSummaryDonutPreferenceController extends AbstractPreferenceC public void updateState(Preference preference) { super.updateState(preference); StorageSummaryDonutPreference summary = (StorageSummaryDonutPreference) preference; final Formatter.BytesResult result = Formatter.formatBytes(mContext.getResources(), mUsedBytes, 0); summary.setTitle(TextUtils.expandTemplate( mContext.getText(R.string.storage_size_large_alternate), result.value, result.units)); summary.setTitle(convertUsedBytesToFormattedText(mContext, mUsedBytes)); summary.setSummary(mContext.getString(R.string.storage_volume_total, Formatter.formatShortFileSize(mContext, mTotalBytes))); summary.setPercent(mUsedBytes, mTotalBytes); Loading @@ -83,6 +93,7 @@ public class StorageSummaryDonutPreferenceController extends AbstractPreferenceC /** * Updates the state of the donut preference for the next update. * * @param used Total number of used bytes on the summarized volume. * @param total Total number of bytes on the summarized volume. */ Loading @@ -94,6 +105,7 @@ public class StorageSummaryDonutPreferenceController extends AbstractPreferenceC /** * Updates the state of the donut preference for the next update using volume to summarize. * * @param volume VolumeInfo to use to populate the informayion. */ public void updateSizes(StorageVolumeProvider svp, VolumeInfo volume) { Loading src/com/android/settings/homepage/CardContentLoader.java +10 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import androidx.annotation.VisibleForTesting; import com.android.settings.homepage.deviceinfo.DataUsageSlice; import com.android.settings.homepage.deviceinfo.DeviceInfoSlice; import com.android.settings.homepage.deviceinfo.StorageSlice; import com.android.settingslib.utils.AsyncLoaderCompat; import java.util.ArrayList; Loading Loading @@ -112,6 +113,15 @@ public class CardContentLoader extends AsyncLoaderCompat<List<ContextualCard>> { .setCardType(ContextualCard.CardType.SLICE) .setIsHalfWidth(true) .build()); add(new ContextualCard.Builder() .setSliceUri(StorageSlice.STORAGE_CARD_URI.toString()) .setName(StorageSlice.PATH_STORAGE_CARD) .setPackageName(packageName) .setRankingScore(rankingScore) .setAppVersion(appVersionCode) .setCardType(ContextualCard.CardType.SLICE) .setIsHalfWidth(true) .build()); }}; return result; } Loading src/com/android/settings/homepage/deviceinfo/StorageSlice.java 0 → 100644 +133 −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.os.storage.StorageManager; import android.text.format.Formatter; 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.deviceinfo.StorageDashboardFragment; import com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SettingsSliceProvider; import com.android.settings.slices.SliceBuilderUtils; import com.android.settingslib.deviceinfo.PrivateStorageInfo; import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider; public class StorageSlice implements CustomSliceable { private static final String TAG = "StorageSlice"; /** * The path denotes the unique name of storage slicel */ public static final String PATH_STORAGE_CARD = "storage_card"; /** * Backing Uri for the storage slice. */ public static final Uri STORAGE_CARD_URI = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(SettingsSliceProvider.SLICE_AUTHORITY) .appendPath(PATH_STORAGE_CARD) .build(); private final Context mContext; public StorageSlice(Context context) { mContext = context; } @Override public Uri getUri() { return STORAGE_CARD_URI; } /** * Return a storage slice bound to {@link #STORAGE_CARD_URI} */ @Override public Slice getSlice() { final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_homepage_storage); final String title = mContext.getString(R.string.storage_label); final SliceAction primaryAction = new SliceAction(getPrimaryAction(), icon, title); final PrivateStorageInfo info = getPrivateStorageInfo(); return new ListBuilder(mContext, STORAGE_CARD_URI, ListBuilder.INFINITY) .setAccentColor(Utils.getColorAccentDefaultColor(mContext)) .setHeader(new ListBuilder.HeaderBuilder().setTitle(title)) .addRow(new ListBuilder.RowBuilder() .setTitle(getStorageUsedText(info)) .setSubtitle(getStorageSummaryText(info)) .setPrimaryAction(primaryAction)) .build(); } @Override public Intent getIntent() { final String screenTitle = mContext.getText(R.string.storage_label).toString(); final Uri contentUri = new Uri.Builder().appendPath(PATH_STORAGE_CARD).build(); return SliceBuilderUtils.buildSearchResultPageIntent(mContext, StorageDashboardFragment.class.getName(), PATH_STORAGE_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 PrivateStorageInfo getPrivateStorageInfo() { final StorageManager storageManager = mContext.getSystemService(StorageManager.class); final StorageManagerVolumeProvider smvp = new StorageManagerVolumeProvider(storageManager); return PrivateStorageInfo.getPrivateStorageInfo(smvp); } @VisibleForTesting CharSequence getStorageUsedText(PrivateStorageInfo info) { final long usedBytes = info.totalBytes - info.freeBytes; return StorageSummaryDonutPreferenceController.convertUsedBytesToFormattedText(mContext, usedBytes); } @VisibleForTesting CharSequence getStorageSummaryText(PrivateStorageInfo info) { return mContext.getString(R.string.storage_volume_total, Formatter.formatShortFileSize(mContext, info.totalBytes)); } @Override public void onNotifyChange(Intent intent) { } } src/com/android/settings/slices/CustomSliceManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.util.ArrayMap; import com.android.settings.homepage.deviceinfo.DataUsageSlice; import com.android.settings.homepage.deviceinfo.DeviceInfoSlice; import com.android.settings.homepage.deviceinfo.StorageSlice; import com.android.settings.wifi.WifiSlice; import java.util.Map; Loading Loading @@ -91,5 +92,6 @@ public class CustomSliceManager { mUriMap.put(WifiSlice.WIFI_URI, WifiSlice.class); mUriMap.put(DataUsageSlice.DATA_USAGE_CARD_URI, DataUsageSlice.class); mUriMap.put(DeviceInfoSlice.DEVICE_INFO_CARD_URI, DeviceInfoSlice.class); mUriMap.put(StorageSlice.STORAGE_CARD_URI, StorageSlice.class); } } No newline at end of file tests/robotests/src/com/android/settings/homepage/deviceinfo/StorageSliceTest.java 0 → 100644 +83 −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.settingslib.deviceinfo.PrivateStorageInfo; 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 StorageSliceTest { private static final String USED_BYTES_TEXT = "test used bytes"; private static final String SUMMARY_TEXT = "test summary"; private Context mContext; private StorageSlice mStorageSlice; @Before public void setUp() { mContext = spy(RuntimeEnvironment.application); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); mStorageSlice = spy(new StorageSlice(mContext)); } @Test public void getSlice_shouldBeCorrectSliceContent() { final PrivateStorageInfo info = new PrivateStorageInfo(100L, 600L); doReturn(info).when(mStorageSlice).getPrivateStorageInfo(); doReturn(USED_BYTES_TEXT).when(mStorageSlice).getStorageUsedText(any()); doReturn(SUMMARY_TEXT).when(mStorageSlice).getStorageSummaryText(any()); final Slice slice = mStorageSlice.getSlice(); final SliceMetadata metadata = SliceMetadata.from(mContext, slice); final SliceAction primaryAction = metadata.getPrimaryAction(); final IconCompat expectedIcon = IconCompat.createWithResource(mContext, R.drawable.ic_homepage_storage); assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedIcon.toString()); final List<SliceItem> sliceItems = slice.getItems(); SliceTester.assertTitle(sliceItems, mContext.getString(R.string.storage_label)); } } Loading
src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceController.java +17 −5 Original line number Diff line number Diff line Loading @@ -43,6 +43,20 @@ public class StorageSummaryDonutPreferenceController extends AbstractPreferenceC super(context); } /** * Converts a used storage amount to a formatted text. * * @param context Context * @param usedBytes used bytes of storage * @return a formatted text. */ public static CharSequence convertUsedBytesToFormattedText(Context context, long usedBytes) { final Formatter.BytesResult result = Formatter.formatBytes(context.getResources(), usedBytes, 0); return TextUtils.expandTemplate(context.getText(R.string.storage_size_large_alternate), result.value, result.units); } @Override public void displayPreference(PreferenceScreen screen) { mSummary = (StorageSummaryDonutPreference) screen.findPreference("pref_summary"); Loading @@ -53,11 +67,7 @@ public class StorageSummaryDonutPreferenceController extends AbstractPreferenceC public void updateState(Preference preference) { super.updateState(preference); StorageSummaryDonutPreference summary = (StorageSummaryDonutPreference) preference; final Formatter.BytesResult result = Formatter.formatBytes(mContext.getResources(), mUsedBytes, 0); summary.setTitle(TextUtils.expandTemplate( mContext.getText(R.string.storage_size_large_alternate), result.value, result.units)); summary.setTitle(convertUsedBytesToFormattedText(mContext, mUsedBytes)); summary.setSummary(mContext.getString(R.string.storage_volume_total, Formatter.formatShortFileSize(mContext, mTotalBytes))); summary.setPercent(mUsedBytes, mTotalBytes); Loading @@ -83,6 +93,7 @@ public class StorageSummaryDonutPreferenceController extends AbstractPreferenceC /** * Updates the state of the donut preference for the next update. * * @param used Total number of used bytes on the summarized volume. * @param total Total number of bytes on the summarized volume. */ Loading @@ -94,6 +105,7 @@ public class StorageSummaryDonutPreferenceController extends AbstractPreferenceC /** * Updates the state of the donut preference for the next update using volume to summarize. * * @param volume VolumeInfo to use to populate the informayion. */ public void updateSizes(StorageVolumeProvider svp, VolumeInfo volume) { Loading
src/com/android/settings/homepage/CardContentLoader.java +10 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import androidx.annotation.VisibleForTesting; import com.android.settings.homepage.deviceinfo.DataUsageSlice; import com.android.settings.homepage.deviceinfo.DeviceInfoSlice; import com.android.settings.homepage.deviceinfo.StorageSlice; import com.android.settingslib.utils.AsyncLoaderCompat; import java.util.ArrayList; Loading Loading @@ -112,6 +113,15 @@ public class CardContentLoader extends AsyncLoaderCompat<List<ContextualCard>> { .setCardType(ContextualCard.CardType.SLICE) .setIsHalfWidth(true) .build()); add(new ContextualCard.Builder() .setSliceUri(StorageSlice.STORAGE_CARD_URI.toString()) .setName(StorageSlice.PATH_STORAGE_CARD) .setPackageName(packageName) .setRankingScore(rankingScore) .setAppVersion(appVersionCode) .setCardType(ContextualCard.CardType.SLICE) .setIsHalfWidth(true) .build()); }}; return result; } Loading
src/com/android/settings/homepage/deviceinfo/StorageSlice.java 0 → 100644 +133 −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.os.storage.StorageManager; import android.text.format.Formatter; 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.deviceinfo.StorageDashboardFragment; import com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SettingsSliceProvider; import com.android.settings.slices.SliceBuilderUtils; import com.android.settingslib.deviceinfo.PrivateStorageInfo; import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider; public class StorageSlice implements CustomSliceable { private static final String TAG = "StorageSlice"; /** * The path denotes the unique name of storage slicel */ public static final String PATH_STORAGE_CARD = "storage_card"; /** * Backing Uri for the storage slice. */ public static final Uri STORAGE_CARD_URI = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(SettingsSliceProvider.SLICE_AUTHORITY) .appendPath(PATH_STORAGE_CARD) .build(); private final Context mContext; public StorageSlice(Context context) { mContext = context; } @Override public Uri getUri() { return STORAGE_CARD_URI; } /** * Return a storage slice bound to {@link #STORAGE_CARD_URI} */ @Override public Slice getSlice() { final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_homepage_storage); final String title = mContext.getString(R.string.storage_label); final SliceAction primaryAction = new SliceAction(getPrimaryAction(), icon, title); final PrivateStorageInfo info = getPrivateStorageInfo(); return new ListBuilder(mContext, STORAGE_CARD_URI, ListBuilder.INFINITY) .setAccentColor(Utils.getColorAccentDefaultColor(mContext)) .setHeader(new ListBuilder.HeaderBuilder().setTitle(title)) .addRow(new ListBuilder.RowBuilder() .setTitle(getStorageUsedText(info)) .setSubtitle(getStorageSummaryText(info)) .setPrimaryAction(primaryAction)) .build(); } @Override public Intent getIntent() { final String screenTitle = mContext.getText(R.string.storage_label).toString(); final Uri contentUri = new Uri.Builder().appendPath(PATH_STORAGE_CARD).build(); return SliceBuilderUtils.buildSearchResultPageIntent(mContext, StorageDashboardFragment.class.getName(), PATH_STORAGE_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 PrivateStorageInfo getPrivateStorageInfo() { final StorageManager storageManager = mContext.getSystemService(StorageManager.class); final StorageManagerVolumeProvider smvp = new StorageManagerVolumeProvider(storageManager); return PrivateStorageInfo.getPrivateStorageInfo(smvp); } @VisibleForTesting CharSequence getStorageUsedText(PrivateStorageInfo info) { final long usedBytes = info.totalBytes - info.freeBytes; return StorageSummaryDonutPreferenceController.convertUsedBytesToFormattedText(mContext, usedBytes); } @VisibleForTesting CharSequence getStorageSummaryText(PrivateStorageInfo info) { return mContext.getString(R.string.storage_volume_total, Formatter.formatShortFileSize(mContext, info.totalBytes)); } @Override public void onNotifyChange(Intent intent) { } }
src/com/android/settings/slices/CustomSliceManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.util.ArrayMap; import com.android.settings.homepage.deviceinfo.DataUsageSlice; import com.android.settings.homepage.deviceinfo.DeviceInfoSlice; import com.android.settings.homepage.deviceinfo.StorageSlice; import com.android.settings.wifi.WifiSlice; import java.util.Map; Loading Loading @@ -91,5 +92,6 @@ public class CustomSliceManager { mUriMap.put(WifiSlice.WIFI_URI, WifiSlice.class); mUriMap.put(DataUsageSlice.DATA_USAGE_CARD_URI, DataUsageSlice.class); mUriMap.put(DeviceInfoSlice.DEVICE_INFO_CARD_URI, DeviceInfoSlice.class); mUriMap.put(StorageSlice.STORAGE_CARD_URI, StorageSlice.class); } } No newline at end of file
tests/robotests/src/com/android/settings/homepage/deviceinfo/StorageSliceTest.java 0 → 100644 +83 −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.settingslib.deviceinfo.PrivateStorageInfo; 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 StorageSliceTest { private static final String USED_BYTES_TEXT = "test used bytes"; private static final String SUMMARY_TEXT = "test summary"; private Context mContext; private StorageSlice mStorageSlice; @Before public void setUp() { mContext = spy(RuntimeEnvironment.application); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); mStorageSlice = spy(new StorageSlice(mContext)); } @Test public void getSlice_shouldBeCorrectSliceContent() { final PrivateStorageInfo info = new PrivateStorageInfo(100L, 600L); doReturn(info).when(mStorageSlice).getPrivateStorageInfo(); doReturn(USED_BYTES_TEXT).when(mStorageSlice).getStorageUsedText(any()); doReturn(SUMMARY_TEXT).when(mStorageSlice).getStorageSummaryText(any()); final Slice slice = mStorageSlice.getSlice(); final SliceMetadata metadata = SliceMetadata.from(mContext, slice); final SliceAction primaryAction = metadata.getPrimaryAction(); final IconCompat expectedIcon = IconCompat.createWithResource(mContext, R.drawable.ic_homepage_storage); assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedIcon.toString()); final List<SliceItem> sliceItems = slice.getItems(); SliceTester.assertTitle(sliceItems, mContext.getString(R.string.storage_label)); } }