| /* |
| * Copyright (C) 2021 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.app.Dialog; |
| import android.app.settings.SettingsEnums; |
| import android.content.Context; |
| import android.os.AsyncTask; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.storage.DiskInfo; |
| import android.os.storage.StorageManager; |
| import android.os.storage.VolumeInfo; |
| import android.os.storage.VolumeRecord; |
| import android.text.TextUtils; |
| import android.text.format.Formatter; |
| import android.util.Log; |
| import android.widget.Toast; |
| |
| import androidx.appcompat.app.AlertDialog; |
| import androidx.fragment.app.Fragment; |
| |
| import com.android.settings.R; |
| import com.android.settings.core.SubSettingLauncher; |
| import com.android.settings.core.instrumentation.InstrumentedDialogFragment; |
| import com.android.settings.deviceinfo.PrivateVolumeForget; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.stream.Collectors; |
| |
| /** Storage utilities */ |
| public class StorageUtils { |
| |
| private static final String TAG = "StorageUtils"; |
| |
| /** |
| * Collects and returns all kinds of StorageEntry which will show in Storage Settings. |
| */ |
| public static List<StorageEntry> getAllStorageEntries(Context context, |
| StorageManager storageManager) { |
| final List<StorageEntry> storageEntries = new ArrayList<>(); |
| storageEntries.addAll(storageManager.getVolumes().stream() |
| .filter(volumeInfo -> isStorageSettingsInterestedVolume(volumeInfo)) |
| .map(volumeInfo -> new StorageEntry(context, volumeInfo)) |
| .collect(Collectors.toList())); |
| storageEntries.addAll(storageManager.getDisks().stream() |
| .filter(disk -> isDiskUnsupported(disk)) |
| .map(disk -> new StorageEntry(disk)) |
| .collect(Collectors.toList())); |
| storageEntries.addAll(storageManager.getVolumeRecords().stream() |
| .filter(volumeRecord -> isVolumeRecordMissed(storageManager, volumeRecord)) |
| .map(volumeRecord -> new StorageEntry(volumeRecord)) |
| .collect(Collectors.toList())); |
| return storageEntries; |
| } |
| |
| /** |
| * Returns true if the volumeInfo may be displayed in Storage Settings. |
| */ |
| public static boolean isStorageSettingsInterestedVolume(VolumeInfo volumeInfo) { |
| switch (volumeInfo.getType()) { |
| case VolumeInfo.TYPE_PRIVATE: |
| case VolumeInfo.TYPE_PUBLIC: |
| case VolumeInfo.TYPE_STUB: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| /** |
| * VolumeRecord is a metadata of VolumeInfo, this is the case where a VolumeInfo is missing. |
| * (e.g., internal SD card is removed.) |
| */ |
| public static boolean isVolumeRecordMissed(StorageManager storageManager, |
| VolumeRecord volumeRecord) { |
| return volumeRecord.getType() == VolumeInfo.TYPE_PRIVATE |
| && storageManager.findVolumeByUuid(volumeRecord.getFsUuid()) == null; |
| } |
| |
| /** |
| * A unsupported disk is the disk of problem format, android is not able to mount automatically. |
| */ |
| public static boolean isDiskUnsupported(DiskInfo disk) { |
| return disk.volumeCount == 0 && disk.size > 0; |
| } |
| |
| /** Launches the fragment to forget a specified missing volume record. */ |
| public static void launchForgetMissingVolumeRecordFragment(Context context, |
| StorageEntry storageEntry) { |
| if (storageEntry == null || !storageEntry.isVolumeRecordMissed()) { |
| return; |
| } |
| |
| final Bundle args = new Bundle(); |
| args.putString(VolumeRecord.EXTRA_FS_UUID, storageEntry.getFsUuid()); |
| new SubSettingLauncher(context) |
| .setDestination(PrivateVolumeForget.class.getCanonicalName()) |
| .setTitleRes(R.string.storage_menu_forget) |
| .setSourceMetricsCategory(SettingsEnums.SETTINGS_STORAGE_CATEGORY) |
| .setArguments(args) |
| .launch(); |
| } |
| |
| /** Returns size label of changing units. (e.g., 1kB, 2MB, 3GB) */ |
| public static String getStorageSizeLabel(Context context, long bytes) { |
| final Formatter.BytesResult result = Formatter.formatBytes(context.getResources(), |
| bytes, Formatter.FLAG_SHORTER); |
| return TextUtils.expandTemplate(context.getText(R.string.storage_size_large), |
| result.value, result.units).toString(); |
| } |
| |
| /** An AsyncTask to unmount a specified volume. */ |
| public static class UnmountTask extends AsyncTask<Void, Void, Exception> { |
| private final Context mContext; |
| private final StorageManager mStorageManager; |
| private final String mVolumeId; |
| private final String mDescription; |
| |
| public UnmountTask(Context context, VolumeInfo volume) { |
| mContext = context.getApplicationContext(); |
| mStorageManager = mContext.getSystemService(StorageManager.class); |
| mVolumeId = volume.getId(); |
| mDescription = mStorageManager.getBestVolumeDescription(volume); |
| } |
| |
| @Override |
| protected Exception doInBackground(Void... params) { |
| try { |
| mStorageManager.unmount(mVolumeId); |
| return null; |
| } catch (Exception e) { |
| return e; |
| } |
| } |
| |
| @Override |
| protected void onPostExecute(Exception e) { |
| if (e == null) { |
| Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_success, |
| mDescription), Toast.LENGTH_SHORT).show(); |
| } else { |
| Log.e(TAG, "Failed to unmount " + mVolumeId, e); |
| Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_failure, |
| mDescription), Toast.LENGTH_SHORT).show(); |
| } |
| } |
| } |
| |
| /** An AsyncTask to mount a specified volume. */ |
| public static class MountTask extends AsyncTask<Void, Void, Exception> { |
| private final Context mContext; |
| private final StorageManager mStorageManager; |
| private final String mVolumeId; |
| private final String mDescription; |
| |
| public MountTask(Context context, VolumeInfo volume) { |
| mContext = context.getApplicationContext(); |
| mStorageManager = mContext.getSystemService(StorageManager.class); |
| mVolumeId = volume.getId(); |
| mDescription = mStorageManager.getBestVolumeDescription(volume); |
| } |
| |
| @Override |
| protected Exception doInBackground(Void... params) { |
| try { |
| mStorageManager.mount(mVolumeId); |
| return null; |
| } catch (Exception e) { |
| return e; |
| } |
| } |
| |
| @Override |
| protected void onPostExecute(Exception e) { |
| if (e == null) { |
| Toast.makeText(mContext, mContext.getString(R.string.storage_mount_success, |
| mDescription), Toast.LENGTH_SHORT).show(); |
| } else { |
| Log.e(TAG, "Failed to mount " + mVolumeId, e); |
| Toast.makeText(mContext, mContext.getString(R.string.storage_mount_failure, |
| mDescription), Toast.LENGTH_SHORT).show(); |
| } |
| } |
| } |
| |
| /* Shows information about system storage. */ |
| public static class SystemInfoFragment extends InstrumentedDialogFragment { |
| /** Shows the fragment. */ |
| public static void show(Fragment parent) { |
| if (!parent.isAdded()) return; |
| |
| final SystemInfoFragment dialog = new SystemInfoFragment(); |
| dialog.setTargetFragment(parent, 0); |
| dialog.show(parent.getFragmentManager(), "systemInfo"); |
| } |
| |
| @Override |
| public int getMetricsCategory() { |
| return SettingsEnums.DIALOG_STORAGE_SYSTEM_INFO; |
| } |
| |
| @Override |
| public Dialog onCreateDialog(Bundle savedInstanceState) { |
| return new AlertDialog.Builder(getActivity()) |
| .setMessage(getContext().getString(R.string.storage_detail_dialog_system, |
| Build.VERSION.RELEASE_OR_CODENAME)) |
| .setPositiveButton(android.R.string.ok, null) |
| .create(); |
| } |
| } |
| } |