1 /*
2  * Copyright (C) 2021 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.Intent;
23 import android.content.pm.PackageManager;
24 import android.os.Bundle;
25 import android.os.UserManager;
26 import android.os.storage.DiskInfo;
27 import android.os.storage.StorageManager;
28 import android.os.storage.VolumeInfo;
29 import android.util.Log;
30 import android.view.Menu;
31 import android.view.MenuInflater;
32 import android.view.MenuItem;
33 import android.widget.Toast;
34 
35 import androidx.annotation.VisibleForTesting;
36 import androidx.fragment.app.Fragment;
37 
38 import com.android.settings.R;
39 import com.android.settings.core.SubSettingLauncher;
40 import com.android.settings.deviceinfo.storage.StorageEntry;
41 import com.android.settings.deviceinfo.storage.StorageRenameFragment;
42 import com.android.settings.deviceinfo.storage.StorageUtils;
43 import com.android.settings.deviceinfo.storage.StorageUtils.MountTask;
44 import com.android.settings.deviceinfo.storage.StorageUtils.UnmountTask;
45 import com.android.settingslib.core.lifecycle.LifecycleObserver;
46 import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
47 import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;
48 import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu;
49 
50 import java.util.Objects;
51 
52 /**
53  * Handles the option menu on the Storage settings.
54  */
55 public class VolumeOptionMenuController implements LifecycleObserver, OnCreateOptionsMenu,
56         OnPrepareOptionsMenu, OnOptionsItemSelected {
57 
58     private static final String TAG = "VolumeOptionMenuController";
59     private final Context mContext;
60     private final Fragment mFragment;
61     private final PackageManager mPackageManager;
62     @VisibleForTesting
63     MenuItem mRename;
64     @VisibleForTesting
65     MenuItem mMount;
66     @VisibleForTesting
67     MenuItem mUnmount;
68     @VisibleForTesting
69     MenuItem mFormat;
70     @VisibleForTesting
71     MenuItem mFormatAsPortable;
72     @VisibleForTesting
73     MenuItem mFormatAsInternal;
74     @VisibleForTesting
75     MenuItem mMigrate;
76     @VisibleForTesting
77     MenuItem mFree;
78     @VisibleForTesting
79     MenuItem mForget;
80     private StorageEntry mStorageEntry;
81 
VolumeOptionMenuController(Context context, Fragment parent, StorageEntry storageEntry)82     public VolumeOptionMenuController(Context context, Fragment parent, StorageEntry storageEntry) {
83         mContext = context;
84         mFragment = parent;
85         mPackageManager = context.getPackageManager();
86         mStorageEntry = storageEntry;
87     }
88 
89     @Override
onCreateOptionsMenu(final Menu menu, final MenuInflater inflater)90     public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
91         inflater.inflate(R.menu.storage_volume, menu);
92     }
93 
94     @Override
onPrepareOptionsMenu(Menu menu)95     public void onPrepareOptionsMenu(Menu menu) {
96         mRename = menu.findItem(R.id.storage_rename);
97         mMount = menu.findItem(R.id.storage_mount);
98         mUnmount = menu.findItem(R.id.storage_unmount);
99         mFormat = menu.findItem(R.id.storage_format);
100         mFormatAsPortable = menu.findItem(R.id.storage_format_as_portable);
101         mFormatAsInternal = menu.findItem(R.id.storage_format_as_internal);
102         mMigrate = menu.findItem(R.id.storage_migrate);
103         mFree = menu.findItem(R.id.storage_free);
104         mForget = menu.findItem(R.id.storage_forget);
105 
106         updateOptionsMenu();
107     }
108 
updateOptionsMenu()109     private void updateOptionsMenu() {
110         if (mRename == null || mMount == null || mUnmount == null || mFormat == null
111                 || mFormatAsPortable == null || mFormatAsInternal == null || mMigrate == null
112                 || mFree == null || mForget == null) {
113             Log.d(TAG, "Menu items are not available");
114             return;
115         }
116 
117         mRename.setVisible(false);
118         mMount.setVisible(false);
119         mUnmount.setVisible(false);
120         mFormat.setVisible(false);
121         mFormatAsPortable.setVisible(false);
122         mFormatAsInternal.setVisible(false);
123         mMigrate.setVisible(false);
124         mFree.setVisible(false);
125         mForget.setVisible(false);
126 
127         if (mStorageEntry.isDiskInfoUnsupported()) {
128             mFormat.setVisible(true);
129             return;
130         }
131         if (mStorageEntry.isVolumeRecordMissed()) {
132             mForget.setVisible(true);
133             return;
134         }
135         if (mStorageEntry.isUnmounted()) {
136             mMount.setVisible(true);
137             return;
138         }
139         if (!mStorageEntry.isMounted()) {
140             return;
141         }
142 
143         if (mStorageEntry.isPrivate()) {
144             if (!mStorageEntry.isDefaultInternalStorage()) {
145                 mRename.setVisible(true);
146                 mFormatAsPortable.setVisible(true);
147             }
148 
149             // Only offer to migrate when not current storage.
150             final VolumeInfo primaryVolumeInfo = mPackageManager.getPrimaryStorageCurrentVolume();
151             final VolumeInfo selectedVolumeInfo = mStorageEntry.getVolumeInfo();
152             mMigrate.setVisible(primaryVolumeInfo != null
153                     && primaryVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE
154                     && !Objects.equals(selectedVolumeInfo, primaryVolumeInfo)
155                     && primaryVolumeInfo.isMountedWritable());
156             return;
157         }
158 
159         if (mStorageEntry.isPublic()) {
160             mRename.setVisible(true);
161             mUnmount.setVisible(true);
162             mFormatAsInternal.setVisible(true);
163             return;
164         }
165     }
166 
167     @Override
onOptionsItemSelected(MenuItem menuItem)168     public boolean onOptionsItemSelected(MenuItem menuItem) {
169         if (!mFragment.isAdded()) {
170             return false;
171         }
172 
173         final int menuId = menuItem.getItemId();
174         if (menuId == R.id.storage_mount) {
175             if (mStorageEntry.isUnmounted()) {
176                 new MountTask(mFragment.getActivity(), mStorageEntry.getVolumeInfo()).execute();
177                 return true;
178             }
179             return false;
180         }
181         if (menuId == R.id.storage_unmount) {
182             if (mStorageEntry.isMounted()) {
183                 if (mStorageEntry.isPublic()) {
184                     new UnmountTask(mFragment.getActivity(),
185                             mStorageEntry.getVolumeInfo()).execute();
186                     return true;
187                 }
188                 if (mStorageEntry.isPrivate() && !mStorageEntry.isDefaultInternalStorage()) {
189                     final Bundle args = new Bundle();
190                     args.putString(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
191                     new SubSettingLauncher(mContext)
192                             .setDestination(PrivateVolumeUnmount.class.getCanonicalName())
193                             .setTitleRes(R.string.storage_menu_unmount)
194                             .setSourceMetricsCategory(SettingsEnums.DEVICEINFO_STORAGE)
195                             .setArguments(args)
196                             .launch();
197                     return true;
198                 }
199             }
200             return false;
201         }
202         if (menuId == R.id.storage_rename) {
203             if ((mStorageEntry.isPrivate() && !mStorageEntry.isDefaultInternalStorage())
204                     ||  mStorageEntry.isPublic()) {
205                 StorageRenameFragment.show(mFragment, mStorageEntry.getVolumeInfo());
206                 return true;
207             }
208             return false;
209         }
210         if (menuId == R.id.storage_format) {
211             if (mStorageEntry.isDiskInfoUnsupported() || mStorageEntry.isPublic()) {
212                 StorageWizardFormatConfirm.showPublic(mFragment.getActivity(),
213                         mStorageEntry.getDiskId());
214                 return true;
215             }
216             return false;
217         }
218         if (menuId == R.id.storage_format_as_portable) {
219             if (mStorageEntry.isPrivate()) {
220                 boolean mIsPermittedToAdopt = UserManager.get(mContext).isAdminUser()
221                     && !ActivityManager.isUserAMonkey();
222 
223                 if(!mIsPermittedToAdopt){
224                     //Notify guest users as to why formatting is disallowed
225                     Toast.makeText(mFragment.getActivity(),
226                                  R.string.storage_wizard_guest,Toast.LENGTH_LONG).show();
227                     (mFragment.getActivity()).finish();
228                     return false;
229                 }
230                 final Bundle args = new Bundle();
231                 args.putString(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
232                 new SubSettingLauncher(mContext)
233                         .setDestination(PrivateVolumeFormat.class.getCanonicalName())
234                         .setTitleRes(R.string.storage_menu_format)
235                         .setSourceMetricsCategory(SettingsEnums.DEVICEINFO_STORAGE)
236                         .setArguments(args)
237                         .launch();
238                 return true;
239             }
240             return false;
241         }
242         if (menuId == R.id.storage_format_as_internal) {
243             if (mStorageEntry.isPublic()) {
244                 final Intent intent = new Intent(mFragment.getActivity(), StorageWizardInit.class);
245                 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
246                 mContext.startActivity(intent);
247                 return true;
248             }
249             return false;
250         }
251         if (menuId == R.id.storage_migrate) {
252             if (mStorageEntry.isPrivate()) {
253                 final Intent intent = new Intent(mContext, StorageWizardMigrateConfirm.class);
254                 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
255                 mContext.startActivity(intent);
256                 return true;
257             }
258             return false;
259         }
260         if (menuId == R.id.storage_forget) {
261             if (mStorageEntry.isVolumeRecordMissed()) {
262                 StorageUtils.launchForgetMissingVolumeRecordFragment(mContext, mStorageEntry);
263                 return true;
264             }
265             return false;
266         }
267         return false;
268     }
269 
setSelectedStorageEntry(StorageEntry storageEntry)270     public void setSelectedStorageEntry(StorageEntry storageEntry) {
271         mStorageEntry = storageEntry;
272 
273         updateOptionsMenu();
274     }
275 }