Merge "Fix a crash when transferring an app."
This commit is contained in:
@@ -50,24 +50,12 @@
|
||||
android:selectable="false"
|
||||
android:layout="@layout/horizontal_preference" />
|
||||
|
||||
<Preference
|
||||
android:key="external_code_size"
|
||||
android:title="@string/external_code_size_label"
|
||||
android:selectable="false"
|
||||
android:layout="@layout/horizontal_preference" />
|
||||
|
||||
<Preference
|
||||
android:key="data_size"
|
||||
android:title="@string/data_size_label"
|
||||
android:selectable="false"
|
||||
android:layout="@layout/horizontal_preference" />
|
||||
|
||||
<Preference
|
||||
android:key="external_data_size"
|
||||
android:title="@string/external_data_size_label"
|
||||
android:selectable="false"
|
||||
android:layout="@layout/horizontal_preference" />
|
||||
|
||||
<com.android.settings.applications.LayoutPreference
|
||||
android:key="clear_data_button"
|
||||
android:selectable="false"
|
||||
|
@@ -105,12 +105,6 @@ public class AppStorageSettings extends AppInfoWithHeader
|
||||
private static final String KEY_URI_CATEGORY = "uri_category";
|
||||
private static final String KEY_CLEAR_URI = "clear_uri_button";
|
||||
|
||||
private Preference mTotalSize;
|
||||
private Preference mAppSize;
|
||||
private Preference mDataSize;
|
||||
private Preference mExternalCodeSize;
|
||||
private Preference mExternalDataSize;
|
||||
|
||||
// Views related to cache info
|
||||
private Preference mCacheSize;
|
||||
private Button mClearDataButton;
|
||||
@@ -125,15 +119,9 @@ public class AppStorageSettings extends AppInfoWithHeader
|
||||
private PreferenceCategory mUri;
|
||||
|
||||
private boolean mCanClearData = true;
|
||||
private boolean mHaveSizes = false;
|
||||
|
||||
private AppStorageStats mLastResult;
|
||||
private long mLastCodeSize = -1;
|
||||
private long mLastDataSize = -1;
|
||||
private long mLastExternalCodeSize = -1;
|
||||
private long mLastExternalDataSize = -1;
|
||||
private long mLastCacheSize = -1;
|
||||
private long mLastTotalSize = -1;
|
||||
private AppStorageSizesController mSizeController;
|
||||
|
||||
private ClearCacheObserver mClearCacheObserver;
|
||||
private ClearUserDataObserver mClearDataObserver;
|
||||
@@ -166,17 +154,15 @@ public class AppStorageSettings extends AppInfoWithHeader
|
||||
mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
|
||||
|
||||
// Set default values on sizes
|
||||
mTotalSize = findPreference(KEY_TOTAL_SIZE);
|
||||
mAppSize = findPreference(KEY_APP_SIZE);
|
||||
mDataSize = findPreference(KEY_DATA_SIZE);
|
||||
mExternalCodeSize = findPreference(KEY_EXTERNAL_CODE_SIZE);
|
||||
mExternalDataSize = findPreference(KEY_EXTERNAL_DATA_SIZE);
|
||||
mSizeController = new AppStorageSizesController.Builder()
|
||||
.setTotalSizePreference(findPreference(KEY_TOTAL_SIZE))
|
||||
.setAppSizePreference(findPreference(KEY_APP_SIZE))
|
||||
.setDataSizePreference(findPreference(KEY_DATA_SIZE))
|
||||
.setCacheSizePreference(findPreference(KEY_CACHE_SIZE))
|
||||
.setComputingString(R.string.computing_size)
|
||||
.setErrorString(R.string.invalid_size_value)
|
||||
.build();
|
||||
|
||||
if (Environment.isExternalStorageEmulated()) {
|
||||
PreferenceCategory category = (PreferenceCategory) findPreference(KEY_STORAGE_CATEGORY);
|
||||
category.removePreference(mExternalCodeSize);
|
||||
category.removePreference(mExternalDataSize);
|
||||
}
|
||||
mClearDataButton = (Button) ((LayoutPreference) findPreference(KEY_CLEAR_DATA))
|
||||
.findViewById(R.id.button);
|
||||
|
||||
@@ -265,13 +251,6 @@ public class AppStorageSettings extends AppInfoWithHeader
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
private String getSizeStr(long size) {
|
||||
if (size == SIZE_INVALID) {
|
||||
return mInvalidSizeStr.toString();
|
||||
}
|
||||
return Formatter.formatFileSize(getActivity(), size);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean refreshUi() {
|
||||
retrieveAppEntry();
|
||||
@@ -521,7 +500,7 @@ public class AppStorageSettings extends AppInfoWithHeader
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<AppStorageStats> loader, AppStorageStats result) {
|
||||
mLastResult = result;
|
||||
mSizeController.setResult(result);
|
||||
updateUiWithSize(result);
|
||||
}
|
||||
|
||||
@@ -545,39 +524,15 @@ public class AppStorageSettings extends AppInfoWithHeader
|
||||
}
|
||||
|
||||
private void updateUiWithSize(AppStorageStats result) {
|
||||
mSizeController.updateUi(getContext());
|
||||
|
||||
if (result == null) {
|
||||
mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1;
|
||||
if (!mHaveSizes) {
|
||||
mAppSize.setSummary(mComputingStr);
|
||||
mDataSize.setSummary(mComputingStr);
|
||||
mCacheSize.setSummary(mComputingStr);
|
||||
mTotalSize.setSummary(mComputingStr);
|
||||
}
|
||||
mClearDataButton.setEnabled(false);
|
||||
mClearCacheButton.setEnabled(false);
|
||||
} else {
|
||||
mHaveSizes = true;
|
||||
long codeSize = result.getCodeBytes();
|
||||
long dataSize = result.getDataBytes();
|
||||
if (mLastCodeSize != codeSize) {
|
||||
mLastCodeSize = codeSize;
|
||||
mAppSize.setSummary(getSizeStr(codeSize));
|
||||
}
|
||||
if (mLastDataSize != dataSize) {
|
||||
mLastDataSize = dataSize;
|
||||
mDataSize.setSummary(getSizeStr(dataSize));
|
||||
}
|
||||
long cacheSize = result.getCacheBytes();
|
||||
if (mLastCacheSize != cacheSize) {
|
||||
mLastCacheSize = cacheSize;
|
||||
mCacheSize.setSummary(getSizeStr(cacheSize));
|
||||
}
|
||||
|
||||
long totalSize = codeSize + dataSize + cacheSize;
|
||||
if (mLastTotalSize != totalSize) {
|
||||
mLastTotalSize = totalSize;
|
||||
mTotalSize.setSummary(getSizeStr(totalSize));
|
||||
}
|
||||
|
||||
if (dataSize <= 0 || !mCanClearData) {
|
||||
mClearDataButton.setEnabled(false);
|
||||
|
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.applications;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.text.format.Formatter;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.settingslib.applications.StorageStatsSource;
|
||||
|
||||
/**
|
||||
* Handles setting the sizes for the app info screen.
|
||||
*/
|
||||
public class AppStorageSizesController {
|
||||
private final Preference mTotalSize;
|
||||
private final Preference mAppSize;
|
||||
private final Preference mDataSize;
|
||||
private final Preference mCacheSize;
|
||||
private final @StringRes int mComputing;
|
||||
private final @StringRes int mError;
|
||||
|
||||
@Nullable
|
||||
private StorageStatsSource.AppStorageStats mLastResult;
|
||||
private boolean mLastResultFailed;
|
||||
private long mLastCodeSize = -1;
|
||||
private long mLastDataSize = -1;
|
||||
private long mLastCacheSize = -1;
|
||||
private long mLastTotalSize = -1;
|
||||
|
||||
private AppStorageSizesController(Preference total, Preference app,
|
||||
Preference data, Preference cache, @StringRes int computing, @StringRes int error) {
|
||||
mTotalSize = total;
|
||||
mAppSize = app;
|
||||
mDataSize = data;
|
||||
mCacheSize = cache;
|
||||
mComputing = computing;
|
||||
mError = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI using storage stats.
|
||||
* @param context Context to use to fetch strings
|
||||
*/
|
||||
public void updateUi(Context context) {
|
||||
if (mLastResult == null) {
|
||||
int errorRes = mLastResultFailed ? mError : mComputing;
|
||||
|
||||
mAppSize.setSummary(errorRes);
|
||||
mDataSize.setSummary(errorRes);
|
||||
mCacheSize.setSummary(errorRes);
|
||||
mTotalSize.setSummary(errorRes);
|
||||
} else {
|
||||
long codeSize = mLastResult.getCodeBytes();
|
||||
long dataSize = mLastResult.getDataBytes();
|
||||
if (mLastCodeSize != codeSize) {
|
||||
mLastCodeSize = codeSize;
|
||||
mAppSize.setSummary(getSizeStr(context, codeSize));
|
||||
}
|
||||
if (mLastDataSize != dataSize) {
|
||||
mLastDataSize = dataSize;
|
||||
mDataSize.setSummary(getSizeStr(context, dataSize));
|
||||
}
|
||||
long cacheSize = mLastResult.getCacheBytes();
|
||||
if (mLastCacheSize != cacheSize) {
|
||||
mLastCacheSize = cacheSize;
|
||||
mCacheSize.setSummary(getSizeStr(context, cacheSize));
|
||||
}
|
||||
|
||||
long totalSize = codeSize + dataSize + cacheSize;
|
||||
if (mLastTotalSize != totalSize) {
|
||||
mLastTotalSize = totalSize;
|
||||
mTotalSize.setSummary(getSizeStr(context, totalSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a result for the controller to use to update the UI.
|
||||
* @param result A result for the UI. If null, count as a failed calculation.
|
||||
*/
|
||||
public void setResult(StorageStatsSource.AppStorageStats result) {
|
||||
mLastResult = result;
|
||||
mLastResultFailed = result == null;
|
||||
}
|
||||
|
||||
private String getSizeStr(Context context, long size) {
|
||||
return Formatter.formatFileSize(context, size);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Preference mTotalSize;
|
||||
private Preference mAppSize;
|
||||
private Preference mDataSize;
|
||||
private Preference mCacheSize;
|
||||
private @StringRes int mComputing;
|
||||
private @StringRes int mError;
|
||||
|
||||
public Builder setAppSizePreference(Preference preference) {
|
||||
mAppSize = preference;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDataSizePreference(Preference preference) {
|
||||
mDataSize = preference;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCacheSizePreference(Preference preference) {
|
||||
mCacheSize = preference;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTotalSizePreference(Preference preference) {
|
||||
mTotalSize = preference;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setComputingString(@StringRes int sequence) {
|
||||
mComputing = sequence;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setErrorString(@StringRes int sequence) {
|
||||
mError = sequence;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AppStorageSizesController build() {
|
||||
return new AppStorageSizesController(
|
||||
Preconditions.checkNotNull(mTotalSize),
|
||||
Preconditions.checkNotNull(mAppSize),
|
||||
Preconditions.checkNotNull(mDataSize),
|
||||
Preconditions.checkNotNull(mCacheSize),
|
||||
mComputing,
|
||||
mError);
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.settings.utils.AsyncLoader;
|
||||
@@ -30,6 +31,7 @@ import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
|
||||
* Fetches the storage stats using the StorageStatsManager for a given package and user tuple.
|
||||
*/
|
||||
public class FetchPackageStorageAsyncLoader extends AsyncLoader<AppStorageStats> {
|
||||
private static final String TAG = "FetchPackageStorage";
|
||||
private final StorageStatsSource mSource;
|
||||
private final ApplicationInfo mInfo;
|
||||
private final UserHandle mUser;
|
||||
@@ -44,7 +46,13 @@ public class FetchPackageStorageAsyncLoader extends AsyncLoader<AppStorageStats>
|
||||
|
||||
@Override
|
||||
public AppStorageStats loadInBackground() {
|
||||
return mSource.getStatsForPackage(mInfo.volumeUuid, mInfo.packageName, mUser);
|
||||
AppStorageStats result = null;
|
||||
try {
|
||||
result = mSource.getStatsForPackage(mInfo.volumeUuid, mInfo.packageName, mUser);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.w(TAG, "Package may have been removed during query, failing gracefully", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -0,0 +1,95 @@
|
||||
package com.android.settings.applications;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.preference.Preference;
|
||||
|
||||
import com.android.settings.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.TestConfig;
|
||||
|
||||
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 org.robolectric.shadows.ShadowApplication;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class AppStorageSizesControllerTest {
|
||||
private static final String COMPUTING = "Computing…";
|
||||
private static final String INVALID_SIZE = "Couldn’t compute package size.";
|
||||
private AppStorageSizesController mController;
|
||||
private Context mContext;
|
||||
|
||||
private Preference mAppPreference;
|
||||
private Preference mCachePreference;
|
||||
private Preference mDataPreference;
|
||||
private Preference mTotalPreference;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mAppPreference = new Preference(mContext);
|
||||
mCachePreference = new Preference(mContext);
|
||||
mDataPreference = new Preference(mContext);
|
||||
mTotalPreference = new Preference(mContext);
|
||||
|
||||
mController = new AppStorageSizesController.Builder()
|
||||
.setAppSizePreference(mAppPreference)
|
||||
.setCacheSizePreference(mCachePreference)
|
||||
.setDataSizePreference(mDataPreference)
|
||||
.setTotalSizePreference(mTotalPreference)
|
||||
.setErrorString(R.string.invalid_size_value)
|
||||
.setComputingString(R.string.computing_size)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestingUpdateBeforeValuesSetIsComputing() {
|
||||
mController.updateUi(mContext);
|
||||
|
||||
assertThat(mAppPreference.getSummary()).isEqualTo(COMPUTING);
|
||||
assertThat(mCachePreference.getSummary()).isEqualTo(COMPUTING);
|
||||
assertThat(mDataPreference.getSummary()).isEqualTo(COMPUTING);
|
||||
assertThat(mTotalPreference.getSummary()).isEqualTo(COMPUTING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestingUpdateAfterFailureHasErrorText() {
|
||||
mController.setResult(null);
|
||||
mController.updateUi(mContext);
|
||||
|
||||
assertThat(mAppPreference.getSummary()).isEqualTo(INVALID_SIZE);
|
||||
assertThat(mCachePreference.getSummary()).isEqualTo(INVALID_SIZE);
|
||||
assertThat(mDataPreference.getSummary()).isEqualTo(INVALID_SIZE);
|
||||
assertThat(mTotalPreference.getSummary()).isEqualTo(INVALID_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void properlyPopulatedAfterValidEntry() {
|
||||
AppStorageStats result = mock(AppStorageStats.class);
|
||||
when(result.getCodeBytes()).thenReturn(1L);
|
||||
when(result.getCacheBytes()).thenReturn(10L);
|
||||
when(result.getDataBytes()).thenReturn(100L);
|
||||
when(result.getTotalBytes()).thenReturn(111L);
|
||||
|
||||
mController.setResult(result);
|
||||
mController.updateUi(mContext);
|
||||
|
||||
assertThat(mAppPreference.getSummary()).isEqualTo("1.00B");
|
||||
assertThat(mCachePreference.getSummary()).isEqualTo("10.00B");
|
||||
assertThat(mDataPreference.getSummary()).isEqualTo("100B");
|
||||
assertThat(mTotalPreference.getSummary()).isEqualTo("111B");
|
||||
}
|
||||
}
|
@@ -43,6 +43,7 @@ import org.robolectric.annotation.Config;
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class FetchPackageStorageAsyncLoaderTest {
|
||||
private static final String PACKAGE_NAME = "com.test.package";
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Context mContext;
|
||||
@Mock
|
||||
@@ -63,10 +64,22 @@ public class FetchPackageStorageAsyncLoaderTest {
|
||||
when(mSource.getStatsForPackage(anyString(), anyString(), any(UserHandle.class)))
|
||||
.thenReturn(stats);
|
||||
ApplicationInfo info = new ApplicationInfo();
|
||||
info.packageName = "com.test.package";
|
||||
info.packageName = PACKAGE_NAME;
|
||||
|
||||
FetchPackageStorageAsyncLoader task = new FetchPackageStorageAsyncLoader(
|
||||
mContext, mSource, info, new UserHandle(0));
|
||||
assertThat(task.loadInBackground()).isEqualTo(stats);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void installerExceptionHandledCleanly() {
|
||||
when(mSource.getStatsForPackage(anyString(), anyString(), any(UserHandle.class))).
|
||||
thenThrow(new IllegalStateException("intentional failure"));
|
||||
ApplicationInfo info = new ApplicationInfo();
|
||||
info.packageName = PACKAGE_NAME;
|
||||
FetchPackageStorageAsyncLoader task = new FetchPackageStorageAsyncLoader(
|
||||
mContext, mSource, info, new UserHandle(0));
|
||||
|
||||
assertThat(task.loadInBackground()).isNull();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user