2 * Copyright (C) 2018 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.homepage.contextualcards.slices;
19 import static android.app.slice.Slice.HINT_ERROR;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.net.Uri;
24 import android.util.ArrayMap;
25 import android.util.ArraySet;
26 import android.util.Log;
27 import android.view.View;
28 import android.widget.Button;
30 import androidx.annotation.LayoutRes;
31 import androidx.annotation.VisibleForTesting;
32 import androidx.lifecycle.Lifecycle;
33 import androidx.lifecycle.LifecycleObserver;
34 import androidx.lifecycle.LifecycleOwner;
35 import androidx.lifecycle.LiveData;
36 import androidx.lifecycle.OnLifecycleEvent;
37 import androidx.recyclerview.widget.RecyclerView;
38 import androidx.slice.Slice;
39 import androidx.slice.widget.SliceLiveData;
41 import com.android.settings.R;
42 import com.android.settings.homepage.contextualcards.CardContentProvider;
43 import com.android.settings.homepage.contextualcards.ContextualCard;
44 import com.android.settings.homepage.contextualcards.ContextualCardRenderer;
45 import com.android.settings.homepage.contextualcards.ControllerRendererPool;
51 * Card renderer for {@link ContextualCard} built as slice full card or slice half card.
53 public class SliceContextualCardRenderer implements ContextualCardRenderer, LifecycleObserver {
54 public static final int VIEW_TYPE_FULL_WIDTH = R.layout.contextual_slice_full_tile;
55 public static final int VIEW_TYPE_HALF_WIDTH = R.layout.contextual_slice_half_tile;
56 public static final int VIEW_TYPE_DEFERRED_SETUP = R.layout.contextual_slice_deferred_setup;
58 private static final String TAG = "SliceCardRenderer";
61 final Map<Uri, LiveData<Slice>> mSliceLiveDataMap;
63 final Set<RecyclerView.ViewHolder> mFlippedCardSet;
65 private final Context mContext;
66 private final LifecycleOwner mLifecycleOwner;
67 private final ControllerRendererPool mControllerRendererPool;
68 private final SliceDeferredSetupCardRendererHelper mDeferredSetupCardHelper;
69 private final SliceFullCardRendererHelper mFullCardHelper;
70 private final SliceHalfCardRendererHelper mHalfCardHelper;
72 public SliceContextualCardRenderer(Context context, LifecycleOwner lifecycleOwner,
73 ControllerRendererPool controllerRendererPool) {
75 mLifecycleOwner = lifecycleOwner;
76 mSliceLiveDataMap = new ArrayMap<>();
77 mControllerRendererPool = controllerRendererPool;
78 mFlippedCardSet = new ArraySet<>();
79 mLifecycleOwner.getLifecycle().addObserver(this);
80 mFullCardHelper = new SliceFullCardRendererHelper(context);
81 mHalfCardHelper = new SliceHalfCardRendererHelper(context);
82 mDeferredSetupCardHelper = new SliceDeferredSetupCardRendererHelper(context);
86 public RecyclerView.ViewHolder createViewHolder(View view, @LayoutRes int viewType) {
88 case VIEW_TYPE_DEFERRED_SETUP:
89 return mDeferredSetupCardHelper.createViewHolder(view);
90 case VIEW_TYPE_HALF_WIDTH:
91 return mHalfCardHelper.createViewHolder(view);
93 return mFullCardHelper.createViewHolder(view);
98 public void bindView(RecyclerView.ViewHolder holder, ContextualCard card) {
99 final Uri uri = card.getSliceUri();
101 if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
102 Log.w(TAG, "Invalid uri, skipping slice: " + uri);
106 LiveData<Slice> sliceLiveData = mSliceLiveDataMap.get(uri);
108 if (sliceLiveData == null) {
109 sliceLiveData = SliceLiveData.fromUri(mContext, uri);
110 mSliceLiveDataMap.put(uri, sliceLiveData);
113 sliceLiveData.removeObservers(mLifecycleOwner);
114 sliceLiveData.observe(mLifecycleOwner, slice -> {
116 Log.w(TAG, "Slice is null");
117 mContext.getContentResolver().notifyChange(CardContentProvider.REFRESH_CARD_URI,
122 if (slice.hasHint(HINT_ERROR)) {
123 Log.w(TAG, "Slice has HINT_ERROR, skipping rendering. uri=" + slice.getUri());
124 mSliceLiveDataMap.get(slice.getUri()).removeObservers(mLifecycleOwner);
125 mContext.getContentResolver().notifyChange(CardContentProvider.REFRESH_CARD_URI,
130 switch (holder.getItemViewType()) {
131 case VIEW_TYPE_DEFERRED_SETUP:
132 mDeferredSetupCardHelper.bindView(holder, card, slice);
134 case VIEW_TYPE_HALF_WIDTH:
135 mHalfCardHelper.bindView(holder, card, slice);
138 mFullCardHelper.bindView(holder, card, slice);
142 switch (holder.getItemViewType()) {
143 case VIEW_TYPE_DEFERRED_SETUP:
144 // Deferred setup is never dismissible.
146 case VIEW_TYPE_HALF_WIDTH:
147 initDismissalActions(holder, card);
150 initDismissalActions(holder, card);
153 if (card.isPendingDismiss()) {
154 showDismissalView(holder);
155 mFlippedCardSet.add(holder);
159 private void initDismissalActions(RecyclerView.ViewHolder holder, ContextualCard card) {
160 final Button btnKeep = holder.itemView.findViewById(R.id.keep);
161 btnKeep.setOnClickListener(v -> {
162 mFlippedCardSet.remove(holder);
163 resetCardView(holder);
166 final Button btnRemove = holder.itemView.findViewById(R.id.remove);
167 btnRemove.setOnClickListener(v -> {
168 mControllerRendererPool.getController(mContext, card.getCardType()).onDismissed(card);
169 mFlippedCardSet.remove(holder);
170 resetCardView(holder);
171 mSliceLiveDataMap.get(card.getSliceUri()).removeObservers(mLifecycleOwner);
175 @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
176 public void onStop() {
177 mFlippedCardSet.stream().forEach(holder -> resetCardView(holder));
178 mFlippedCardSet.clear();
181 private void resetCardView(RecyclerView.ViewHolder holder) {
182 holder.itemView.findViewById(R.id.dismissal_view).setVisibility(View.GONE);
183 getInitialView(holder).setVisibility(View.VISIBLE);
186 private void showDismissalView(RecyclerView.ViewHolder holder) {
187 holder.itemView.findViewById(R.id.dismissal_view).setVisibility(View.VISIBLE);
188 getInitialView(holder).setVisibility(View.INVISIBLE);
191 private View getInitialView(RecyclerView.ViewHolder viewHolder) {
192 if (viewHolder.getItemViewType() == VIEW_TYPE_HALF_WIDTH) {
193 return ((SliceHalfCardRendererHelper.HalfCardViewHolder) viewHolder).content;
195 return ((SliceFullCardRendererHelper.SliceViewHolder) viewHolder).sliceView;