1 /* 2 * Copyright (C) 2019 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.clock; 17 18 import android.app.Activity; 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.os.Bundle; 22 import android.util.Log; 23 import android.view.LayoutInflater; 24 import android.view.View; 25 import android.view.ViewGroup; 26 import android.widget.ImageView; 27 import android.widget.Toast; 28 29 import androidx.annotation.NonNull; 30 import androidx.annotation.Nullable; 31 import androidx.core.widget.ContentLoadingProgressBar; 32 import androidx.recyclerview.widget.RecyclerView; 33 34 import com.android.customization.model.CustomizationManager.Callback; 35 import com.android.customization.model.CustomizationManager.OptionsFetchedListener; 36 import com.android.customization.model.clock.BaseClockManager; 37 import com.android.customization.model.clock.Clockface; 38 import com.android.customization.module.ThemesUserEventLogger; 39 import com.android.customization.picker.BasePreviewAdapter; 40 import com.android.customization.picker.BasePreviewAdapter.PreviewPage; 41 import com.android.customization.widget.OptionSelectorController; 42 import com.android.wallpaper.R; 43 import com.android.wallpaper.asset.Asset; 44 import com.android.wallpaper.module.InjectorProvider; 45 import com.android.wallpaper.picker.AppbarFragment; 46 import com.android.wallpaper.widget.PreviewPager; 47 48 import java.util.List; 49 50 /** 51 * Fragment that contains the main UI for selecting and applying a Clockface. 52 */ 53 public class ClockFragment extends AppbarFragment { 54 55 private static final String TAG = "ClockFragment"; 56 57 /** 58 * Interface to be implemented by an Activity hosting a {@link ClockFragment} 59 */ 60 public interface ClockFragmentHost { getClockManager()61 BaseClockManager getClockManager(); 62 } 63 newInstance(CharSequence title)64 public static ClockFragment newInstance(CharSequence title) { 65 ClockFragment fragment = new ClockFragment(); 66 fragment.setArguments(AppbarFragment.createArguments(title)); 67 return fragment; 68 } 69 70 private RecyclerView mOptionsContainer; 71 private OptionSelectorController<Clockface> mOptionsController; 72 private Clockface mSelectedOption; 73 private BaseClockManager mClockManager; 74 private PreviewPager mPreviewPager; 75 private ContentLoadingProgressBar mLoading; 76 private View mContent; 77 private View mError; 78 private ThemesUserEventLogger mEventLogger; 79 80 @Override onAttach(Context context)81 public void onAttach(Context context) { 82 super.onAttach(context); 83 mClockManager = ((ClockFragmentHost) context).getClockManager(); 84 mEventLogger = (ThemesUserEventLogger) 85 InjectorProvider.getInjector().getUserEventLogger(context); 86 } 87 88 @Nullable 89 @Override onCreateView(@onNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)90 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, 91 @Nullable Bundle savedInstanceState) { 92 View view = inflater.inflate( 93 R.layout.fragment_clock_picker, container, /* attachToRoot */ false); 94 setUpToolbar(view); 95 mContent = view.findViewById(R.id.content_section); 96 mPreviewPager = view.findViewById(R.id.clock_preview_pager); 97 mOptionsContainer = view.findViewById(R.id.options_container); 98 mLoading = view.findViewById(R.id.loading_indicator); 99 mError = view.findViewById(R.id.error_section); 100 setUpOptions(); 101 view.findViewById(R.id.apply_button).setOnClickListener(v -> { 102 mClockManager.apply(mSelectedOption, new Callback() { 103 @Override 104 public void onSuccess() { 105 mOptionsController.setAppliedOption(mSelectedOption); 106 Toast.makeText(getContext(), R.string.applied_clock_msg, 107 Toast.LENGTH_SHORT).show(); 108 } 109 110 @Override 111 public void onError(@Nullable Throwable throwable) { 112 if (throwable != null) { 113 Log.e(TAG, "Error loading clockfaces", throwable); 114 } 115 //TODO(santie): handle 116 } 117 }); 118 119 }); 120 return view; 121 } 122 createAdapter()123 private void createAdapter() { 124 mPreviewPager.setAdapter(new ClockPreviewAdapter(getActivity(), mSelectedOption)); 125 } 126 setUpOptions()127 private void setUpOptions() { 128 hideError(); 129 mLoading.show(); 130 mClockManager.fetchOptions(new OptionsFetchedListener<Clockface>() { 131 @Override 132 public void onOptionsLoaded(List<Clockface> options) { 133 mLoading.hide(); 134 mOptionsController = new OptionSelectorController<>(mOptionsContainer, options); 135 136 mOptionsController.addListener(selected -> { 137 mSelectedOption = (Clockface) selected; 138 mEventLogger.logClockSelected(mSelectedOption); 139 createAdapter(); 140 }); 141 mOptionsController.initOptions(mClockManager); 142 for (Clockface option : options) { 143 if (option.isActive(mClockManager)) { 144 mSelectedOption = option; 145 } 146 } 147 // For development only, as there should always be a grid set. 148 if (mSelectedOption == null) { 149 mSelectedOption = options.get(0); 150 } 151 createAdapter(); 152 } 153 @Override 154 public void onError(@Nullable Throwable throwable) { 155 if (throwable != null) { 156 Log.e(TAG, "Error loading clockfaces", throwable); 157 } 158 showError(); 159 } 160 }, false); 161 } 162 hideError()163 private void hideError() { 164 mContent.setVisibility(View.VISIBLE); 165 mError.setVisibility(View.GONE); 166 } 167 showError()168 private void showError() { 169 mLoading.hide(); 170 mContent.setVisibility(View.GONE); 171 mError.setVisibility(View.VISIBLE); 172 } 173 174 private static class ClockfacePreviewPage extends PreviewPage { 175 176 private final Asset mPreviewAsset; 177 ClockfacePreviewPage(String title, Activity activity, Asset previewAsset)178 public ClockfacePreviewPage(String title, Activity activity, Asset previewAsset) { 179 super(title, activity); 180 mPreviewAsset = previewAsset; 181 } 182 183 @Override bindPreviewContent()184 public void bindPreviewContent() { 185 ImageView previewImage = card.findViewById(R.id.clock_preview_image); 186 Context context = previewImage.getContext(); 187 Resources res = previewImage.getResources(); 188 mPreviewAsset.loadDrawableWithTransition(context, previewImage, 189 100 /* transitionDurationMillis */, 190 null /* drawableLoadedListener */, 191 res.getColor(android.R.color.transparent, null) /* placeholderColor */); 192 card.setContentDescription(card.getResources().getString( 193 R.string.clock_preview_content_description, title)); 194 } 195 } 196 197 /** 198 * Adapter class for mPreviewPager. 199 * This is a ViewPager as it allows for a nice pagination effect (ie, pages snap on swipe, 200 * we don't want to just scroll) 201 */ 202 private static class ClockPreviewAdapter extends BasePreviewAdapter<ClockfacePreviewPage> { ClockPreviewAdapter(Activity activity, Clockface clockface)203 ClockPreviewAdapter(Activity activity, Clockface clockface) { 204 super(activity, R.layout.clock_preview_card); 205 addPage(new ClockfacePreviewPage( 206 clockface.getTitle(), activity , clockface.getPreviewAsset())); 207 } 208 } 209 } 210