Cache storage values for fast loading.
If the user moves away from the storage fragment and returns, this
allows us to use cached data from the previous calculation. If the data
is > 1 minute old, we consider it stale. Otherwise, we can bypass the
loading screen.
Fixes: 37923463
Test: Settings Robotest
Change-Id: I7650d4d742852f8d447878c077b9190bc0a0bb22
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index 33d7d36..dd0db9a 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -40,6 +40,7 @@
import com.android.settings.applications.UserManagerWrapperImpl;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController;
+import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
import com.android.settings.deviceinfo.storage.SecondaryUserController;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
@@ -68,6 +69,7 @@
private VolumeInfo mVolume;
private PrivateStorageInfo mStorageInfo;
private SparseArray<StorageAsyncLoader.AppsStorageResult> mAppsResult;
+ private CachedStorageValuesHelper mCachedStorageValuesHelper;
private StorageSummaryDonutPreferenceController mSummaryController;
private StorageItemPreferenceController mPreferenceController;
@@ -102,7 +104,10 @@
@Override
public void onViewCreated(View v, Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
- setLoading(true, false);
+ initializeCacheProvider();
+ if (mAppsResult == null || mStorageInfo == null) {
+ setLoading(true, false);
+ }
}
@Override
@@ -249,6 +254,7 @@
public void onLoadFinished(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader,
SparseArray<StorageAsyncLoader.AppsStorageResult> data) {
mAppsResult = data;
+ maybeCacheFreshValues();
onReceivedSizes();
}
@@ -256,6 +262,48 @@
public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader) {
}
+ @VisibleForTesting
+ public void setCachedStorageValuesHelper(CachedStorageValuesHelper helper) {
+ mCachedStorageValuesHelper = helper;
+ }
+
+ @VisibleForTesting
+ public PrivateStorageInfo getPrivateStorageInfo() {
+ return mStorageInfo;
+ }
+
+ @VisibleForTesting
+ public SparseArray<StorageAsyncLoader.AppsStorageResult> getAppsStorageResult() {
+ return mAppsResult;
+ }
+
+ @VisibleForTesting
+ public void initializeCachedValues() {
+ PrivateStorageInfo info = mCachedStorageValuesHelper.getCachedPrivateStorageInfo();
+ SparseArray<StorageAsyncLoader.AppsStorageResult> loaderResult =
+ mCachedStorageValuesHelper.getCachedAppsStorageResult();
+ if (info == null || loaderResult == null) {
+ return;
+ }
+
+ mStorageInfo = info;
+ mAppsResult = loaderResult;
+ }
+
+ private void initializeCacheProvider() {
+ mCachedStorageValuesHelper =
+ new CachedStorageValuesHelper(getContext(), UserHandle.myUserId());
+ initializeCachedValues();
+ onReceivedSizes();
+ }
+
+ private void maybeCacheFreshValues() {
+ if (mStorageInfo != null && mAppsResult != null) {
+ mCachedStorageValuesHelper.cacheResult(
+ mStorageInfo, mAppsResult.get(UserHandle.myUserId()));
+ }
+ }
+
/**
* IconLoaderCallbacks exists because StorageDashboardFragment already implements
* LoaderCallbacks for a different type.
@@ -308,6 +356,7 @@
}
mStorageInfo = privateStorageInfo;
+ maybeCacheFreshValues();
onReceivedSizes();
}
}
diff --git a/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelper.java b/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelper.java
new file mode 100644
index 0000000..8225db3
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelper.java
@@ -0,0 +1,172 @@
+/*
+ * 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.settings.deviceinfo.storage;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.util.SparseArray;
+
+import com.android.settingslib.applications.StorageStatsSource;
+import com.android.settingslib.deviceinfo.PrivateStorageInfo;
+
+import java.util.concurrent.TimeUnit;
+
+public class CachedStorageValuesHelper {
+
+ @VisibleForTesting public static final String SHARED_PREFERENCES_NAME = "CachedStorageValues";
+ public static final String TIMESTAMP_KEY = "last_query_timestamp";
+ public static final String FREE_BYTES_KEY = "free_bytes";
+ public static final String TOTAL_BYTES_KEY = "total_bytes";
+ public static final String GAME_APPS_SIZE_KEY = "game_apps_size";
+ public static final String MUSIC_APPS_SIZE_KEY = "music_apps_size";
+ public static final String VIDEO_APPS_SIZE_KEY = "video_apps_size";
+ public static final String PHOTO_APPS_SIZE_KEY = "photo_apps_size";
+ public static final String OTHER_APPS_SIZE_KEY = "other_apps_size";
+ public static final String CACHE_APPS_SIZE_KEY = "cache_apps_size";
+ public static final String EXTERNAL_TOTAL_BYTES = "external_total_bytes";
+ public static final String EXTERNAL_AUDIO_BYTES = "external_audio_bytes";
+ public static final String EXTERNAL_VIDEO_BYTES = "external_video_bytes";
+ public static final String EXTERNAL_IMAGE_BYTES = "external_image_bytes";
+ public static final String EXTERNAL_APP_BYTES = "external_apps_bytes";
+ public static final String USER_ID_KEY = "user_id";
+ private final Long mClobberThreshold;
+ private final SharedPreferences mSharedPreferences;
+ private final int mUserId;
+ // This clock is used to provide the time. By default, it uses the system clock, but can be
+ // replaced for test purposes.
+ protected Clock mClock;
+
+ public CachedStorageValuesHelper(Context context, int userId) {
+ mSharedPreferences =
+ context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
+ mClock = new Clock();
+ mUserId = userId;
+ mClobberThreshold =
+ Settings.Global.getLong(
+ context.getContentResolver(),
+ Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD,
+ TimeUnit.MINUTES.toMillis(5));
+ }
+
+ public PrivateStorageInfo getCachedPrivateStorageInfo() {
+ if (!isDataValid()) {
+ return null;
+ }
+ final long freeBytes = mSharedPreferences.getLong(FREE_BYTES_KEY, -1);
+ final long totalBytes = mSharedPreferences.getLong(TOTAL_BYTES_KEY, -1);
+ if (freeBytes < 0 || totalBytes < 0) {
+ return null;
+ }
+
+ return new PrivateStorageInfo(freeBytes, totalBytes);
+ }
+
+ public SparseArray<StorageAsyncLoader.AppsStorageResult> getCachedAppsStorageResult() {
+ if (!isDataValid()) {
+ return null;
+ }
+ final long gamesSize = mSharedPreferences.getLong(GAME_APPS_SIZE_KEY, -1);
+ final long musicAppsSize = mSharedPreferences.getLong(MUSIC_APPS_SIZE_KEY, -1);
+ final long videoAppsSize = mSharedPreferences.getLong(VIDEO_APPS_SIZE_KEY, -1);
+ final long photoAppSize = mSharedPreferences.getLong(PHOTO_APPS_SIZE_KEY, -1);
+ final long otherAppsSize = mSharedPreferences.getLong(OTHER_APPS_SIZE_KEY, -1);
+ final long cacheSize = mSharedPreferences.getLong(CACHE_APPS_SIZE_KEY, -1);
+ if (gamesSize < 0
+ || musicAppsSize < 0
+ || videoAppsSize < 0
+ || photoAppSize < 0
+ || otherAppsSize < 0
+ || cacheSize < 0) {
+ return null;
+ }
+
+ final long externalTotalBytes = mSharedPreferences.getLong(EXTERNAL_TOTAL_BYTES, -1);
+ final long externalAudioBytes = mSharedPreferences.getLong(EXTERNAL_AUDIO_BYTES, -1);
+ final long externalVideoBytes = mSharedPreferences.getLong(EXTERNAL_VIDEO_BYTES, -1);
+ final long externalImageBytes = mSharedPreferences.getLong(EXTERNAL_IMAGE_BYTES, -1);
+ final long externalAppBytes = mSharedPreferences.getLong(EXTERNAL_APP_BYTES, -1);
+ if (externalTotalBytes < 0
+ || externalAudioBytes < 0
+ || externalVideoBytes < 0
+ || externalImageBytes < 0
+ || externalAppBytes < 0) {
+ return null;
+ }
+
+ final StorageStatsSource.ExternalStorageStats externalStats =
+ new StorageStatsSource.ExternalStorageStats(
+ externalTotalBytes,
+ externalAudioBytes,
+ externalVideoBytes,
+ externalImageBytes,
+ externalAppBytes);
+ final StorageAsyncLoader.AppsStorageResult result =
+ new StorageAsyncLoader.AppsStorageResult();
+ result.gamesSize = gamesSize;
+ result.musicAppsSize = musicAppsSize;
+ result.videoAppsSize = videoAppsSize;
+ result.photosAppsSize = photoAppSize;
+ result.otherAppsSize = otherAppsSize;
+ result.cacheSize = cacheSize;
+ result.externalStats = externalStats;
+ final SparseArray<StorageAsyncLoader.AppsStorageResult> resultArray = new SparseArray<>();
+ resultArray.append(mUserId, result);
+ return resultArray;
+ }
+
+ public void cacheResult(
+ PrivateStorageInfo storageInfo, StorageAsyncLoader.AppsStorageResult result) {
+ mSharedPreferences
+ .edit()
+ .putLong(FREE_BYTES_KEY, storageInfo.freeBytes)
+ .putLong(TOTAL_BYTES_KEY, storageInfo.totalBytes)
+ .putLong(GAME_APPS_SIZE_KEY, result.gamesSize)
+ .putLong(MUSIC_APPS_SIZE_KEY, result.musicAppsSize)
+ .putLong(VIDEO_APPS_SIZE_KEY, result.videoAppsSize)
+ .putLong(PHOTO_APPS_SIZE_KEY, result.photosAppsSize)
+ .putLong(OTHER_APPS_SIZE_KEY, result.otherAppsSize)
+ .putLong(CACHE_APPS_SIZE_KEY, result.cacheSize)
+ .putLong(EXTERNAL_TOTAL_BYTES, result.externalStats.totalBytes)
+ .putLong(EXTERNAL_AUDIO_BYTES, result.externalStats.audioBytes)
+ .putLong(EXTERNAL_VIDEO_BYTES, result.externalStats.videoBytes)
+ .putLong(EXTERNAL_IMAGE_BYTES, result.externalStats.imageBytes)
+ .putLong(EXTERNAL_APP_BYTES, result.externalStats.appBytes)
+ .putInt(USER_ID_KEY, mUserId)
+ .putLong(TIMESTAMP_KEY, mClock.getCurrentTime())
+ .apply();
+ }
+
+ private boolean isDataValid() {
+ final int cachedUserId = mSharedPreferences.getInt(USER_ID_KEY, -1);
+ if (cachedUserId != mUserId) {
+ return false;
+ }
+
+ final long lastQueryTime = mSharedPreferences.getLong(TIMESTAMP_KEY, Long.MAX_VALUE);
+ final long currentTime = mClock.getCurrentTime();
+ return currentTime - lastQueryTime < mClobberThreshold;
+ }
+
+ /** Clock provides the current time. */
+ static class Clock {
+ public long getCurrentTime() {
+ return System.currentTimeMillis();
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java
index b2d259a..a87f563 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java
@@ -20,13 +20,18 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.Activity;
import android.os.storage.StorageManager;
import android.provider.SearchIndexableResource;
+import android.util.SparseArray;
+import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
+import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settingslib.deviceinfo.PrivateStorageInfo;
import com.android.settingslib.drawer.CategoryKey;
import org.junit.Before;
@@ -69,6 +74,47 @@
}
@Test
+ public void test_cacheProviderProvidesValuesIfBothCached() {
+ CachedStorageValuesHelper helper = mock(CachedStorageValuesHelper.class);
+ PrivateStorageInfo info = new PrivateStorageInfo(0, 0);
+ when(helper.getCachedPrivateStorageInfo()).thenReturn(info);
+ SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
+ when(helper.getCachedAppsStorageResult()).thenReturn(result);
+
+ mFragment.setCachedStorageValuesHelper(helper);
+ mFragment.initializeCachedValues();
+
+ assertThat(mFragment.getPrivateStorageInfo()).isEqualTo(info);
+ assertThat(mFragment.getAppsStorageResult()).isEqualTo(result);
+ }
+
+ @Test
+ public void test_cacheProviderDoesntProvideValuesIfAppsMissing() {
+ CachedStorageValuesHelper helper = mock(CachedStorageValuesHelper.class);
+ PrivateStorageInfo info = new PrivateStorageInfo(0, 0);
+ when(helper.getCachedPrivateStorageInfo()).thenReturn(info);
+
+ mFragment.setCachedStorageValuesHelper(helper);
+ mFragment.initializeCachedValues();
+
+ assertThat(mFragment.getPrivateStorageInfo()).isNull();
+ assertThat(mFragment.getAppsStorageResult()).isNull();
+ }
+
+ @Test
+ public void test_cacheProviderDoesntProvideValuesIfVolumeInfoMissing() {
+ CachedStorageValuesHelper helper = mock(CachedStorageValuesHelper.class);
+ SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
+ when(helper.getCachedAppsStorageResult()).thenReturn(result);
+
+ mFragment.setCachedStorageValuesHelper(helper);
+ mFragment.initializeCachedValues();
+
+ assertThat(mFragment.getPrivateStorageInfo()).isNull();
+ assertThat(mFragment.getAppsStorageResult()).isNull();
+ }
+
+ @Test
public void testSearchIndexProvider_shouldIndexResource() {
final List<SearchIndexableResource> indexRes =
StorageDashboardFragment.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelperTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelperTest.java
new file mode 100644
index 0000000..154a7a1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelperTest.java
@@ -0,0 +1,295 @@
+/*
+ * 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.settings.deviceinfo.storage;
+
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.CACHE_APPS_SIZE_KEY;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_APP_BYTES;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_AUDIO_BYTES;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_IMAGE_BYTES;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_TOTAL_BYTES;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_VIDEO_BYTES;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.FREE_BYTES_KEY;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.GAME_APPS_SIZE_KEY;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.MUSIC_APPS_SIZE_KEY;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.OTHER_APPS_SIZE_KEY;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.PHOTO_APPS_SIZE_KEY;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.SHARED_PREFERENCES_NAME;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.TIMESTAMP_KEY;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.TOTAL_BYTES_KEY;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.USER_ID_KEY;
+import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.VIDEO_APPS_SIZE_KEY;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.SparseArray;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.applications.StorageStatsSource;
+import com.android.settingslib.deviceinfo.PrivateStorageInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class CachedStorageValuesHelperTest {
+ private Context mContext;
+
+ @Mock private CachedStorageValuesHelper.Clock mMockClock;
+ private CachedStorageValuesHelper mCachedValuesHelper;
+ private SharedPreferences mSharedPreferences;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application.getApplicationContext();
+ mSharedPreferences = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, 0);
+ mCachedValuesHelper = new CachedStorageValuesHelper(mContext, 0);
+ mCachedValuesHelper.mClock = mMockClock;
+ }
+
+ @Test
+ public void getCachedPrivateStorageInfo_cachedValuesAreLoaded() throws Exception {
+ when(mMockClock.getCurrentTime()).thenReturn(10001L);
+ mSharedPreferences
+ .edit()
+ .putLong(GAME_APPS_SIZE_KEY, 0)
+ .putLong(MUSIC_APPS_SIZE_KEY, 10)
+ .putLong(VIDEO_APPS_SIZE_KEY, 100)
+ .putLong(PHOTO_APPS_SIZE_KEY, 1000)
+ .putLong(OTHER_APPS_SIZE_KEY, 10000)
+ .putLong(CACHE_APPS_SIZE_KEY, 100000)
+ .putLong(EXTERNAL_TOTAL_BYTES, 2)
+ .putLong(EXTERNAL_AUDIO_BYTES, 22)
+ .putLong(EXTERNAL_VIDEO_BYTES, 222)
+ .putLong(EXTERNAL_IMAGE_BYTES, 2222)
+ .putLong(EXTERNAL_APP_BYTES, 22222)
+ .putLong(FREE_BYTES_KEY, 1000L)
+ .putLong(TOTAL_BYTES_KEY, 6000L)
+ .putInt(USER_ID_KEY, 0)
+ .putLong(TIMESTAMP_KEY, 10000L)
+ .apply();
+
+ PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo();
+
+ assertThat(info.freeBytes).isEqualTo(1000L);
+ assertThat(info.totalBytes).isEqualTo(6000L);
+ }
+
+ @Test
+ public void getCachedAppsStorageResult_cachedValuesAreLoaded() throws Exception {
+ when(mMockClock.getCurrentTime()).thenReturn(10001L);
+ mSharedPreferences
+ .edit()
+ .putLong(GAME_APPS_SIZE_KEY, 1)
+ .putLong(MUSIC_APPS_SIZE_KEY, 10)
+ .putLong(VIDEO_APPS_SIZE_KEY, 100)
+ .putLong(PHOTO_APPS_SIZE_KEY, 1000)
+ .putLong(OTHER_APPS_SIZE_KEY, 10000)
+ .putLong(CACHE_APPS_SIZE_KEY, 100000)
+ .putLong(EXTERNAL_TOTAL_BYTES, 222222)
+ .putLong(EXTERNAL_AUDIO_BYTES, 22)
+ .putLong(EXTERNAL_VIDEO_BYTES, 222)
+ .putLong(EXTERNAL_IMAGE_BYTES, 2222)
+ .putLong(EXTERNAL_APP_BYTES, 22222)
+ .putLong(FREE_BYTES_KEY, 1000L)
+ .putLong(TOTAL_BYTES_KEY, 5000L)
+ .putInt(USER_ID_KEY, 0)
+ .putLong(TIMESTAMP_KEY, 10000L)
+ .apply();
+
+ SparseArray<StorageAsyncLoader.AppsStorageResult> result =
+ mCachedValuesHelper.getCachedAppsStorageResult();
+
+ StorageAsyncLoader.AppsStorageResult primaryResult = result.get(0);
+ assertThat(primaryResult.gamesSize).isEqualTo(1L);
+ assertThat(primaryResult.musicAppsSize).isEqualTo(10L);
+ assertThat(primaryResult.videoAppsSize).isEqualTo(100L);
+ assertThat(primaryResult.photosAppsSize).isEqualTo(1000L);
+ assertThat(primaryResult.otherAppsSize).isEqualTo(10000L);
+ assertThat(primaryResult.cacheSize).isEqualTo(100000L);
+ assertThat(primaryResult.externalStats.totalBytes).isEqualTo(222222L);
+ assertThat(primaryResult.externalStats.audioBytes).isEqualTo(22L);
+ assertThat(primaryResult.externalStats.videoBytes).isEqualTo(222L);
+ assertThat(primaryResult.externalStats.imageBytes).isEqualTo(2222L);
+ assertThat(primaryResult.externalStats.appBytes).isEqualTo(22222L);
+ }
+
+ @Test
+ public void getCachedPrivateStorageInfo_nullIfDataIsStale() throws Exception {
+ when(mMockClock.getCurrentTime()).thenReturn(10000000L);
+ mSharedPreferences
+ .edit()
+ .putLong(GAME_APPS_SIZE_KEY, 0)
+ .putLong(MUSIC_APPS_SIZE_KEY, 10)
+ .putLong(VIDEO_APPS_SIZE_KEY, 100)
+ .putLong(PHOTO_APPS_SIZE_KEY, 1000)
+ .putLong(OTHER_APPS_SIZE_KEY, 10000)
+ .putLong(CACHE_APPS_SIZE_KEY, 100000)
+ .putLong(EXTERNAL_TOTAL_BYTES, 2)
+ .putLong(EXTERNAL_AUDIO_BYTES, 22)
+ .putLong(EXTERNAL_VIDEO_BYTES, 222)
+ .putLong(EXTERNAL_IMAGE_BYTES, 2222)
+ .putLong(EXTERNAL_APP_BYTES, 22222)
+ .putLong(FREE_BYTES_KEY, 1000L)
+ .putLong(TOTAL_BYTES_KEY, 5000L)
+ .putInt(USER_ID_KEY, 0)
+ .putLong(TIMESTAMP_KEY, 10000L)
+ .apply();
+
+ PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo();
+ assertThat(info).isNull();
+ }
+
+ @Test
+ public void getCachedAppsStorageResult_nullIfDataIsStale() throws Exception {
+ when(mMockClock.getCurrentTime()).thenReturn(10000000L);
+ mSharedPreferences
+ .edit()
+ .putLong(GAME_APPS_SIZE_KEY, 0)
+ .putLong(MUSIC_APPS_SIZE_KEY, 10)
+ .putLong(VIDEO_APPS_SIZE_KEY, 100)
+ .putLong(PHOTO_APPS_SIZE_KEY, 1000)
+ .putLong(OTHER_APPS_SIZE_KEY, 10000)
+ .putLong(CACHE_APPS_SIZE_KEY, 100000)
+ .putLong(EXTERNAL_TOTAL_BYTES, 2)
+ .putLong(EXTERNAL_AUDIO_BYTES, 22)
+ .putLong(EXTERNAL_VIDEO_BYTES, 222)
+ .putLong(EXTERNAL_IMAGE_BYTES, 2222)
+ .putLong(EXTERNAL_APP_BYTES, 22222)
+ .putLong(FREE_BYTES_KEY, 1000L)
+ .putLong(TOTAL_BYTES_KEY, 5000L)
+ .putInt(USER_ID_KEY, 0)
+ .putLong(TIMESTAMP_KEY, 10000L)
+ .apply();
+
+ SparseArray<StorageAsyncLoader.AppsStorageResult> result =
+ mCachedValuesHelper.getCachedAppsStorageResult();
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void getCachedPrivateStorageInfo_nullIfWrongUser() throws Exception {
+ when(mMockClock.getCurrentTime()).thenReturn(10001L);
+ mSharedPreferences
+ .edit()
+ .putLong(GAME_APPS_SIZE_KEY, 0)
+ .putLong(MUSIC_APPS_SIZE_KEY, 10)
+ .putLong(VIDEO_APPS_SIZE_KEY, 100)
+ .putLong(PHOTO_APPS_SIZE_KEY, 1000)
+ .putLong(OTHER_APPS_SIZE_KEY, 10000)
+ .putLong(CACHE_APPS_SIZE_KEY, 100000)
+ .putLong(EXTERNAL_TOTAL_BYTES, 2)
+ .putLong(EXTERNAL_AUDIO_BYTES, 22)
+ .putLong(EXTERNAL_VIDEO_BYTES, 222)
+ .putLong(EXTERNAL_IMAGE_BYTES, 2222)
+ .putLong(EXTERNAL_APP_BYTES, 22222)
+ .putLong(FREE_BYTES_KEY, 1000L)
+ .putLong(TOTAL_BYTES_KEY, 5000L)
+ .putInt(USER_ID_KEY, 1)
+ .putLong(TIMESTAMP_KEY, 10000L)
+ .apply();
+
+ PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo();
+ assertThat(info).isNull();
+ }
+
+ @Test
+ public void getCachedAppsStorageResult_nullIfWrongUser() throws Exception {
+ when(mMockClock.getCurrentTime()).thenReturn(10001L);
+ mSharedPreferences
+ .edit()
+ .putLong(GAME_APPS_SIZE_KEY, 0)
+ .putLong(MUSIC_APPS_SIZE_KEY, 10)
+ .putLong(VIDEO_APPS_SIZE_KEY, 100)
+ .putLong(PHOTO_APPS_SIZE_KEY, 1000)
+ .putLong(OTHER_APPS_SIZE_KEY, 10000)
+ .putLong(CACHE_APPS_SIZE_KEY, 100000)
+ .putLong(EXTERNAL_TOTAL_BYTES, 2)
+ .putLong(EXTERNAL_AUDIO_BYTES, 22)
+ .putLong(EXTERNAL_VIDEO_BYTES, 222)
+ .putLong(EXTERNAL_IMAGE_BYTES, 2222)
+ .putLong(EXTERNAL_APP_BYTES, 22222)
+ .putLong(FREE_BYTES_KEY, 1000L)
+ .putLong(TOTAL_BYTES_KEY, 5000L)
+ .putInt(USER_ID_KEY, 1)
+ .putLong(TIMESTAMP_KEY, 10000L)
+ .apply();
+
+ SparseArray<StorageAsyncLoader.AppsStorageResult> result =
+ mCachedValuesHelper.getCachedAppsStorageResult();
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void getCachedPrivateStorageInfo_nullIfEmpty() throws Exception {
+ PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo();
+ assertThat(info).isNull();
+ }
+
+ @Test
+ public void getCachedAppsStorageResult_nullIfEmpty() throws Exception {
+ SparseArray<StorageAsyncLoader.AppsStorageResult> result =
+ mCachedValuesHelper.getCachedAppsStorageResult();
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void cacheResult_succeeds() throws Exception {
+ when(mMockClock.getCurrentTime()).thenReturn(10000L);
+ final StorageStatsSource.ExternalStorageStats externalStats =
+ new StorageStatsSource.ExternalStorageStats(22222l, 2l, 20L, 200L, 2000L);
+ final StorageAsyncLoader.AppsStorageResult result =
+ new StorageAsyncLoader.AppsStorageResult();
+ result.gamesSize = 1L;
+ result.musicAppsSize = 10l;
+ result.videoAppsSize = 100L;
+ result.photosAppsSize = 1000L;
+ result.otherAppsSize = 10000L;
+ result.cacheSize = 100000l;
+ result.externalStats = externalStats;
+ final PrivateStorageInfo info = new PrivateStorageInfo(1000L, 6000L);
+
+ mCachedValuesHelper.cacheResult(info, result);
+
+ assertThat(mSharedPreferences.getLong(GAME_APPS_SIZE_KEY, -1)).isEqualTo(1L);
+ assertThat(mSharedPreferences.getLong(MUSIC_APPS_SIZE_KEY, -1)).isEqualTo(10L);
+ assertThat(mSharedPreferences.getLong(VIDEO_APPS_SIZE_KEY, -1)).isEqualTo(100L);
+ assertThat(mSharedPreferences.getLong(PHOTO_APPS_SIZE_KEY, -1)).isEqualTo(1000L);
+ assertThat(mSharedPreferences.getLong(OTHER_APPS_SIZE_KEY, -1)).isEqualTo(10000L);
+ assertThat(mSharedPreferences.getLong(CACHE_APPS_SIZE_KEY, -1)).isEqualTo(100000L);
+ assertThat(mSharedPreferences.getLong(EXTERNAL_TOTAL_BYTES, -1)).isEqualTo(22222L);
+ assertThat(mSharedPreferences.getLong(EXTERNAL_AUDIO_BYTES, -1)).isEqualTo(2L);
+ assertThat(mSharedPreferences.getLong(EXTERNAL_VIDEO_BYTES, -1)).isEqualTo(20L);
+ assertThat(mSharedPreferences.getLong(EXTERNAL_IMAGE_BYTES, -1)).isEqualTo(200L);
+ assertThat(mSharedPreferences.getLong(EXTERNAL_APP_BYTES, -1)).isEqualTo(2000L);
+ assertThat(mSharedPreferences.getLong(FREE_BYTES_KEY, -1)).isEqualTo(1000L);
+ assertThat(mSharedPreferences.getLong(TOTAL_BYTES_KEY, -1)).isEqualTo(6000L);
+ assertThat(mSharedPreferences.getInt(USER_ID_KEY, -1)).isEqualTo(0);
+ assertThat(mSharedPreferences.getLong(TIMESTAMP_KEY, -1)).isEqualTo(10000L);
+ };
+}