Merge "Remove suggestion UI v1 codes."

This commit is contained in:
TreeHugger Robot
2018-01-31 21:15:18 +00:00
committed by Android (Google) Code Review
29 changed files with 746 additions and 3114 deletions

View File

@@ -24,7 +24,6 @@ public class FeatureFlags {
public static final String BATTERY_SETTINGS_V2 = "settings_battery_v2";
public static final String BATTERY_DISPLAY_APP_LIST = "settings_battery_display_app_list";
public static final String ZONE_PICKER_V2 = "settings_zone_picker_v2";
public static final String SUGGESTION_UI_V2 = "settings_suggestion_ui_v2";
public static final String ABOUT_PHONE_V2 = "settings_about_phone_v2";
public static final String BLUETOOTH_WHILE_DRIVING = "settings_bluetooth_while_driving";
}

View File

@@ -17,9 +17,6 @@ package com.android.settings.dashboard;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -41,44 +38,37 @@ import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.R.id;
import com.android.settings.dashboard.DashboardData.SuggestionConditionHeaderData;
import com.android.settings.dashboard.DashboardData.ConditionHeaderData;
import com.android.settings.dashboard.conditional.Condition;
import com.android.settings.dashboard.conditional.ConditionAdapter;
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
import com.android.settings.dashboard.suggestions.SuggestionDismissController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.Utils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.suggestions.SuggestionControllerMixin;
import java.util.ArrayList;
import java.util.List;
public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder>
implements SummaryLoader.SummaryConsumer {
implements SummaryLoader.SummaryConsumer, SuggestionAdapter.Callback, LifecycleObserver,
OnSaveInstanceState {
public static final String TAG = "DashboardAdapter";
private static final String STATE_SUGGESTION_LIST = "suggestion_list";
private static final String STATE_CATEGORY_LIST = "category_list";
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
@VisibleForTesting
static final String STATE_SUGGESTION_CONDITION_MODE = "suggestion_condition_mode";
@VisibleForTesting
static final int SUGGESTION_CONDITION_HEADER_POSITION = 0;
static final String STATE_CONDITION_EXPANDED = "condition_expanded";
private final IconCache mCache;
private final Context mContext;
private final SuggestionControllerMixin mSuggestionControllerMixin;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final DashboardFeatureProvider mDashboardFeatureProvider;
private final ArrayList<String> mSuggestionsShownLogged;
private boolean mFirstFrameDrawn;
private RecyclerView mRecyclerView;
private SuggestionAdapter mSuggestionAdapter;
private SuggestionDismissController mSuggestionDismissHandler;
private SuggestionDismissController.Callback mCallback;
@VisibleForTesting
DashboardData mDashboardData;
@@ -92,57 +82,54 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
};
public DashboardAdapter(Context context, Bundle savedInstanceState,
List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
SuggestionDismissController.Callback callback) {
List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
Lifecycle lifecycle) {
List<Suggestion> suggestions = null;
DashboardCategory category = null;
int suggestionConditionMode = DashboardData.HEADER_MODE_DEFAULT;
boolean conditionExpanded = false;
mContext = context;
final FeatureFactory factory = FeatureFactory.getFactory(context);
mSuggestionControllerMixin = suggestionControllerMixin;
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
mCache = new IconCache(context);
mCallback = callback;
mSuggestionAdapter = new SuggestionAdapter(mContext, suggestionControllerMixin,
savedInstanceState, this /* callback */, lifecycle);
setHasStableIds(true);
if (savedInstanceState != null) {
suggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
suggestionConditionMode = savedInstanceState.getInt(
STATE_SUGGESTION_CONDITION_MODE, suggestionConditionMode);
mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
STATE_SUGGESTIONS_SHOWN_LOGGED);
} else {
mSuggestionsShownLogged = new ArrayList<>();
conditionExpanded = savedInstanceState.getBoolean(
STATE_CONDITION_EXPANDED, conditionExpanded);
}
if (lifecycle != null) {
lifecycle.addObserver(this);
}
mDashboardData = new DashboardData.Builder()
.setConditions(conditions)
.setSuggestions(suggestions)
.setCategory(category)
.setSuggestionConditionMode(suggestionConditionMode)
.build();
.setConditions(conditions)
.setSuggestions(mSuggestionAdapter.getSuggestions())
.setCategory(category)
.setConditionExpanded(conditionExpanded)
.build();
}
public void setSuggestions(List<Suggestion> data) {
final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
.setSuggestions(data)
.build();
.setSuggestions(data)
.build();
notifyDashboardDataChanged(prevData);
}
public void setCategory(DashboardCategory category) {
tintIcons(category, null);
final DashboardData prevData = mDashboardData;
Log.d(TAG, "adapter setCategory called");
mDashboardData = new DashboardData.Builder(prevData)
.setCategory(category)
.build();
.setCategory(category)
.build();
notifyDashboardDataChanged(prevData);
}
@@ -150,12 +137,13 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
final DashboardData prevData = mDashboardData;
Log.d(TAG, "adapter setConditions called");
mDashboardData = new DashboardData.Builder(prevData)
.setConditions(conditions)
.build();
.setConditions(conditions)
.build();
notifyDashboardDataChanged(prevData);
}
public void onSuggestionDismissed(Suggestion suggestion) {
@Override
public void onSuggestionClosed(Suggestion suggestion) {
final List<Suggestion> list = mDashboardData.getSuggestions();
if (list == null || list.size() == 0) {
return;
@@ -163,13 +151,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
if (list.size() == 1) {
// The only suggestion is dismissed, and the the empty suggestion container will
// remain as the dashboard item. Need to refresh the dashboard list.
final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
.setSuggestions(null)
.build();
notifyDashboardDataChanged(prevData);
setSuggestions(null);
} else {
mSuggestionAdapter.removeSuggestion(suggestion);
notifyItemChanged(0, null);
}
}
@@ -186,11 +171,14 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
@Override
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
if (viewType == R.layout.suggestion_condition_header) {
return new SuggestionAndConditionHeaderHolder(view);
if (viewType == R.layout.condition_header) {
return new ConditionHeaderHolder(view);
}
if (viewType == R.layout.suggestion_condition_container) {
return new SuggestionAndConditionContainerHolder(view);
if (viewType == R.layout.condition_container) {
return new ConditionContainerHolder(view);
}
if (viewType == R.layout.suggestion_container) {
return new SuggestionContainerHolder(view);
}
return new DashboardItemHolder(view);
}
@@ -205,24 +193,25 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
holder.itemView.setTag(tile);
holder.itemView.setOnClickListener(mTileClickListener);
break;
case R.layout.suggestion_condition_container:
onBindConditionAndSuggestion(
(SuggestionAndConditionContainerHolder) holder, position);
case R.layout.suggestion_container:
onBindSuggestion((SuggestionContainerHolder) holder, position);
break;
case R.layout.suggestion_condition_header:
onBindSuggestionConditionHeader((SuggestionAndConditionHeaderHolder) holder,
(SuggestionConditionHeaderData)
mDashboardData.getItemEntityByPosition(position));
case R.layout.condition_container:
onBindCondition((ConditionContainerHolder) holder, position);
break;
case R.layout.suggestion_condition_footer:
case R.layout.condition_header:
onBindConditionHeader((ConditionHeaderHolder) holder,
(ConditionHeaderData) mDashboardData.getItemEntityByPosition(position));
break;
case R.layout.condition_footer:
holder.itemView.setOnClickListener(v -> {
mMetricsFeatureProvider.action(mContext,
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData).setSuggestionConditionMode(
DashboardData.HEADER_MODE_COLLAPSED).build();
mDashboardData = new DashboardData.Builder(prevData).
setConditionExpanded(false).build();
notifyDashboardDataChanged(prevData);
mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);
scrollToTopOfConditions();
});
break;
}
@@ -263,7 +252,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
void notifyDashboardDataChanged(DashboardData prevData) {
if (mFirstFrameDrawn && prevData != null) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardData
.ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
.ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
diffResult.dispatchUpdatesTo(this);
} else {
mFirstFrameDrawn = true;
@@ -272,120 +261,66 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
}
@VisibleForTesting
void onBindSuggestionConditionHeader(final SuggestionAndConditionHeaderHolder holder,
SuggestionConditionHeaderData data) {
final int curMode = mDashboardData.getSuggestionConditionMode();
final int nextMode = data.hiddenSuggestionCount > 0
&& data.conditionCount > 0
&& curMode != DashboardData.HEADER_MODE_SUGGESTION_EXPANDED
? DashboardData.HEADER_MODE_SUGGESTION_EXPANDED
: DashboardData.HEADER_MODE_FULLY_EXPANDED;
final boolean hasConditions = data.conditionCount > 0;
if (data.conditionCount > 0) {
holder.icon.setImageIcon(data.conditionIcons.get(0));
holder.icon.setVisibility(View.VISIBLE);
if (data.conditionCount == 1) {
holder.title.setText(data.title);
holder.title.setTextColor(Utils.getColorAccent(mContext));
holder.icons.setVisibility(View.INVISIBLE);
} else {
holder.title.setText(null);
updateConditionIcons(data.conditionIcons, holder.icons);
holder.icons.setVisibility(View.VISIBLE);
}
} else {
holder.icon.setVisibility(View.INVISIBLE);
holder.icons.setVisibility(View.INVISIBLE);
}
if (data.hiddenSuggestionCount > 0) {
holder.summary.setTextColor(Color.BLACK);
if (curMode == DashboardData.HEADER_MODE_COLLAPSED) {
if (data.conditionCount > 0) {
holder.summary.setText(mContext.getResources().getQuantityString(
R.plurals.suggestions_collapsed_summary,
data.hiddenSuggestionCount, data.hiddenSuggestionCount));
} else {
holder.title.setText(mContext.getResources().getQuantityString(
R.plurals.suggestions_collapsed_title,
data.hiddenSuggestionCount, data.hiddenSuggestionCount));
holder.title.setTextColor(Color.BLACK);
holder.summary.setText(null);
}
} else if (curMode == DashboardData.HEADER_MODE_DEFAULT) {
if (data.conditionCount > 0) {
holder.summary.setText(mContext.getString(
R.string.suggestions_summary, data.hiddenSuggestionCount));
} else {
holder.title.setText(mContext.getString(
R.string.suggestions_more_title, data.hiddenSuggestionCount));
holder.title.setTextColor(Color.BLACK);
holder.summary.setText(null);
}
}
} else if (data.conditionCount > 1) {
holder.summary.setTextColor(Utils.getColorAccent(mContext));
holder.summary.setText(
mContext.getString(R.string.condition_summary, data.conditionCount));
} else {
void onBindConditionHeader(final ConditionHeaderHolder holder, ConditionHeaderData data) {
holder.icon.setImageIcon(data.conditionIcons.get(0));
if (data.conditionCount == 1) {
holder.title.setText(data.title);
holder.summary.setText(null);
holder.icons.setVisibility(View.INVISIBLE);
} else {
holder.title.setText(null);
holder.summary.setText(
mContext.getString(R.string.condition_summary, data.conditionCount));
updateConditionIcons(data.conditionIcons, holder.icons);
holder.icons.setVisibility(View.VISIBLE);
}
final Resources res = mContext.getResources();
final int padding = res.getDimensionPixelOffset(
curMode == DashboardData.HEADER_MODE_COLLAPSED
? R.dimen.suggestion_condition_header_padding_collapsed
: R.dimen.suggestion_condition_header_padding_expanded);
holder.itemView.setPadding(0, padding, 0, padding);
holder.itemView.setOnClickListener(v -> {
if (hasConditions) {
mMetricsFeatureProvider.action(mContext,
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
}
DashboardData prevData = mDashboardData;
final boolean wasCollapsed = curMode == DashboardData.HEADER_MODE_COLLAPSED;
mMetricsFeatureProvider.action(mContext,
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
.setSuggestionConditionMode(nextMode).build();
.setConditionExpanded(true).build();
notifyDashboardDataChanged(prevData);
if (wasCollapsed) {
mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);
}
scrollToTopOfConditions();
});
}
@VisibleForTesting
void onBindConditionAndSuggestion(final SuggestionAndConditionContainerHolder holder,
int position) {
// If there is suggestions to show, it will be at position 0 as we don't show the suggestion
// header anymore.
final List<Suggestion> suggestions = mDashboardData.getSuggestions();
boolean conditionOnly = true;
if (position == SUGGESTION_CONDITION_HEADER_POSITION) {
if (suggestions != null && suggestions.size() > 0) {
conditionOnly = false;
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
(List<Suggestion>) mDashboardData.getItemEntityByPosition(position),
mSuggestionsShownLogged);
mSuggestionDismissHandler = new SuggestionDismissController(mContext,
holder.data, mSuggestionControllerMixin, mCallback);
holder.data.setAdapter(mSuggestionAdapter);
}
}
if (conditionOnly) {
ConditionAdapter adapter = new ConditionAdapter(mContext,
(List<Condition>) mDashboardData.getItemEntityByPosition(position),
mDashboardData.getSuggestionConditionMode());
adapter.addDismissHandling(holder.data);
holder.data.setAdapter(adapter);
}
void onBindCondition(final ConditionContainerHolder holder, int position) {
final ConditionAdapter adapter = new ConditionAdapter(mContext,
(List<Condition>) mDashboardData.getItemEntityByPosition(position),
mDashboardData.isConditionExpanded());
adapter.addDismissHandling(holder.data);
holder.data.setAdapter(adapter);
holder.data.setLayoutManager(new LinearLayoutManager(mContext));
}
private void onBindTile(DashboardItemHolder holder, Tile tile) {
holder.icon.setImageDrawable(mCache.getIcon(tile.icon));
@VisibleForTesting
void onBindSuggestion(final SuggestionContainerHolder holder, int position) {
// If there is suggestions to show, it will be at position 0 as we don't show the suggestion
// header anymore.
final List<Suggestion> suggestions =
(List<Suggestion>) mDashboardData.getItemEntityByPosition(position);
final int suggestionCount = suggestions.size();
if (suggestions != null && suggestionCount > 0) {
holder.summary.setText("" + suggestionCount);
mSuggestionAdapter.setSuggestions(suggestions);
holder.data.setAdapter(mSuggestionAdapter);
}
final LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
holder.data.setLayoutManager(layoutManager);
}
@VisibleForTesting
void onBindTile(DashboardItemHolder holder, Tile tile) {
Drawable icon = mCache.getIcon(tile.icon);
if (!TextUtils.equals(tile.icon.getResPackage(), mContext.getPackageName())) {
icon = new RoundedHomepageIcon(mContext, icon);
mCache.updateIcon(tile.icon, icon);
}
holder.icon.setImageDrawable(icon);
holder.title.setText(tile.title);
if (!TextUtils.isEmpty(tile.summary)) {
holder.summary.setText(tile.summary);
@@ -395,45 +330,13 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
}
}
private void tintIcons(DashboardCategory category, List<Tile> suggestions) {
if (!mDashboardFeatureProvider.shouldTintIcon()) {
return;
}
// TODO: Better place for tinting?
final TypedArray a = mContext.obtainStyledAttributes(new int[]{
android.R.attr.colorControlNormal});
final int tintColor = a.getColor(0, mContext.getColor(R.color.fallback_tintColor));
a.recycle();
if (category != null) {
for (Tile tile : category.getTiles()) {
if (tile.isIconTintable) {
// If this drawable is tintable, tint it to match the color.
tile.icon.setTint(tintColor);
}
}
}
if (suggestions != null) {
for (Tile suggestion : suggestions) {
if (suggestion.isIconTintable) {
suggestion.icon.setTint(tintColor);
}
}
}
}
void onSaveInstanceState(Bundle outState) {
@Override
public void onSaveInstanceState(Bundle outState) {
final DashboardCategory category = mDashboardData.getCategory();
final List<Suggestion> suggestions = mDashboardData.getSuggestions();
if (suggestions != null) {
outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
new ArrayList<>(suggestions));
}
if (category != null) {
outState.putParcelable(STATE_CATEGORY_LIST, category);
}
outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
outState.putInt(STATE_SUGGESTION_CONDITION_MODE,
mDashboardData.getSuggestionConditionMode());
outState.putBoolean(STATE_CONDITION_EXPANDED, mDashboardData.isConditionExpanded());
}
private void updateConditionIcons(List<Icon> icons, ViewGroup parent) {
@@ -452,6 +355,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
parent.setVisibility(View.VISIBLE);
}
private void scrollToTopOfConditions() {
mRecyclerView.scrollToPosition(mDashboardData.hasSuggestion() ? 1 : 0);
}
public static class IconCache {
private final Context mContext;
private final ArrayMap<Icon, Drawable> mMap = new ArrayMap<>();
@@ -467,10 +374,14 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
Drawable drawable = mMap.get(icon);
if (drawable == null) {
drawable = icon.loadDrawable(mContext);
mMap.put(icon, drawable);
updateIcon(icon, drawable);
}
return drawable;
}
public void updateIcon(Icon icon, Drawable drawable) {
mMap.put(icon, drawable);
}
}
public static class DashboardItemHolder extends RecyclerView.ViewHolder {
@@ -486,24 +397,33 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
}
}
public static class SuggestionAndConditionHeaderHolder extends DashboardItemHolder {
public static class ConditionHeaderHolder extends DashboardItemHolder {
public final LinearLayout icons;
public final ImageView expandIndicator;
public SuggestionAndConditionHeaderHolder(View itemView) {
public ConditionHeaderHolder(View itemView) {
super(itemView);
icons = itemView.findViewById(id.additional_icons);
expandIndicator = itemView.findViewById(id.expand_indicator);
}
}
public static class SuggestionAndConditionContainerHolder extends DashboardItemHolder {
public static class ConditionContainerHolder extends DashboardItemHolder {
public final RecyclerView data;
public SuggestionAndConditionContainerHolder(View itemView) {
public ConditionContainerHolder(View itemView) {
super(itemView);
data = itemView.findViewById(id.data);
}
}
public static class SuggestionContainerHolder extends DashboardItemHolder {
public final RecyclerView data;
public SuggestionContainerHolder(View itemView) {
super(itemView);
data = itemView.findViewById(id.suggestion_list);
}
}
}

View File

@@ -1,429 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dashboard;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.service.settings.suggestions.Suggestion;
import android.support.annotation.VisibleForTesting;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.R.id;
import com.android.settings.dashboard.DashboardDataV2.ConditionHeaderData;
import com.android.settings.dashboard.conditional.Condition;
import com.android.settings.dashboard.conditional.ConditionAdapterV2;
import com.android.settings.dashboard.suggestions.SuggestionAdapterV2;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.suggestions.SuggestionControllerMixin;
import java.util.List;
public class DashboardAdapterV2 extends RecyclerView.Adapter<DashboardAdapterV2.DashboardItemHolder>
implements SummaryLoader.SummaryConsumer, SuggestionAdapterV2.Callback, LifecycleObserver,
OnSaveInstanceState {
public static final String TAG = "DashboardAdapterV2";
private static final String STATE_CATEGORY_LIST = "category_list";
@VisibleForTesting
static final String STATE_CONDITION_EXPANDED = "condition_expanded";
private final IconCache mCache;
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final DashboardFeatureProvider mDashboardFeatureProvider;
private boolean mFirstFrameDrawn;
private RecyclerView mRecyclerView;
private SuggestionAdapterV2 mSuggestionAdapter;
@VisibleForTesting
DashboardDataV2 mDashboardData;
private View.OnClickListener mTileClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO: get rid of setTag/getTag
mDashboardFeatureProvider.openTileIntent((Activity) mContext, (Tile) v.getTag());
}
};
public DashboardAdapterV2(Context context, Bundle savedInstanceState,
List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
Lifecycle lifecycle) {
DashboardCategory category = null;
boolean conditionExpanded = false;
mContext = context;
final FeatureFactory factory = FeatureFactory.getFactory(context);
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
mCache = new IconCache(context);
mSuggestionAdapter = new SuggestionAdapterV2(mContext, suggestionControllerMixin,
savedInstanceState, this /* callback */, lifecycle);
setHasStableIds(true);
if (savedInstanceState != null) {
category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
conditionExpanded = savedInstanceState.getBoolean(
STATE_CONDITION_EXPANDED, conditionExpanded);
}
if (lifecycle != null) {
lifecycle.addObserver(this);
}
mDashboardData = new DashboardDataV2.Builder()
.setConditions(conditions)
.setSuggestions(mSuggestionAdapter.getSuggestions())
.setCategory(category)
.setConditionExpanded(conditionExpanded)
.build();
}
public void setSuggestions(List<Suggestion> data) {
final DashboardDataV2 prevData = mDashboardData;
mDashboardData = new DashboardDataV2.Builder(prevData)
.setSuggestions(data)
.build();
notifyDashboardDataChanged(prevData);
}
public void setCategory(DashboardCategory category) {
final DashboardDataV2 prevData = mDashboardData;
Log.d(TAG, "adapter setCategory called");
mDashboardData = new DashboardDataV2.Builder(prevData)
.setCategory(category)
.build();
notifyDashboardDataChanged(prevData);
}
public void setConditions(List<Condition> conditions) {
final DashboardDataV2 prevData = mDashboardData;
Log.d(TAG, "adapter setConditions called");
mDashboardData = new DashboardDataV2.Builder(prevData)
.setConditions(conditions)
.build();
notifyDashboardDataChanged(prevData);
}
@Override
public void onSuggestionClosed(Suggestion suggestion) {
final List<Suggestion> list = mDashboardData.getSuggestions();
if (list == null || list.size() == 0) {
return;
}
if (list.size() == 1) {
// The only suggestion is dismissed, and the the empty suggestion container will
// remain as the dashboard item. Need to refresh the dashboard list.
setSuggestions(null);
} else {
mSuggestionAdapter.removeSuggestion(suggestion);
notifyItemChanged(0, null);
}
}
@Override
public void notifySummaryChanged(Tile tile) {
final int position = mDashboardData.getPositionByTile(tile);
if (position != DashboardDataV2.POSITION_NOT_FOUND) {
// Since usually tile in parameter and tile in mCategories are same instance,
// which is hard to be detected by DiffUtil, so we notifyItemChanged directly.
notifyItemChanged(position, mDashboardData.getItemTypeByPosition(position));
}
}
@Override
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
if (viewType == R.layout.suggestion_condition_header) {
return new ConditionHeaderHolder(view);
}
if (viewType == R.layout.condition_container) {
return new ConditionContainerHolder(view);
}
if (viewType == R.layout.suggestion_container) {
return new SuggestionContainerHolder(view);
}
return new DashboardItemHolder(view);
}
@Override
public void onBindViewHolder(DashboardItemHolder holder, int position) {
final int type = mDashboardData.getItemTypeByPosition(position);
switch (type) {
case R.layout.dashboard_tile:
final Tile tile = (Tile) mDashboardData.getItemEntityByPosition(position);
onBindTile(holder, tile);
holder.itemView.setTag(tile);
holder.itemView.setOnClickListener(mTileClickListener);
break;
case R.layout.suggestion_container:
onBindSuggestion((SuggestionContainerHolder) holder, position);
break;
case R.layout.condition_container:
onBindCondition((ConditionContainerHolder) holder, position);
break;
case R.layout.suggestion_condition_header:
onBindConditionHeader((ConditionHeaderHolder) holder,
(ConditionHeaderData) mDashboardData.getItemEntityByPosition(position));
break;
case R.layout.suggestion_condition_footer:
holder.itemView.setOnClickListener(v -> {
mMetricsFeatureProvider.action(mContext,
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
DashboardDataV2 prevData = mDashboardData;
mDashboardData = new DashboardDataV2.Builder(prevData).
setConditionExpanded(false).build();
notifyDashboardDataChanged(prevData);
scrollToTopOfConditions();
});
break;
}
}
@Override
public long getItemId(int position) {
return mDashboardData.getItemIdByPosition(position);
}
@Override
public int getItemViewType(int position) {
return mDashboardData.getItemTypeByPosition(position);
}
@Override
public int getItemCount() {
return mDashboardData.size();
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
// save the view so that we can scroll it when expanding/collapsing the suggestion and
// conditions.
mRecyclerView = recyclerView;
}
public Object getItem(long itemId) {
return mDashboardData.getItemEntityById(itemId);
}
public Suggestion getSuggestion(int position) {
return mSuggestionAdapter.getSuggestion(position);
}
@VisibleForTesting
void notifyDashboardDataChanged(DashboardDataV2 prevData) {
if (mFirstFrameDrawn && prevData != null) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardDataV2
.ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
diffResult.dispatchUpdatesTo(this);
} else {
mFirstFrameDrawn = true;
notifyDataSetChanged();
}
}
@VisibleForTesting
void onBindConditionHeader(final ConditionHeaderHolder holder, ConditionHeaderData data) {
holder.icon.setImageIcon(data.conditionIcons.get(0));
if (data.conditionCount == 1) {
holder.title.setText(data.title);
holder.summary.setText(null);
holder.icons.setVisibility(View.INVISIBLE);
} else {
holder.title.setText(null);
holder.summary.setText(
mContext.getString(R.string.condition_summary, data.conditionCount));
updateConditionIcons(data.conditionIcons, holder.icons);
holder.icons.setVisibility(View.VISIBLE);
}
holder.itemView.setOnClickListener(v -> {
mMetricsFeatureProvider.action(mContext,
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
final DashboardDataV2 prevData = mDashboardData;
mDashboardData = new DashboardDataV2.Builder(prevData)
.setConditionExpanded(true).build();
notifyDashboardDataChanged(prevData);
scrollToTopOfConditions();
});
}
@VisibleForTesting
void onBindCondition(final ConditionContainerHolder holder, int position) {
final ConditionAdapterV2 adapter = new ConditionAdapterV2(mContext,
(List<Condition>) mDashboardData.getItemEntityByPosition(position),
mDashboardData.isConditionExpanded());
adapter.addDismissHandling(holder.data);
holder.data.setAdapter(adapter);
holder.data.setLayoutManager(new LinearLayoutManager(mContext));
}
@VisibleForTesting
void onBindSuggestion(final SuggestionContainerHolder holder, int position) {
// If there is suggestions to show, it will be at position 0 as we don't show the suggestion
// header anymore.
final List<Suggestion> suggestions =
(List<Suggestion>) mDashboardData.getItemEntityByPosition(position);
final int suggestionCount = suggestions.size();
if (suggestions != null && suggestionCount > 0) {
holder.summary.setText("" + suggestionCount);
mSuggestionAdapter.setSuggestions(suggestions);
holder.data.setAdapter(mSuggestionAdapter);
}
final LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
holder.data.setLayoutManager(layoutManager);
}
@VisibleForTesting
void onBindTile(DashboardItemHolder holder, Tile tile) {
Drawable icon = mCache.getIcon(tile.icon);
if (!TextUtils.equals(tile.icon.getResPackage(), mContext.getPackageName())) {
icon = new RoundedHomepageIcon(mContext, icon);
mCache.updateIcon(tile.icon, icon);
}
holder.icon.setImageDrawable(icon);
holder.title.setText(tile.title);
if (!TextUtils.isEmpty(tile.summary)) {
holder.summary.setText(tile.summary);
holder.summary.setVisibility(View.VISIBLE);
} else {
holder.summary.setVisibility(View.GONE);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
final DashboardCategory category = mDashboardData.getCategory();
if (category != null) {
outState.putParcelable(STATE_CATEGORY_LIST, category);
}
outState.putBoolean(STATE_CONDITION_EXPANDED, mDashboardData.isConditionExpanded());
}
private void updateConditionIcons(List<Icon> icons, ViewGroup parent) {
if (icons == null || icons.size() < 2) {
parent.setVisibility(View.INVISIBLE);
return;
}
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
parent.removeAllViews();
for (int i = 1, size = icons.size(); i < size; i++) {
ImageView icon = (ImageView) inflater.inflate(
R.layout.condition_header_icon, parent, false);
icon.setImageIcon(icons.get(i));
parent.addView(icon);
}
parent.setVisibility(View.VISIBLE);
}
private void scrollToTopOfConditions() {
mRecyclerView.scrollToPosition(mDashboardData.hasSuggestion() ? 1 : 0);
}
public static class IconCache {
private final Context mContext;
private final ArrayMap<Icon, Drawable> mMap = new ArrayMap<>();
public IconCache(Context context) {
mContext = context;
}
public Drawable getIcon(Icon icon) {
if (icon == null) {
return null;
}
Drawable drawable = mMap.get(icon);
if (drawable == null) {
drawable = icon.loadDrawable(mContext);
updateIcon(icon, drawable);
}
return drawable;
}
public void updateIcon(Icon icon, Drawable drawable) {
mMap.put(icon, drawable);
}
}
public static class DashboardItemHolder extends RecyclerView.ViewHolder {
public final ImageView icon;
public final TextView title;
public final TextView summary;
public DashboardItemHolder(View itemView) {
super(itemView);
icon = itemView.findViewById(android.R.id.icon);
title = itemView.findViewById(android.R.id.title);
summary = itemView.findViewById(android.R.id.summary);
}
}
public static class ConditionHeaderHolder extends DashboardItemHolder {
public final LinearLayout icons;
public final ImageView expandIndicator;
public ConditionHeaderHolder(View itemView) {
super(itemView);
icons = itemView.findViewById(id.additional_icons);
expandIndicator = itemView.findViewById(id.expand_indicator);
}
}
public static class ConditionContainerHolder extends DashboardItemHolder {
public final RecyclerView data;
public ConditionContainerHolder(View itemView) {
super(itemView);
data = itemView.findViewById(id.data);
}
}
public static class SuggestionContainerHolder extends DashboardItemHolder {
public final RecyclerView data;
public SuggestionContainerHolder(View itemView) {
super(itemView);
data = itemView.findViewById(id.suggestion_list);
}
}
}

View File

@@ -40,29 +40,17 @@ import java.util.Objects;
* ItemsData has inner class Item, which represents the Item in data list.
*/
public class DashboardData {
public static final int HEADER_MODE_DEFAULT = 0;
public static final int HEADER_MODE_SUGGESTION_EXPANDED = 1;
public static final int HEADER_MODE_FULLY_EXPANDED = 2;
public static final int HEADER_MODE_COLLAPSED = 3;
@Retention(RetentionPolicy.SOURCE)
@IntDef({HEADER_MODE_DEFAULT, HEADER_MODE_SUGGESTION_EXPANDED, HEADER_MODE_FULLY_EXPANDED,
HEADER_MODE_COLLAPSED})
public @interface HeaderMode {
}
public static final int POSITION_NOT_FOUND = -1;
public static final int DEFAULT_SUGGESTION_COUNT = 2;
public static final int MAX_SUGGESTION_COUNT = 4;
// stable id for different type of items.
@VisibleForTesting
static final int STABLE_ID_SUGGESTION_CONDITION_TOP_HEADER = 0;
static final int STABLE_ID_SUGGESTION_CONTAINER = 0;
static final int STABLE_ID_SUGGESTION_CONDITION_DIVIDER = 1;
@VisibleForTesting
static final int STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER = 1;
static final int STABLE_ID_CONDITION_HEADER = 2;
@VisibleForTesting
static final int STABLE_ID_SUGGESTION_CONDITION_FOOTER = 2;
@VisibleForTesting
static final int STABLE_ID_SUGGESTION_CONTAINER = 3;
static final int STABLE_ID_CONDITION_FOOTER = 3;
@VisibleForTesting
static final int STABLE_ID_CONDITION_CONTAINER = 4;
@@ -70,15 +58,13 @@ public class DashboardData {
private final DashboardCategory mCategory;
private final List<Condition> mConditions;
private final List<Suggestion> mSuggestions;
@HeaderMode
private final int mSuggestionConditionMode;
private final boolean mConditionExpanded;
private DashboardData(Builder builder) {
mCategory = builder.mCategory;
mConditions = builder.mConditions;
mSuggestions = builder.mSuggestionsV2;
mSuggestionConditionMode = builder.mSuggestionConditionMode;
mSuggestions = builder.mSuggestions;
mConditionExpanded = builder.mConditionExpanded;
mItems = new ArrayList<>();
buildItemsData();
@@ -125,8 +111,12 @@ public class DashboardData {
return mSuggestions;
}
public int getSuggestionConditionMode() {
return mSuggestionConditionMode;
public boolean hasSuggestion() {
return sizeOf(mSuggestions) > 0;
}
public boolean isConditionExpanded() {
return mConditionExpanded;
}
/**
@@ -188,69 +178,42 @@ public class DashboardData {
/**
* Build the mItems list using mConditions, mSuggestions, mCategories data
* and mIsShowingAll, mSuggestionConditionMode flag.
* and mIsShowingAll, mConditionExpanded flag.
*/
private void buildItemsData() {
final boolean hasSuggestions = sizeOf(mSuggestions) > 0;
final List<Condition> conditions = getConditionsToShow(mConditions);
final boolean hasConditions = sizeOf(conditions) > 0;
final List<Suggestion> suggestions = getSuggestionsToShow(mSuggestions);
final boolean hasSuggestions = sizeOf(suggestions) > 0;
final int hiddenSuggestion = hasSuggestions
? sizeOf(mSuggestions) - sizeOf(suggestions)
: 0;
/* Suggestion container. This is the card view that contains the list of suggestions.
* This will be added whenever the suggestion list is not empty */
addToItemList(suggestions, R.layout.suggestion_container,
STABLE_ID_SUGGESTION_CONTAINER, hasSuggestions);
final boolean hasSuggestionAndCollapsed = hasSuggestions
&& mSuggestionConditionMode == HEADER_MODE_COLLAPSED;
final boolean onlyHasConditionAndCollapsed = !hasSuggestions
&& hasConditions
&& mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED;
/* Divider between suggestion and conditions if both are present. */
addToItemList(null /* item */, R.layout.horizontal_divider,
STABLE_ID_SUGGESTION_CONDITION_DIVIDER, hasSuggestions && hasConditions);
/* Top suggestion/condition header. This will be present when there is any suggestion
* and the mode is collapsed */
addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
R.layout.suggestion_condition_header,
STABLE_ID_SUGGESTION_CONDITION_TOP_HEADER, hasSuggestionAndCollapsed);
/* Condition header. This will be present when there is condition and it is collapsed */
addToItemList(new ConditionHeaderData(conditions),
R.layout.condition_header,
STABLE_ID_CONDITION_HEADER, hasConditions && !mConditionExpanded);
/* Use mid header if there is only condition & it's in collapsed mode */
addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
R.layout.suggestion_condition_header,
STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER, onlyHasConditionAndCollapsed);
/* Condition container. This is the card view that contains the list of conditions.
* This will be added whenever the condition list is not empty and expanded */
addToItemList(conditions, R.layout.condition_container,
STABLE_ID_CONDITION_CONTAINER, hasConditions && mConditionExpanded);
addToItemList(suggestions, R.layout.suggestion_condition_container,
STABLE_ID_SUGGESTION_CONTAINER, sizeOf(suggestions) > 0);
/* Second suggestion/condition header. This will be added when there is at least one
* suggestion or condition that is not currently displayed, and the user can expand the
* section to view more items. */
addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
R.layout.suggestion_condition_header,
STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER,
mSuggestionConditionMode != HEADER_MODE_COLLAPSED
&& mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED
&& (hiddenSuggestion > 0 || hasConditions && hasSuggestions));
/* Condition container. This is the card view that contains the list of conditions.
* This will be added whenever the condition list is not empty */
addToItemList(conditions, R.layout.suggestion_condition_container,
STABLE_ID_CONDITION_CONTAINER,
hasConditions && mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED);
/* Suggestion/condition footer. This will be present when the section is fully expanded
* or when there is no conditions and no hidden suggestions */
addToItemList(null /* item */, R.layout.suggestion_condition_footer,
STABLE_ID_SUGGESTION_CONDITION_FOOTER,
(hasConditions || hasSuggestions)
&& mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED
|| hasSuggestions
&& !hasConditions
&& hiddenSuggestion == 0);
/* Condition footer. This will be present when there is condition and it is expanded */
addToItemList(null /* item */, R.layout.condition_footer,
STABLE_ID_CONDITION_FOOTER, hasConditions && mConditionExpanded);
if (mCategory != null) {
final List<Tile> tiles = mCategory.getTiles();
for (int j = 0; j < tiles.size(); j++) {
final Tile tile = tiles.get(j);
for (int i = 0; i < tiles.size(); i++) {
final Tile tile = tiles.get(i);
addToItemList(tile, R.layout.dashboard_tile, Objects.hash(tile.title),
true /* add */);
}
@@ -277,28 +240,23 @@ public class DashboardData {
}
private List<Suggestion> getSuggestionsToShow(List<Suggestion> suggestions) {
if (suggestions == null || mSuggestionConditionMode == HEADER_MODE_COLLAPSED) {
if (suggestions == null) {
return null;
}
if (mSuggestionConditionMode != HEADER_MODE_DEFAULT
|| suggestions.size() <= DEFAULT_SUGGESTION_COUNT) {
if (suggestions.size() <= MAX_SUGGESTION_COUNT) {
return suggestions;
}
return suggestions.subList(0, DEFAULT_SUGGESTION_COUNT);
return suggestions.subList(0, MAX_SUGGESTION_COUNT);
}
/**
* Builder used to build the ItemsData
* <p>
* {@link #mSuggestionConditionMode} have default value while others are not.
*/
public static class Builder {
@HeaderMode
private int mSuggestionConditionMode = HEADER_MODE_DEFAULT;
private DashboardCategory mCategory;
private List<Condition> mConditions;
private List<Suggestion> mSuggestionsV2;
private List<Suggestion> mSuggestions;
private boolean mConditionExpanded;
public Builder() {
}
@@ -306,8 +264,8 @@ public class DashboardData {
public Builder(DashboardData dashboardData) {
mCategory = dashboardData.mCategory;
mConditions = dashboardData.mConditions;
mSuggestionsV2 = dashboardData.mSuggestions;
mSuggestionConditionMode = dashboardData.mSuggestionConditionMode;
mSuggestions = dashboardData.mSuggestions;
mConditionExpanded = dashboardData.mConditionExpanded;
}
public Builder setCategory(DashboardCategory category) {
@@ -321,12 +279,12 @@ public class DashboardData {
}
public Builder setSuggestions(List<Suggestion> suggestions) {
this.mSuggestionsV2 = suggestions;
this.mSuggestions = suggestions;
return this;
}
public Builder setSuggestionConditionMode(@HeaderMode int mode) {
this.mSuggestionConditionMode = mode;
public Builder setConditionExpanded(boolean expanded) {
this.mConditionExpanded = expanded;
return this;
}
@@ -376,17 +334,18 @@ public class DashboardData {
static class Item {
// valid types in field type
private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
private static final int TYPE_SUGGESTION_CONDITION_CONTAINER =
R.layout.suggestion_condition_container;
private static final int TYPE_SUGGESTION_CONDITION_HEADER =
R.layout.suggestion_condition_header;
private static final int TYPE_SUGGESTION_CONDITION_FOOTER =
R.layout.suggestion_condition_footer;
private static final int TYPE_DASHBOARD_SPACER = R.layout.dashboard_spacer;
private static final int TYPE_SUGGESTION_CONTAINER =
R.layout.suggestion_container;
private static final int TYPE_CONDITION_CONTAINER =
R.layout.condition_container;
private static final int TYPE_CONDITION_HEADER =
R.layout.condition_header;
private static final int TYPE_CONDITION_FOOTER =
R.layout.condition_footer;
private static final int TYPE_SUGGESTION_CONDITION_DIVIDER = R.layout.horizontal_divider;
@IntDef({TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_CONDITION_CONTAINER,
TYPE_SUGGESTION_CONDITION_HEADER, TYPE_SUGGESTION_CONDITION_FOOTER,
TYPE_DASHBOARD_SPACER})
@IntDef({TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_CONTAINER, TYPE_CONDITION_CONTAINER,
TYPE_CONDITION_HEADER, TYPE_CONDITION_FOOTER, TYPE_SUGGESTION_CONDITION_DIVIDER})
@Retention(RetentionPolicy.SOURCE)
public @interface ItemTypes {
}
@@ -444,8 +403,9 @@ public class DashboardData {
// Only check title and summary for dashboard tile
return TextUtils.equals(localTile.title, targetTile.title)
&& TextUtils.equals(localTile.summary, targetTile.summary);
case TYPE_SUGGESTION_CONDITION_CONTAINER:
&& TextUtils.equals(localTile.summary, targetTile.summary);
case TYPE_SUGGESTION_CONTAINER:
case TYPE_CONDITION_CONTAINER:
// If entity is suggestion and contains remote view, force refresh
final List entities = (List) entity;
if (!entities.isEmpty()) {
@@ -467,16 +427,13 @@ public class DashboardData {
* This class contains the data needed to build the suggestion/condition header. The data can
* also be used to check the diff in DiffUtil.Callback
*/
public static class SuggestionConditionHeaderData {
public static class ConditionHeaderData {
public final List<Icon> conditionIcons;
public final CharSequence title;
public final int conditionCount;
public final int hiddenSuggestionCount;
public SuggestionConditionHeaderData(List<Condition> conditions,
int hiddenSuggestionCount) {
public ConditionHeaderData(List<Condition> conditions) {
conditionCount = sizeOf(conditions);
this.hiddenSuggestionCount = hiddenSuggestionCount;
title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
conditionIcons = new ArrayList<>();
for (int i = 0; conditions != null && i < conditions.size(); i++) {

View File

@@ -1,446 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dashboard;
import android.annotation.IntDef;
import android.graphics.drawable.Icon;
import android.service.settings.suggestions.Suggestion;
import android.support.annotation.VisibleForTesting;
import android.support.v7.util.DiffUtil;
import android.text.TextUtils;
import com.android.settings.R;
import com.android.settings.dashboard.conditional.Condition;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Description about data list used in the DashboardAdapter. In the data list each item can be
* Condition, suggestion or category tile.
* <p>
* ItemsData has inner class Item, which represents the Item in data list.
*/
public class DashboardDataV2 {
public static final int POSITION_NOT_FOUND = -1;
public static final int MAX_SUGGESTION_COUNT = 4;
// stable id for different type of items.
@VisibleForTesting
static final int STABLE_ID_SUGGESTION_CONTAINER = 0;
static final int STABLE_ID_SUGGESTION_CONDITION_DIVIDER = 1;
@VisibleForTesting
static final int STABLE_ID_CONDITION_HEADER = 2;
@VisibleForTesting
static final int STABLE_ID_CONDITION_FOOTER = 3;
@VisibleForTesting
static final int STABLE_ID_CONDITION_CONTAINER = 4;
private final List<Item> mItems;
private final DashboardCategory mCategory;
private final List<Condition> mConditions;
private final List<Suggestion> mSuggestions;
private final boolean mConditionExpanded;
private DashboardDataV2(Builder builder) {
mCategory = builder.mCategory;
mConditions = builder.mConditions;
mSuggestions = builder.mSuggestions;
mConditionExpanded = builder.mConditionExpanded;
mItems = new ArrayList<>();
buildItemsData();
}
public int getItemIdByPosition(int position) {
return mItems.get(position).id;
}
public int getItemTypeByPosition(int position) {
return mItems.get(position).type;
}
public Object getItemEntityByPosition(int position) {
return mItems.get(position).entity;
}
public List<Item> getItemList() {
return mItems;
}
public int size() {
return mItems.size();
}
public Object getItemEntityById(long id) {
for (final Item item : mItems) {
if (item.id == id) {
return item.entity;
}
}
return null;
}
public DashboardCategory getCategory() {
return mCategory;
}
public List<Condition> getConditions() {
return mConditions;
}
public List<Suggestion> getSuggestions() {
return mSuggestions;
}
public boolean hasSuggestion() {
return sizeOf(mSuggestions) > 0;
}
public boolean isConditionExpanded() {
return mConditionExpanded;
}
/**
* Find the position of the object in mItems list, using the equals method to compare
*
* @param entity the object that need to be found in list
* @return position of the object, return POSITION_NOT_FOUND if object isn't in the list
*/
public int getPositionByEntity(Object entity) {
if (entity == null) return POSITION_NOT_FOUND;
final int size = mItems.size();
for (int i = 0; i < size; i++) {
final Object item = mItems.get(i).entity;
if (entity.equals(item)) {
return i;
}
}
return POSITION_NOT_FOUND;
}
/**
* Find the position of the Tile object.
* <p>
* First, try to find the exact identical instance of the tile object, if not found,
* then try to find a tile has the same title.
*
* @param tile tile that need to be found
* @return position of the object, return INDEX_NOT_FOUND if object isn't in the list
*/
public int getPositionByTile(Tile tile) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
final Object entity = mItems.get(i).entity;
if (entity == tile) {
return i;
} else if (entity instanceof Tile && tile.title.equals(((Tile) entity).title)) {
return i;
}
}
return POSITION_NOT_FOUND;
}
/**
* Add item into list when {@paramref add} is true.
*
* @param item maybe {@link Condition}, {@link Tile}, {@link DashboardCategory} or null
* @param type type of the item, and value is the layout id
* @param stableId The stable id for this item
* @param add flag about whether to add item into list
*/
private void addToItemList(Object item, int type, int stableId, boolean add) {
if (add) {
mItems.add(new Item(item, type, stableId));
}
}
/**
* Build the mItems list using mConditions, mSuggestions, mCategories data
* and mIsShowingAll, mConditionExpanded flag.
*/
private void buildItemsData() {
final List<Condition> conditions = getConditionsToShow(mConditions);
final boolean hasConditions = sizeOf(conditions) > 0;
final List<Suggestion> suggestions = getSuggestionsToShow(mSuggestions);
final boolean hasSuggestions = sizeOf(suggestions) > 0;
/* Suggestion container. This is the card view that contains the list of suggestions.
* This will be added whenever the suggestion list is not empty */
addToItemList(suggestions, R.layout.suggestion_container,
STABLE_ID_SUGGESTION_CONTAINER, hasSuggestions);
/* Divider between suggestion and conditions if both are present. */
addToItemList(suggestions, R.layout.horizontal_divider,
STABLE_ID_SUGGESTION_CONDITION_DIVIDER, hasSuggestions && hasConditions);
/* Condition header. This will be present when there is condition and it is collapsed */
addToItemList(new ConditionHeaderData(conditions),
R.layout.suggestion_condition_header,
STABLE_ID_CONDITION_HEADER, hasConditions && !mConditionExpanded);
/* Condition container. This is the card view that contains the list of conditions.
* This will be added whenever the condition list is not empty and expanded */
addToItemList(conditions, R.layout.condition_container,
STABLE_ID_CONDITION_CONTAINER, hasConditions && mConditionExpanded);
/* Condition footer. This will be present when there is condition and it is expanded */
addToItemList(null /* item */, R.layout.suggestion_condition_footer,
STABLE_ID_CONDITION_FOOTER, hasConditions && mConditionExpanded);
if (mCategory != null) {
final List<Tile> tiles = mCategory.getTiles();
for (int i = 0; i < tiles.size(); i++) {
final Tile tile = tiles.get(i);
addToItemList(tile, R.layout.dashboard_tile, Objects.hash(tile.title),
true /* add */);
}
}
}
private static int sizeOf(List<?> list) {
return list == null ? 0 : list.size();
}
private List<Condition> getConditionsToShow(List<Condition> conditions) {
if (conditions == null) {
return null;
}
List<Condition> result = new ArrayList<>();
final int size = conditions == null ? 0 : conditions.size();
for (int i = 0; i < size; i++) {
final Condition condition = conditions.get(i);
if (condition.shouldShow()) {
result.add(condition);
}
}
return result;
}
private List<Suggestion> getSuggestionsToShow(List<Suggestion> suggestions) {
if (suggestions == null) {
return null;
}
if (suggestions.size() <= MAX_SUGGESTION_COUNT) {
return suggestions;
}
return suggestions.subList(0, MAX_SUGGESTION_COUNT);
}
/**
* Builder used to build the ItemsData
*/
public static class Builder {
private DashboardCategory mCategory;
private List<Condition> mConditions;
private List<Suggestion> mSuggestions;
private boolean mConditionExpanded;
public Builder() {
}
public Builder(DashboardDataV2 dashboardData) {
mCategory = dashboardData.mCategory;
mConditions = dashboardData.mConditions;
mSuggestions = dashboardData.mSuggestions;
mConditionExpanded = dashboardData.mConditionExpanded;
}
public Builder setCategory(DashboardCategory category) {
this.mCategory = category;
return this;
}
public Builder setConditions(List<Condition> conditions) {
this.mConditions = conditions;
return this;
}
public Builder setSuggestions(List<Suggestion> suggestions) {
this.mSuggestions = suggestions;
return this;
}
public Builder setConditionExpanded(boolean expanded) {
this.mConditionExpanded = expanded;
return this;
}
public DashboardDataV2 build() {
return new DashboardDataV2(this);
}
}
/**
* A DiffCallback to calculate the difference between old and new Item
* List in DashboardDataV2
*/
public static class ItemsDataDiffCallback extends DiffUtil.Callback {
final private List<Item> mOldItems;
final private List<Item> mNewItems;
public ItemsDataDiffCallback(List<Item> oldItems, List<Item> newItems) {
mOldItems = oldItems;
mNewItems = newItems;
}
@Override
public int getOldListSize() {
return mOldItems.size();
}
@Override
public int getNewListSize() {
return mNewItems.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mOldItems.get(oldItemPosition).id == mNewItems.get(newItemPosition).id;
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return mOldItems.get(oldItemPosition).equals(mNewItems.get(newItemPosition));
}
}
/**
* An item contains the data needed in the DashboardDataV2.
*/
static class Item {
// valid types in field type
private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
private static final int TYPE_SUGGESTION_CONTAINER =
R.layout.suggestion_container;
private static final int TYPE_CONDITION_CONTAINER =
R.layout.condition_container;
private static final int TYPE_CONDITION_HEADER =
R.layout.suggestion_condition_header;
private static final int TYPE_CONDITION_FOOTER =
R.layout.suggestion_condition_footer;
private static final int TYPE_SUGGESTION_CONDITION_DIVIDER = R.layout.horizontal_divider;
@IntDef({TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_CONTAINER, TYPE_CONDITION_CONTAINER,
TYPE_CONDITION_HEADER, TYPE_CONDITION_FOOTER, TYPE_SUGGESTION_CONDITION_DIVIDER})
@Retention(RetentionPolicy.SOURCE)
public @interface ItemTypes {
}
/**
* The main data object in item, usually is a {@link Tile}, {@link Condition}
* object. This object can also be null when the
* item is an divider line. Please refer to {@link #buildItemsData()} for
* detail usage of the Item.
*/
public final Object entity;
/**
* The type of item, value inside is the layout id(e.g. R.layout.dashboard_tile)
*/
@ItemTypes
public final int type;
/**
* Id of this item, used in the {@link ItemsDataDiffCallback} to identify the same item.
*/
public final int id;
public Item(Object entity, @ItemTypes int type, int id) {
this.entity = entity;
this.type = type;
this.id = id;
}
/**
* Override it to make comparision in the {@link ItemsDataDiffCallback}
*
* @param obj object to compared with
* @return true if the same object or has equal value.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Item)) {
return false;
}
final Item targetItem = (Item) obj;
if (type != targetItem.type || id != targetItem.id) {
return false;
}
switch (type) {
case TYPE_DASHBOARD_TILE:
final Tile localTile = (Tile) entity;
final Tile targetTile = (Tile) targetItem.entity;
// Only check title and summary for dashboard tile
return TextUtils.equals(localTile.title, targetTile.title)
&& TextUtils.equals(localTile.summary, targetTile.summary);
case TYPE_SUGGESTION_CONTAINER:
case TYPE_CONDITION_CONTAINER:
// If entity is suggestion and contains remote view, force refresh
final List entities = (List) entity;
if (!entities.isEmpty()) {
Object firstEntity = entities.get(0);
if (firstEntity instanceof Tile
&& ((Tile) firstEntity).remoteViews != null) {
return false;
}
}
// Otherwise Fall through to default
default:
return entity == null ? targetItem.entity == null
: entity.equals(targetItem.entity);
}
}
}
/**
* This class contains the data needed to build the suggestion/condition header. The data can
* also be used to check the diff in DiffUtil.Callback
*/
public static class ConditionHeaderData {
public final List<Icon> conditionIcons;
public final CharSequence title;
public final int conditionCount;
public ConditionHeaderData(List<Condition> conditions) {
conditionCount = sizeOf(conditions);
title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
conditionIcons = new ArrayList<>();
for (int i = 0; conditions != null && i < conditions.size(); i++) {
final Condition condition = conditions.get(i);
conditionIcons.add(condition.getIcon());
}
}
}
}

View File

@@ -90,9 +90,4 @@ public interface DashboardFeatureProvider {
*/
void openTileIntent(Activity activity, Tile tile);
/**
* Whether or not we should use the v2 of suggestions UI.
*/
boolean useSuggestionUiV2();
}

View File

@@ -217,11 +217,6 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
launchIntentOrSelectProfile(activity, tile, intent, MetricsEvent.DASHBOARD_SUMMARY);
}
@Override
public boolean useSuggestionUiV2() {
return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.SUGGESTION_UI_V2);
}
private void bindSummary(Preference preference, Tile tile) {
if (tile.summary != null) {
preference.setSummary(tile.summary);

View File

@@ -38,7 +38,6 @@ import com.android.settings.dashboard.conditional.ConditionManager;
import com.android.settings.dashboard.conditional.ConditionManager.ConditionListener;
import com.android.settings.dashboard.conditional.FocusRecyclerView;
import com.android.settings.dashboard.conditional.FocusRecyclerView.FocusListener;
import com.android.settings.dashboard.suggestions.SuggestionDismissController;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.ActionBarShadowController;
@@ -53,8 +52,7 @@ import java.util.List;
public class DashboardSummary extends InstrumentedFragment
implements CategoryListener, ConditionListener,
FocusListener, SuggestionDismissController.Callback,
SuggestionControllerMixin.SuggestionControllerHost {
FocusListener, SuggestionControllerMixin.SuggestionControllerHost {
public static final boolean DEBUG = false;
private static final boolean DEBUG_TIMING = false;
private static final int MAX_WAIT_MILLIS = 700;
@@ -66,7 +64,6 @@ public class DashboardSummary extends InstrumentedFragment
private FocusRecyclerView mDashboard;
private DashboardAdapter mAdapter;
private DashboardAdapterV2 mAdapterV2;
private SummaryLoader mSummaryLoader;
private ConditionManager mConditionManager;
private LinearLayoutManager mLayoutManager;
@@ -181,13 +178,10 @@ public class DashboardSummary extends InstrumentedFragment
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mLayoutManager == null) return;
outState.putInt(EXTRA_SCROLL_POSITION, mLayoutManager.findFirstVisibleItemPosition());
if (!mDashboardFeatureProvider.useSuggestionUiV2()) {
if (mAdapter != null) {
mAdapter.onSaveInstanceState(outState);
}
if (mLayoutManager == null) {
return;
}
outState.putInt(EXTRA_SCROLL_POSITION, mLayoutManager.findFirstVisibleItemPosition());
}
@Override
@@ -205,17 +199,10 @@ public class DashboardSummary extends InstrumentedFragment
mDashboard.setHasFixedSize(true);
mDashboard.setListener(this);
mDashboard.setItemAnimator(new DashboardItemAnimator());
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
mAdapterV2 = new DashboardAdapterV2(getContext(), bundle,
mConditionManager.getConditions(), mSuggestionControllerMixin, getLifecycle());
mDashboard.setAdapter(mAdapterV2);
mSummaryLoader.setSummaryConsumer(mAdapterV2);
} else {
mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions(),
mSuggestionControllerMixin, this /* SuggestionDismissController.Callback */);
mDashboard.setAdapter(mAdapter);
mSummaryLoader.setSummaryConsumer(mAdapter);
}
mAdapter = new DashboardAdapter(getContext(), bundle,
mConditionManager.getConditions(), mSuggestionControllerMixin, getLifecycle());
mDashboard.setAdapter(mAdapter);
mSummaryLoader.setSummaryConsumer(mAdapter);
ActionBarShadowController.attachToRecyclerView(
getActivity().findViewById(R.id.search_bar_container), getLifecycle(), mDashboard);
rebuildUI();
@@ -254,11 +241,7 @@ public class DashboardSummary extends InstrumentedFragment
if (mOnConditionsChangedCalled) {
final boolean scrollToTop =
mLayoutManager.findFirstCompletelyVisibleItemPosition() <= 1;
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
mAdapterV2.setConditions(mConditionManager.getConditions());
} else {
mAdapter.setConditions(mConditionManager.getConditions());
}
mAdapter.setConditions(mConditionManager.getConditions());
if (scrollToTop) {
mDashboard.scrollToPosition(0);
}
@@ -267,37 +250,14 @@ public class DashboardSummary extends InstrumentedFragment
}
}
@Override
public Suggestion getSuggestionAt(int position) {
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
return mAdapterV2.getSuggestion(position);
} else {
return mAdapter.getSuggestion(position);
}
}
@Override
public void onSuggestionDismissed(Suggestion suggestion) {
mAdapter.onSuggestionDismissed(suggestion);
}
@Override
public void onSuggestionReady(List<Suggestion> suggestions) {
mStagingSuggestions = suggestions;
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
mAdapterV2.setSuggestions(suggestions);
if (mStagingCategory != null) {
Log.d(TAG, "Category has loaded, setting category from suggestionReady");
mHandler.removeCallbacksAndMessages(null);
mAdapterV2.setCategory(mStagingCategory);
}
} else {
mAdapter.setSuggestions(suggestions);
if (mStagingCategory != null) {
Log.d(TAG, "Category has loaded, setting category from suggestionReady");
mHandler.removeCallbacksAndMessages(null);
mAdapter.setCategory(mStagingCategory);
}
mAdapter.setSuggestions(suggestions);
if (mStagingCategory != null) {
Log.d(TAG, "Category has loaded, setting category from suggestionReady");
mHandler.removeCallbacksAndMessages(null);
mAdapter.setCategory(mStagingCategory);
}
}
@@ -313,26 +273,14 @@ public class DashboardSummary extends InstrumentedFragment
if (mSuggestionControllerMixin.isSuggestionLoaded()) {
Log.d(TAG, "Suggestion has loaded, setting suggestion/category");
ThreadUtils.postOnMainThread(() -> {
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
if (mStagingSuggestions != null) {
mAdapterV2.setSuggestions(mStagingSuggestions);
}
mAdapterV2.setCategory(mStagingCategory);
} else {
if (mStagingSuggestions != null) {
mAdapter.setSuggestions(mStagingSuggestions);
}
mAdapter.setCategory(mStagingCategory);
if (mStagingSuggestions != null) {
mAdapter.setSuggestions(mStagingSuggestions);
}
mAdapter.setCategory(mStagingCategory);
});
} else {
Log.d(TAG, "Suggestion NOT loaded, delaying setCategory by " + MAX_WAIT_MILLIS + "ms");
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
mHandler.postDelayed(()
-> mAdapterV2.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
} else {
mHandler.postDelayed(() -> mAdapter.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
}
mHandler.postDelayed(() -> mAdapter.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
}
}
}

View File

@@ -27,10 +27,7 @@ import android.widget.Button;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardAdapter;
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
import com.android.settings.dashboard.DashboardData;
import com.android.settings.dashboard.DashboardData.HeaderMode;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
@@ -44,7 +41,7 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private List<Condition> mConditions;
private @HeaderMode int mMode;
private boolean mExpanded;
private View.OnClickListener mConditionClickListener = new View.OnClickListener() {
@@ -84,10 +81,10 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
}
};
public ConditionAdapter(Context context, List<Condition> conditions, @HeaderMode int mode) {
public ConditionAdapter(Context context, List<Condition> conditions, boolean expanded) {
mContext = context;
mConditions = conditions;
mMode = mode;
mExpanded = expanded;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
setHasStableIds(true);
@@ -126,7 +123,7 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
@Override
public int getItemCount() {
if (mMode == DashboardData.HEADER_MODE_FULLY_EXPANDED) {
if (mExpanded) {
return mConditions.size();
}
return 0;
@@ -138,7 +135,7 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
}
private void bindViews(final Condition condition,
DashboardAdapter.DashboardItemHolder view, boolean isLastItem,
DashboardItemHolder view, boolean isLastItem,
View.OnClickListener onClickListener) {
if (condition instanceof AirplaneModeCondition) {
Log.d(TAG, "Airplane mode condition has been bound with "

View File

@@ -1,186 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dashboard.conditional;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import java.util.List;
import java.util.Objects;
public class ConditionAdapterV2 extends RecyclerView.Adapter<DashboardItemHolder> {
public static final String TAG = "ConditionAdapter";
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private List<Condition> mConditions;
private boolean mExpanded;
private View.OnClickListener mConditionClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO: get rid of setTag/getTag
Condition condition = (Condition) v.getTag();
mMetricsFeatureProvider.action(mContext,
MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
condition.getMetricsConstant());
condition.onPrimaryClick();
}
};
@VisibleForTesting
ItemTouchHelper.SimpleCallback mSwipeCallback = new ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.START | ItemTouchHelper.END) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
return true;
}
@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
return viewHolder.getItemViewType() == R.layout.condition_tile
? super.getSwipeDirs(recyclerView, viewHolder) : 0;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
Object item = getItem(viewHolder.getItemId());
// item can become null when running monkey
if (item != null) {
((Condition) item).silence();
}
}
};
public ConditionAdapterV2(Context context, List<Condition> conditions, boolean expanded) {
mContext = context;
mConditions = conditions;
mExpanded = expanded;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
setHasStableIds(true);
}
public Object getItem(long itemId) {
for (Condition condition : mConditions) {
if (Objects.hash(condition.getTitle()) == itemId) {
return condition;
}
}
return null;
}
@Override
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
viewType, parent, false));
}
@Override
public void onBindViewHolder(DashboardItemHolder holder, int position) {
bindViews(mConditions.get(position), holder,
position == mConditions.size() - 1, mConditionClickListener);
}
@Override
public long getItemId(int position) {
return Objects.hash(mConditions.get(position).getTitle());
}
@Override
public int getItemViewType(int position) {
return R.layout.condition_tile;
}
@Override
public int getItemCount() {
if (mExpanded) {
return mConditions.size();
}
return 0;
}
public void addDismissHandling(final RecyclerView recyclerView) {
final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(mSwipeCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
private void bindViews(final Condition condition,
DashboardItemHolder view, boolean isLastItem,
View.OnClickListener onClickListener) {
if (condition instanceof AirplaneModeCondition) {
Log.d(TAG, "Airplane mode condition has been bound with "
+ "isActive=" + condition.isActive() + ". Airplane mode is currently " +
WirelessUtils.isAirplaneModeOn(condition.mManager.getContext()));
}
View card = view.itemView.findViewById(R.id.content);
card.setTag(condition);
card.setOnClickListener(onClickListener);
view.icon.setImageIcon(condition.getIcon());
view.title.setText(condition.getTitle());
CharSequence[] actions = condition.getActions();
final boolean hasButtons = actions.length > 0;
setViewVisibility(view.itemView, R.id.buttonBar, hasButtons);
view.summary.setText(condition.getSummary());
for (int i = 0; i < 2; i++) {
Button button = (Button) view.itemView.findViewById(i == 0
? R.id.first_action : R.id.second_action);
if (actions.length > i) {
button.setVisibility(View.VISIBLE);
button.setText(actions[i]);
final int index = i;
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
FeatureFactory.getFactory(context).getMetricsFeatureProvider()
.action(context, MetricsEvent.ACTION_SETTINGS_CONDITION_BUTTON,
condition.getMetricsConstant());
condition.onActionClick(index);
}
});
} else {
button.setVisibility(View.GONE);
}
}
setViewVisibility(view.itemView, R.id.divider, !isLastItem);
}
private void setViewVisibility(View containerView, int viewId, boolean visible) {
View view = containerView.findViewById(viewId);
if (view != null) {
view.setVisibility(visible ? View.VISIBLE : View.GONE);
}
}
}

View File

@@ -17,6 +17,10 @@ package com.android.settings.dashboard.suggestions;
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.service.settings.suggestions.Suggestion;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
@@ -24,37 +28,71 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
import com.android.settings.dashboard.DashboardAdapter.IconCache;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.Utils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import com.android.settingslib.suggestions.SuggestionControllerMixin;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder> {
public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder> implements
LifecycleObserver, OnSaveInstanceState {
public static final String TAG = "SuggestionAdapter";
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
private static final String STATE_SUGGESTION_LIST = "suggestion_list";
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final List<Suggestion> mSuggestions;
private final IconCache mCache;
private final List<String> mSuggestionsShownLogged;
private final ArrayList<String> mSuggestionsShownLogged;
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
private final SuggestionControllerMixin mSuggestionControllerMixin;
private final Callback mCallback;
private final CardConfig mConfig;
private List<Suggestion> mSuggestions;
public interface Callback {
/**
* Called when the close button of the suggestion card is clicked.
*/
void onSuggestionClosed(Suggestion suggestion);
}
public SuggestionAdapter(Context context, SuggestionControllerMixin suggestionControllerMixin,
List<Suggestion> suggestions, List<String> suggestionsShownLogged) {
Bundle savedInstanceState, Callback callback, Lifecycle lifecycle) {
mContext = context;
mSuggestionControllerMixin = suggestionControllerMixin;
mSuggestions = suggestions;
mSuggestionsShownLogged = suggestionsShownLogged;
mCache = new IconCache(context);
final FeatureFactory factory = FeatureFactory.getFactory(context);
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
mCallback = callback;
if (savedInstanceState != null) {
mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
STATE_SUGGESTIONS_SHOWN_LOGGED);
} else {
mSuggestionsShownLogged = new ArrayList<>();
}
if (lifecycle != null) {
lifecycle.addObserver(this);
}
mConfig = CardConfig.get(context);
setHasStableIds(true);
}
@@ -67,31 +105,49 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
@Override
public void onBindViewHolder(DashboardItemHolder holder, int position) {
bindSuggestion(holder, position);
}
private void bindSuggestion(DashboardItemHolder holder, int position) {
final Suggestion suggestion = mSuggestions.get(position);
final String id = suggestion.getId();
final int suggestionCount = mSuggestions.size();
if (!mSuggestionsShownLogged.contains(id)) {
mMetricsFeatureProvider.action(
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, id);
mSuggestionsShownLogged.add(id);
}
holder.icon.setImageDrawable(mCache.getIcon(suggestion.getIcon()));
mConfig.setCardLayout(holder, suggestionCount, position);
final Icon icon = suggestion.getIcon();
final Drawable drawable = mCache.getIcon(icon);
if (drawable != null && TextUtils.equals(icon.getResPackage(), mContext.getPackageName())) {
drawable.setTint(Utils.getColorAccent(mContext));
}
holder.icon.setImageDrawable(drawable);
holder.title.setText(suggestion.getTitle());
final CharSequence summary = suggestion.getSummary();
if (!TextUtils.isEmpty(summary)) {
holder.summary.setText(summary);
holder.summary.setVisibility(View.VISIBLE);
holder.title.setSingleLine(suggestionCount == 1);
if (suggestionCount == 1) {
final CharSequence summary = suggestion.getSummary();
if (!TextUtils.isEmpty(summary)) {
holder.summary.setText(summary);
holder.summary.setVisibility(View.VISIBLE);
} else {
holder.summary.setVisibility(View.GONE);
}
} else {
// Do not show summary if there are more than 1 suggestions
holder.summary.setVisibility(View.GONE);
holder.title.setMaxLines(3);
}
final View divider = holder.itemView.findViewById(R.id.divider);
if (divider != null) {
divider.setVisibility(position < mSuggestions.size() - 1 ? View.VISIBLE : View.GONE);
final ImageView closeButton = holder.itemView.findViewById(R.id.close_button);
if (closeButton != null) {
closeButton.setOnClickListener(v -> {
mSuggestionFeatureProvider.dismissSuggestion(
mContext, mSuggestionControllerMixin, suggestion);
if (mCallback != null) {
mCallback.onSuggestionClosed(suggestion);
}
});
}
View clickHandler = holder.itemView;
// If a view with @android:id/primary is defined, use that as the click handler
// instead.
@@ -144,7 +200,83 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
}
public void removeSuggestion(Suggestion suggestion) {
final int position = mSuggestions.indexOf(suggestion);
mSuggestions.remove(suggestion);
notifyDataSetChanged();
notifyItemRemoved(position);
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (mSuggestions != null) {
outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
new ArrayList<>(mSuggestions));
}
outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
}
public void setSuggestions(List<Suggestion> suggestions) {
mSuggestions = suggestions;
}
public List<Suggestion> getSuggestions() {
return mSuggestions;
}
private static class CardConfig {
// Card start/end margin
private final int mMarginInner;
private final int mMarginOuter;
// Card width for different numbers of cards
private final int mWidthSingleCard;
private final int mWidthTwoCards;
private final int mWidthMultipleCards;
// padding between icon and title
private final int mPaddingTitleTopSingleCard;
private final int mPaddingTitleTopMultipleCards;
private static CardConfig sConfig;
private CardConfig(Context context) {
final Resources res = context.getResources();
mMarginInner =
res.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin);
mMarginOuter =
res.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin);
mWidthSingleCard = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_one_card);
mWidthTwoCards = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_two_cards);
mWidthMultipleCards =
res.getDimensionPixelOffset(R.dimen.suggestion_card_width_multiple_cards);
mPaddingTitleTopSingleCard =
res.getDimensionPixelOffset(R.dimen.suggestion_card_title_padding_bottom_one_card);
mPaddingTitleTopMultipleCards = res.getDimensionPixelOffset(
R.dimen.suggestion_card_title_padding_bottom_multiple_cards);
}
public static CardConfig get(Context context) {
if (sConfig == null) {
sConfig = new CardConfig(context);
}
return sConfig;
}
private void setCardLayout(DashboardItemHolder holder, int suggestionCount,
int position) {
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
suggestionCount == 1
? mWidthSingleCard : suggestionCount == 2
? mWidthTwoCards : mWidthMultipleCards,
LinearLayout.LayoutParams.WRAP_CONTENT);
if (suggestionCount == 1) {
params.setMarginStart(mMarginOuter);
params.setMarginEnd(mMarginOuter);
} else {
params.setMarginStart(
position == 0 ? mMarginOuter : mMarginInner);
params.setMarginEnd(position == suggestionCount - 1 ? mMarginOuter : 0);
}
holder.itemView.setLayoutParams(params);
}
}
}

View File

@@ -1,282 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dashboard.suggestions;
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.service.settings.suggestions.Suggestion;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder;
import com.android.settings.dashboard.DashboardAdapterV2.IconCache;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.Utils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import com.android.settingslib.suggestions.SuggestionControllerMixin;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class SuggestionAdapterV2 extends RecyclerView.Adapter<DashboardItemHolder> implements
LifecycleObserver, OnSaveInstanceState {
public static final String TAG = "SuggestionAdapterV2";
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
private static final String STATE_SUGGESTION_LIST = "suggestion_list";
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final IconCache mCache;
private final ArrayList<String> mSuggestionsShownLogged;
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
private final SuggestionControllerMixin mSuggestionControllerMixin;
private final Callback mCallback;
private final CardConfig mConfig;
private List<Suggestion> mSuggestions;
public interface Callback {
/**
* Called when the close button of the suggestion card is clicked.
*/
void onSuggestionClosed(Suggestion suggestion);
}
public SuggestionAdapterV2(Context context, SuggestionControllerMixin suggestionControllerMixin,
Bundle savedInstanceState, Callback callback, Lifecycle lifecycle) {
mContext = context;
mSuggestionControllerMixin = suggestionControllerMixin;
mCache = new IconCache(context);
final FeatureFactory factory = FeatureFactory.getFactory(context);
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
mCallback = callback;
if (savedInstanceState != null) {
mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
STATE_SUGGESTIONS_SHOWN_LOGGED);
} else {
mSuggestionsShownLogged = new ArrayList<>();
}
if (lifecycle != null) {
lifecycle.addObserver(this);
}
mConfig = CardConfig.get(context);
setHasStableIds(true);
}
@Override
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
viewType, parent, false));
}
@Override
public void onBindViewHolder(DashboardItemHolder holder, int position) {
final Suggestion suggestion = mSuggestions.get(position);
final String id = suggestion.getId();
final int suggestionCount = mSuggestions.size();
if (!mSuggestionsShownLogged.contains(id)) {
mMetricsFeatureProvider.action(
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, id);
mSuggestionsShownLogged.add(id);
}
mConfig.setCardLayout(holder, suggestionCount, position);
final Icon icon = suggestion.getIcon();
final Drawable drawable = mCache.getIcon(icon);
if (drawable != null && TextUtils.equals(icon.getResPackage(), mContext.getPackageName())) {
drawable.setTint(Utils.getColorAccent(mContext));
}
holder.icon.setImageDrawable(drawable);
holder.title.setText(suggestion.getTitle());
holder.title.setSingleLine(suggestionCount == 1);
if (suggestionCount == 1) {
final CharSequence summary = suggestion.getSummary();
if (!TextUtils.isEmpty(summary)) {
holder.summary.setText(summary);
holder.summary.setVisibility(View.VISIBLE);
} else {
holder.summary.setVisibility(View.GONE);
}
} else {
// Do not show summary if there are more than 1 suggestions
holder.summary.setVisibility(View.GONE);
holder.title.setMaxLines(3);
}
final ImageView closeButton = holder.itemView.findViewById(R.id.close_button);
if (closeButton != null) {
closeButton.setOnClickListener(v -> {
mSuggestionFeatureProvider.dismissSuggestion(
mContext, mSuggestionControllerMixin, suggestion);
if (mCallback != null) {
mCallback.onSuggestionClosed(suggestion);
}
});
}
View clickHandler = holder.itemView;
// If a view with @android:id/primary is defined, use that as the click handler
// instead.
final View primaryAction = holder.itemView.findViewById(android.R.id.primary);
if (primaryAction != null) {
clickHandler = primaryAction;
}
clickHandler.setOnClickListener(v -> {
mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_SETTINGS_SUGGESTION, id);
try {
suggestion.getPendingIntent().send();
mSuggestionControllerMixin.launchSuggestion(suggestion);
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Failed to start suggestion " + suggestion.getTitle());
}
});
}
@Override
public long getItemId(int position) {
return Objects.hash(mSuggestions.get(position).getId());
}
@Override
public int getItemViewType(int position) {
final Suggestion suggestion = getSuggestion(position);
if ((suggestion.getFlags() & Suggestion.FLAG_HAS_BUTTON) != 0) {
return R.layout.suggestion_tile_with_button_v2;
} else {
return R.layout.suggestion_tile_v2;
}
}
@Override
public int getItemCount() {
return mSuggestions.size();
}
public Suggestion getSuggestion(int position) {
final long itemId = getItemId(position);
if (mSuggestions == null) {
return null;
}
for (Suggestion suggestion : mSuggestions) {
if (Objects.hash(suggestion.getId()) == itemId) {
return suggestion;
}
}
return null;
}
public void removeSuggestion(Suggestion suggestion) {
final int position = mSuggestions.indexOf(suggestion);
mSuggestions.remove(suggestion);
notifyItemRemoved(position);
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (mSuggestions != null) {
outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
new ArrayList<>(mSuggestions));
}
outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
}
public void setSuggestions(List<Suggestion> suggestions) {
mSuggestions = suggestions;
}
public List<Suggestion> getSuggestions() {
return mSuggestions;
}
private static class CardConfig {
// Card start/end margin
private final int mMarginInner;
private final int mMarginOuter;
// Card width for different numbers of cards
private final int mWidthSingleCard;
private final int mWidthTwoCards;
private final int mWidthMultipleCards;
// padding between icon and title
private final int mPaddingTitleTopSingleCard;
private final int mPaddingTitleTopMultipleCards;
private static CardConfig sConfig;
private CardConfig(Context context) {
final Resources res = context.getResources();
mMarginInner =
res.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin);
mMarginOuter =
res.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin);
mWidthSingleCard = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_one_card);
mWidthTwoCards = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_two_cards);
mWidthMultipleCards =
res.getDimensionPixelOffset(R.dimen.suggestion_card_width_multiple_cards);
mPaddingTitleTopSingleCard =
res.getDimensionPixelOffset(R.dimen.suggestion_card_title_padding_bottom_one_card);
mPaddingTitleTopMultipleCards = res.getDimensionPixelOffset(
R.dimen.suggestion_card_title_padding_bottom_multiple_cards);
}
public static CardConfig get(Context context) {
if (sConfig == null) {
sConfig = new CardConfig(context);
}
return sConfig;
}
private void setCardLayout(DashboardItemHolder holder, int suggestionCount,
int position) {
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
suggestionCount == 1
? mWidthSingleCard : suggestionCount == 2
? mWidthTwoCards : mWidthMultipleCards,
LinearLayout.LayoutParams.WRAP_CONTENT);
if (suggestionCount == 1) {
params.setMarginStart(mMarginOuter);
params.setMarginEnd(mMarginOuter);
} else {
params.setMarginStart(
position == 0 ? mMarginOuter : mMarginInner);
params.setMarginEnd(position == suggestionCount - 1 ? mMarginOuter : 0);
}
holder.itemView.setLayoutParams(params);
}
}
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dashboard.suggestions;
import android.content.Context;
import android.service.settings.suggestions.Suggestion;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.suggestions.SuggestionControllerMixin;
/**
* Deprecated as a close button is provided to dismiss the suggestion.
*/
@Deprecated
public class SuggestionDismissController extends ItemTouchHelper.SimpleCallback {
public interface Callback {
/**
* Returns suggestion tile data from the callback
*/
Suggestion getSuggestionAt(int position);
/**
* Called when a suggestion is dismissed.
*/
void onSuggestionDismissed(Suggestion suggestion);
}
private final Context mContext;
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
private final SuggestionControllerMixin mSuggestionMixin;
private final Callback mCallback;
public SuggestionDismissController(Context context, RecyclerView recyclerView,
SuggestionControllerMixin suggestionMixin, Callback callback) {
super(0, ItemTouchHelper.START | ItemTouchHelper.END);
mSuggestionMixin = suggestionMixin;
mContext = context;
mSuggestionFeatureProvider = FeatureFactory.getFactory(context)
.getSuggestionFeatureProvider(context);
mCallback = callback;
final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(this);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
return true;
}
@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final int layoutId = viewHolder.getItemViewType();
if (layoutId == R.layout.suggestion_tile
|| layoutId == R.layout.suggestion_tile_with_button) {
// Only return swipe direction for suggestion tiles. All other types are not swipeable.
return super.getSwipeDirs(recyclerView, viewHolder);
}
return 0;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
if (mCallback == null) {
return;
}
final int position = viewHolder.getAdapterPosition();
final Suggestion suggestionV2 = mCallback.getSuggestionAt(position);
mSuggestionFeatureProvider.dismissSuggestion(mContext, mSuggestionMixin, suggestionV2);
mCallback.onSuggestionDismissed(suggestionV2);
}
}