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