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 static android.os.storage.DiskInfo.EXTRA_DISK_ID; 20 import static android.os.storage.VolumeInfo.EXTRA_VOLUME_ID; 21 22 import android.annotation.LayoutRes; 23 import android.annotation.NonNull; 24 import android.content.Intent; 25 import android.content.res.Resources.Theme; 26 import android.graphics.drawable.Drawable; 27 import android.os.Bundle; 28 import android.os.SystemClock; 29 import android.os.storage.DiskInfo; 30 import android.os.storage.StorageEventListener; 31 import android.os.storage.StorageManager; 32 import android.os.storage.VolumeInfo; 33 import android.text.TextUtils; 34 import android.util.Log; 35 import android.view.LayoutInflater; 36 import android.view.View; 37 import android.widget.FrameLayout; 38 import android.widget.ProgressBar; 39 import android.widget.TextView; 40 41 import androidx.fragment.app.FragmentActivity; 42 43 import com.android.settings.R; 44 import com.android.settingslib.Utils; 45 46 import com.google.android.setupcompat.template.FooterBarMixin; 47 import com.google.android.setupcompat.template.FooterButton; 48 import com.google.android.setupdesign.GlifLayout; 49 50 import java.text.NumberFormat; 51 import java.util.List; 52 import java.util.Objects; 53 54 public abstract class StorageWizardBase extends FragmentActivity { 55 private static final String TAG = "StorageWizardBase"; 56 57 protected static final String EXTRA_FORMAT_FORGET_UUID = "format_forget_uuid"; 58 protected static final String EXTRA_FORMAT_PRIVATE = "format_private"; 59 protected static final String EXTRA_FORMAT_SLOW = "format_slow"; 60 protected static final String EXTRA_MIGRATE_SKIP = "migrate_skip"; 61 62 protected StorageManager mStorage; 63 64 protected VolumeInfo mVolume; 65 protected DiskInfo mDisk; 66 67 private FooterBarMixin mFooterBarMixin; 68 private FooterButton mBack; 69 private FooterButton mNext; 70 71 @Override onCreate(Bundle savedInstanceState)72 protected void onCreate(Bundle savedInstanceState) { 73 super.onCreate(savedInstanceState); 74 75 mStorage = getSystemService(StorageManager.class); 76 77 final String volumeId = getIntent().getStringExtra(EXTRA_VOLUME_ID); 78 if (!TextUtils.isEmpty(volumeId)) { 79 mVolume = mStorage.findVolumeById(volumeId); 80 } 81 82 final String diskId = getIntent().getStringExtra(EXTRA_DISK_ID); 83 if (!TextUtils.isEmpty(diskId)) { 84 mDisk = mStorage.findDiskById(diskId); 85 } else if (mVolume != null) { 86 mDisk = mVolume.getDisk(); 87 } 88 89 if (mDisk != null) { 90 mStorage.registerListener(mStorageListener); 91 } 92 } 93 94 @Override setContentView(@ayoutRes int layoutResID)95 public void setContentView(@LayoutRes int layoutResID) { 96 super.setContentView(layoutResID); 97 98 mFooterBarMixin = getGlifLayout().getMixin(FooterBarMixin.class); 99 mFooterBarMixin.setSecondaryButton( 100 new FooterButton.Builder(this) 101 .setText(R.string.wizard_back) 102 .setListener(this::onNavigateBack) 103 .setButtonType(FooterButton.ButtonType.OTHER) 104 .setTheme(R.style.SudGlifButton_Secondary) 105 .build() 106 ); 107 mFooterBarMixin.setPrimaryButton( 108 new FooterButton.Builder(this) 109 .setText(R.string.wizard_next) 110 .setListener(this::onNavigateNext) 111 .setButtonType(FooterButton.ButtonType.NEXT) 112 .setTheme(R.style.SudGlifButton_Primary) 113 .build() 114 ); 115 mBack = mFooterBarMixin.getSecondaryButton(); 116 mNext = mFooterBarMixin.getPrimaryButton(); 117 118 setIcon(com.android.internal.R.drawable.ic_sd_card_48dp); 119 } 120 121 @Override onDestroy()122 protected void onDestroy() { 123 mStorage.unregisterListener(mStorageListener); 124 super.onDestroy(); 125 } 126 127 @Override onApplyThemeResource(Theme theme, int resid, boolean first)128 protected void onApplyThemeResource(Theme theme, int resid, boolean first) { 129 theme.applyStyle(R.style.SetupWizardPartnerResource, true); 130 super.onApplyThemeResource(theme, resid, first); 131 } 132 getBackButton()133 protected FooterButton getBackButton() { 134 return mBack; 135 } 136 getNextButton()137 protected FooterButton getNextButton() { 138 return mNext; 139 } 140 getGlifLayout()141 protected GlifLayout getGlifLayout() { 142 return requireViewById(R.id.setup_wizard_layout); 143 } 144 getProgressBar()145 protected ProgressBar getProgressBar() { 146 return requireViewById(R.id.storage_wizard_progress); 147 } 148 setCurrentProgress(int progress)149 protected void setCurrentProgress(int progress) { 150 getProgressBar().setProgress(progress); 151 ((TextView) requireViewById(R.id.storage_wizard_progress_summary)).setText( 152 NumberFormat.getPercentInstance().format((double) progress / 100)); 153 } 154 setHeaderText(int resId, CharSequence... args)155 protected void setHeaderText(int resId, CharSequence... args) { 156 final CharSequence headerText = TextUtils.expandTemplate(getText(resId), args); 157 getGlifLayout().setHeaderText(headerText); 158 setTitle(headerText); 159 } 160 setBodyText(int resId, CharSequence... args)161 protected void setBodyText(int resId, CharSequence... args) { 162 final TextView body = requireViewById(R.id.storage_wizard_body); 163 body.setText(TextUtils.expandTemplate(getText(resId), args)); 164 body.setVisibility(View.VISIBLE); 165 } 166 setAuxChecklist()167 protected void setAuxChecklist() { 168 final FrameLayout aux = requireViewById(R.id.storage_wizard_aux); 169 aux.addView(LayoutInflater.from(aux.getContext()) 170 .inflate(R.layout.storage_wizard_checklist, aux, false)); 171 aux.setVisibility(View.VISIBLE); 172 173 // Customize string based on disk 174 ((TextView) aux.requireViewById(R.id.storage_wizard_migrate_v2_checklist_media)) 175 .setText(TextUtils.expandTemplate( 176 getText(R.string.storage_wizard_migrate_v2_checklist_media), 177 getDiskShortDescription())); 178 } 179 setBackButtonText(int resId, CharSequence... args)180 protected void setBackButtonText(int resId, CharSequence... args) { 181 mBack.setText(TextUtils.expandTemplate(getText(resId), args)); 182 mBack.setVisibility(View.VISIBLE); 183 } 184 setNextButtonText(int resId, CharSequence... args)185 protected void setNextButtonText(int resId, CharSequence... args) { 186 mNext.setText(TextUtils.expandTemplate(getText(resId), args)); 187 mNext.setVisibility(View.VISIBLE); 188 } 189 setBackButtonVisibility(int visible)190 protected void setBackButtonVisibility(int visible) { 191 mBack.setVisibility(visible); 192 } 193 setNextButtonVisibility(int visible)194 protected void setNextButtonVisibility(int visible) { 195 mNext.setVisibility(visible); 196 } 197 setIcon(int resId)198 protected void setIcon(int resId) { 199 final GlifLayout layout = getGlifLayout(); 200 final Drawable icon = getDrawable(resId).mutate(); 201 icon.setTintList(Utils.getColorAccent(layout.getContext())); 202 layout.setIcon(icon); 203 } 204 setKeepScreenOn(boolean keepScreenOn)205 protected void setKeepScreenOn(boolean keepScreenOn) { 206 getGlifLayout().setKeepScreenOn(keepScreenOn); 207 } 208 onNavigateBack(View view)209 public void onNavigateBack(View view) { 210 throw new UnsupportedOperationException(); 211 } 212 onNavigateNext(View view)213 public void onNavigateNext(View view) { 214 throw new UnsupportedOperationException(); 215 } 216 copyStringExtra(Intent from, Intent to, String key)217 private void copyStringExtra(Intent from, Intent to, String key) { 218 if (from.hasExtra(key) && !to.hasExtra(key)) { 219 to.putExtra(key, from.getStringExtra(key)); 220 } 221 } 222 copyBooleanExtra(Intent from, Intent to, String key)223 private void copyBooleanExtra(Intent from, Intent to, String key) { 224 if (from.hasExtra(key) && !to.hasExtra(key)) { 225 to.putExtra(key, from.getBooleanExtra(key, false)); 226 } 227 } 228 229 @Override startActivity(Intent intent)230 public void startActivity(Intent intent) { 231 final Intent from = getIntent(); 232 final Intent to = intent; 233 234 copyStringExtra(from, to, EXTRA_DISK_ID); 235 copyStringExtra(from, to, EXTRA_VOLUME_ID); 236 copyStringExtra(from, to, EXTRA_FORMAT_FORGET_UUID); 237 copyBooleanExtra(from, to, EXTRA_FORMAT_PRIVATE); 238 copyBooleanExtra(from, to, EXTRA_FORMAT_SLOW); 239 copyBooleanExtra(from, to, EXTRA_MIGRATE_SKIP); 240 241 super.startActivity(intent); 242 } 243 findFirstVolume(int type)244 protected VolumeInfo findFirstVolume(int type) { 245 return findFirstVolume(type, 1); 246 } 247 findFirstVolume(int type, int attempts)248 protected VolumeInfo findFirstVolume(int type, int attempts) { 249 while (true) { 250 final List<VolumeInfo> vols = mStorage.getVolumes(); 251 for (VolumeInfo vol : vols) { 252 if (Objects.equals(mDisk.getId(), vol.getDiskId()) && (vol.getType() == type) 253 && (vol.getState() == VolumeInfo.STATE_MOUNTED)) { 254 return vol; 255 } 256 } 257 258 if (--attempts > 0) { 259 Log.w(TAG, "Missing mounted volume of type " + type + " hosted by disk " 260 + mDisk.getId() + "; trying again"); 261 SystemClock.sleep(250); 262 } else { 263 return null; 264 } 265 } 266 } 267 getDiskDescription()268 protected @NonNull CharSequence getDiskDescription() { 269 if (mDisk != null) { 270 return mDisk.getDescription(); 271 } else if (mVolume != null) { 272 return mVolume.getDescription(); 273 } else { 274 return getText(R.string.unknown); 275 } 276 } 277 getDiskShortDescription()278 protected @NonNull CharSequence getDiskShortDescription() { 279 if (mDisk != null) { 280 return mDisk.getShortDescription(); 281 } else if (mVolume != null) { 282 return mVolume.getDescription(); 283 } else { 284 return getText(R.string.unknown); 285 } 286 } 287 288 private final StorageEventListener mStorageListener = new StorageEventListener() { 289 @Override 290 public void onDiskDestroyed(DiskInfo disk) { 291 // We know mDisk != null. 292 if (mDisk.id.equals(disk.id)) { 293 finish(); 294 } 295 } 296 }; 297 } 298