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 
17 package com.android.settings.homepage.contextualcards;
18 
19 import android.content.Context;
20 import android.view.LayoutInflater;
21 import android.view.View;
22 import android.view.ViewGroup;
23 
24 import androidx.annotation.LayoutRes;
25 import androidx.annotation.VisibleForTesting;
26 import androidx.lifecycle.LifecycleOwner;
27 import androidx.recyclerview.widget.DiffUtil;
28 import androidx.recyclerview.widget.GridLayoutManager;
29 import androidx.recyclerview.widget.RecyclerView;
30 
31 import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer;
32 import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;
33 import com.android.settings.homepage.contextualcards.slices.SwipeDismissalDelegate;
34 
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Map;
38 
39 public class ContextualCardsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
40         implements ContextualCardUpdateListener, SwipeDismissalDelegate.Listener {
41     static final int SPAN_COUNT = 2;
42 
43     private static final String TAG = "ContextualCardsAdapter";
44     private static final int HALF_WIDTH = 1;
45     private static final int FULL_WIDTH = 2;
46 
47     @VisibleForTesting
48     final List<ContextualCard> mContextualCards;
49 
50     private final Context mContext;
51     private final ControllerRendererPool mControllerRendererPool;
52     private final LifecycleOwner mLifecycleOwner;
53 
54     private RecyclerView mRecyclerView;
55 
ContextualCardsAdapter(Context context, LifecycleOwner lifecycleOwner, ContextualCardManager manager)56     public ContextualCardsAdapter(Context context, LifecycleOwner lifecycleOwner,
57             ContextualCardManager manager) {
58         mContext = context;
59         mContextualCards = new ArrayList<>();
60         mControllerRendererPool = manager.getControllerRendererPool();
61         mLifecycleOwner = lifecycleOwner;
62         setHasStableIds(true);
63     }
64 
65     @Override
getItemId(int position)66     public long getItemId(int position) {
67         return mContextualCards.get(position).hashCode();
68     }
69 
70     @Override
getItemViewType(int position)71     public int getItemViewType(int position) {
72         final ContextualCard card = mContextualCards.get(position);
73         return card.getViewType();
74     }
75 
76     @Override
onCreateViewHolder(ViewGroup parent, @LayoutRes int viewType)77     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, @LayoutRes int viewType) {
78         final ContextualCardRenderer renderer = mControllerRendererPool.getRendererByViewType(
79                 mContext, mLifecycleOwner, viewType);
80         final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
81         return renderer.createViewHolder(view, viewType);
82     }
83 
84     @Override
onBindViewHolder(RecyclerView.ViewHolder holder, int position)85     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
86         final ContextualCard card = mContextualCards.get(position);
87         final ContextualCardRenderer renderer = mControllerRendererPool.getRendererByViewType(
88                 mContext, mLifecycleOwner, card.getViewType());
89         renderer.bindView(holder, card);
90     }
91 
92     @Override
getItemCount()93     public int getItemCount() {
94         return mContextualCards.size();
95     }
96 
97     @Override
onAttachedToRecyclerView(RecyclerView recyclerView)98     public void onAttachedToRecyclerView(RecyclerView recyclerView) {
99         super.onAttachedToRecyclerView(recyclerView);
100         mRecyclerView = recyclerView;
101         final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
102         if (layoutManager instanceof GridLayoutManager) {
103             final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
104             gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
105                 @Override
106                 public int getSpanSize(int position) {
107                     final int viewType = mContextualCards.get(position).getViewType();
108                     if (viewType == ConditionContextualCardRenderer.VIEW_TYPE_HALF_WIDTH
109                             || viewType == SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH) {
110                         return HALF_WIDTH;
111                     }
112                     return FULL_WIDTH;
113                 }
114             });
115         }
116     }
117 
118     @Override
onContextualCardUpdated(Map<Integer, List<ContextualCard>> cards)119     public void onContextualCardUpdated(Map<Integer, List<ContextualCard>> cards) {
120         final List<ContextualCard> contextualCards = cards.get(ContextualCard.CardType.DEFAULT);
121         final boolean previouslyEmpty = mContextualCards.isEmpty();
122         final boolean nowEmpty = contextualCards == null || contextualCards.isEmpty();
123         if (contextualCards == null) {
124             mContextualCards.clear();
125             notifyDataSetChanged();
126         } else {
127             final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
128                     new ContextualCardsDiffCallback(mContextualCards, contextualCards));
129             mContextualCards.clear();
130             mContextualCards.addAll(contextualCards);
131             diffResult.dispatchUpdatesTo(this);
132         }
133 
134         if (mRecyclerView != null && previouslyEmpty && !nowEmpty) {
135             // Adding items to empty list, should animate.
136             mRecyclerView.scheduleLayoutAnimation();
137         }
138     }
139 
140     @Override
onSwiped(int position)141     public void onSwiped(int position) {
142         final ContextualCard card = mContextualCards.get(position).mutate()
143                 .setIsPendingDismiss(true).build();
144         mContextualCards.set(position, card);
145         notifyItemChanged(position);
146     }
147 }
148