OSDN Git Service

Update suggestion card layout
[android-x86/packages-apps-Settings.git] / src / com / android / settings / dashboard / suggestions / SuggestionAdapterV2.java
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.settings.dashboard.suggestions;
17
18 import android.app.PendingIntent;
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.os.Bundle;
22 import android.service.settings.suggestions.Suggestion;
23 import android.support.v7.widget.RecyclerView;
24 import android.text.TextUtils;
25 import android.util.Log;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.widget.ImageView;
30 import android.widget.LinearLayout;
31
32 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
33 import com.android.settings.R;
34 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
35 import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder;
36 import com.android.settings.dashboard.DashboardAdapterV2.IconCache;
37 import com.android.settings.overlay.FeatureFactory;
38 import com.android.settingslib.core.lifecycle.Lifecycle;
39 import com.android.settingslib.core.lifecycle.LifecycleObserver;
40 import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
41
42 import java.util.ArrayList;
43 import java.util.List;
44 import java.util.Objects;
45
46 public class SuggestionAdapterV2 extends RecyclerView.Adapter<DashboardItemHolder> implements
47         LifecycleObserver, OnSaveInstanceState {
48     public static final String TAG = "SuggestionAdapterV2";
49
50     private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
51     private static final String STATE_SUGGESTION_LIST = "suggestion_list";
52
53     private final Context mContext;
54     private final MetricsFeatureProvider mMetricsFeatureProvider;
55     private final IconCache mCache;
56     private final ArrayList<String> mSuggestionsShownLogged;
57     private final SuggestionControllerMixin mSuggestionControllerMixin;
58     private final Callback mCallback;
59     private final CardConfig mConfig;
60
61     private List<Suggestion> mSuggestions;
62
63     public interface Callback {
64         /**
65          * Called when the close button of the suggestion card is clicked.
66          */
67         void onSuggestionClosed(Suggestion suggestion);
68     }
69
70     public SuggestionAdapterV2(Context context, SuggestionControllerMixin suggestionControllerMixin,
71             Bundle savedInstanceState, Callback callback, Lifecycle lifecycle) {
72         mContext = context;
73         mSuggestionControllerMixin = suggestionControllerMixin;
74         mCache = new IconCache(context);
75         final FeatureFactory factory = FeatureFactory.getFactory(context);
76         mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
77         mCallback = callback;
78         if (savedInstanceState != null) {
79             mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
80             mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
81                 STATE_SUGGESTIONS_SHOWN_LOGGED);
82         } else {
83             mSuggestionsShownLogged = new ArrayList<>();
84         }
85
86         if (lifecycle != null) {
87             lifecycle.addObserver(this);
88         }
89         mConfig = CardConfig.get(context);
90
91         setHasStableIds(true);
92     }
93
94     @Override
95     public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
96         return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
97                 viewType, parent, false));
98     }
99
100     @Override
101     public void onBindViewHolder(DashboardItemHolder holder, int position) {
102         final Suggestion suggestion = mSuggestions.get(position);
103         final String id = suggestion.getId();
104         final int suggestionCount = mSuggestions.size();
105         if (!mSuggestionsShownLogged.contains(id)) {
106             mMetricsFeatureProvider.action(
107                     mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, id);
108             mSuggestionsShownLogged.add(id);
109         }
110         mConfig.setCardLayout(holder, suggestionCount, position);
111         holder.icon.setImageDrawable(mCache.getIcon(suggestion.getIcon()));
112         holder.title.setText(suggestion.getTitle());
113         holder.title.setSingleLine(suggestionCount == 1);
114
115         if (suggestionCount == 1) {
116             final CharSequence summary = suggestion.getSummary();
117             if (!TextUtils.isEmpty(summary)) {
118                 holder.summary.setText(summary);
119                 holder.summary.setVisibility(View.VISIBLE);
120             } else {
121                 holder.summary.setVisibility(View.GONE);
122             }
123         } else {
124             // Do not show summary if there are more than 1 suggestions
125             holder.summary.setVisibility(View.GONE);
126             holder.title.setMaxLines(3);
127         }
128
129         final ImageView closeButton = holder.itemView.findViewById(R.id.close_button);
130         if (closeButton != null) {
131             if (mCallback != null) {
132                 closeButton.setOnClickListener(v -> {
133                     mCallback.onSuggestionClosed(suggestion);
134                 });
135             } else {
136                 closeButton.setOnClickListener(null);
137             }
138         }
139
140         View clickHandler = holder.itemView;
141         // If a view with @android:id/primary is defined, use that as the click handler
142         // instead.
143         final View primaryAction = holder.itemView.findViewById(android.R.id.primary);
144         if (primaryAction != null) {
145             clickHandler = primaryAction;
146         }
147         clickHandler.setOnClickListener(v -> {
148             mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_SETTINGS_SUGGESTION, id);
149             try {
150                 suggestion.getPendingIntent().send();
151                 mSuggestionControllerMixin.launchSuggestion(suggestion);
152             } catch (PendingIntent.CanceledException e) {
153                 Log.w(TAG, "Failed to start suggestion " + suggestion.getTitle());
154             }
155         });
156     }
157
158     @Override
159     public long getItemId(int position) {
160         return Objects.hash(mSuggestions.get(position).getId());
161     }
162
163     @Override
164     public int getItemViewType(int position) {
165         final Suggestion suggestion = getSuggestion(position);
166         if ((suggestion.getFlags() & Suggestion.FLAG_HAS_BUTTON) != 0) {
167             return R.layout.suggestion_tile_with_button_v2;
168         } else {
169             return R.layout.suggestion_tile_v2;
170         }
171     }
172
173     @Override
174     public int getItemCount() {
175         return mSuggestions.size();
176     }
177
178     public Suggestion getSuggestion(int position) {
179         final long itemId = getItemId(position);
180         if (mSuggestions == null) {
181             return null;
182         }
183         for (Suggestion suggestion : mSuggestions) {
184             if (Objects.hash(suggestion.getId()) == itemId) {
185                 return suggestion;
186             }
187         }
188         return null;
189     }
190
191     public void removeSuggestion(Suggestion suggestion) {
192         final int position = mSuggestions.indexOf(suggestion);
193         mSuggestions.remove(suggestion);
194         notifyItemRemoved(position);
195     }
196
197     @Override
198     public void onSaveInstanceState(Bundle outState) {
199         if (mSuggestions != null) {
200             outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
201                 new ArrayList<>(mSuggestions));
202         }
203         outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
204     }
205
206     public void setSuggestions(List<Suggestion> suggestions) {
207         mSuggestions = suggestions;
208     }
209
210     public List<Suggestion> getSuggestions() {
211         return mSuggestions;
212     }
213
214     private static class CardConfig {
215         // Card start/end margin
216         private final int mMarginInner;
217         private final int mMarginOuter;
218         // Card width for different numbers of cards
219         private final int mWidthSingleCard;
220         private final int mWidthTwoCards;
221         private final int mWidthMultipleCards;
222         // padding between icon and title
223         private final int mPaddingTitleTopSingleCard;
224         private final int mPaddingTitleTopMultipleCards;
225
226         private static CardConfig sConfig;
227
228         private CardConfig(Context context) {
229             final Resources res = context.getResources();
230             mMarginInner =
231                 res.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin);
232             mMarginOuter =
233                 res.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin);
234             mWidthSingleCard = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_one_card);
235             mWidthTwoCards = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_two_cards);
236             mWidthMultipleCards =
237                 res.getDimensionPixelOffset(R.dimen.suggestion_card_width_multiple_cards);
238             mPaddingTitleTopSingleCard =
239                 res.getDimensionPixelOffset(R.dimen.suggestion_card_title_padding_bottom_one_card);
240             mPaddingTitleTopMultipleCards = res.getDimensionPixelOffset(
241                 R.dimen.suggestion_card_title_padding_bottom_multiple_cards);
242         }
243
244         public static CardConfig get(Context context) {
245             if (sConfig == null) {
246                 sConfig = new CardConfig(context);
247             }
248             return sConfig;
249         }
250
251         private void setCardLayout(DashboardItemHolder holder, int suggestionCount,
252             int position) {
253             final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
254                 suggestionCount == 1
255                     ? mWidthSingleCard : suggestionCount == 2
256                     ? mWidthTwoCards : mWidthMultipleCards,
257                 LinearLayout.LayoutParams.WRAP_CONTENT);
258             if (suggestionCount == 1) {
259                 params.setMarginStart(mMarginOuter);
260                 params.setMarginEnd(mMarginOuter);
261             } else {
262                 params.setMarginStart(
263                     position == 0 ? mMarginOuter : mMarginInner);
264                 params.setMarginEnd(position == suggestionCount - 1 ? mMarginOuter : 0);
265             }
266             holder.itemView.setLayoutParams(params);
267         }
268
269     }
270
271 }