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.tv.settings.device.storage; 18 19 import android.app.Activity; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.os.Bundle; 25 import android.os.storage.DiskInfo; 26 import android.os.storage.StorageEventListener; 27 import android.os.storage.StorageManager; 28 import android.os.storage.VolumeInfo; 29 import android.os.storage.VolumeRecord; 30 import android.support.annotation.NonNull; 31 import android.support.annotation.Nullable; 32 import android.support.v17.leanback.app.GuidedStepFragment; 33 import android.support.v17.leanback.widget.GuidanceStylist; 34 import android.support.v17.leanback.widget.GuidedAction; 35 import android.text.TextUtils; 36 37 import com.android.tv.settings.R; 38 import com.android.tv.settings.device.StorageResetActivity; 39 40 import java.util.List; 41 42 public class NewStorageActivity extends Activity { 43 44 private static final String TAG = "NewStorageActivity"; 45 46 private static final String ACTION_NEW_STORAGE = 47 "com.android.tv.settings.device.storage.NewStorageActivity.NEW_STORAGE"; 48 private static final String ACTION_MISSING_STORAGE = 49 "com.android.tv.settings.device.storage.NewStorageActivity.MISSING_STORAGE"; 50 getNewStorageLaunchIntent(Context context, String volumeId, String diskId)51 public static Intent getNewStorageLaunchIntent(Context context, String volumeId, 52 String diskId) { 53 final Intent i = new Intent(context, NewStorageActivity.class); 54 i.setAction(ACTION_NEW_STORAGE); 55 i.putExtra(VolumeInfo.EXTRA_VOLUME_ID, volumeId); 56 i.putExtra(DiskInfo.EXTRA_DISK_ID, diskId); 57 return i; 58 } 59 getMissingStorageLaunchIntent(Context context, String fsUuid)60 public static Intent getMissingStorageLaunchIntent(Context context, String fsUuid) { 61 final Intent i = new Intent(context, NewStorageActivity.class); 62 i.setAction(ACTION_MISSING_STORAGE); 63 i.putExtra(VolumeRecord.EXTRA_FS_UUID, fsUuid); 64 return i; 65 } 66 67 @Override onCreate(@ullable Bundle savedInstanceState)68 protected void onCreate(@Nullable Bundle savedInstanceState) { 69 super.onCreate(savedInstanceState); 70 71 if (savedInstanceState == null) { 72 final String action = getIntent().getAction(); 73 74 if (TextUtils.equals(action, ACTION_MISSING_STORAGE)) { 75 // Launched from a notification, see com.android.systemui.usb.StorageNotification 76 final String fsUuid = getIntent().getStringExtra(VolumeRecord.EXTRA_FS_UUID); 77 if (TextUtils.isEmpty(fsUuid)) { 78 throw new IllegalStateException( 79 "NewStorageActivity launched without specifying missing storage"); 80 } 81 82 getFragmentManager().beginTransaction() 83 .add(android.R.id.content, MissingStorageFragment.newInstance(fsUuid)) 84 .commit(); 85 } else { 86 final String volumeId = getIntent().getStringExtra(VolumeInfo.EXTRA_VOLUME_ID); 87 final String diskId = getIntent().getStringExtra(DiskInfo.EXTRA_DISK_ID); 88 if (TextUtils.isEmpty(volumeId) && TextUtils.isEmpty(diskId)) { 89 throw new IllegalStateException( 90 "NewStorageActivity launched without specifying new storage"); 91 } 92 93 getFragmentManager().beginTransaction() 94 .add(android.R.id.content, NewStorageFragment.newInstance(volumeId, diskId)) 95 .commit(); 96 } 97 } 98 } 99 100 public static class NewStorageFragment extends GuidedStepFragment { 101 102 private static final int ACTION_BROWSE = 1; 103 private static final int ACTION_FORMAT_AS_PRIVATE = 2; 104 private static final int ACTION_UNMOUNT = 3; 105 private static final int ACTION_FORMAT_AS_PUBLIC = 4; 106 107 private String mVolumeId; 108 private String mDiskId; 109 private String mDescription; 110 111 private final StorageEventListener mStorageEventListener = new StorageEventListener() { 112 @Override 113 public void onDiskDestroyed(DiskInfo disk) { 114 checkForUnmount(); 115 } 116 117 @Override 118 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 119 checkForUnmount(); 120 } 121 }; 122 newInstance(String volumeId, String diskId)123 public static NewStorageFragment newInstance(String volumeId, String diskId) { 124 final Bundle b = new Bundle(1); 125 b.putString(VolumeInfo.EXTRA_VOLUME_ID, volumeId); 126 b.putString(DiskInfo.EXTRA_DISK_ID, diskId); 127 final NewStorageFragment fragment = new NewStorageFragment(); 128 fragment.setArguments(b); 129 return fragment; 130 } 131 132 @Override onCreate(Bundle savedInstanceState)133 public void onCreate(Bundle savedInstanceState) { 134 StorageManager storageManager = getActivity().getSystemService(StorageManager.class); 135 mVolumeId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID); 136 mDiskId = getArguments().getString(DiskInfo.EXTRA_DISK_ID); 137 if (TextUtils.isEmpty(mVolumeId) && TextUtils.isEmpty(mDiskId)) { 138 throw new IllegalStateException( 139 "NewStorageFragment launched without specifying new storage"); 140 } 141 if (!TextUtils.isEmpty(mVolumeId)) { 142 final VolumeInfo info = storageManager.findVolumeById(mVolumeId); 143 mDescription = storageManager.getBestVolumeDescription(info); 144 mDiskId = info.getDiskId(); 145 } else { 146 final DiskInfo info = storageManager.findDiskById(mDiskId); 147 mDescription = info.getDescription(); 148 } 149 150 super.onCreate(savedInstanceState); 151 } 152 153 @Override onStart()154 public void onStart() { 155 super.onStart(); 156 checkForUnmount(); 157 getActivity().getSystemService(StorageManager.class) 158 .registerListener(mStorageEventListener); 159 } 160 161 @Override onStop()162 public void onStop() { 163 super.onStop(); 164 getActivity().getSystemService(StorageManager.class) 165 .unregisterListener(mStorageEventListener); 166 } 167 168 @Override onCreateGuidance(Bundle savedInstanceState)169 public @NonNull GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) { 170 return new GuidanceStylist.Guidance( 171 getString(R.string.storage_new_title), 172 mDescription, 173 null, 174 getActivity().getDrawable(R.drawable.ic_storage_132dp)); 175 } 176 177 @Override onCreateActions(@onNull List<GuidedAction> actions, Bundle savedInstanceState)178 public void onCreateActions(@NonNull List<GuidedAction> actions, 179 Bundle savedInstanceState) { 180 if (TextUtils.isEmpty(mVolumeId)) { 181 actions.add(new GuidedAction.Builder(getContext()) 182 .title(R.string.storage_new_action_format_public) 183 .id(ACTION_FORMAT_AS_PUBLIC) 184 .build()); 185 } else { 186 actions.add(new GuidedAction.Builder(getContext()) 187 .title(R.string.storage_new_action_browse) 188 .id(ACTION_BROWSE) 189 .build()); 190 } 191 actions.add(new GuidedAction.Builder(getContext()) 192 .title(R.string.storage_new_action_adopt) 193 .id(ACTION_FORMAT_AS_PRIVATE) 194 .build()); 195 actions.add(new GuidedAction.Builder(getContext()) 196 .title(R.string.storage_new_action_eject) 197 .id(ACTION_UNMOUNT) 198 .build()); 199 } 200 201 @Override onGuidedActionClicked(GuidedAction action)202 public void onGuidedActionClicked(GuidedAction action) { 203 switch ((int) action.getId()) { 204 case ACTION_FORMAT_AS_PUBLIC: 205 startActivity(FormatActivity.getFormatAsPublicIntent(getActivity(), mDiskId)); 206 break; 207 case ACTION_BROWSE: 208 startActivity(new Intent(getActivity(), StorageResetActivity.class)); 209 break; 210 case ACTION_FORMAT_AS_PRIVATE: 211 startActivity(FormatActivity.getFormatAsPrivateIntent(getActivity(), mDiskId)); 212 break; 213 case ACTION_UNMOUNT: 214 // If we've mounted a volume, eject it. Otherwise just treat eject as cancel 215 if (!TextUtils.isEmpty(mVolumeId)) { 216 startActivity( 217 UnmountActivity.getIntent(getActivity(), mVolumeId, mDescription)); 218 } 219 break; 220 } 221 getActivity().finish(); 222 } 223 checkForUnmount()224 private void checkForUnmount() { 225 if (!isAdded()) { 226 return; 227 } 228 229 final StorageManager storageManager = 230 getContext().getSystemService(StorageManager.class); 231 232 if (!TextUtils.isEmpty(mDiskId)) { 233 // If the disk disappears, assume we're done 234 final List<DiskInfo> diskInfos = storageManager.getDisks(); 235 boolean found = false; 236 for (DiskInfo diskInfo : diskInfos) { 237 if (TextUtils.equals(diskInfo.getId(), mDiskId)) { 238 found = true; 239 break; 240 } 241 } 242 if (!found) { 243 getActivity().finish(); 244 } 245 } else if (!TextUtils.isEmpty(mVolumeId)) { 246 final List<VolumeInfo> volumeInfos = storageManager.getVolumes(); 247 boolean found = false; 248 for (VolumeInfo volumeInfo : volumeInfos) { 249 if (TextUtils.equals(volumeInfo.getId(), mVolumeId)) { 250 found = true; 251 break; 252 } 253 } 254 if (!found) { 255 getActivity().finish(); 256 } 257 } 258 } 259 } 260 261 public static class MissingStorageFragment extends GuidedStepFragment { 262 263 private String mFsUuid; 264 private String mDescription; 265 266 private final BroadcastReceiver mDiskReceiver = new BroadcastReceiver() { 267 @Override 268 public void onReceive(Context context, Intent intent) { 269 if (TextUtils.equals(intent.getAction(), VolumeInfo.ACTION_VOLUME_STATE_CHANGED)) { 270 checkForRemount(); 271 } 272 } 273 }; 274 newInstance(String fsUuid)275 public static MissingStorageFragment newInstance(String fsUuid) { 276 final MissingStorageFragment fragment = new MissingStorageFragment(); 277 final Bundle b = new Bundle(1); 278 b.putString(VolumeRecord.EXTRA_FS_UUID, fsUuid); 279 fragment.setArguments(b); 280 return fragment; 281 } 282 283 @Override onCreate(Bundle savedInstanceState)284 public void onCreate(Bundle savedInstanceState) { 285 StorageManager storageManager = getActivity().getSystemService(StorageManager.class); 286 mFsUuid = getArguments().getString(VolumeRecord.EXTRA_FS_UUID); 287 if (TextUtils.isEmpty(mFsUuid)) { 288 throw new IllegalStateException( 289 "MissingStorageFragment launched without specifying missing storage"); 290 } 291 final VolumeRecord volumeRecord = storageManager.findRecordByUuid(mFsUuid); 292 mDescription = volumeRecord.getNickname(); 293 294 super.onCreate(savedInstanceState); 295 } 296 297 @Override onStart()298 public void onStart() { 299 super.onStart(); 300 getContext().registerReceiver(mDiskReceiver, 301 new IntentFilter(VolumeInfo.ACTION_VOLUME_STATE_CHANGED)); 302 checkForRemount(); 303 } 304 305 @Override onStop()306 public void onStop() { 307 super.onStop(); 308 getContext().unregisterReceiver(mDiskReceiver); 309 } 310 311 @Override onCreateGuidance(Bundle savedInstanceState)312 public @NonNull GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) { 313 return new GuidanceStylist.Guidance( 314 getString(R.string.storage_missing_title, mDescription), 315 getString(R.string.storage_missing_description), 316 null, 317 getActivity().getDrawable(R.drawable.ic_error_132dp)); 318 } 319 320 @Override onCreateActions(@onNull List<GuidedAction> actions, Bundle savedInstanceState)321 public void onCreateActions(@NonNull List<GuidedAction> actions, 322 Bundle savedInstanceState) { 323 actions.add(new GuidedAction.Builder(getContext()) 324 .clickAction(GuidedAction.ACTION_ID_OK) 325 .build()); 326 } 327 328 @Override onGuidedActionClicked(GuidedAction action)329 public void onGuidedActionClicked(GuidedAction action) { 330 getActivity().finish(); 331 } 332 checkForRemount()333 private void checkForRemount() { 334 if (!isAdded()) { 335 return; 336 } 337 338 final List<VolumeInfo> volumeInfos = 339 getContext().getSystemService(StorageManager.class).getVolumes(); 340 341 for (final VolumeInfo info : volumeInfos) { 342 if (!TextUtils.equals(info.getFsUuid(), mFsUuid)) { 343 continue; 344 } 345 if (info.isMountedReadable()) { 346 getActivity().finish(); 347 } 348 } 349 } 350 } 351 352 } 353