1 /* 2 * Copyright (C) 2021 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 package com.android.wallpaper.picker; 17 18 import static com.android.wallpaper.util.ActivityUtils.isSUWMode; 19 import static com.android.wallpaper.util.ActivityUtils.isWallpaperOnlyMode; 20 import static com.android.wallpaper.util.ActivityUtils.startActivityForResultSafely; 21 22 import android.app.Activity; 23 import android.content.Intent; 24 import android.os.Bundle; 25 import android.provider.Settings; 26 import android.text.TextUtils; 27 import android.util.Log; 28 29 import androidx.annotation.NonNull; 30 import androidx.annotation.Nullable; 31 import androidx.core.view.WindowCompat; 32 import androidx.fragment.app.Fragment; 33 import androidx.fragment.app.FragmentActivity; 34 import androidx.fragment.app.FragmentManager; 35 36 import com.android.wallpaper.R; 37 import com.android.wallpaper.model.Category; 38 import com.android.wallpaper.model.PermissionRequester; 39 import com.android.wallpaper.model.WallpaperCategory; 40 import com.android.wallpaper.model.WallpaperInfo; 41 import com.android.wallpaper.model.WallpaperPreviewNavigator; 42 import com.android.wallpaper.module.DailyLoggingAlarmScheduler; 43 import com.android.wallpaper.module.Injector; 44 import com.android.wallpaper.module.InjectorProvider; 45 import com.android.wallpaper.module.LargeScreenMultiPanesChecker; 46 import com.android.wallpaper.module.MultiPanesChecker; 47 import com.android.wallpaper.module.NetworkStatusNotifier; 48 import com.android.wallpaper.module.NetworkStatusNotifier.NetworkStatus; 49 import com.android.wallpaper.module.UserEventLogger; 50 import com.android.wallpaper.picker.AppbarFragment.AppbarFragmentHost; 51 import com.android.wallpaper.picker.CategoryFragment.CategoryFragmentHost; 52 import com.android.wallpaper.picker.CategorySelectorFragment.CategorySelectorFragmentHost; 53 import com.android.wallpaper.picker.MyPhotosStarter.PermissionChangedListener; 54 import com.android.wallpaper.picker.individual.IndividualPickerFragment.IndividualPickerFragmentHost; 55 import com.android.wallpaper.util.ActivityUtils; 56 import com.android.wallpaper.util.DeepLinkUtils; 57 import com.android.wallpaper.util.LaunchUtils; 58 import com.android.wallpaper.widget.BottomActionBar; 59 import com.android.wallpaper.widget.BottomActionBar.BottomActionBarHost; 60 61 /** 62 * Main Activity allowing containing view sections for the user to switch between the different 63 * Fragments providing customization options. 64 */ 65 public class CustomizationPickerActivity extends FragmentActivity implements AppbarFragmentHost, 66 WallpapersUiContainer, CategoryFragmentHost, BottomActionBarHost, 67 FragmentTransactionChecker, PermissionRequester, CategorySelectorFragmentHost, 68 IndividualPickerFragmentHost, WallpaperPreviewNavigator { 69 70 private static final String TAG = "CustomizationPickerActivity"; 71 72 private WallpaperPickerDelegate mDelegate; 73 private UserEventLogger mUserEventLogger; 74 private NetworkStatusNotifier mNetworkStatusNotifier; 75 private NetworkStatusNotifier.Listener mNetworkStatusListener; 76 @NetworkStatus private int mNetworkStatus; 77 78 private BottomActionBar mBottomActionBar; 79 private boolean mIsSafeToCommitFragmentTransaction; 80 81 @Override onCreate(@ullable Bundle savedInstanceState)82 protected void onCreate(@Nullable Bundle savedInstanceState) { 83 Injector injector = InjectorProvider.getInjector(); 84 mDelegate = new WallpaperPickerDelegate(this, this, injector); 85 mUserEventLogger = injector.getUserEventLogger(this); 86 mNetworkStatusNotifier = injector.getNetworkStatusNotifier(this); 87 mNetworkStatus = mNetworkStatusNotifier.getNetworkStatus(); 88 89 // Restore this Activity's state before restoring contained Fragments state. 90 super.onCreate(savedInstanceState); 91 // Trampoline for the two panes 92 final MultiPanesChecker mMultiPanesChecker = new LargeScreenMultiPanesChecker(); 93 if (mMultiPanesChecker.isMultiPanesEnabled(this)) { 94 Intent intent = getIntent(); 95 if (!ActivityUtils.isLaunchedFromSettingsTrampoline(intent) 96 && !ActivityUtils.isLaunchedFromSettingsRelated(intent)) { 97 startActivityForResultSafely(this, 98 mMultiPanesChecker.getMultiPanesIntent(intent), /* requestCode= */ 0); 99 finish(); 100 } 101 } 102 103 setContentView(R.layout.activity_customization_picker); 104 mBottomActionBar = findViewById(R.id.bottom_actionbar); 105 106 // See go/pdr-edge-to-edge-guide. 107 WindowCompat.setDecorFitsSystemWindows(getWindow(), isSUWMode(this)); 108 109 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container); 110 if (fragment == null) { 111 // App launch specific logic: log the "app launch source" event. 112 if (getIntent() != null) { 113 mUserEventLogger.logAppLaunched(getIntent()); 114 } 115 injector.getPreferences(this).incrementAppLaunched(); 116 DailyLoggingAlarmScheduler.setAlarm(getApplicationContext()); 117 118 // Switch to the target fragment. 119 switchFragment(isWallpaperOnlyMode(getIntent()) 120 ? new WallpaperOnlyFragment() 121 : new CustomizationPickerFragment()); 122 } 123 124 // Deep link case 125 Intent intent = getIntent(); 126 String deepLinkCollectionId = DeepLinkUtils.getCollectionId(intent); 127 if (!TextUtils.isEmpty(deepLinkCollectionId)) { 128 switchFragmentWithBackStack(new CategorySelectorFragment()); 129 switchFragmentWithBackStack(InjectorProvider.getInjector().getIndividualPickerFragment( 130 deepLinkCollectionId)); 131 intent.setData(null); 132 } 133 mDelegate.prefetchCategories(); 134 } 135 136 @Override onStart()137 protected void onStart() { 138 super.onStart(); 139 if (mNetworkStatusListener == null) { 140 mNetworkStatusListener = status -> { 141 if (status == mNetworkStatus) { 142 return; 143 } 144 Log.i(TAG, "Network status changes, refresh wallpaper categories."); 145 mNetworkStatus = status; 146 mDelegate.initialize(/* forceCategoryRefresh= */ true); 147 }; 148 // Upon registering a listener, the onNetworkChanged method is immediately called with 149 // the initial network status. 150 mNetworkStatusNotifier.registerListener(mNetworkStatusListener); 151 } 152 } 153 154 @Override onResume()155 protected void onResume() { 156 super.onResume(); 157 mIsSafeToCommitFragmentTransaction = true; 158 boolean wallpaperOnly = isWallpaperOnlyMode(getIntent()); 159 boolean provisioned = Settings.Global.getInt(getContentResolver(), 160 Settings.Global.DEVICE_PROVISIONED, 0) != 0; 161 162 mUserEventLogger.logResumed(provisioned, wallpaperOnly); 163 } 164 165 @Override onPause()166 protected void onPause() { 167 super.onPause(); 168 mIsSafeToCommitFragmentTransaction = false; 169 } 170 171 @Override onStop()172 protected void onStop() { 173 mUserEventLogger.logStopped(); 174 if (mNetworkStatusListener != null) { 175 mNetworkStatusNotifier.unregisterListener(mNetworkStatusListener); 176 mNetworkStatusListener = null; 177 } 178 super.onStop(); 179 } 180 181 @Override onBackPressed()182 public void onBackPressed() { 183 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container); 184 if (fragment instanceof BottomActionBarFragment 185 && ((BottomActionBarFragment) fragment).onBackPressed()) { 186 return; 187 } 188 189 if (getSupportFragmentManager().popBackStackImmediate()) { 190 return; 191 } 192 if (moveTaskToBack(false)) { 193 return; 194 } 195 super.onBackPressed(); 196 } 197 switchFragment(Fragment fragment)198 private void switchFragment(Fragment fragment) { 199 getSupportFragmentManager() 200 .beginTransaction() 201 .replace(R.id.fragment_container, fragment) 202 .commitNow(); 203 } 204 switchFragmentWithBackStack(Fragment fragment)205 private void switchFragmentWithBackStack(Fragment fragment) { 206 getSupportFragmentManager() 207 .beginTransaction() 208 .replace(R.id.fragment_container, fragment) 209 .addToBackStack(null) 210 .commit(); 211 getSupportFragmentManager().executePendingTransactions(); 212 } 213 214 215 @Override requestExternalStoragePermission(PermissionChangedListener listener)216 public void requestExternalStoragePermission(PermissionChangedListener listener) { 217 mDelegate.requestExternalStoragePermission(listener); 218 } 219 220 @Override isReadExternalStoragePermissionGranted()221 public boolean isReadExternalStoragePermissionGranted() { 222 return mDelegate.isReadExternalStoragePermissionGranted(); 223 } 224 225 @Override showViewOnlyPreview(WallpaperInfo wallpaperInfo, boolean isViewAsHome)226 public void showViewOnlyPreview(WallpaperInfo wallpaperInfo, boolean isViewAsHome) { 227 mDelegate.showViewOnlyPreview(wallpaperInfo, isViewAsHome); 228 } 229 230 @Override show(String collectionId)231 public void show(String collectionId) { 232 mDelegate.show(collectionId); 233 } 234 235 @Override requestCustomPhotoPicker(PermissionChangedListener listener)236 public void requestCustomPhotoPicker(PermissionChangedListener listener) { 237 mDelegate.requestCustomPhotoPicker(listener); 238 } 239 240 @Override show(Category category)241 public void show(Category category) { 242 if (!(category instanceof WallpaperCategory)) { 243 show(category.getCollectionId()); 244 return; 245 } 246 switchFragmentWithBackStack(InjectorProvider.getInjector().getIndividualPickerFragment( 247 category.getCollectionId())); 248 } 249 250 @Override isHostToolbarShown()251 public boolean isHostToolbarShown() { 252 return false; 253 } 254 255 @Override setToolbarTitle(CharSequence title)256 public void setToolbarTitle(CharSequence title) { 257 258 } 259 260 @Override setToolbarMenu(int menuResId)261 public void setToolbarMenu(int menuResId) { 262 263 } 264 265 @Override removeToolbarMenu()266 public void removeToolbarMenu() { 267 268 } 269 270 @Override moveToPreviousFragment()271 public void moveToPreviousFragment() { 272 getSupportFragmentManager().popBackStack(); 273 } 274 275 @Override fetchCategories()276 public void fetchCategories() { 277 mDelegate.initialize(mDelegate.getCategoryProvider().shouldForceReload(this)); 278 } 279 280 @Override cleanUp()281 public void cleanUp() { 282 mDelegate.cleanUp(); 283 } 284 285 @Override onWallpapersReady()286 public void onWallpapersReady() { 287 288 } 289 290 @Nullable 291 @Override getCategorySelectorFragment()292 public CategorySelectorFragment getCategorySelectorFragment() { 293 FragmentManager fm = getSupportFragmentManager(); 294 Fragment fragment = fm.findFragmentById(R.id.fragment_container); 295 if (fragment instanceof CategorySelectorFragment) { 296 return (CategorySelectorFragment) fragment; 297 } else { 298 return null; 299 } 300 } 301 302 @Override onDestroy()303 protected void onDestroy() { 304 super.onDestroy(); 305 } 306 307 @Override doneFetchingCategories()308 public void doneFetchingCategories() { 309 310 } 311 312 @Override onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)313 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 314 @NonNull int[] grantResults) { 315 mDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults); 316 } 317 318 @Override getMyPhotosStarter()319 public MyPhotosStarter getMyPhotosStarter() { 320 return mDelegate; 321 } 322 323 @Override onActivityResult(int requestCode, int resultCode, @Nullable Intent data)324 protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 325 super.onActivityResult(requestCode, resultCode, data); 326 if (mDelegate.handleActivityResult(requestCode, resultCode, data)) { 327 if (isSUWMode(this)) { 328 finishActivityForSUW(); 329 } else { 330 finishActivityWithResultOk(); 331 } 332 } 333 } 334 finishActivityWithResultOk()335 private void finishActivityWithResultOk() { 336 overridePendingTransition(R.anim.fade_in, R.anim.fade_out); 337 setResult(Activity.RESULT_OK); 338 finish(); 339 340 // Go back to launcher home 341 LaunchUtils.launchHome(this); 342 } 343 finishActivityForSUW()344 private void finishActivityForSUW() { 345 overridePendingTransition(R.anim.fade_in, R.anim.fade_out); 346 // Return RESULT_CANCELED to make the "Change wallpaper" tile in SUW not be disabled. 347 setResult(Activity.RESULT_CANCELED); 348 finish(); 349 } 350 351 @Override getBottomActionBar()352 public BottomActionBar getBottomActionBar() { 353 return mBottomActionBar; 354 } 355 356 @Override isSafeToCommitFragmentTransaction()357 public boolean isSafeToCommitFragmentTransaction() { 358 return mIsSafeToCommitFragmentTransaction; 359 } 360 361 @Override onUpArrowPressed()362 public void onUpArrowPressed() { 363 // TODO(b/189166781): Remove interface AppbarFragmentHost#onUpArrowPressed. 364 onBackPressed(); 365 } 366 367 @Override isUpArrowSupported()368 public boolean isUpArrowSupported() { 369 return !isSUWMode(this); 370 } 371 } 372