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.
16 package com.android.settings.dashboard.suggestions;
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;
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;
42 import java.util.ArrayList;
43 import java.util.List;
44 import java.util.Objects;
46 public class SuggestionAdapterV2 extends RecyclerView.Adapter<DashboardItemHolder> implements
47 LifecycleObserver, OnSaveInstanceState {
48 public static final String TAG = "SuggestionAdapterV2";
50 private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
51 private static final String STATE_SUGGESTION_LIST = "suggestion_list";
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 int mMultipleCardsMarginEnd;
60 private final int mWidthSingleCard;
61 private final int mWidthTwoCards;
62 private final int mWidthMultipleCards;
64 private List<Suggestion> mSuggestions;
66 public interface Callback {
68 * Called when the close button of the suggestion card is clicked.
70 void onSuggestionClosed(Suggestion suggestion);
73 public SuggestionAdapterV2(Context context, SuggestionControllerMixin suggestionControllerMixin,
74 Bundle savedInstanceState, Callback callback, Lifecycle lifecycle) {
76 mSuggestionControllerMixin = suggestionControllerMixin;
77 mCache = new IconCache(context);
78 final FeatureFactory factory = FeatureFactory.getFactory(context);
79 mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
81 if (savedInstanceState != null) {
82 mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
83 mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
84 STATE_SUGGESTIONS_SHOWN_LOGGED);
86 mSuggestionsShownLogged = new ArrayList<>();
89 if (lifecycle != null) {
90 lifecycle.addObserver(this);
93 final Resources res = mContext.getResources();
94 mMultipleCardsMarginEnd = res.getDimensionPixelOffset(R.dimen.suggestion_card_margin_end);
95 mWidthSingleCard = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_one_card);
96 mWidthTwoCards = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_two_cards);
98 res.getDimensionPixelOffset(R.dimen.suggestion_card_width_multiple_cards);
100 setHasStableIds(true);
104 public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
105 return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
106 viewType, parent, false));
110 public void onBindViewHolder(DashboardItemHolder holder, int position) {
111 final Suggestion suggestion = mSuggestions.get(position);
112 final String id = suggestion.getId();
113 final int suggestionCount = mSuggestions.size();
114 if (!mSuggestionsShownLogged.contains(id)) {
115 mMetricsFeatureProvider.action(
116 mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, id);
117 mSuggestionsShownLogged.add(id);
119 setCardWidthAndMargin(holder, suggestionCount);
120 holder.icon.setImageDrawable(mCache.getIcon(suggestion.getIcon()));
121 holder.title.setText(suggestion.getTitle());
122 holder.title.setSingleLine(suggestionCount == 1);
124 if (suggestionCount == 1) {
125 final CharSequence summary = suggestion.getSummary();
126 if (!TextUtils.isEmpty(summary)) {
127 holder.summary.setText(summary);
128 holder.summary.setVisibility(View.VISIBLE);
130 holder.summary.setVisibility(View.GONE);
133 // Do not show summary if there are more than 1 suggestions
134 holder.summary.setVisibility(View.GONE);
135 holder.title.setMaxLines(3);
138 final ImageView closeButton = holder.itemView.findViewById(R.id.close_button);
139 if (closeButton != null) {
140 if (mCallback != null) {
141 closeButton.setOnClickListener(v -> {
142 mCallback.onSuggestionClosed(suggestion);
145 closeButton.setOnClickListener(null);
149 View clickHandler = holder.itemView;
150 // If a view with @android:id/primary is defined, use that as the click handler
152 final View primaryAction = holder.itemView.findViewById(android.R.id.primary);
153 if (primaryAction != null) {
154 clickHandler = primaryAction;
156 clickHandler.setOnClickListener(v -> {
157 mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_SETTINGS_SUGGESTION, id);
159 suggestion.getPendingIntent().send();
160 mSuggestionControllerMixin.launchSuggestion(suggestion);
161 } catch (PendingIntent.CanceledException e) {
162 Log.w(TAG, "Failed to start suggestion " + suggestion.getTitle());
168 public long getItemId(int position) {
169 return Objects.hash(mSuggestions.get(position).getId());
173 public int getItemViewType(int position) {
174 final Suggestion suggestion = getSuggestion(position);
175 if ((suggestion.getFlags() & Suggestion.FLAG_HAS_BUTTON) != 0) {
176 return R.layout.suggestion_tile_with_button_v2;
178 return R.layout.suggestion_tile_v2;
183 public int getItemCount() {
184 return mSuggestions.size();
187 public Suggestion getSuggestion(int position) {
188 final long itemId = getItemId(position);
189 if (mSuggestions == null) {
192 for (Suggestion suggestion : mSuggestions) {
193 if (Objects.hash(suggestion.getId()) == itemId) {
200 public void removeSuggestion(Suggestion suggestion) {
201 final int position = mSuggestions.indexOf(suggestion);
202 mSuggestions.remove(suggestion);
203 notifyItemRemoved(position);
207 public void onSaveInstanceState(Bundle outState) {
208 if (mSuggestions != null) {
209 outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
210 new ArrayList<>(mSuggestions));
212 outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
215 public void setSuggestions(List<Suggestion> suggestions) {
216 mSuggestions = suggestions;
219 public List<Suggestion> getSuggestions() {
223 private void setCardWidthAndMargin(DashboardItemHolder holder, int suggestionCount) {
224 final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
226 ? mWidthSingleCard : suggestionCount == 2 ? mWidthTwoCards : mWidthMultipleCards,
227 LinearLayout.LayoutParams.WRAP_CONTENT);
228 params.setMarginEnd(suggestionCount == 1 ? 0 : mMultipleCardsMarginEnd);
229 holder.itemView.setLayoutParams(params);