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