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