import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.AppGlobals;
+import android.app.LoaderManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.Loader;
import android.content.UriPermission;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
+import com.android.settingslib.applications.StorageStatsSource;
+import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
public class AppStorageSettings extends AppInfoWithHeader
- implements OnClickListener, Callbacks, DialogInterface.OnClickListener {
+ implements OnClickListener, Callbacks, DialogInterface.OnClickListener,
+ LoaderManager.LoaderCallbacks<AppStorageStats> {
private static final String TAG = AppStorageSettings.class.getSimpleName();
//internal constants used in Handler
private boolean mCanClearData = true;
private boolean mHaveSizes = false;
+ private AppStorageStats mLastResult;
private long mLastCodeSize = -1;
private long mLastDataSize = -1;
private long mLastExternalCodeSize = -1;
private VolumeInfo[] mCandidates;
private AlertDialog.Builder mDialogBuilder;
+ private ApplicationInfo mInfo;
@Override
public void onCreate(Bundle savedInstanceState) {
@Override
public void onResume() {
super.onResume();
- mState.requestSize(mPackageName, mUserId);
+ updateSize();
}
private void setupViews() {
return Formatter.formatFileSize(getActivity(), size);
}
- private void refreshSizeInfo() {
- if (mAppEntry.size == ApplicationsState.SIZE_INVALID
- || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) {
- 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 = mAppEntry.codeSize;
- long dataSize = mAppEntry.dataSize;
- if (Environment.isExternalStorageEmulated()) {
- codeSize += mAppEntry.externalCodeSize;
- dataSize += mAppEntry.externalDataSize;
- } else {
- if (mLastExternalCodeSize != mAppEntry.externalCodeSize) {
- mLastExternalCodeSize = mAppEntry.externalCodeSize;
- mExternalCodeSize.setSummary(getSizeStr(mAppEntry.externalCodeSize));
- }
- if (mLastExternalDataSize != mAppEntry.externalDataSize) {
- mLastExternalDataSize = mAppEntry.externalDataSize;
- mExternalDataSize.setSummary(getSizeStr( mAppEntry.externalDataSize));
- }
- }
- if (mLastCodeSize != codeSize) {
- mLastCodeSize = codeSize;
- mAppSize.setSummary(getSizeStr(codeSize));
- }
- if (mLastDataSize != dataSize) {
- mLastDataSize = dataSize;
- mDataSize.setSummary(getSizeStr(dataSize));
- }
- long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize;
- if (mLastCacheSize != cacheSize) {
- mLastCacheSize = cacheSize;
- mCacheSize.setSummary(getSizeStr(cacheSize));
- }
- if (mLastTotalSize != mAppEntry.size) {
- mLastTotalSize = mAppEntry.size;
- mTotalSize.setSummary(getSizeStr(mAppEntry.size));
- }
-
- if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) {
- mClearDataButton.setEnabled(false);
- } else {
- mClearDataButton.setEnabled(true);
- mClearDataButton.setOnClickListener(this);
- }
- if (cacheSize <= 0) {
- mClearCacheButton.setEnabled(false);
- } else {
- mClearCacheButton.setEnabled(true);
- mClearCacheButton.setOnClickListener(this);
- }
- }
- if (mAppsControlDisallowedBySystem) {
- mClearCacheButton.setEnabled(false);
- mClearDataButton.setEnabled(false);
- }
- }
-
@Override
protected boolean refreshUi() {
retrieveAppEntry();
if (mAppEntry == null) {
return false;
}
- refreshSizeInfo();
+ updateUiWithSize(mLastResult);
refreshGrantedUriPermissions();
final VolumeInfo currentVol = getActivity().getPackageManager()
mClearDataButton.setText(R.string.clear_user_data_text);
if (result == OP_SUCCESSFUL) {
Log.i(TAG, "Cleared user data for package : "+packageName);
- mState.requestSize(mPackageName, mUserId);
+ updateSize();
} else {
mClearDataButton.setEnabled(true);
}
@Override
public void onPackageSizeChanged(String packageName) {
- if (packageName.equals(mAppEntry.info.packageName)) {
- refreshSizeInfo();
+ }
+
+ @Override
+ public Loader<AppStorageStats> onCreateLoader(int id, Bundle args) {
+ Context context = getContext();
+ return new FetchPackageStorageAsyncLoader(
+ context, new StorageStatsSource(context), mInfo, UserHandle.of(mUserId));
+ }
+
+ @Override
+ public void onLoadFinished(Loader<AppStorageStats> loader, AppStorageStats result) {
+ mLastResult = result;
+ updateUiWithSize(result);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<AppStorageStats> loader) {
+ }
+
+ private void updateSize() {
+ PackageManager packageManager = getPackageManager();
+ try {
+ mInfo = packageManager.getApplicationInfo(mPackageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Could not find package", e);
+ }
+
+ if (mInfo == null) {
+ return;
+ }
+
+ getLoaderManager().restartLoader(1, Bundle.EMPTY, this);
+ }
+
+ private void updateUiWithSize(AppStorageStats result) {
+ 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);
+ } else {
+ mClearDataButton.setEnabled(true);
+ mClearDataButton.setOnClickListener(this);
+ }
+ if (cacheSize <= 0) {
+ mClearCacheButton.setEnabled(false);
+ } else {
+ mClearCacheButton.setEnabled(true);
+ mClearCacheButton.setOnClickListener(this);
+ }
+ }
+ if (mAppsControlDisallowedBySystem) {
+ mClearCacheButton.setEnabled(false);
+ mClearDataButton.setEnabled(false);
}
}
break;
case MSG_CLEAR_CACHE:
// Refresh size info
- mState.requestSize(mPackageName, mUserId);
+ updateSize();
break;
}
}
--- /dev/null
+/*
+ * 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.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+import com.android.settings.utils.AsyncLoader;
+import com.android.settingslib.applications.StorageStatsSource;
+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 final StorageStatsSource mSource;
+ private final ApplicationInfo mInfo;
+ private final UserHandle mUser;
+
+ public FetchPackageStorageAsyncLoader(Context context, @NonNull StorageStatsSource source,
+ @NonNull ApplicationInfo info, @NonNull UserHandle user) {
+ super(context);
+ mSource = Preconditions.checkNotNull(source);
+ mInfo = info;
+ mUser = user;
+ }
+
+ @Override
+ public AppStorageStats loadInBackground() {
+ return mSource.getStatsForPackage(mInfo.volumeUuid, mInfo.packageName, mUser);
+ }
+
+ @Override
+ protected void onDiscardResult(AppStorageStats result) {
+ }
+}
--- /dev/null
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.UserHandle;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settingslib.applications.StorageStatsSource;
+import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class FetchPackageStorageAsyncLoaderTest {
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+ @Mock
+ private StorageStatsSource mSource;
+
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void worksForValidPackageNameAndUid() {
+ AppStorageStats stats = mock(AppStorageStats.class);
+ when(stats.getCodeBytes()).thenReturn(1L);
+ when(stats.getDataBytes()).thenReturn(2L);
+ when(stats.getCacheBytes()).thenReturn(3L);
+ when(mSource.getStatsForPackage(anyString(), anyString(), any(UserHandle.class)))
+ .thenReturn(stats);
+ ApplicationInfo info = new ApplicationInfo();
+ info.packageName = "com.test.package";
+
+ FetchPackageStorageAsyncLoader task = new FetchPackageStorageAsyncLoader(
+ mContext, mSource, info, new UserHandle(0));
+ assertThat(task.loadInBackground()).isEqualTo(stats);
+ }
+}