Settings 体系架构六 DashboardSumarry

DashboardSumarry,即Settings应用首页面,和其它页面不同的是,它是以一个RecyclerView的形式来显示列表,并没有使用Preference

Settings

首页中有几个比较重要的概念

  • Condition : 某些特定情况下才会出现的菜单,比如飞行模式,低电情况下,点击能展开/折叠,展开后有相应的可操作功能。
  • Tile : 单个菜单项,点击可进入二级页面,但它又存储为两类,一类是固定的DashboardTile,另一类是常用的SuggestionTile
  • SuggestionHeader : 当SuggestionTile多于2项之后,可以点击它展开/折叠菜单。

DashboardSummary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
public class DashboardSummary extends InstrumentedFragment
implements SettingsDrawerActivity.CategoryListener, ConditionManager.ConditionListener,
FocusRecyclerView.FocusListener {
private static final int MAX_WAIT_MILLIS = 700;
private static final String SUGGESTIONS = "suggestions";
private static final String EXTRA_SCROLL_POSITION = "scroll_position";
private final Handler mHandler = new Handler();
private FocusRecyclerView mDashboard;
private DashboardAdapter mAdapter; // adapter
private SummaryLoader mSummaryLoader;
private ConditionManager mConditionManager;
private SuggestionParser mSuggestionParser;
private LinearLayoutManager mLayoutManager;
private SuggestionsChecks mSuggestionsChecks;
private DashboardFeatureProvider mDashboardFeatureProvider;
private SuggestionFeatureProvider mSuggestionFeatureProvider;
private boolean isOnCategoriesChangedCalled;
private SuggestionDismissController mSuggestionDismissHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
long startTime = System.currentTimeMillis();
super.onCreate(savedInstanceState);
final Activity activity = getActivity();
mDashboardFeatureProvider = FeatureFactory.getFactory(activity)
.getDashboardFeatureProvider(activity);
mSuggestionFeatureProvider = FeatureFactory.getFactory(activity)
.getSuggestionFeatureProvider(activity);
// ...
}
@Override
public void onDestroy() {
mSummaryLoader.release();
super.onDestroy();
}
@Override
public void onResume() {
long startTime = System.currentTimeMillis();
super.onResume();
((SettingsDrawerActivity) getActivity()).addCategoryListener(this);
mSummaryLoader.setListening(true);
final int metricsCategory = getMetricsCategory();
for (Condition c : mConditionManager.getConditions()) {
if (c.shouldShow()) {
mMetricsFeatureProvider.visible(getContext(), metricsCategory,
c.getMetricsConstant());
}
}
if (DEBUG_TIMING) {
Log.d(TAG, "onResume took " + (System.currentTimeMillis() - startTime) + " ms");
}
}
@Override
public void onPause() {
super.onPause();
((SettingsDrawerActivity) getActivity()).remCategoryListener(this);
mSummaryLoader.setListening(false);
for (Condition c : mConditionManager.getConditions()) {
if (c.shouldShow()) {
mMetricsFeatureProvider.hidden(getContext(), c.getMetricsConstant());
}
}
if (!getActivity().isChangingConfigurations()) {
mAdapter.onPause();
}
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
long startTime = System.currentTimeMillis();
if (hasWindowFocus) { // 添加Condtion监听器,比如到达低电模式,更新UI
mConditionManager.addListener(this); // -> onConditionsChanged
mConditionManager.refreshAll();
} else {
mConditionManager.remListener(this);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mLayoutManager == null) return;
outState.putInt(EXTRA_SCROLL_POSITION, mLayoutManager.findFirstVisibleItemPosition());
if (mAdapter != null) {
mAdapter.onSaveInstanceState(outState);
}
// 保存滚动位置.
}
@Override
public void onViewCreated(View view, Bundle bundle) {
long startTime = System.currentTimeMillis();
mDashboard = view.findViewById(R.id.dashboard_container);
mLayoutManager = new LinearLayoutManager(getContext());
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
if (bundle != null) { // 获取保存好的滚动位置
int scrollPosition = bundle.getInt(EXTRA_SCROLL_POSITION);
mLayoutManager.scrollToPosition(scrollPosition);
}
mDashboard.setLayoutManager(mLayoutManager);
mDashboard.setHasFixedSize(true);
mDashboard.addItemDecoration(new DashboardDecorator(getContext()));
mDashboard.setListener(this);
mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions()); // 创建Adapter
mDashboard.setAdapter(mAdapter);
mSuggestionDismissHandler = new SuggestionDismissController(
getContext(), mDashboard, mSuggestionParser, mAdapter);
mDashboard.setItemAnimator(new DashboardItemAnimator());
mSummaryLoader.setSummaryConsumer(mAdapter);
ConditionAdapterUtils.addDismiss(mDashboard);
rebuildUI(); // rebuildUI
}
@VisibleForTesting
void rebuildUI() {
new SuggestionLoader().execute();
// 调用SuggestionLoader加载SuggestionTile
}
@Override
public void onConditionsChanged() {
final boolean scrollToTop = mLayoutManager.findFirstCompletelyVisibleItemPosition() <= 1;
mAdapter.setConditions(mConditionManager.getConditions());
// 设置Conditions.
if (scrollToTop) {
mDashboard.scrollToPosition(0);
}
}
private class SuggestionLoader extends AsyncTask<Void, Void, List<Tile>> {
@Override
protected List<Tile> doInBackground(Void... params) {
final Context context = getContext();
boolean isSmartSuggestionEnabled =
mSuggestionFeatureProvider.isSmartSuggestionEnabled(context);
List<Tile> suggestions = mSuggestionParser.getSuggestions(isSmartSuggestionEnabled);
if (isSmartSuggestionEnabled) {
List<String> suggestionIds = new ArrayList<>(suggestions.size());
for (Tile suggestion : suggestions) {
suggestionIds.add(mSuggestionFeatureProvider.getSuggestionIdentifier(
context, suggestion));
}
// TODO: create a Suggestion class to maintain the id and other info
mSuggestionFeatureProvider.rankSuggestions(suggestions, suggestionIds);
}
for (int i = 0; i < suggestions.size(); i++) {
Tile suggestion = suggestions.get(i);
if (mSuggestionsChecks.isSuggestionComplete(suggestion)) {
mSuggestionFeatureProvider.dismissSuggestion(
context, mSuggestionParser, suggestion);
suggestions.remove(i--);
}
}
return suggestions;
}
@Override
protected void onPostExecute(List<Tile> tiles) {
// 加载完成,更新UI。
mHandler.removeCallbacksAndMessages(null);
updateCategoryAndSuggestion(tiles);
}
}
@VisibleForTesting
void updateCategoryAndSuggestion(List<Tile> suggestions) {
// ...
// Temporary hack to wrap homepage category into a list. Soon we will create adapter
// API that takes a single category.
List<DashboardCategory> categories = new ArrayList<>();
// 加载固定的Tile,这些tile被包含在一个Category里面。
categories.add(mDashboardFeatureProvider.getTilesForCategory(
CategoryKey.CATEGORY_HOMEPAGE));
if (suggestions != null) {
mAdapter.setCategoriesAndSuggestions(categories, suggestions);
} else {
mAdapter.setCategory(categories);
}
// 设置Category
}
}

DashboardAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder> {
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_SUGGESTION_MODE = "suggestion_mode";
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
@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 int type = mDashboardData.getItemTypeByPosition(position);
switch (type) {
case R.layout.dashboard_category: // category
onBindCategory(holder,
(DashboardCategory) mDashboardData.getItemEntityByPosition(position));
break;
case R.layout.dashboard_tile: // dashboardTyle
final Tile tile = (Tile) mDashboardData.getItemEntityByPosition(position);
onBindTile(holder, tile); // 设置Tile
holder.itemView.setTag(tile);
holder.itemView.setOnClickListener(mTileClickListener);
break;
case R.layout.suggestion_header: // suggestionHeader
onBindSuggestionHeader(holder, (DashboardData.SuggestionHeaderData)
mDashboardData.getItemEntityByPosition(position));
break;
case R.layout.suggestion_tile: // suggestionTile
final Tile suggestion = (Tile) mDashboardData.getItemEntityByPosition(position);
final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
mContext, suggestion);
// This is for cases when a suggestion is dismissed and the next one comes to view
if (!mSuggestionsShownLogged.contains(suggestionId)) {
mMetricsFeatureProvider.action(
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, suggestionId);
mSuggestionsShownLogged.add(suggestionId);
}
onBindTile(holder, suggestion); // 设置Tile
// 设置点击事件
holder.itemView.setOnClickListener(v -> {
mMetricsFeatureProvider.action(mContext,
MetricsEvent.ACTION_SETTINGS_SUGGESTION, suggestionId);
((SettingsActivity) mContext).startSuggestion(suggestion.intent);
});
break;
case R.layout.condition_card: // ConditonCard
final boolean isExpanded = mDashboardData.getItemEntityByPosition(position)
== mDashboardData.getExpandedCondition();
ConditionAdapterUtils.bindViews( /** 设置点击事件 */
(Condition) mDashboardData.getItemEntityByPosition(position),
holder, isExpanded, mConditionClickListener, v -> onExpandClick(v));
break;
}
}
// Condition点击事件监听器
private View.OnClickListener mConditionClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Condition expandedCondition = mDashboardData.getExpandedCondition();
//TODO: get rid of setTag/getTag
if (v.getTag() == expandedCondition) { // 点击进入对应的设置页面
mMetricsFeatureProvider.action(mContext,
MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
expandedCondition.getMetricsConstant());
expandedCondition.onPrimaryClick(); // Condition处理相应的事件
} else {
expandedCondition = (Condition) v.getTag();
mMetricsFeatureProvider.action(mContext,
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND,
expandedCondition.getMetricsConstant());
updateExpandedCondition(expandedCondition);
}
}
};
// 点击Tile打开Intent
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());
}
};
private void onBindTile(DashboardItemHolder holder, Tile tile) {
holder.icon.setImageDrawable(mCache.getIcon(tile.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);
}
}