• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.CustomizationSectionController.CustomizationSectionNavigationController;
39 import com.android.wallpaper.model.PermissionRequester;
40 import com.android.wallpaper.model.WallpaperCategory;
41 import com.android.wallpaper.model.WallpaperInfo;
42 import com.android.wallpaper.model.WallpaperPreviewNavigator;
43 import com.android.wallpaper.module.DailyLoggingAlarmScheduler;
44 import com.android.wallpaper.module.Injector;
45 import com.android.wallpaper.module.InjectorProvider;
46 import com.android.wallpaper.module.LargeScreenMultiPanesChecker;
47 import com.android.wallpaper.module.MultiPanesChecker;
48 import com.android.wallpaper.module.NetworkStatusNotifier;
49 import com.android.wallpaper.module.NetworkStatusNotifier.NetworkStatus;
50 import com.android.wallpaper.module.UserEventLogger;
51 import com.android.wallpaper.picker.AppbarFragment.AppbarFragmentHost;
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, BottomActionBarHost, FragmentTransactionChecker,
67         PermissionRequester, CategorySelectorFragmentHost, IndividualPickerFragmentHost,
68         WallpaperPreviewNavigator {
69 
70     private static final String TAG = "CustomizationPickerActivity";
71     private static final String EXTRA_DESTINATION = "destination";
72 
73     private WallpaperPickerDelegate mDelegate;
74     private UserEventLogger mUserEventLogger;
75     private NetworkStatusNotifier mNetworkStatusNotifier;
76     private NetworkStatusNotifier.Listener mNetworkStatusListener;
77     @NetworkStatus private int mNetworkStatus;
78 
79     private BottomActionBar mBottomActionBar;
80     private boolean mIsSafeToCommitFragmentTransaction;
81     private boolean mIsUseRevampedUi;
82 
83     @Override
onCreate(@ullable Bundle savedInstanceState)84     protected void onCreate(@Nullable Bundle savedInstanceState) {
85         Injector injector = InjectorProvider.getInjector();
86         mDelegate = new WallpaperPickerDelegate(this, this, injector);
87         mUserEventLogger = injector.getUserEventLogger(this);
88         mNetworkStatusNotifier = injector.getNetworkStatusNotifier(this);
89         mNetworkStatus = mNetworkStatusNotifier.getNetworkStatus();
90 
91         // Restore this Activity's state before restoring contained Fragments state.
92         super.onCreate(savedInstanceState);
93         // Trampoline for the two panes
94         final MultiPanesChecker mMultiPanesChecker = new LargeScreenMultiPanesChecker();
95         if (mMultiPanesChecker.isMultiPanesEnabled(this)) {
96             Intent intent = getIntent();
97             if (!ActivityUtils.isLaunchedFromSettingsTrampoline(intent)
98                     && !ActivityUtils.isLaunchedFromSettingsRelated(intent)) {
99                 startActivityForResultSafely(this,
100                         mMultiPanesChecker.getMultiPanesIntent(intent), /* requestCode= */ 0);
101                 finish();
102             }
103         }
104 
105         setContentView(R.layout.activity_customization_picker);
106         mBottomActionBar = findViewById(R.id.bottom_actionbar);
107 
108         // See go/pdr-edge-to-edge-guide.
109         WindowCompat.setDecorFitsSystemWindows(getWindow(), isSUWMode(this));
110 
111         mIsUseRevampedUi = injector.getFlags().isUseRevampedUiEnabled(this);
112         final boolean startFromLockScreen = getIntent() == null
113                 || !ActivityUtils.isLaunchedFromLauncher(getIntent());
114 
115         Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
116         if (fragment == null) {
117             // App launch specific logic: log the "app launch source" event.
118             if (getIntent() != null) {
119                 mUserEventLogger.logAppLaunched(getIntent());
120             }
121             injector.getPreferences(this).incrementAppLaunched();
122             DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
123 
124             // Switch to the target fragment.
125             switchFragment(isWallpaperOnlyMode(getIntent())
126                     ? WallpaperOnlyFragment.newInstance(mIsUseRevampedUi)
127                     : CustomizationPickerFragment.newInstance(
128                             mIsUseRevampedUi, startFromLockScreen));
129 
130             // Cache the categories, but only if we're not restoring state (b/276767415).
131             mDelegate.prefetchCategories();
132         }
133 
134         if (savedInstanceState == null) {
135             // We only want to start a new undo session if this activity is brand-new. A non-new
136             // activity will have a non-null savedInstanceState.
137             if (mIsUseRevampedUi) {
138                 injector.getUndoInteractor(this).startSession();
139             }
140         }
141 
142         final Intent intent = getIntent();
143         final String navigationDestination = intent.getStringExtra(EXTRA_DESTINATION);
144         // Consume the destination and commit the intent back so the OS doesn't revert to the same
145         // destination when we change color or wallpaper (which causes the activity to be
146         // recreated).
147         intent.removeExtra(EXTRA_DESTINATION);
148         setIntent(intent);
149 
150         final String deepLinkCollectionId = DeepLinkUtils.getCollectionId(intent);
151 
152         if (!TextUtils.isEmpty(navigationDestination)) {
153             // Navigation deep link case
154             fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
155             if (fragment instanceof CustomizationSectionNavigationController) {
156                 final CustomizationSectionNavigationController navController =
157                         (CustomizationSectionNavigationController) fragment;
158                 navController.navigateTo(navigationDestination);
159             }
160         } else if (!TextUtils.isEmpty(deepLinkCollectionId)) {
161             // Wallpaper Collection deep link case
162             switchFragmentWithBackStack(new CategorySelectorFragment());
163             switchFragmentWithBackStack(InjectorProvider.getInjector().getIndividualPickerFragment(
164                     this, deepLinkCollectionId));
165             intent.setData(null);
166         }
167     }
168 
169     @Override
onStart()170     protected void onStart() {
171         super.onStart();
172         if (mNetworkStatusListener == null) {
173             mNetworkStatusListener = status -> {
174                 if (status == mNetworkStatus) {
175                     return;
176                 }
177                 Log.i(TAG, "Network status changes, refresh wallpaper categories.");
178                 mNetworkStatus = status;
179                 mDelegate.initialize(/* forceCategoryRefresh= */ true);
180             };
181             // Upon registering a listener, the onNetworkChanged method is immediately called with
182             // the initial network status.
183             mNetworkStatusNotifier.registerListener(mNetworkStatusListener);
184         }
185     }
186 
187     @Override
onResume()188     protected void onResume() {
189         super.onResume();
190         mIsSafeToCommitFragmentTransaction = true;
191         boolean wallpaperOnly = isWallpaperOnlyMode(getIntent());
192         boolean provisioned = Settings.Global.getInt(getContentResolver(),
193                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
194 
195         mUserEventLogger.logResumed(provisioned, wallpaperOnly);
196     }
197 
198     @Override
onPause()199     protected void onPause() {
200         super.onPause();
201         mIsSafeToCommitFragmentTransaction = false;
202     }
203 
204     @Override
onStop()205     protected void onStop() {
206         mUserEventLogger.logStopped();
207         if (mNetworkStatusListener != null) {
208             mNetworkStatusNotifier.unregisterListener(mNetworkStatusListener);
209             mNetworkStatusListener = null;
210         }
211         super.onStop();
212     }
213 
214     @Override
onBackPressed()215     public void onBackPressed() {
216         Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
217         if (fragment instanceof BottomActionBarFragment
218                 && ((BottomActionBarFragment) fragment).onBackPressed()) {
219             return;
220         }
221 
222         if (getSupportFragmentManager().popBackStackImmediate()) {
223             return;
224         }
225         if (moveTaskToBack(false)) {
226             return;
227         }
228         super.onBackPressed();
229     }
230 
switchFragment(Fragment fragment)231     private void switchFragment(Fragment fragment) {
232         getSupportFragmentManager()
233                 .beginTransaction()
234                 .replace(R.id.fragment_container, fragment)
235                 .commitNow();
236     }
237 
switchFragmentWithBackStack(Fragment fragment)238     private void switchFragmentWithBackStack(Fragment fragment) {
239         getSupportFragmentManager()
240                 .beginTransaction()
241                 .replace(R.id.fragment_container, fragment)
242                 .addToBackStack(null)
243                 .commit();
244         getSupportFragmentManager().executePendingTransactions();
245     }
246 
247 
248     @Override
requestExternalStoragePermission(PermissionChangedListener listener)249     public void requestExternalStoragePermission(PermissionChangedListener listener) {
250         mDelegate.requestExternalStoragePermission(listener);
251     }
252 
253     @Override
showViewOnlyPreview(WallpaperInfo wallpaperInfo, boolean isViewAsHome)254     public void showViewOnlyPreview(WallpaperInfo wallpaperInfo, boolean isViewAsHome) {
255         mDelegate.showViewOnlyPreview(wallpaperInfo, isViewAsHome);
256     }
257 
258     @Override
requestCustomPhotoPicker(PermissionChangedListener listener)259     public void requestCustomPhotoPicker(PermissionChangedListener listener) {
260         mDelegate.requestCustomPhotoPicker(listener);
261     }
262 
263     @Override
show(Category category)264     public void show(Category category) {
265         if (!(category instanceof WallpaperCategory)) {
266             mDelegate.show(category.getCollectionId());
267             return;
268         }
269         switchFragmentWithBackStack(InjectorProvider.getInjector().getIndividualPickerFragment(
270                 this, category.getCollectionId()));
271     }
272 
273     @Override
isHostToolbarShown()274     public boolean isHostToolbarShown() {
275         return false;
276     }
277 
278     @Override
setToolbarTitle(CharSequence title)279     public void setToolbarTitle(CharSequence title) {
280 
281     }
282 
283     @Override
setToolbarMenu(int menuResId)284     public void setToolbarMenu(int menuResId) {
285 
286     }
287 
288     @Override
removeToolbarMenu()289     public void removeToolbarMenu() {
290 
291     }
292 
293     @Override
moveToPreviousFragment()294     public void moveToPreviousFragment() {
295         getSupportFragmentManager().popBackStack();
296     }
297 
298     @Override
fetchCategories()299     public void fetchCategories() {
300         mDelegate.initialize(mDelegate.getCategoryProvider().shouldForceReload(this));
301     }
302 
303     @Override
cleanUp()304     public void cleanUp() {
305         mDelegate.cleanUp();
306     }
307 
308     @Override
onWallpapersReady()309     public void onWallpapersReady() {
310 
311     }
312 
313     @Nullable
314     @Override
getCategorySelectorFragment()315     public CategorySelectorFragment getCategorySelectorFragment() {
316         FragmentManager fm = getSupportFragmentManager();
317         Fragment fragment = fm.findFragmentById(R.id.fragment_container);
318         if (fragment instanceof CategorySelectorFragment) {
319             return (CategorySelectorFragment) fragment;
320         } else {
321             return null;
322         }
323     }
324 
325     @Override
onDestroy()326     protected void onDestroy() {
327         super.onDestroy();
328     }
329 
330     @Override
doneFetchingCategories()331     public void doneFetchingCategories() {
332 
333     }
334 
335     @Override
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)336     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
337             @NonNull int[] grantResults) {
338         mDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
339     }
340 
341     @Override
onActivityResult(int requestCode, int resultCode, @Nullable Intent data)342     protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
343         super.onActivityResult(requestCode, resultCode, data);
344         if (mDelegate.handleActivityResult(requestCode, resultCode, data)) {
345             if (isSUWMode(this)) {
346                 finishActivityForSUW();
347             } else if (mIsUseRevampedUi) {
348                 // We don't finish in the revamped UI to let the user have a chance to reset the
349                 // change they made, should they want to. We do, however, remove all the fragments
350                 // from our back stack to reveal the root fragment, revealing the main screen of the
351                 // app.
352                 final FragmentManager fragmentManager = getSupportFragmentManager();
353                 while (fragmentManager.getBackStackEntryCount() > 0) {
354                     fragmentManager.popBackStackImmediate();
355                 }
356             } else {
357                 finishActivityWithResultOk();
358             }
359         }
360     }
361 
finishActivityWithResultOk()362     private void finishActivityWithResultOk() {
363         overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
364         setResult(Activity.RESULT_OK);
365         finish();
366 
367         // Go back to launcher home
368         LaunchUtils.launchHome(this);
369     }
370 
finishActivityForSUW()371     private void finishActivityForSUW() {
372         overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
373         // Return RESULT_CANCELED to make the "Change wallpaper" tile in SUW not be disabled.
374         setResult(Activity.RESULT_CANCELED);
375         finish();
376     }
377 
378     @Override
getBottomActionBar()379     public BottomActionBar getBottomActionBar() {
380         return mBottomActionBar;
381     }
382 
383     @Override
isSafeToCommitFragmentTransaction()384     public boolean isSafeToCommitFragmentTransaction() {
385         return mIsSafeToCommitFragmentTransaction;
386     }
387 
388     @Override
onUpArrowPressed()389     public void onUpArrowPressed() {
390         // TODO(b/189166781): Remove interface AppbarFragmentHost#onUpArrowPressed.
391         onBackPressed();
392     }
393 
394     @Override
isUpArrowSupported()395     public boolean isUpArrowSupported() {
396         return !isSUWMode(this);
397     }
398 }
399