1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.deviceinfo;
18 
19 import android.app.ActivityManager;
20 import android.app.settings.SettingsEnums;
21 import android.content.Context;
22 import android.content.res.Resources;
23 import android.net.Uri;
24 import android.os.Bundle;
25 import android.os.UserManager;
26 import android.os.storage.DiskInfo;
27 import android.os.storage.StorageEventListener;
28 import android.os.storage.StorageManager;
29 import android.os.storage.VolumeInfo;
30 import android.os.storage.VolumeRecord;
31 import android.provider.DocumentsContract;
32 import android.text.TextUtils;
33 import android.text.format.Formatter;
34 import android.text.format.Formatter.BytesResult;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.widget.Button;
38 
39 import androidx.preference.Preference;
40 import androidx.preference.PreferenceScreen;
41 
42 import com.android.internal.util.Preconditions;
43 import com.android.settings.R;
44 import com.android.settings.SettingsPreferenceFragment;
45 import com.android.settings.deviceinfo.StorageSettings.MountTask;
46 import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
47 
48 import java.io.File;
49 import java.util.Objects;
50 
51 /**
52  * Panel showing summary and actions for a {@link VolumeInfo#TYPE_PUBLIC}
53  * storage volume.
54  */
55 public class PublicVolumeSettings extends SettingsPreferenceFragment {
56     // TODO: disable unmount when providing over MTP/PTP
57 
58     private StorageManager mStorageManager;
59 
60     private String mVolumeId;
61     private VolumeInfo mVolume;
62     private DiskInfo mDisk;
63 
64     private StorageSummaryPreference mSummary;
65 
66     private Preference mMount;
67     private Preference mFormatPublic;
68     private Preference mFormatPrivate;
69     private Button mUnmount;
70 
71     private boolean mIsPermittedToAdopt;
72 
isVolumeValid()73     private boolean isVolumeValid() {
74         return (mVolume != null) && (mVolume.getType() == VolumeInfo.TYPE_PUBLIC)
75                 && mVolume.isMountedReadable();
76     }
77 
78     @Override
getMetricsCategory()79     public int getMetricsCategory() {
80         return SettingsEnums.DEVICEINFO_STORAGE;
81     }
82 
83     @Override
onCreate(Bundle icicle)84     public void onCreate(Bundle icicle) {
85         super.onCreate(icicle);
86 
87         final Context context = getActivity();
88 
89         mIsPermittedToAdopt = UserManager.get(context).isAdminUser()
90                 && !ActivityManager.isUserAMonkey();
91 
92         mStorageManager = context.getSystemService(StorageManager.class);
93 
94         if (DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS.equals(
95                 getActivity().getIntent().getAction())) {
96             final Uri rootUri = getActivity().getIntent().getData();
97             final String fsUuid = DocumentsContract.getRootId(rootUri);
98             mVolume = mStorageManager.findVolumeByUuid(fsUuid);
99         } else {
100             final String volId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID);
101             if (volId != null) {
102                 mVolume = mStorageManager.findVolumeById(volId);
103             }
104         }
105 
106         if (!isVolumeValid()) {
107             getActivity().finish();
108             return;
109         }
110 
111         mDisk = mStorageManager.findDiskById(mVolume.getDiskId());
112         Preconditions.checkNotNull(mDisk);
113 
114         mVolumeId = mVolume.getId();
115 
116         addPreferencesFromResource(R.xml.device_info_storage_volume);
117         getPreferenceScreen().setOrderingAsAdded(true);
118 
119         mSummary = new StorageSummaryPreference(getPrefContext());
120 
121         mMount = buildAction(R.string.storage_menu_mount);
122         mUnmount = new Button(getActivity());
123         mUnmount.setText(R.string.storage_menu_unmount);
124         mUnmount.setOnClickListener(mUnmountListener);
125         mFormatPublic = buildAction(R.string.storage_menu_format);
126         if (mIsPermittedToAdopt) {
127             mFormatPrivate = buildAction(R.string.storage_menu_format_private);
128         }
129     }
130 
131     @Override
onActivityCreated(Bundle savedInstanceState)132     public void onActivityCreated(Bundle savedInstanceState) {
133         super.onActivityCreated(savedInstanceState);
134 
135         // If the volume isn't valid, we are not scaffolded to set up a view.
136         if (!isVolumeValid()) {
137             return;
138         }
139 
140         final Resources resources = getResources();
141         final int padding = resources.getDimensionPixelSize(
142                 R.dimen.unmount_button_padding);
143         final ViewGroup buttonBar = getButtonBar();
144         buttonBar.removeAllViews();
145         buttonBar.setPadding(padding, padding, padding, padding);
146         buttonBar.addView(mUnmount, new ViewGroup.LayoutParams(
147                 ViewGroup.LayoutParams.MATCH_PARENT,
148                 ViewGroup.LayoutParams.WRAP_CONTENT));
149     }
150 
update()151     public void update() {
152         if (!isVolumeValid()) {
153             getActivity().finish();
154             return;
155         }
156 
157         getActivity().setTitle(mStorageManager.getBestVolumeDescription(mVolume));
158 
159         final Context context = getActivity();
160         final PreferenceScreen screen = getPreferenceScreen();
161 
162         screen.removeAll();
163 
164         if (mVolume.isMountedReadable()) {
165             addPreference(mSummary);
166 
167             final File file = mVolume.getPath();
168             final long totalBytes = file.getTotalSpace();
169             final long freeBytes = file.getFreeSpace();
170             final long usedBytes = totalBytes - freeBytes;
171 
172             final BytesResult result = Formatter.formatBytes(getResources(), usedBytes, 0);
173             mSummary.setTitle(TextUtils.expandTemplate(getText(R.string.storage_size_large),
174                     result.value, result.units));
175             mSummary.setSummary(getString(R.string.storage_volume_used,
176                     Formatter.formatFileSize(context, totalBytes)));
177             mSummary.setPercent(usedBytes, totalBytes);
178         }
179 
180         if (mVolume.getState() == VolumeInfo.STATE_UNMOUNTED) {
181             addPreference(mMount);
182         }
183         if (mVolume.isMountedReadable()) {
184             getButtonBar().setVisibility(View.VISIBLE);
185         }
186         addPreference(mFormatPublic);
187         if (mDisk.isAdoptable() && mIsPermittedToAdopt) {
188             addPreference(mFormatPrivate);
189         }
190     }
191 
addPreference(Preference pref)192     private void addPreference(Preference pref) {
193         pref.setOrder(Preference.DEFAULT_ORDER);
194         getPreferenceScreen().addPreference(pref);
195     }
196 
buildAction(int titleRes)197     private Preference buildAction(int titleRes) {
198         final Preference pref = new Preference(getPrefContext());
199         pref.setTitle(titleRes);
200         return pref;
201     }
202 
203     @Override
onResume()204     public void onResume() {
205         super.onResume();
206 
207         // Refresh to verify that we haven't been formatted away
208         mVolume = mStorageManager.findVolumeById(mVolumeId);
209         if (!isVolumeValid()) {
210             getActivity().finish();
211             return;
212         }
213 
214         mStorageManager.registerListener(mStorageListener);
215         update();
216     }
217 
218     @Override
onPause()219     public void onPause() {
220         super.onPause();
221         mStorageManager.unregisterListener(mStorageListener);
222     }
223 
224     @Override
onPreferenceTreeClick(Preference pref)225     public boolean onPreferenceTreeClick(Preference pref) {
226         if (pref == mMount) {
227             new MountTask(getActivity(), mVolume).execute();
228         } else if (pref == mFormatPublic) {
229             StorageWizardFormatConfirm.showPublic(getActivity(), mDisk.getId());
230         } else if (pref == mFormatPrivate) {
231             StorageWizardFormatConfirm.showPrivate(getActivity(), mDisk.getId());
232         }
233 
234         return super.onPreferenceTreeClick(pref);
235     }
236 
237     private final View.OnClickListener mUnmountListener = new View.OnClickListener() {
238         @Override
239         public void onClick(View v) {
240             new UnmountTask(getActivity(), mVolume).execute();
241         }
242     };
243 
244     private final StorageEventListener mStorageListener = new StorageEventListener() {
245         @Override
246         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
247             if (Objects.equals(mVolume.getId(), vol.getId())) {
248                 mVolume = vol;
249                 update();
250             }
251         }
252 
253         @Override
254         public void onVolumeRecordChanged(VolumeRecord rec) {
255             if (Objects.equals(mVolume.getFsUuid(), rec.getFsUuid())) {
256                 mVolume = mStorageManager.findVolumeById(mVolumeId);
257                 update();
258             }
259         }
260     };
261 }
262