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.content.Context;
21 import android.content.Intent;
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.support.v7.preference.Preference;
33 import android.support.v7.preference.PreferenceScreen;
34 import android.text.TextUtils;
35 import android.text.format.Formatter;
36 import android.text.format.Formatter.BytesResult;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.widget.Button;
40 
41 import com.android.internal.logging.MetricsProto.MetricsEvent;
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     protected int getMetricsCategory() {
80         return MetricsEvent.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             mVolume = mStorageManager.findVolumeById(volId);
102         }
103 
104         if (!isVolumeValid()) {
105             getActivity().finish();
106             return;
107         }
108 
109         mDisk = mStorageManager.findDiskById(mVolume.getDiskId());
110         Preconditions.checkNotNull(mDisk);
111 
112         mVolumeId = mVolume.getId();
113 
114         addPreferencesFromResource(R.xml.device_info_storage_volume);
115         getPreferenceScreen().setOrderingAsAdded(true);
116 
117         mSummary = new StorageSummaryPreference(getPrefContext());
118 
119         mMount = buildAction(R.string.storage_menu_mount);
120         mUnmount = new Button(getActivity());
121         mUnmount.setText(R.string.storage_menu_unmount);
122         mUnmount.setOnClickListener(mUnmountListener);
123         mFormatPublic = buildAction(R.string.storage_menu_format);
124         if (mIsPermittedToAdopt) {
125             mFormatPrivate = buildAction(R.string.storage_menu_format_private);
126         }
127     }
128 
129     @Override
onActivityCreated(Bundle savedInstanceState)130     public void onActivityCreated(Bundle savedInstanceState) {
131         super.onActivityCreated(savedInstanceState);
132         final Resources resources = getResources();
133         final int padding = resources.getDimensionPixelSize(
134                 R.dimen.unmount_button_padding);
135         final ViewGroup buttonBar = getButtonBar();
136         buttonBar.removeAllViews();
137         buttonBar.setPadding(padding, padding, padding, padding);
138         buttonBar.addView(mUnmount, new ViewGroup.LayoutParams(
139                 ViewGroup.LayoutParams.MATCH_PARENT,
140                 ViewGroup.LayoutParams.WRAP_CONTENT));
141     }
142 
update()143     public void update() {
144         if (!isVolumeValid()) {
145             getActivity().finish();
146             return;
147         }
148 
149         getActivity().setTitle(mStorageManager.getBestVolumeDescription(mVolume));
150 
151         final Context context = getActivity();
152         final PreferenceScreen screen = getPreferenceScreen();
153 
154         screen.removeAll();
155 
156         if (mVolume.isMountedReadable()) {
157             addPreference(mSummary);
158 
159             final File file = mVolume.getPath();
160             final long totalBytes = file.getTotalSpace();
161             final long freeBytes = file.getFreeSpace();
162             final long usedBytes = totalBytes - freeBytes;
163 
164             final BytesResult result = Formatter.formatBytes(getResources(), usedBytes, 0);
165             mSummary.setTitle(TextUtils.expandTemplate(getText(R.string.storage_size_large),
166                     result.value, result.units));
167             mSummary.setSummary(getString(R.string.storage_volume_used,
168                     Formatter.formatFileSize(context, totalBytes)));
169             mSummary.setPercent((int) ((usedBytes * 100) / totalBytes));
170         }
171 
172         if (mVolume.getState() == VolumeInfo.STATE_UNMOUNTED) {
173             addPreference(mMount);
174         }
175         if (mVolume.isMountedReadable()) {
176             getButtonBar().setVisibility(View.VISIBLE);
177         }
178         addPreference(mFormatPublic);
179         if (mDisk.isAdoptable() && mIsPermittedToAdopt) {
180             addPreference(mFormatPrivate);
181         }
182     }
183 
addPreference(Preference pref)184     private void addPreference(Preference pref) {
185         pref.setOrder(Preference.DEFAULT_ORDER);
186         getPreferenceScreen().addPreference(pref);
187     }
188 
buildAction(int titleRes)189     private Preference buildAction(int titleRes) {
190         final Preference pref = new Preference(getPrefContext());
191         pref.setTitle(titleRes);
192         return pref;
193     }
194 
195     @Override
onResume()196     public void onResume() {
197         super.onResume();
198 
199         // Refresh to verify that we haven't been formatted away
200         mVolume = mStorageManager.findVolumeById(mVolumeId);
201         if (!isVolumeValid()) {
202             getActivity().finish();
203             return;
204         }
205 
206         mStorageManager.registerListener(mStorageListener);
207         update();
208     }
209 
210     @Override
onPause()211     public void onPause() {
212         super.onPause();
213         mStorageManager.unregisterListener(mStorageListener);
214     }
215 
216     @Override
onPreferenceTreeClick(Preference pref)217     public boolean onPreferenceTreeClick(Preference pref) {
218         final Context context = getActivity();
219         if (pref == mMount) {
220             new MountTask(context, mVolume).execute();
221         } else if (pref == mFormatPublic) {
222             final Intent intent = new Intent(context, StorageWizardFormatConfirm.class);
223             intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId());
224             intent.putExtra(StorageWizardFormatConfirm.EXTRA_FORMAT_PRIVATE, false);
225             startActivity(intent);
226         } else if (pref == mFormatPrivate) {
227             final Intent intent = new Intent(context, StorageWizardFormatConfirm.class);
228             intent.putExtra(DiskInfo.EXTRA_DISK_ID, mDisk.getId());
229             intent.putExtra(StorageWizardFormatConfirm.EXTRA_FORMAT_PRIVATE, true);
230             startActivity(intent);
231         }
232 
233         return super.onPreferenceTreeClick(pref);
234     }
235 
236     private final View.OnClickListener mUnmountListener = new View.OnClickListener() {
237         @Override
238         public void onClick(View v) {
239             new UnmountTask(getActivity(), mVolume).execute();
240         }
241     };
242 
243     private final StorageEventListener mStorageListener = new StorageEventListener() {
244         @Override
245         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
246             if (Objects.equals(mVolume.getId(), vol.getId())) {
247                 mVolume = vol;
248                 update();
249             }
250         }
251 
252         @Override
253         public void onVolumeRecordChanged(VolumeRecord rec) {
254             if (Objects.equals(mVolume.getFsUuid(), rec.getFsUuid())) {
255                 mVolume = mStorageManager.findVolumeById(mVolumeId);
256                 update();
257             }
258         }
259     };
260 }
261