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