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