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 17package com.android.tv.settings.device.storage; 18 19import android.annotation.Nullable; 20import android.app.Activity; 21import android.app.Fragment; 22import android.app.LoaderManager; 23import android.content.Context; 24import android.content.Intent; 25import android.content.Loader; 26import android.os.Bundle; 27import android.os.Handler; 28import android.os.storage.StorageManager; 29import android.os.storage.VolumeInfo; 30import android.support.annotation.NonNull; 31import android.support.v17.leanback.widget.GuidanceStylist; 32import android.support.v17.leanback.widget.GuidedAction; 33import android.text.TextUtils; 34import android.util.Log; 35import android.view.View; 36import android.widget.Toast; 37 38import com.android.tv.settings.R; 39import com.android.tv.settings.dialog.ProgressDialogFragment; 40import com.android.tv.settings.util.SettingsAsyncTaskLoader; 41 42import java.util.List; 43 44public class UnmountActivity extends Activity { 45 46 private static final String TAG = "UnmountActivity"; 47 48 public static final String EXTRA_VOLUME_DESC = "UnmountActivity.volumeDesc"; 49 50 private static final int LOADER_UNMOUNT = 0; 51 52 private String mUnmountVolumeId; 53 private String mUnmountVolumeDesc; 54 // True if we're waiting for an unmount loader to complete 55 private boolean mUnmounting; 56 57 private final Handler mHandler = new Handler(); 58 59 public static Intent getIntent(Context context, String volumeId, String volumeDesc) { 60 final Intent i = new Intent(context, UnmountActivity.class); 61 i.putExtra(VolumeInfo.EXTRA_VOLUME_ID, volumeId); 62 i.putExtra(EXTRA_VOLUME_DESC, volumeDesc); 63 return i; 64 } 65 66 @Override 67 protected void onCreate(@Nullable Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 70 mUnmountVolumeId = getIntent().getStringExtra(VolumeInfo.EXTRA_VOLUME_ID); 71 mUnmountVolumeDesc = getIntent().getStringExtra(EXTRA_VOLUME_DESC); 72 73 if (savedInstanceState == null) { 74 final StorageManager storageManager = getSystemService(StorageManager.class); 75 final VolumeInfo volumeInfo = storageManager.findVolumeById(mUnmountVolumeId); 76 77 if (volumeInfo == null) { 78 // Unmounted already, just bail 79 finish(); 80 return; 81 } 82 83 if (volumeInfo.getType() == VolumeInfo.TYPE_PRIVATE) { 84 final Fragment fragment = UnmountInternalStepFragment.newInstance(mUnmountVolumeId); 85 getFragmentManager().beginTransaction() 86 .replace(android.R.id.content, fragment) 87 .commit(); 88 } else { 89 // Jump straight to unmounting 90 onRequestUnmount(); 91 } 92 } 93 } 94 95 public void onRequestUnmount() { 96 mUnmounting = true; 97 final Fragment fragment = UnmountProgressFragment.newInstance(mUnmountVolumeDesc); 98 getFragmentManager().beginTransaction() 99 .replace(android.R.id.content, fragment) 100 .commit(); 101 kickUnmountLoader(); 102 } 103 104 @Override 105 protected void onResume() { 106 super.onResume(); 107 kickUnmountLoader(); 108 } 109 110 private void kickUnmountLoader() { 111 if (mUnmounting) { 112 getLoaderManager().initLoader(LOADER_UNMOUNT, null, 113 new UnmountLoaderCallback(mUnmountVolumeId, mUnmountVolumeDesc)); 114 } 115 } 116 117 private static class UnmountTaskLoader extends SettingsAsyncTaskLoader<Boolean> { 118 119 private final StorageManager mStorageManager; 120 private final String mVolumeId; 121 122 public UnmountTaskLoader(Context context, String volumeId) { 123 super(context); 124 mStorageManager = context.getSystemService(StorageManager.class); 125 mVolumeId = volumeId; 126 } 127 128 @Override 129 protected void onDiscardResult(Boolean result) {} 130 131 @Override 132 public Boolean loadInBackground() { 133 try { 134 final long minTime = System.currentTimeMillis() + 3000; 135 136 mStorageManager.unmount(mVolumeId); 137 138 long waitTime = minTime - System.currentTimeMillis(); 139 while (waitTime > 0) { 140 try { 141 Thread.sleep(waitTime); 142 } catch (InterruptedException e) { 143 // Ignore 144 } 145 waitTime = minTime - System.currentTimeMillis(); 146 } 147 return true; 148 } catch (Exception e) { 149 Log.d(TAG, "Could not unmount", e); 150 return false; 151 } 152 } 153 } 154 155 private class UnmountLoaderCallback implements LoaderManager.LoaderCallbacks<Boolean> { 156 157 private final String mVolumeId; 158 private final String mVolumeDescription; 159 160 public UnmountLoaderCallback(String volumeId, String volumeDescription) { 161 mVolumeId = volumeId; 162 mVolumeDescription = volumeDescription; 163 } 164 165 @Override 166 public Loader<Boolean> onCreateLoader(int id, Bundle args) { 167 return new UnmountTaskLoader(UnmountActivity.this, mVolumeId); 168 } 169 170 @Override 171 public void onLoadFinished(Loader<Boolean> loader, final Boolean success) { 172 if (success == null) { 173 // No results yet, wait for something interesting to come in. 174 return; 175 } 176 mHandler.post(new Runnable() { 177 @Override 178 public void run() { 179 if (isResumed() && TextUtils.equals(mUnmountVolumeId, mVolumeId)) { 180 if (success) { 181 Toast.makeText(UnmountActivity.this, 182 getString(R.string.storage_unmount_success, mVolumeDescription), 183 Toast.LENGTH_SHORT).show(); 184 } else { 185 Toast.makeText(UnmountActivity.this, 186 getString(R.string.storage_unmount_failure, mVolumeDescription), 187 Toast.LENGTH_SHORT).show(); 188 } 189 190 mUnmountVolumeId = null; 191 mUnmountVolumeDesc = null; 192 getLoaderManager().destroyLoader(LOADER_UNMOUNT); 193 194 finish(); 195 } 196 } 197 }); 198 } 199 200 @Override 201 public void onLoaderReset(Loader<Boolean> loader) {} 202 } 203 204 public static class UnmountInternalStepFragment extends StorageGuidedStepFragment { 205 206 private static final int ACTION_ID_CANCEL = 0; 207 private static final int ACTION_ID_UNMOUNT = 1; 208 209 public static UnmountInternalStepFragment newInstance(String volumeId) { 210 final UnmountInternalStepFragment fragment = new UnmountInternalStepFragment(); 211 final Bundle b = new Bundle(1); 212 b.putString(VolumeInfo.EXTRA_VOLUME_ID, volumeId); 213 fragment.setArguments(b); 214 return fragment; 215 } 216 217 @Override 218 public @NonNull 219 GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) { 220 return new GuidanceStylist.Guidance( 221 getString(R.string.storage_wizard_eject_internal_title), 222 getString(R.string.storage_wizard_eject_internal_description), "", 223 getActivity().getDrawable(R.drawable.ic_settings_storage)); 224 } 225 226 @Override 227 public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { 228 actions.add(new GuidedAction.Builder() 229 .id(ACTION_ID_CANCEL) 230 .title(getString(android.R.string.cancel)) 231 .build()); 232 actions.add(new GuidedAction.Builder() 233 .id(ACTION_ID_UNMOUNT) 234 .title(getString(R.string.storage_eject)) 235 .build()); 236 } 237 238 @Override 239 public void onGuidedActionClicked(GuidedAction action) { 240 final long id = action.getId(); 241 242 if (id == ACTION_ID_CANCEL) { 243 getFragmentManager().popBackStack(); 244 } else if (id == ACTION_ID_UNMOUNT) { 245 ((UnmountActivity) getActivity()).onRequestUnmount(); 246 } 247 } 248 } 249 250 public static class UnmountProgressFragment extends ProgressDialogFragment { 251 252 private static final String ARG_DESCRIPTION = "description"; 253 254 public static UnmountProgressFragment newInstance(CharSequence description) { 255 final Bundle b = new Bundle(1); 256 b.putCharSequence(ARG_DESCRIPTION, description); 257 final UnmountProgressFragment fragment = new UnmountProgressFragment(); 258 fragment.setArguments(b); 259 return fragment; 260 } 261 262 @Override 263 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 264 super.onViewCreated(view, savedInstanceState); 265 final CharSequence description = getArguments().getCharSequence(ARG_DESCRIPTION); 266 // TODO: fix this string name 267 setTitle(getActivity().getString(R.string.sotrage_wizard_eject_progress_title, 268 description)); 269 } 270 } 271}