1 /* 2 * Copyright (C) 2018 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.customization.picker.grid; 17 18 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY_TEXT; 19 20 import android.content.Context; 21 import android.os.Bundle; 22 import android.util.Log; 23 import android.view.LayoutInflater; 24 import android.view.SurfaceView; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import android.view.accessibility.AccessibilityManager; 28 import android.widget.Toast; 29 30 import androidx.annotation.NonNull; 31 import androidx.annotation.Nullable; 32 import androidx.core.widget.ContentLoadingProgressBar; 33 import androidx.lifecycle.ViewModelProvider; 34 import androidx.recyclerview.widget.RecyclerView; 35 36 import com.android.customization.model.CustomizationManager.Callback; 37 import com.android.customization.model.CustomizationManager.OptionsFetchedListener; 38 import com.android.customization.model.CustomizationOption; 39 import com.android.customization.model.grid.GridOption; 40 import com.android.customization.model.grid.GridOptionViewModel; 41 import com.android.customization.model.grid.GridOptionsManager; 42 import com.android.customization.module.ThemesUserEventLogger; 43 import com.android.customization.picker.WallpaperPreviewer; 44 import com.android.customization.widget.OptionSelectorController; 45 import com.android.customization.widget.OptionSelectorController.CheckmarkStyle; 46 import com.android.wallpaper.R; 47 import com.android.wallpaper.model.WallpaperInfo; 48 import com.android.wallpaper.module.CurrentWallpaperInfoFactory; 49 import com.android.wallpaper.module.InjectorProvider; 50 import com.android.wallpaper.picker.AppbarFragment; 51 import com.android.wallpaper.util.LaunchUtils; 52 import com.android.wallpaper.widget.BottomActionBar; 53 54 import com.bumptech.glide.Glide; 55 56 import java.util.List; 57 58 /** 59 * Fragment that contains the UI for selecting and applying a GridOption. 60 */ 61 public class GridFragment extends AppbarFragment { 62 63 private static final String TAG = "GridFragment"; 64 65 private WallpaperInfo mHomeWallpaper; 66 private RecyclerView mOptionsContainer; 67 private OptionSelectorController<GridOption> mOptionsController; 68 private GridOptionsManager mGridManager; 69 private ContentLoadingProgressBar mLoading; 70 private View mContent; 71 private View mError; 72 private BottomActionBar mBottomActionBar; 73 private ThemesUserEventLogger mEventLogger; 74 private GridOptionPreviewer mGridOptionPreviewer; 75 private GridOptionViewModel mGridOptionViewModel; 76 77 private final Callback mApplyGridCallback = new Callback() { 78 @Override 79 public void onSuccess() { 80 Toast.makeText(getContext(), R.string.applied_grid_msg, Toast.LENGTH_SHORT).show(); 81 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out); 82 getActivity().finish(); 83 84 // Go back to launcher home 85 LaunchUtils.launchHome(getContext()); 86 } 87 88 @Override 89 public void onError(@Nullable Throwable throwable) { 90 // Since we disabled it when clicked apply button. 91 mBottomActionBar.enableActions(); 92 mBottomActionBar.hide(); 93 mGridOptionViewModel.setBottomActionBarVisible(false); 94 //TODO(chihhangchuang): handle 95 } 96 }; 97 98 @Override onCreate(Bundle savedInstanceState)99 public void onCreate(Bundle savedInstanceState) { 100 super.onCreate(savedInstanceState); 101 mGridOptionViewModel = new ViewModelProvider(requireActivity()).get( 102 GridOptionViewModel.class); 103 } 104 105 @Nullable 106 @Override onCreateView(@onNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)107 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, 108 @Nullable Bundle savedInstanceState) { 109 View view = inflater.inflate( 110 R.layout.fragment_grid_picker, container, /* attachToRoot */ false); 111 setUpToolbar(view); 112 mContent = view.findViewById(R.id.content_section); 113 mOptionsContainer = view.findViewById(R.id.options_container); 114 AccessibilityManager accessibilityManager = 115 (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); 116 if (accessibilityManager.isEnabled()) { 117 // Make Talkback focus won't reset when notifyDataSetChange 118 mOptionsContainer.setItemAnimator(null); 119 } 120 mLoading = view.findViewById(R.id.loading_indicator); 121 mError = view.findViewById(R.id.error_section); 122 123 // For nav bar edge-to-edge effect. 124 view.setOnApplyWindowInsetsListener((v, windowInsets) -> { 125 v.setPadding( 126 v.getPaddingLeft(), 127 windowInsets.getSystemWindowInsetTop(), 128 v.getPaddingRight(), 129 windowInsets.getSystemWindowInsetBottom()); 130 return windowInsets.consumeSystemWindowInsets(); 131 }); 132 133 // Clear memory cache whenever grid fragment view is being loaded. 134 Glide.get(getContext()).clearMemory(); 135 136 mGridManager = GridOptionsManager.getInstance(getContext()); 137 mEventLogger = (ThemesUserEventLogger) InjectorProvider.getInjector() 138 .getUserEventLogger(getContext()); 139 setUpOptions(); 140 141 SurfaceView wallpaperSurface = view.findViewById(R.id.wallpaper_preview_surface); 142 WallpaperPreviewer wallpaperPreviewer = new WallpaperPreviewer(getLifecycle(), 143 getActivity(), view.findViewById(R.id.wallpaper_preview_image), wallpaperSurface); 144 // Loads current Wallpaper. 145 CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector() 146 .getCurrentWallpaperFactory(getContext().getApplicationContext()); 147 factory.createCurrentWallpaperInfos((homeWallpaper, lockWallpaper, presentationMode) -> { 148 mHomeWallpaper = homeWallpaper; 149 wallpaperPreviewer.setWallpaper(mHomeWallpaper, /* listener= */ null); 150 }, false); 151 152 mGridOptionPreviewer = new GridOptionPreviewer(mGridManager, 153 view.findViewById(R.id.grid_preview_container)); 154 155 return view; 156 } 157 158 @Override onBackPressed()159 public boolean onBackPressed() { 160 mGridOptionViewModel.setSelectedOption(null); 161 mGridOptionViewModel.setBottomActionBarVisible(false); 162 return super.onBackPressed(); 163 } 164 165 @Override onDestroy()166 public void onDestroy() { 167 super.onDestroy(); 168 if (mGridOptionPreviewer != null) { 169 mGridOptionPreviewer.release(); 170 } 171 } 172 173 @Override getDefaultTitle()174 public CharSequence getDefaultTitle() { 175 return getString(R.string.grid_title); 176 } 177 178 @Override onBottomActionBarReady(BottomActionBar bottomActionBar)179 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) { 180 super.onBottomActionBarReady(bottomActionBar); 181 mBottomActionBar = bottomActionBar; 182 mBottomActionBar.showActionsOnly(APPLY_TEXT); 183 mBottomActionBar.setActionClickListener(APPLY_TEXT, 184 v -> applyGridOption(mGridOptionViewModel.getSelectedOption())); 185 } 186 applyGridOption(GridOption gridOption)187 private void applyGridOption(GridOption gridOption) { 188 mBottomActionBar.disableActions(); 189 mGridManager.apply(gridOption, mApplyGridCallback); 190 } 191 setUpOptions()192 private void setUpOptions() { 193 hideError(); 194 mLoading.show(); 195 mGridManager.fetchOptions(new OptionsFetchedListener<GridOption>() { 196 @Override 197 public void onOptionsLoaded(List<GridOption> options) { 198 mLoading.hide(); 199 mOptionsController = new OptionSelectorController<>( 200 mOptionsContainer, options, /* useGrid= */ false, 201 CheckmarkStyle.CENTER_CHANGE_COLOR_WHEN_NOT_SELECTED); 202 mOptionsController.initOptions(mGridManager); 203 GridOption previouslySelectedOption = findEquivalent(options, 204 mGridOptionViewModel.getSelectedOption()); 205 mGridOptionViewModel.setSelectedOption( 206 previouslySelectedOption != null 207 ? previouslySelectedOption 208 : getActiveOption(options)); 209 210 mOptionsController.setSelectedOption(mGridOptionViewModel.getSelectedOption()); 211 onOptionSelected(mGridOptionViewModel.getSelectedOption()); 212 restoreBottomActionBarVisibility(); 213 214 mOptionsController.addListener(selectedOption -> { 215 String title = selectedOption.getTitle(); 216 int stringId = R.string.option_previewed_description; 217 if (selectedOption.isActive(mGridManager)) { 218 stringId = R.string.option_applied_previewed_description; 219 } 220 CharSequence cd = getContext().getString(stringId, title); 221 mOptionsContainer.announceForAccessibility(cd); 222 onOptionSelected(selectedOption); 223 mBottomActionBar.show(); 224 mGridOptionViewModel.setBottomActionBarVisible(true); 225 }); 226 } 227 228 @Override 229 public void onError(@Nullable Throwable throwable) { 230 if (throwable != null) { 231 Log.e(TAG, "Error loading grid options", throwable); 232 } 233 showError(); 234 } 235 }, /*reload= */ true); 236 } 237 getActiveOption(List<GridOption> options)238 private GridOption getActiveOption(List<GridOption> options) { 239 return options.stream() 240 .filter(option -> option.isActive(mGridManager)) 241 .findAny() 242 // For development only, as there should always be a grid set. 243 .orElse(options.get(0)); 244 } 245 246 @Nullable findEquivalent(List<GridOption> options, GridOption target)247 private GridOption findEquivalent(List<GridOption> options, GridOption target) { 248 return options.stream() 249 .filter(option -> option.equals(target)) 250 .findAny() 251 .orElse(null); 252 } 253 hideError()254 private void hideError() { 255 mContent.setVisibility(View.VISIBLE); 256 mError.setVisibility(View.GONE); 257 } 258 showError()259 private void showError() { 260 mLoading.hide(); 261 mContent.setVisibility(View.GONE); 262 mError.setVisibility(View.VISIBLE); 263 } 264 onOptionSelected(CustomizationOption selectedOption)265 private void onOptionSelected(CustomizationOption selectedOption) { 266 mGridOptionViewModel.setSelectedOption((GridOption) selectedOption); 267 mEventLogger.logGridSelected(mGridOptionViewModel.getSelectedOption()); 268 mGridOptionPreviewer.setGridOption(mGridOptionViewModel.getSelectedOption()); 269 } 270 restoreBottomActionBarVisibility()271 private void restoreBottomActionBarVisibility() { 272 if (mGridOptionViewModel.getBottomActionBarVisible()) { 273 mBottomActionBar.show(); 274 } else { 275 mBottomActionBar.hide(); 276 } 277 } 278 } 279