Combine settings suggestion and condition.
- Add a flag in dashboard feature provider to specify whether to use the combined UI for suggestions and conditions. - Move Conditions below Suggestions. - Add dashboard entity for condition and suggestion container, and wrap the condition and suggestion list inside the container. The container itself will be a single dashboard item, and within it will be the list of suggestion or condition. - Add suggestion/condition header that will show the combined info for the conditions and suggestion data, and have the expand button to control expanding both the suggestion and condition list. - Change the individual condition card to be always expanded, and remove the logic to collapse/expand individual condition card. - Remove the divider between the action button and condition detail within each condition card. - Add suggestion/condition footer for collapsing the whole suggestion and condition list. Bug: 37645754 Test: make RunSettingsRoboTests Change-Id: I86df75f7e4551778f79d730851c03121fd0dcbdf
This commit is contained in:
25
res/layout/condition_header_icon.xml
Normal file
25
res/layout/condition_header_icon.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/icon"
|
||||||
|
android:layout_width="@dimen/dashboard_tile_image_size"
|
||||||
|
android:layout_height="@dimen/dashboard_tile_image_size"
|
||||||
|
android:layout_marginStart="0dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:tint="?android:attr/colorAccent"
|
||||||
|
android:scaleType="centerInside"/>
|
94
res/layout/condition_tile_new_ui.xml
Normal file
94
res/layout/condition_tile_new_ui.xml
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/colorSecondary"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@android:id/icon"
|
||||||
|
android:layout_width="@dimen/dashboard_tile_image_size"
|
||||||
|
android:layout_height="@dimen/dashboard_tile_image_size"
|
||||||
|
android:layout_marginStart="14dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:tint="?android:attr/colorAccent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="?android:attr/colorAccent" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/summary"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="62dp"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||||
|
android:alpha=".7"
|
||||||
|
android:textColor="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
|
<android.support.v7.widget.ButtonBarLayout
|
||||||
|
android:id="@+id/buttonBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="62dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
style="?android:attr/buttonBarStyle"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/first_action"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:alpha=".8"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textColor="?android:attr/colorAccent"
|
||||||
|
style="?android:attr/buttonBarButtonStyle" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/second_action"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:alpha=".8"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textColor="?android:attr/colorAccent"
|
||||||
|
style="?android:attr/buttonBarButtonStyle" />
|
||||||
|
|
||||||
|
</android.support.v7.widget.ButtonBarLayout>
|
||||||
|
|
||||||
|
<include layout="@layout/horizontal_divider" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
23
res/layout/horizontal_divider.xml
Normal file
23
res/layout/horizontal_divider.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<View
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/divider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height=".75dp"
|
||||||
|
android:background="?android:attr/dividerHorizontal" />
|
42
res/layout/suggestion_condition_container.xml
Normal file
42
res/layout/suggestion_condition_container.xml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="@style/SuggestionConditionStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingBottom="@dimen/dashboard_padding_bottom">
|
||||||
|
|
||||||
|
<android.support.v7.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/data"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scrollbars="none"/>
|
||||||
|
|
||||||
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
|
</FrameLayout>
|
33
res/layout/suggestion_condition_footer.xml
Normal file
33
res/layout/suggestion_condition_footer.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
style="@style/SuggestionConditionStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center|end">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/collapse_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:src="@drawable/ic_expand_less"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
76
res/layout/suggestion_condition_header.xml
Normal file
76
res/layout/suggestion_condition_header.xml
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
style="@style/SuggestionConditionStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_centerHorizontal="true">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@android:id/icon_frame"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_centerVertical="true">
|
||||||
|
|
||||||
|
<include layout="@layout/condition_header_icon" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/expand_indicator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:src="@drawable/ic_expand_more"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/summary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_toStartOf="@id/expand_indicator"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textAppearance="@style/TextAppearance.SuggestionTitle"
|
||||||
|
android:textColor="?android:attr/colorAccent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toEndOf="@android:id/icon_frame"
|
||||||
|
android:layout_toStartOf="@android:id/summary"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textAppearance="@style/TextAppearance.SuggestionTitle"
|
||||||
|
android:textColor="?android:attr/colorAccent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/additional_icons"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_toStartOf="@android:id/summary"
|
||||||
|
android:layout_toEndOf="@android:id/icon_frame"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
63
res/layout/suggestion_tile_new_ui.xml
Normal file
63
res/layout/suggestion_tile_new_ui.xml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:minHeight="@dimen/dashboard_tile_minimum_height">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@android:id/icon"
|
||||||
|
android:layout_width="@dimen/dashboard_tile_image_size"
|
||||||
|
android:layout_height="@dimen/dashboard_tile_image_size"
|
||||||
|
android:layout_marginStart="14dp"
|
||||||
|
android:layout_marginEnd="24dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView android:id="@android:id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.TileTitle"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:fadingEdge="horizontal"/>
|
||||||
|
|
||||||
|
<TextView android:id="@android:id/summary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.Small"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<include layout="@layout/horizontal_divider" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@@ -7999,12 +7999,30 @@
|
|||||||
<!-- Summary of condition that night display is on (renamed "Night Light" with title caps). [CHAR LIMIT=NONE] -->
|
<!-- Summary of condition that night display is on (renamed "Night Light" with title caps). [CHAR LIMIT=NONE] -->
|
||||||
<string name="condition_night_display_summary">Screen is tinted amber. This may help you fall asleep.</string>
|
<string name="condition_night_display_summary">Screen is tinted amber. This may help you fall asleep.</string>
|
||||||
|
|
||||||
|
<!-- Summary for the condition section on the dashboard, representing number of conditions. [CHAR LIMIT=10] -->
|
||||||
|
<string name="condition_summary" translatable="false"><xliff:g name="count" example="3">%1$d</xliff:g></string>
|
||||||
|
|
||||||
<!-- Title for the suggestions section on the dashboard [CHAR LIMIT=30] -->
|
<!-- Title for the suggestions section on the dashboard [CHAR LIMIT=30] -->
|
||||||
<string name="suggestions_title">Suggestions</string>
|
<string name="suggestions_title">Suggestions</string>
|
||||||
|
|
||||||
<!-- Summary for the suggestions section on the dashboard, representing number of suggestions. [CHAR LIMIT=10] -->
|
<!-- Summary for the suggestions section on the dashboard, representing number of suggestions. [CHAR LIMIT=10] -->
|
||||||
<string name="suggestions_summary">+<xliff:g name="count" example="3">%1$d</xliff:g></string>
|
<string name="suggestions_summary">+<xliff:g name="count" example="3">%1$d</xliff:g></string>
|
||||||
|
|
||||||
|
<!-- Title for the suggestions section on the dashboard, representing number of suggestions to show when expanded. [CHAR LIMIT=10] -->
|
||||||
|
<string name="suggestions_more_title">+<xliff:g name="count" example="3">%1$d</xliff:g> more</string>
|
||||||
|
|
||||||
|
<!-- Title for the collapsed suggestions section on the dashboard, representing number of suggestions. [CHAR LIMIT=30] -->
|
||||||
|
<plurals name="suggestions_collapsed_title">
|
||||||
|
<item quantity="one">1 suggestion</item>
|
||||||
|
<item quantity="other"><xliff:g id="count" example="10">%1$d</xliff:g> suggestions</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<!-- Summary for the collapsed suggestions section on the dashboard, representing number of suggestions. [CHAR LIMIT=30] -->
|
||||||
|
<plurals name="suggestions_collapsed_summary">
|
||||||
|
<item quantity="one">+1 suggestion</item>
|
||||||
|
<item quantity="other">+<xliff:g id="count" example="10">%1$d</xliff:g> suggestions</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
<!-- Name of option to remove a suggestion from the list [CHAR LIMIT=30] -->
|
<!-- Name of option to remove a suggestion from the list [CHAR LIMIT=30] -->
|
||||||
<string name="suggestion_remove">Remove</string>
|
<string name="suggestion_remove">Remove</string>
|
||||||
|
|
||||||
|
@@ -454,4 +454,8 @@
|
|||||||
<item name="android:visibility">gone</item>
|
<item name="android:visibility">gone</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="SuggestionConditionStyle">
|
||||||
|
<item name="android:background">@color/material_grey_300</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -20,11 +20,13 @@ import android.annotation.ColorInt;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
||||||
import android.support.v7.util.DiffUtil;
|
import android.support.v7.util.DiffUtil;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
@@ -33,19 +35,28 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.R.id;
|
||||||
import com.android.settings.SettingsActivity;
|
import com.android.settings.SettingsActivity;
|
||||||
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
import com.android.settings.dashboard.DashboardData.SuggestionConditionHeaderData;
|
||||||
import com.android.settings.dashboard.conditional.Condition;
|
import com.android.settings.dashboard.conditional.Condition;
|
||||||
|
import com.android.settings.dashboard.conditional.ConditionAdapter;
|
||||||
import com.android.settings.dashboard.conditional.ConditionAdapterUtils;
|
import com.android.settings.dashboard.conditional.ConditionAdapterUtils;
|
||||||
|
import com.android.settings.dashboard.conditional.FocusRecyclerView;
|
||||||
|
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
|
||||||
|
import com.android.settings.dashboard.suggestions.SuggestionDismissController;
|
||||||
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
|
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.Utils;
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
import com.android.settingslib.drawer.DashboardCategory;
|
||||||
import com.android.settingslib.drawer.Tile;
|
import com.android.settingslib.drawer.Tile;
|
||||||
|
|
||||||
|
import com.android.settingslib.suggestions.SuggestionParser;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -57,6 +68,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
private static final String STATE_SUGGESTION_MODE = "suggestion_mode";
|
private static final String STATE_SUGGESTION_MODE = "suggestion_mode";
|
||||||
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
|
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
|
||||||
private static final int DONT_SET_BACKGROUND_ATTR = -1;
|
private static final int DONT_SET_BACKGROUND_ATTR = -1;
|
||||||
|
private static final String STATE_SUGGESTION_CONDITION_MODE = "suggestion_condition_mode";
|
||||||
|
|
||||||
private final IconCache mCache;
|
private final IconCache mCache;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
@@ -65,6 +77,12 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
|
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
|
||||||
private final ArrayList<String> mSuggestionsShownLogged;
|
private final ArrayList<String> mSuggestionsShownLogged;
|
||||||
private boolean mFirstFrameDrawn;
|
private boolean mFirstFrameDrawn;
|
||||||
|
private boolean mCombineSuggestionAndCondition;
|
||||||
|
private RecyclerView mRecyclerView;
|
||||||
|
private SuggestionParser mSuggestionParser;
|
||||||
|
private SuggestionAdapter mSuggestionAdapter;
|
||||||
|
private SuggestionDismissController mSuggestionDismissHandler;
|
||||||
|
private SuggestionDismissController.Callback mCallback;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
DashboardData mDashboardData;
|
DashboardData mDashboardData;
|
||||||
@@ -81,43 +99,65 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Condition expandedCondition = mDashboardData.getExpandedCondition();
|
if (mCombineSuggestionAndCondition) {
|
||||||
|
Condition condition = (Condition) v.getTag();
|
||||||
//TODO: get rid of setTag/getTag
|
//TODO: get rid of setTag/getTag
|
||||||
if (v.getTag() == expandedCondition) {
|
|
||||||
mMetricsFeatureProvider.action(mContext,
|
mMetricsFeatureProvider.action(mContext,
|
||||||
|
MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
|
||||||
|
condition.getMetricsConstant());
|
||||||
|
condition.onPrimaryClick();
|
||||||
|
} else {
|
||||||
|
Condition expandedCondition = mDashboardData.getExpandedCondition();
|
||||||
|
|
||||||
|
//TODO: get rid of setTag/getTag
|
||||||
|
if (v.getTag() == expandedCondition) {
|
||||||
|
mMetricsFeatureProvider.action(mContext,
|
||||||
MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
|
MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
|
||||||
expandedCondition.getMetricsConstant());
|
expandedCondition.getMetricsConstant());
|
||||||
expandedCondition.onPrimaryClick();
|
expandedCondition.onPrimaryClick();
|
||||||
} else {
|
} else {
|
||||||
expandedCondition = (Condition) v.getTag();
|
expandedCondition = (Condition) v.getTag();
|
||||||
mMetricsFeatureProvider.action(mContext,
|
mMetricsFeatureProvider.action(mContext,
|
||||||
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND,
|
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND,
|
||||||
expandedCondition.getMetricsConstant());
|
expandedCondition.getMetricsConstant());
|
||||||
|
|
||||||
updateExpandedCondition(expandedCondition);
|
updateExpandedCondition(expandedCondition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public DashboardAdapter(Context context, Bundle savedInstanceState,
|
public DashboardAdapter(Context context, Bundle savedInstanceState,
|
||||||
List<Condition> conditions) {
|
List<Condition> conditions) {
|
||||||
|
this(context, savedInstanceState, conditions, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DashboardAdapter(Context context, Bundle savedInstanceState,
|
||||||
|
List<Condition> conditions, SuggestionParser suggestionParser,
|
||||||
|
SuggestionDismissController.Callback callback) {
|
||||||
List<Tile> suggestions = null;
|
List<Tile> suggestions = null;
|
||||||
List<DashboardCategory> categories = null;
|
List<DashboardCategory> categories = null;
|
||||||
int suggestionMode = DashboardData.SUGGESTION_MODE_DEFAULT;
|
int suggestionMode = DashboardData.SUGGESTION_MODE_DEFAULT;
|
||||||
|
int suggestionConditionMode = DashboardData.HEADER_MODE_DEFAULT;
|
||||||
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
||||||
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
||||||
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
|
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
|
||||||
mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
|
mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
|
||||||
|
mCombineSuggestionAndCondition = mDashboardFeatureProvider.combineSuggestionAndCondition();
|
||||||
mCache = new IconCache(context);
|
mCache = new IconCache(context);
|
||||||
|
mSuggestionParser = suggestionParser;
|
||||||
|
mCallback = callback;
|
||||||
|
|
||||||
setHasStableIds(true);
|
setHasStableIds(true);
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
suggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
|
suggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
|
||||||
categories = savedInstanceState.getParcelableArrayList(STATE_CATEGORY_LIST);
|
categories = savedInstanceState.getParcelableArrayList(STATE_CATEGORY_LIST);
|
||||||
|
suggestionConditionMode = savedInstanceState.getInt(
|
||||||
|
STATE_SUGGESTION_CONDITION_MODE, suggestionConditionMode);
|
||||||
suggestionMode = savedInstanceState.getInt(
|
suggestionMode = savedInstanceState.getInt(
|
||||||
STATE_SUGGESTION_MODE, DashboardData.SUGGESTION_MODE_DEFAULT);
|
STATE_SUGGESTION_MODE, DashboardData.SUGGESTION_MODE_DEFAULT);
|
||||||
mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
|
mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
|
||||||
@@ -131,6 +171,8 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
.setSuggestions(suggestions)
|
.setSuggestions(suggestions)
|
||||||
.setCategories(categories)
|
.setCategories(categories)
|
||||||
.setSuggestionMode(suggestionMode)
|
.setSuggestionMode(suggestionMode)
|
||||||
|
.setCombineSuggestionAndCondition(mCombineSuggestionAndCondition)
|
||||||
|
.setSuggestionConditionMode(suggestionConditionMode)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,14 +209,24 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
.build();
|
.build();
|
||||||
notifyDashboardDataChanged(prevData);
|
notifyDashboardDataChanged(prevData);
|
||||||
List<Tile> shownSuggestions = null;
|
List<Tile> shownSuggestions = null;
|
||||||
switch (mDashboardData.getSuggestionMode()) {
|
if (mCombineSuggestionAndCondition) {
|
||||||
case DashboardData.SUGGESTION_MODE_DEFAULT:
|
final int mode = mDashboardData.getSuggestionConditionMode();
|
||||||
|
if (mode == DashboardData.HEADER_MODE_DEFAULT) {
|
||||||
shownSuggestions = suggestions.subList(0,
|
shownSuggestions = suggestions.subList(0,
|
||||||
Math.min(suggestions.size(), DashboardData.DEFAULT_SUGGESTION_COUNT));
|
Math.min(suggestions.size(), DashboardData.DEFAULT_SUGGESTION_COUNT));
|
||||||
break;
|
} else if (mode != DashboardData.HEADER_MODE_COLLAPSED) {
|
||||||
case DashboardData.SUGGESTION_MODE_EXPANDED:
|
|
||||||
shownSuggestions = suggestions;
|
shownSuggestions = suggestions;
|
||||||
break;
|
}
|
||||||
|
} else {
|
||||||
|
switch (mDashboardData.getSuggestionMode()) {
|
||||||
|
case DashboardData.SUGGESTION_MODE_DEFAULT:
|
||||||
|
shownSuggestions = suggestions.subList(0,
|
||||||
|
Math.min(suggestions.size(), DashboardData.DEFAULT_SUGGESTION_COUNT));
|
||||||
|
break;
|
||||||
|
case DashboardData.SUGGESTION_MODE_EXPANDED:
|
||||||
|
shownSuggestions = suggestions;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (shownSuggestions != null) {
|
if (shownSuggestions != null) {
|
||||||
for (Tile suggestion : shownSuggestions) {
|
for (Tile suggestion : shownSuggestions) {
|
||||||
@@ -199,10 +251,16 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
public void setConditions(List<Condition> conditions) {
|
public void setConditions(List<Condition> conditions) {
|
||||||
final DashboardData prevData = mDashboardData;
|
final DashboardData prevData = mDashboardData;
|
||||||
Log.d(TAG, "adapter setConditions called");
|
Log.d(TAG, "adapter setConditions called");
|
||||||
mDashboardData = new DashboardData.Builder(prevData)
|
if (mCombineSuggestionAndCondition) {
|
||||||
|
mDashboardData = new DashboardData.Builder(prevData)
|
||||||
|
.setConditions(conditions)
|
||||||
|
.build();
|
||||||
|
} else {
|
||||||
|
mDashboardData = new DashboardData.Builder(prevData)
|
||||||
.setConditions(conditions)
|
.setConditions(conditions)
|
||||||
.setExpandedCondition(null)
|
.setExpandedCondition(null)
|
||||||
.build();
|
.build();
|
||||||
|
}
|
||||||
notifyDashboardDataChanged(prevData);
|
notifyDashboardDataChanged(prevData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,8 +276,14 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
|
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
|
||||||
viewType, parent, false));
|
if (viewType == R.layout.suggestion_condition_header) {
|
||||||
|
return new SuggestionAndConditionHeaderHolder(view);
|
||||||
|
}
|
||||||
|
if (viewType == R.layout.suggestion_condition_container) {
|
||||||
|
return new SuggestionAndConditionContainerHolder(view);
|
||||||
|
}
|
||||||
|
return new DashboardItemHolder(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -277,6 +341,43 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
(Condition) mDashboardData.getItemEntityByPosition(position),
|
(Condition) mDashboardData.getItemEntityByPosition(position),
|
||||||
holder, isExpanded, mConditionClickListener, v -> onExpandClick(v));
|
holder, isExpanded, mConditionClickListener, v -> onExpandClick(v));
|
||||||
break;
|
break;
|
||||||
|
case R.layout.suggestion_condition_container:
|
||||||
|
onBindConditionAndSuggestion(
|
||||||
|
(SuggestionAndConditionContainerHolder) holder, position);
|
||||||
|
break;
|
||||||
|
case R.layout.suggestion_condition_header:
|
||||||
|
/* There are 2 different headers for the suggestions/conditions section. To minimize
|
||||||
|
visual animation when expanding and collapsing the suggestions/conditions, we are
|
||||||
|
using the same layout to represent the 2 headers:
|
||||||
|
1. Suggestion header - when there is any suggestion shown, the suggestion header
|
||||||
|
will be the first item on the section. It only has the text "Suggestion", and
|
||||||
|
do nothing when clicked. This header will not be shown when the section is
|
||||||
|
collapsed, in which case, the SuggestionCondition header will be
|
||||||
|
shown instead to show the summary info.
|
||||||
|
2. SuggestionCondition header - the header that shows the summary info for the
|
||||||
|
suggestion/condition that is currently hidden. It has the expand button to
|
||||||
|
expand the section. */
|
||||||
|
if (mDashboardData.getDisplayableSuggestionCount() > 0 && position == 1
|
||||||
|
&& mDashboardData.getSuggestionConditionMode()
|
||||||
|
!= DashboardData.HEADER_MODE_COLLAPSED) {
|
||||||
|
onBindSuggestionHeader((SuggestionAndConditionHeaderHolder) holder);
|
||||||
|
} else {
|
||||||
|
onBindSuggestionConditionHeader((SuggestionAndConditionHeaderHolder) holder,
|
||||||
|
(SuggestionConditionHeaderData)
|
||||||
|
mDashboardData.getItemEntityByPosition(position));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R.layout.suggestion_condition_footer:
|
||||||
|
holder.itemView.setOnClickListener(v -> {
|
||||||
|
mMetricsFeatureProvider.action(mContext,
|
||||||
|
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
|
||||||
|
DashboardData prevData = mDashboardData;
|
||||||
|
mDashboardData = new DashboardData.Builder(prevData).setSuggestionConditionMode(
|
||||||
|
DashboardData.HEADER_MODE_COLLAPSED).build();
|
||||||
|
notifyDashboardDataChanged(prevData);
|
||||||
|
mRecyclerView.scrollToPosition(1);
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,6 +396,14 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
return mDashboardData.size();
|
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 void onPause() {
|
public void onPause() {
|
||||||
if (mDashboardData.getSuggestions() == null) {
|
if (mDashboardData.getSuggestions() == null) {
|
||||||
return;
|
return;
|
||||||
@@ -310,6 +419,9 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
mSuggestionsShownLogged.clear();
|
mSuggestionsShownLogged.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// condition card is always expanded in new suggestion/condition UI.
|
||||||
|
// TODO: Remove when completely move to new suggestion/condition UI
|
||||||
|
@Deprecated
|
||||||
public void onExpandClick(View v) {
|
public void onExpandClick(View v) {
|
||||||
Condition expandedCondition = mDashboardData.getExpandedCondition();
|
Condition expandedCondition = mDashboardData.getExpandedCondition();
|
||||||
if (v.getTag() == expandedCondition) {
|
if (v.getTag() == expandedCondition) {
|
||||||
@@ -330,6 +442,13 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
return mDashboardData.getItemEntityById(itemId);
|
return mDashboardData.getItemEntityById(itemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Tile getSuggestion(int position) {
|
||||||
|
if (mCombineSuggestionAndCondition) {
|
||||||
|
return mSuggestionAdapter.getSuggestion(position);
|
||||||
|
}
|
||||||
|
return (Tile) getItem(getItemId(position));
|
||||||
|
}
|
||||||
|
|
||||||
private void notifyDashboardDataChanged(DashboardData prevData) {
|
private void notifyDashboardDataChanged(DashboardData prevData) {
|
||||||
if (mFirstFrameDrawn && prevData != null) {
|
if (mFirstFrameDrawn && prevData != null) {
|
||||||
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardData
|
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardData
|
||||||
@@ -405,17 +524,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
final int suggestionMode;
|
final int suggestionMode;
|
||||||
if (moreSuggestions) {
|
if (moreSuggestions) {
|
||||||
suggestionMode = DashboardData.SUGGESTION_MODE_EXPANDED;
|
suggestionMode = DashboardData.SUGGESTION_MODE_EXPANDED;
|
||||||
|
logSuggestions();
|
||||||
for (Tile suggestion : mDashboardData.getSuggestions()) {
|
|
||||||
final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
|
|
||||||
mContext, suggestion);
|
|
||||||
if (!mSuggestionsShownLogged.contains(suggestionId)) {
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
|
|
||||||
suggestionId);
|
|
||||||
mSuggestionsShownLogged.add(suggestionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
suggestionMode = DashboardData.SUGGESTION_MODE_COLLAPSED;
|
suggestionMode = DashboardData.SUGGESTION_MODE_COLLAPSED;
|
||||||
}
|
}
|
||||||
@@ -428,6 +537,130 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void logSuggestions() {
|
||||||
|
for (Tile suggestion : mDashboardData.getSuggestions()) {
|
||||||
|
final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
|
||||||
|
mContext, suggestion);
|
||||||
|
if (!mSuggestionsShownLogged.contains(suggestionId)) {
|
||||||
|
mMetricsFeatureProvider.action(
|
||||||
|
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
|
||||||
|
suggestionId);
|
||||||
|
mSuggestionsShownLogged.add(suggestionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onBindSuggestionHeader(final SuggestionAndConditionHeaderHolder holder) {
|
||||||
|
holder.title.setText(R.string.suggestions_title);
|
||||||
|
holder.title.setTextColor(Color.BLACK);
|
||||||
|
holder.icon.setVisibility(View.INVISIBLE);
|
||||||
|
holder.icons.removeAllViews();
|
||||||
|
holder.icons.setVisibility(View.INVISIBLE);
|
||||||
|
holder.summary.setVisibility(View.INVISIBLE);
|
||||||
|
holder.expandIndicator.setVisibility(View.INVISIBLE);
|
||||||
|
holder.itemView.setOnClickListener(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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 moreSuggestions = data.hiddenSuggestionCount > 0;
|
||||||
|
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 {
|
||||||
|
holder.summary.setText(null);
|
||||||
|
}
|
||||||
|
holder.summary.setVisibility(View.VISIBLE);
|
||||||
|
holder.expandIndicator.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
holder.itemView.setOnClickListener(v -> {
|
||||||
|
if (moreSuggestions ) {
|
||||||
|
logSuggestions();
|
||||||
|
} else if (hasConditions) {
|
||||||
|
mMetricsFeatureProvider.action(mContext,
|
||||||
|
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
|
||||||
|
}
|
||||||
|
DashboardData prevData = mDashboardData;
|
||||||
|
final boolean wasCollapsed = curMode == DashboardData.HEADER_MODE_COLLAPSED;
|
||||||
|
mDashboardData = new DashboardData.Builder(prevData)
|
||||||
|
.setSuggestionConditionMode(nextMode).build();
|
||||||
|
notifyDashboardDataChanged(prevData);
|
||||||
|
if (wasCollapsed) {
|
||||||
|
mRecyclerView.scrollToPosition(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onBindConditionAndSuggestion(final SuggestionAndConditionContainerHolder holder,
|
||||||
|
int position) {
|
||||||
|
RecyclerView.Adapter<DashboardItemHolder> adapter;
|
||||||
|
// If there is suggestions to show, it will be at position 2 (position 0 = header spacer,
|
||||||
|
// position 1 is suggestion header.
|
||||||
|
if (position == 2 && mDashboardData.getSuggestions() != null) {
|
||||||
|
mSuggestionAdapter = new SuggestionAdapter(mContext, (List<Tile>)
|
||||||
|
mDashboardData.getItemEntityByPosition(position), mSuggestionsShownLogged);
|
||||||
|
adapter = mSuggestionAdapter;
|
||||||
|
mSuggestionDismissHandler = new SuggestionDismissController(mContext,
|
||||||
|
holder.data, mSuggestionParser, mCallback);
|
||||||
|
} else {
|
||||||
|
ConditionAdapterUtils.addDismiss(holder.data);
|
||||||
|
adapter = new ConditionAdapter(mContext,
|
||||||
|
(List<Condition>) mDashboardData.getItemEntityByPosition(position),
|
||||||
|
mDashboardData.getSuggestionConditionMode());
|
||||||
|
}
|
||||||
|
holder.data.setLayoutManager(new LinearLayoutManager(mContext));
|
||||||
|
holder.data.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
private void onBindTile(DashboardItemHolder holder, Tile tile) {
|
private void onBindTile(DashboardItemHolder holder, Tile tile) {
|
||||||
if (tile.remoteViews != null) {
|
if (tile.remoteViews != null) {
|
||||||
final ViewGroup itemView = (ViewGroup) holder.itemView;
|
final ViewGroup itemView = (ViewGroup) holder.itemView;
|
||||||
@@ -460,9 +693,27 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
}
|
}
|
||||||
outState.putInt(STATE_SUGGESTION_MODE, mDashboardData.getSuggestionMode());
|
outState.putInt(STATE_SUGGESTION_MODE, mDashboardData.getSuggestionMode());
|
||||||
outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
|
outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
|
||||||
|
outState.putInt(STATE_SUGGESTION_CONDITION_MODE,
|
||||||
|
mDashboardData.getSuggestionConditionMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class IconCache {
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IconCache {
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final ArrayMap<Icon, Drawable> mMap = new ArrayMap<>();
|
private final ArrayMap<Icon, Drawable> mMap = new ArrayMap<>();
|
||||||
|
|
||||||
@@ -492,4 +743,25 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
summary = itemView.findViewById(android.R.id.summary);
|
summary = itemView.findViewById(android.R.id.summary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class SuggestionAndConditionHeaderHolder extends DashboardItemHolder {
|
||||||
|
public final LinearLayout icons;
|
||||||
|
public final ImageView expandIndicator;
|
||||||
|
|
||||||
|
public SuggestionAndConditionHeaderHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
icons = itemView.findViewById(id.additional_icons);
|
||||||
|
expandIndicator = itemView.findViewById(id.expand_indicator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SuggestionAndConditionContainerHolder extends DashboardItemHolder {
|
||||||
|
public final RecyclerView data;
|
||||||
|
|
||||||
|
public SuggestionAndConditionContainerHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
data = itemView.findViewById(id.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,9 @@
|
|||||||
package com.android.settings.dashboard;
|
package com.android.settings.dashboard;
|
||||||
|
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.VisibleForTesting;
|
||||||
import android.support.v7.util.DiffUtil;
|
import android.support.v7.util.DiffUtil;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
@@ -38,9 +40,22 @@ import java.util.Objects;
|
|||||||
* ItemsData has inner class Item, which represents the Item in data list.
|
* ItemsData has inner class Item, which represents the Item in data list.
|
||||||
*/
|
*/
|
||||||
public class DashboardData {
|
public class DashboardData {
|
||||||
|
@Deprecated
|
||||||
public static final int SUGGESTION_MODE_DEFAULT = 0;
|
public static final int SUGGESTION_MODE_DEFAULT = 0;
|
||||||
|
@Deprecated
|
||||||
public static final int SUGGESTION_MODE_COLLAPSED = 1;
|
public static final int SUGGESTION_MODE_COLLAPSED = 1;
|
||||||
|
@Deprecated
|
||||||
public static final int SUGGESTION_MODE_EXPANDED = 2;
|
public static final int SUGGESTION_MODE_EXPANDED = 2;
|
||||||
|
|
||||||
|
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 POSITION_NOT_FOUND = -1;
|
||||||
public static final int DEFAULT_SUGGESTION_COUNT = 2;
|
public static final int DEFAULT_SUGGESTION_COUNT = 2;
|
||||||
|
|
||||||
@@ -49,14 +64,19 @@ public class DashboardData {
|
|||||||
private static final int NS_SPACER = 1000;
|
private static final int NS_SPACER = 1000;
|
||||||
private static final int NS_ITEMS = 2000;
|
private static final int NS_ITEMS = 2000;
|
||||||
private static final int NS_CONDITION = 3000;
|
private static final int NS_CONDITION = 3000;
|
||||||
|
private static final int NS_SUGGESTION_CONDITION = 4000;
|
||||||
|
|
||||||
private final List<Item> mItems;
|
private final List<Item> mItems;
|
||||||
private final List<DashboardCategory> mCategories;
|
private final List<DashboardCategory> mCategories;
|
||||||
private final List<Condition> mConditions;
|
private final List<Condition> mConditions;
|
||||||
private final List<Tile> mSuggestions;
|
private final List<Tile> mSuggestions;
|
||||||
|
@Deprecated
|
||||||
private final int mSuggestionMode;
|
private final int mSuggestionMode;
|
||||||
|
@Deprecated
|
||||||
private final Condition mExpandedCondition;
|
private final Condition mExpandedCondition;
|
||||||
|
private final @HeaderMode int mSuggestionConditionMode;
|
||||||
private int mId;
|
private int mId;
|
||||||
|
private boolean mCombineSuggestionAndCondition;
|
||||||
|
|
||||||
private DashboardData(Builder builder) {
|
private DashboardData(Builder builder) {
|
||||||
mCategories = builder.mCategories;
|
mCategories = builder.mCategories;
|
||||||
@@ -64,6 +84,8 @@ public class DashboardData {
|
|||||||
mSuggestions = builder.mSuggestions;
|
mSuggestions = builder.mSuggestions;
|
||||||
mSuggestionMode = builder.mSuggestionMode;
|
mSuggestionMode = builder.mSuggestionMode;
|
||||||
mExpandedCondition = builder.mExpandedCondition;
|
mExpandedCondition = builder.mExpandedCondition;
|
||||||
|
mSuggestionConditionMode = builder.mSuggestionConditionMode;
|
||||||
|
mCombineSuggestionAndCondition = builder.mCombineSuggestionAndCondition;
|
||||||
|
|
||||||
mItems = new ArrayList<>();
|
mItems = new ArrayList<>();
|
||||||
mId = 0;
|
mId = 0;
|
||||||
@@ -116,6 +138,11 @@ public class DashboardData {
|
|||||||
return mSuggestionMode;
|
return mSuggestionMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getSuggestionConditionMode() {
|
||||||
|
return mSuggestionConditionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public Condition getExpandedCondition() {
|
public Condition getExpandedCondition() {
|
||||||
return mExpandedCondition;
|
return mExpandedCondition;
|
||||||
}
|
}
|
||||||
@@ -177,14 +204,31 @@ public class DashboardData {
|
|||||||
* @return the count of suggestions to display
|
* @return the count of suggestions to display
|
||||||
*/
|
*/
|
||||||
public int getDisplayableSuggestionCount() {
|
public int getDisplayableSuggestionCount() {
|
||||||
final int suggestionSize = mSuggestions.size();
|
final int suggestionSize = sizeOf(mSuggestions);
|
||||||
return mSuggestionMode == SUGGESTION_MODE_DEFAULT
|
if (mCombineSuggestionAndCondition) {
|
||||||
? Math.min(DEFAULT_SUGGESTION_COUNT, suggestionSize)
|
if (mSuggestionConditionMode == HEADER_MODE_COLLAPSED) {
|
||||||
: mSuggestionMode == SUGGESTION_MODE_EXPANDED
|
return 0;
|
||||||
? suggestionSize : 0;
|
}
|
||||||
|
if (mSuggestionConditionMode == HEADER_MODE_DEFAULT) {
|
||||||
|
return Math.min(DEFAULT_SUGGESTION_COUNT, suggestionSize);
|
||||||
|
}
|
||||||
|
return suggestionSize;
|
||||||
|
}
|
||||||
|
if (mSuggestionMode == SUGGESTION_MODE_DEFAULT) {
|
||||||
|
return Math.min(DEFAULT_SUGGESTION_COUNT, suggestionSize);
|
||||||
|
}
|
||||||
|
if (mSuggestionMode == SUGGESTION_MODE_EXPANDED) {
|
||||||
|
return suggestionSize;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasMoreSuggestions() {
|
public boolean hasMoreSuggestions() {
|
||||||
|
if (mCombineSuggestionAndCondition) {
|
||||||
|
return mSuggestionConditionMode == HEADER_MODE_COLLAPSED && mSuggestions.size() > 0
|
||||||
|
|| mSuggestionConditionMode == HEADER_MODE_DEFAULT
|
||||||
|
&& mSuggestions.size() > DEFAULT_SUGGESTION_COUNT;
|
||||||
|
}
|
||||||
return mSuggestionMode == SUGGESTION_MODE_COLLAPSED
|
return mSuggestionMode == SUGGESTION_MODE_COLLAPSED
|
||||||
|| (mSuggestionMode == SUGGESTION_MODE_DEFAULT
|
|| (mSuggestionMode == SUGGESTION_MODE_DEFAULT
|
||||||
&& mSuggestions.size() > DEFAULT_SUGGESTION_COUNT);
|
&& mSuggestions.size() > DEFAULT_SUGGESTION_COUNT);
|
||||||
@@ -208,7 +252,11 @@ public class DashboardData {
|
|||||||
*/
|
*/
|
||||||
private void countItem(Object object, int type, boolean add, int nameSpace) {
|
private void countItem(Object object, int type, boolean add, int nameSpace) {
|
||||||
if (add) {
|
if (add) {
|
||||||
mItems.add(new Item(object, type, mId + nameSpace, object == mExpandedCondition));
|
if (mCombineSuggestionAndCondition) {
|
||||||
|
mItems.add(new Item(object, type, mId + nameSpace));
|
||||||
|
} else {
|
||||||
|
mItems.add(new Item(object, type, mId + nameSpace, object == mExpandedCondition));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mId++;
|
mId++;
|
||||||
}
|
}
|
||||||
@@ -238,26 +286,75 @@ public class DashboardData {
|
|||||||
// add the view that goes under the search bar
|
// add the view that goes under the search bar
|
||||||
countItem(null, R.layout.dashboard_header_spacer, true, NS_HEADER_SPACER);
|
countItem(null, R.layout.dashboard_header_spacer, true, NS_HEADER_SPACER);
|
||||||
resetCount();
|
resetCount();
|
||||||
boolean hasConditions = false;
|
final boolean hasSuggestions = sizeOf(mSuggestions) > 0;
|
||||||
for (int i = 0; mConditions != null && i < mConditions.size(); i++) {
|
if (!mCombineSuggestionAndCondition) {
|
||||||
boolean shouldShow = mConditions.get(i).shouldShow();
|
boolean hasConditions = false;
|
||||||
hasConditions |= shouldShow;
|
for (int i = 0; mConditions != null && i < mConditions.size(); i++) {
|
||||||
countItem(mConditions.get(i), R.layout.condition_card, shouldShow, NS_CONDITION);
|
boolean shouldShow = mConditions.get(i).shouldShow();
|
||||||
}
|
hasConditions |= shouldShow;
|
||||||
|
countItem(mConditions.get(i), R.layout.condition_card, shouldShow, NS_CONDITION);
|
||||||
|
}
|
||||||
|
|
||||||
resetCount();
|
resetCount();
|
||||||
final boolean hasSuggestions = mSuggestions != null && mSuggestions.size() != 0;
|
countItem(null, R.layout.dashboard_spacer, hasConditions && hasSuggestions, NS_SPACER);
|
||||||
countItem(null, R.layout.dashboard_spacer, hasConditions && hasSuggestions, NS_SPACER);
|
countItem(buildSuggestionHeaderData(), R.layout.suggestion_header, hasSuggestions,
|
||||||
countItem(buildSuggestionHeaderData(), R.layout.suggestion_header, hasSuggestions,
|
|
||||||
NS_SPACER);
|
NS_SPACER);
|
||||||
|
|
||||||
resetCount();
|
resetCount();
|
||||||
if (mSuggestions != null) {
|
if (mSuggestions != null) {
|
||||||
int maxSuggestions = getDisplayableSuggestionCount();
|
int maxSuggestions = getDisplayableSuggestionCount();
|
||||||
for (int i = 0; i < mSuggestions.size(); i++) {
|
for (int i = 0; i < mSuggestions.size(); i++) {
|
||||||
countSuggestion(mSuggestions.get(i), i < maxSuggestions);
|
countSuggestion(mSuggestions.get(i), i < maxSuggestions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
final List<Condition> conditions = getConditionsToShow(mConditions);
|
||||||
|
final boolean hasConditions = sizeOf(conditions) > 0;
|
||||||
|
|
||||||
|
final List<Tile> suggestions = getSuggestionsToShow(mSuggestions);
|
||||||
|
final int hiddenSuggestion =
|
||||||
|
hasSuggestions ? sizeOf(mSuggestions) - sizeOf(suggestions) : 0;
|
||||||
|
|
||||||
|
resetCount();
|
||||||
|
/* Top suggestion/condition header. This will be present when there is any suggestion or
|
||||||
|
* condition to show, except in the case that there is only conditions to show and the
|
||||||
|
* mode is fully expanded. */
|
||||||
|
countItem(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
|
||||||
|
R.layout.suggestion_condition_header, hasSuggestions
|
||||||
|
|| hasConditions && mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED,
|
||||||
|
NS_SUGGESTION_CONDITION);
|
||||||
|
|
||||||
|
/* Suggestion container. This is the card view that contains the list of suggestions.
|
||||||
|
* This will be added whenever the suggestion list is not empty */
|
||||||
|
countItem(suggestions, R.layout.suggestion_condition_container, sizeOf(suggestions) > 0,
|
||||||
|
NS_SUGGESTION_CONDITION);
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
countItem(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
|
||||||
|
R.layout.suggestion_condition_header,
|
||||||
|
mSuggestionConditionMode != HEADER_MODE_COLLAPSED
|
||||||
|
&& mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED
|
||||||
|
&& (hiddenSuggestion > 0
|
||||||
|
|| hasConditions && hasSuggestions),
|
||||||
|
NS_SUGGESTION_CONDITION);
|
||||||
|
|
||||||
|
/* Condition container. This is the card view that contains the list of conditions.
|
||||||
|
* This will be added whenever the condition list is not empty */
|
||||||
|
countItem(conditions, R.layout.suggestion_condition_container,
|
||||||
|
hasConditions && mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED,
|
||||||
|
NS_SUGGESTION_CONDITION);
|
||||||
|
|
||||||
|
/* Suggestion/condition footer. This will be present when the section is fully expanded
|
||||||
|
* or when there is no conditions and no hidden suggestions */
|
||||||
|
countItem(null, R.layout.suggestion_condition_footer,
|
||||||
|
(hasConditions || hasSuggestions) &&
|
||||||
|
mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED
|
||||||
|
|| hasSuggestions && !hasConditions && hiddenSuggestion == 0,
|
||||||
|
NS_SUGGESTION_CONDITION);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetCount();
|
resetCount();
|
||||||
for (int i = 0; mCategories != null && i < mCategories.size(); i++) {
|
for (int i = 0; mCategories != null && i < mCategories.size(); i++) {
|
||||||
DashboardCategory category = mCategories.get(i);
|
DashboardCategory category = mCategories.get(i);
|
||||||
@@ -270,6 +367,10 @@ public class DashboardData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int sizeOf(List<?> list) {
|
||||||
|
return list == null ? 0 : list.size();
|
||||||
|
}
|
||||||
|
|
||||||
private SuggestionHeaderData buildSuggestionHeaderData() {
|
private SuggestionHeaderData buildSuggestionHeaderData() {
|
||||||
SuggestionHeaderData data;
|
SuggestionHeaderData data;
|
||||||
if (mSuggestions == null) {
|
if (mSuggestions == null) {
|
||||||
@@ -285,19 +386,49 @@ public class DashboardData {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Condition> getConditionsToShow(List<Condition> conditions) {
|
||||||
|
if (conditions == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Condition> result = new ArrayList<Condition>();
|
||||||
|
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<Tile> getSuggestionsToShow(List<Tile> suggestions) {
|
||||||
|
if (suggestions == null || mSuggestionConditionMode == HEADER_MODE_COLLAPSED) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (mSuggestionConditionMode != HEADER_MODE_DEFAULT
|
||||||
|
|| suggestions.size() <= DEFAULT_SUGGESTION_COUNT) {
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
return suggestions.subList(0, DEFAULT_SUGGESTION_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder used to build the ItemsData
|
* Builder used to build the ItemsData
|
||||||
* <p>
|
* <p>
|
||||||
* {@link #mExpandedCondition} and {@link #mSuggestionMode} have default value
|
* {@link #mExpandedCondition}, {@link #mSuggestionConditionMode} and {@link #mSuggestionMode}
|
||||||
* while others are not.
|
* have default value while others are not.
|
||||||
*/
|
*/
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
@Deprecated
|
||||||
private int mSuggestionMode = SUGGESTION_MODE_DEFAULT;
|
private int mSuggestionMode = SUGGESTION_MODE_DEFAULT;
|
||||||
|
@Deprecated
|
||||||
private Condition mExpandedCondition = null;
|
private Condition mExpandedCondition = null;
|
||||||
|
private @HeaderMode int mSuggestionConditionMode = HEADER_MODE_DEFAULT;
|
||||||
|
|
||||||
private List<DashboardCategory> mCategories;
|
private List<DashboardCategory> mCategories;
|
||||||
private List<Condition> mConditions;
|
private List<Condition> mConditions;
|
||||||
private List<Tile> mSuggestions;
|
private List<Tile> mSuggestions;
|
||||||
|
private boolean mCombineSuggestionAndCondition;
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
}
|
}
|
||||||
@@ -308,6 +439,8 @@ public class DashboardData {
|
|||||||
mSuggestions = dashboardData.mSuggestions;
|
mSuggestions = dashboardData.mSuggestions;
|
||||||
mSuggestionMode = dashboardData.mSuggestionMode;
|
mSuggestionMode = dashboardData.mSuggestionMode;
|
||||||
mExpandedCondition = dashboardData.mExpandedCondition;
|
mExpandedCondition = dashboardData.mExpandedCondition;
|
||||||
|
mSuggestionConditionMode = dashboardData.mSuggestionConditionMode;
|
||||||
|
mCombineSuggestionAndCondition = dashboardData.mCombineSuggestionAndCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setCategories(List<DashboardCategory> categories) {
|
public Builder setCategories(List<DashboardCategory> categories) {
|
||||||
@@ -330,11 +463,22 @@ public class DashboardData {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public Builder setExpandedCondition(Condition expandedCondition) {
|
public Builder setExpandedCondition(Condition expandedCondition) {
|
||||||
this.mExpandedCondition = expandedCondition;
|
this.mExpandedCondition = expandedCondition;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setSuggestionConditionMode(@HeaderMode int mode) {
|
||||||
|
this.mSuggestionConditionMode = mode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setCombineSuggestionAndCondition(boolean combine) {
|
||||||
|
this.mCombineSuggestionAndCondition = combine;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public DashboardData build() {
|
public DashboardData build() {
|
||||||
return new DashboardData(this);
|
return new DashboardData(this);
|
||||||
}
|
}
|
||||||
@@ -373,6 +517,8 @@ public class DashboardData {
|
|||||||
return mOldItems.get(oldItemPosition).equals(mNewItems.get(newItemPosition));
|
return mOldItems.get(oldItemPosition).equals(mNewItems.get(newItemPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// not needed in combined UI
|
||||||
|
@Deprecated
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
|
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
|
||||||
@@ -390,13 +536,24 @@ public class DashboardData {
|
|||||||
// valid types in field type
|
// valid types in field type
|
||||||
private static final int TYPE_DASHBOARD_CATEGORY = R.layout.dashboard_category;
|
private static final int TYPE_DASHBOARD_CATEGORY = R.layout.dashboard_category;
|
||||||
private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
|
private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
|
||||||
|
@Deprecated
|
||||||
private static final int TYPE_SUGGESTION_HEADER = R.layout.suggestion_header;
|
private static final int TYPE_SUGGESTION_HEADER = R.layout.suggestion_header;
|
||||||
|
@Deprecated
|
||||||
private static final int TYPE_SUGGESTION_TILE = R.layout.suggestion_tile;
|
private static final int TYPE_SUGGESTION_TILE = R.layout.suggestion_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;
|
||||||
|
@Deprecated
|
||||||
private static final int TYPE_CONDITION_CARD = R.layout.condition_card;
|
private static final int TYPE_CONDITION_CARD = R.layout.condition_card;
|
||||||
|
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_DASHBOARD_SPACER = R.layout.dashboard_spacer;
|
||||||
|
|
||||||
@IntDef({TYPE_DASHBOARD_CATEGORY, TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_HEADER,
|
@IntDef({TYPE_DASHBOARD_CATEGORY, TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_HEADER,
|
||||||
TYPE_SUGGESTION_TILE, TYPE_CONDITION_CARD, TYPE_DASHBOARD_SPACER})
|
TYPE_SUGGESTION_TILE, TYPE_SUGGESTION_CONDITION_CONTAINER,
|
||||||
|
TYPE_SUGGESTION_CONDITION_HEADER, TYPE_CONDITION_CARD,
|
||||||
|
TYPE_SUGGESTION_CONDITION_FOOTER, TYPE_DASHBOARD_SPACER})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface ItemTypes{}
|
public @interface ItemTypes{}
|
||||||
|
|
||||||
@@ -422,8 +579,10 @@ public class DashboardData {
|
|||||||
* To store whether the condition is expanded, useless when {@link #type} is not
|
* To store whether the condition is expanded, useless when {@link #type} is not
|
||||||
* {@link #TYPE_CONDITION_CARD}
|
* {@link #TYPE_CONDITION_CARD}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public final boolean conditionExpanded;
|
public final boolean conditionExpanded;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public Item(Object entity, @ItemTypes int type, int id, boolean conditionExpanded) {
|
public Item(Object entity, @ItemTypes int type, int id, boolean conditionExpanded) {
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@@ -431,6 +590,10 @@ public class DashboardData {
|
|||||||
this.conditionExpanded = conditionExpanded;
|
this.conditionExpanded = conditionExpanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Item(Object entity, @ItemTypes int type, int id) {
|
||||||
|
this(entity, type, id, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override it to make comparision in the {@link ItemsDataDiffCallback}
|
* Override it to make comparision in the {@link ItemsDataDiffCallback}
|
||||||
* @param obj object to compared with
|
* @param obj object to compared with
|
||||||
@@ -516,4 +679,27 @@ 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 final List<Icon> conditionIcons;
|
||||||
|
public final CharSequence title;
|
||||||
|
public final int conditionCount;
|
||||||
|
public final int hiddenSuggestionCount;
|
||||||
|
|
||||||
|
public SuggestionConditionHeaderData(List<Condition> conditions,
|
||||||
|
int hiddenSuggestionCount) {
|
||||||
|
conditionCount = sizeOf(conditions);
|
||||||
|
this.hiddenSuggestionCount = hiddenSuggestionCount;
|
||||||
|
title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
|
||||||
|
conditionIcons = new ArrayList<Icon>();
|
||||||
|
for (int i = 0; conditions != null && i < conditions.size(); i++) {
|
||||||
|
final Condition condition = conditions.get(i);
|
||||||
|
conditionIcons.add(condition.getIcon());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -95,4 +95,9 @@ public interface DashboardFeatureProvider {
|
|||||||
*/
|
*/
|
||||||
void openTileIntent(Activity activity, Tile tile);
|
void openTileIntent(Activity activity, Tile tile);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not we should use new UI that combines the settings suggestions and conditions.
|
||||||
|
*/
|
||||||
|
boolean combineSuggestionAndCondition();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -206,6 +206,11 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
|
|||||||
launchIntentOrSelectProfile(activity, tile, intent, MetricsEvent.DASHBOARD_SUMMARY);
|
launchIntentOrSelectProfile(activity, tile, intent, MetricsEvent.DASHBOARD_SUMMARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean combineSuggestionAndCondition() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void launchIntentOrSelectProfile(Activity activity, Tile tile, Intent intent,
|
private void launchIntentOrSelectProfile(Activity activity, Tile tile, Intent intent,
|
||||||
int sourceMetricCategory) {
|
int sourceMetricCategory) {
|
||||||
if (!isIntentResolvable(intent)) {
|
if (!isIntentResolvable(intent)) {
|
||||||
|
@@ -196,13 +196,16 @@ public class DashboardSummary extends InstrumentedFragment
|
|||||||
mDashboard.addItemDecoration(new DashboardDecorator(getContext()));
|
mDashboard.addItemDecoration(new DashboardDecorator(getContext()));
|
||||||
mDashboard.setListener(this);
|
mDashboard.setListener(this);
|
||||||
Log.d(TAG, "adapter created");
|
Log.d(TAG, "adapter created");
|
||||||
mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions());
|
mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions(),
|
||||||
|
mSuggestionParser, this /* SuggestionDismissController.Callback */);
|
||||||
mDashboard.setAdapter(mAdapter);
|
mDashboard.setAdapter(mAdapter);
|
||||||
mSuggestionDismissHandler = new SuggestionDismissController(
|
if (!mDashboardFeatureProvider.combineSuggestionAndCondition()) {
|
||||||
|
mSuggestionDismissHandler = new SuggestionDismissController(
|
||||||
getContext(), mDashboard, mSuggestionParser, this);
|
getContext(), mDashboard, mSuggestionParser, this);
|
||||||
|
ConditionAdapterUtils.addDismiss(mDashboard);
|
||||||
|
}
|
||||||
mDashboard.setItemAnimator(new DashboardItemAnimator());
|
mDashboard.setItemAnimator(new DashboardItemAnimator());
|
||||||
mSummaryLoader.setSummaryConsumer(mAdapter);
|
mSummaryLoader.setSummaryConsumer(mAdapter);
|
||||||
ConditionAdapterUtils.addDismiss(mDashboard);
|
|
||||||
if (DEBUG_TIMING) {
|
if (DEBUG_TIMING) {
|
||||||
Log.d(TAG, "onViewCreated took "
|
Log.d(TAG, "onViewCreated took "
|
||||||
+ (System.currentTimeMillis() - startTime) + " ms");
|
+ (System.currentTimeMillis() - startTime) + " ms");
|
||||||
@@ -242,7 +245,7 @@ public class DashboardSummary extends InstrumentedFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tile getSuggestionForPosition(int position) {
|
public Tile getSuggestionForPosition(int position) {
|
||||||
return (Tile) mAdapter.getItem(mAdapter.getItemId(position));
|
return mAdapter.getSuggestion(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* 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.conditional;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
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 java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder> {
|
||||||
|
public static final String TAG = "ConditionAdapter";
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
|
private List<Condition> mConditions;
|
||||||
|
private @HeaderMode int mMode;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public ConditionAdapter(Context context, List<Condition> conditions, @HeaderMode int mode) {
|
||||||
|
mContext = context;
|
||||||
|
mConditions = conditions;
|
||||||
|
mMode = mode;
|
||||||
|
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) {
|
||||||
|
// TODO: merge methods from ConditionAdapterUtils into this class
|
||||||
|
ConditionAdapterUtils.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_new_ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
if (mMode == DashboardData.HEADER_MODE_FULLY_EXPANDED) {
|
||||||
|
return mConditions.size();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -44,22 +44,28 @@ public class ConditionAdapterUtils {
|
|||||||
@Override
|
@Override
|
||||||
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||||
return viewHolder.getItemViewType() == R.layout.condition_card
|
return viewHolder.getItemViewType() == R.layout.condition_card
|
||||||
|
|| viewHolder.getItemViewType() == R.layout.condition_tile_new_ui
|
||||||
? super.getSwipeDirs(recyclerView, viewHolder) : 0;
|
? super.getSwipeDirs(recyclerView, viewHolder) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
||||||
DashboardAdapter adapter = (DashboardAdapter) recyclerView.getAdapter();
|
Object item;
|
||||||
Object item = adapter.getItem(viewHolder.getItemId());
|
if (viewHolder.getItemViewType() == R.layout.condition_card) {
|
||||||
if (item instanceof Condition) {
|
DashboardAdapter adapter = (DashboardAdapter) recyclerView.getAdapter();
|
||||||
((Condition) item).silence();
|
item = adapter.getItem(viewHolder.getItemId());
|
||||||
|
} else {
|
||||||
|
ConditionAdapter adapter = (ConditionAdapter) recyclerView.getAdapter();
|
||||||
|
item = adapter.getItem(viewHolder.getItemId());
|
||||||
}
|
}
|
||||||
|
((Condition) item).silence();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
|
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
|
||||||
itemTouchHelper.attachToRecyclerView(recyclerView);
|
itemTouchHelper.attachToRecyclerView(recyclerView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static void bindViews(final Condition condition,
|
public static void bindViews(final Condition condition,
|
||||||
DashboardAdapter.DashboardItemHolder view, boolean isExpanded,
|
DashboardAdapter.DashboardItemHolder view, boolean isExpanded,
|
||||||
View.OnClickListener onClickListener, View.OnClickListener onExpandListener) {
|
View.OnClickListener onClickListener, View.OnClickListener onExpandListener) {
|
||||||
@@ -121,6 +127,49 @@ public class ConditionAdapterUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void bindViews(final Condition condition,
|
||||||
|
DashboardAdapter.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 static void setViewVisibility(View containerView, int viewId, boolean visible) {
|
private static void setViewVisibility(View containerView, int viewId, boolean visible) {
|
||||||
View view = containerView.findViewById(viewId);
|
View view = containerView.findViewById(viewId);
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
|
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* 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.support.v7.widget.RecyclerView;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.R.layout;
|
||||||
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
|
||||||
|
import com.android.settings.dashboard.DashboardAdapter.IconCache;
|
||||||
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.drawer.Tile;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder> {
|
||||||
|
public static final String TAG = "SuggestionAdapter";
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
|
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
|
||||||
|
private List<Tile> mSuggestions;
|
||||||
|
private final IconCache mCache;
|
||||||
|
private final List<String> mSuggestionsShownLogged;
|
||||||
|
|
||||||
|
public SuggestionAdapter(Context context, List<Tile> suggestions,
|
||||||
|
List<String> suggestionsShownLogged) {
|
||||||
|
mContext = context;
|
||||||
|
mSuggestions = suggestions;
|
||||||
|
mSuggestionsShownLogged = suggestionsShownLogged;
|
||||||
|
mCache = new IconCache(context);
|
||||||
|
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
||||||
|
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
||||||
|
mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(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 Tile suggestion = (Tile) mSuggestions.get(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);
|
||||||
|
}
|
||||||
|
if (suggestion.remoteViews != null) {
|
||||||
|
final ViewGroup itemView = (ViewGroup) holder.itemView;
|
||||||
|
itemView.removeAllViews();
|
||||||
|
itemView.addView(suggestion.remoteViews.apply(itemView.getContext(), itemView));
|
||||||
|
} else {
|
||||||
|
holder.icon.setImageDrawable(mCache.getIcon(suggestion.icon));
|
||||||
|
holder.title.setText(suggestion.title);
|
||||||
|
if (!TextUtils.isEmpty(suggestion.summary)) {
|
||||||
|
holder.summary.setText(suggestion.summary);
|
||||||
|
holder.summary.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
holder.summary.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final View divider = holder.itemView.findViewById(R.id.divider);
|
||||||
|
if (divider != null) {
|
||||||
|
divider.setVisibility(position < mSuggestions.size() - 1 ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
// set the item view to disabled to remove any touch effects
|
||||||
|
holder.itemView.setEnabled(false);
|
||||||
|
}
|
||||||
|
clickHandler.setOnClickListener(v -> {
|
||||||
|
mMetricsFeatureProvider.action(mContext,
|
||||||
|
MetricsEvent.ACTION_SETTINGS_SUGGESTION, suggestionId);
|
||||||
|
((SettingsActivity) mContext).startSuggestion(suggestion.intent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return Objects.hash(mSuggestions.get(position).title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
return layout.suggestion_tile_new_ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mSuggestions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tile getSuggestion(int position) {
|
||||||
|
final long itemId = getItemId(position);
|
||||||
|
for (Tile tile: mSuggestions) {
|
||||||
|
if (Objects.hash(tile.title) == itemId) {
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -67,6 +67,7 @@ public class SuggestionDismissController extends ItemTouchHelper.SimpleCallback
|
|||||||
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||||
final int layoutId = viewHolder.getItemViewType();
|
final int layoutId = viewHolder.getItemViewType();
|
||||||
if (layoutId == R.layout.suggestion_tile
|
if (layoutId == R.layout.suggestion_tile
|
||||||
|
|| layoutId == R.layout.suggestion_tile_new_ui
|
||||||
|| layoutId == R.layout.suggestion_tile_card) {
|
|| layoutId == R.layout.suggestion_tile_card) {
|
||||||
// Only return swipe direction for suggestion tiles. All other types are not swipeable.
|
// Only return swipe direction for suggestion tiles. All other types are not swipeable.
|
||||||
return super.getSwipeDirs(recyclerView, viewHolder);
|
return super.getSwipeDirs(recyclerView, viewHolder);
|
||||||
|
@@ -36,6 +36,8 @@ import com.android.settings.R;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
// Not needed in new UI as the view is always expanded
|
||||||
|
@Deprecated
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||||
public class ConditionAdapterUtilsTest{
|
public class ConditionAdapterUtilsTest{
|
||||||
|
@@ -163,14 +163,6 @@ public class DashboardAdapterTest {
|
|||||||
assertThat(holder.itemView.getBackground()).isInstanceOf(ColorDrawable.class);
|
assertThat(holder.itemView.getBackground()).isInstanceOf(ColorDrawable.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetConditions_AfterSetConditions_ExpandedConditionNull() {
|
|
||||||
mDashboardAdapter.onExpandClick(mView);
|
|
||||||
assertThat(mDashboardAdapter.mDashboardData.getExpandedCondition()).isEqualTo(mCondition);
|
|
||||||
mDashboardAdapter.setConditions(null);
|
|
||||||
assertThat(mDashboardAdapter.mDashboardData.getExpandedCondition()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuggestionsLogs_NotExpanded() {
|
public void testSuggestionsLogs_NotExpanded() {
|
||||||
setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3"));
|
setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3"));
|
||||||
|
@@ -93,12 +93,16 @@ public class DashboardDataTest {
|
|||||||
.setConditions(oneItemConditions)
|
.setConditions(oneItemConditions)
|
||||||
.setCategories(categories)
|
.setCategories(categories)
|
||||||
.setSuggestions(suggestions)
|
.setSuggestions(suggestions)
|
||||||
|
.setSuggestionConditionMode(DashboardData.HEADER_MODE_FULLY_EXPANDED)
|
||||||
|
.setCombineSuggestionAndCondition(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
mDashboardDataWithTwoConditions = new DashboardData.Builder()
|
mDashboardDataWithTwoConditions = new DashboardData.Builder()
|
||||||
.setConditions(twoItemsConditions)
|
.setConditions(twoItemsConditions)
|
||||||
.setCategories(categories)
|
.setCategories(categories)
|
||||||
.setSuggestions(suggestions)
|
.setSuggestions(suggestions)
|
||||||
|
.setSuggestionConditionMode(DashboardData.HEADER_MODE_FULLY_EXPANDED)
|
||||||
|
.setCombineSuggestionAndCondition(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
mDashboardDataWithNoItems = new DashboardData.Builder()
|
mDashboardDataWithNoItems = new DashboardData.Builder()
|
||||||
@@ -110,23 +114,33 @@ public class DashboardDataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBuildItemsData_containsAllData() {
|
public void testBuildItemsData_containsAllData() {
|
||||||
final DashboardData.SuggestionHeaderData data =
|
final DashboardData.SuggestionConditionHeaderData data =
|
||||||
new DashboardData.SuggestionHeaderData(false, 1, 0);
|
new DashboardData.SuggestionConditionHeaderData(
|
||||||
final Object[] expectedObjects = {null, mTestCondition, null, data, mTestSuggestion,
|
mDashboardDataWithOneConditions.getConditions(), 0);
|
||||||
mDashboardCategory, mTestCategoryTile};
|
final Object[] expectedObjects = {null, data,
|
||||||
|
mDashboardDataWithOneConditions.getSuggestions(),
|
||||||
|
mDashboardDataWithOneConditions.getConditions(),
|
||||||
|
null, mDashboardCategory, mTestCategoryTile};
|
||||||
final int expectedSize = expectedObjects.length;
|
final int expectedSize = expectedObjects.length;
|
||||||
|
|
||||||
assertThat(mDashboardDataWithOneConditions.getItemList().size())
|
assertThat(mDashboardDataWithOneConditions.getItemList().size())
|
||||||
.isEqualTo(expectedSize);
|
.isEqualTo(expectedSize);
|
||||||
for (int i = 0; i < expectedSize; i++) {
|
for (int i = 0; i < expectedSize; i++) {
|
||||||
if (mDashboardDataWithOneConditions.getItemEntityByPosition(i)
|
final Object item = mDashboardDataWithOneConditions.getItemEntityByPosition(i);
|
||||||
instanceof DashboardData.SuggestionHeaderData) {
|
if (item instanceof DashboardData.SuggestionHeaderData
|
||||||
|
|| item instanceof List) {
|
||||||
// SuggestionHeaderData is created inside when build, we can only use isEqualTo
|
// SuggestionHeaderData is created inside when build, we can only use isEqualTo
|
||||||
assertThat(mDashboardDataWithOneConditions.getItemEntityByPosition(i))
|
assertThat(item).isEqualTo(expectedObjects[i]);
|
||||||
.isEqualTo(expectedObjects[i]);
|
} else if (item instanceof DashboardData.SuggestionConditionHeaderData) {
|
||||||
|
DashboardData.SuggestionConditionHeaderData i1 =
|
||||||
|
(DashboardData.SuggestionConditionHeaderData)item;
|
||||||
|
DashboardData.SuggestionConditionHeaderData i2 =
|
||||||
|
(DashboardData.SuggestionConditionHeaderData)expectedObjects[i];
|
||||||
|
assertThat(i1.title).isEqualTo(i2.title);
|
||||||
|
assertThat(i1.conditionCount).isEqualTo(i2.conditionCount);
|
||||||
|
assertThat(i1.hiddenSuggestionCount).isEqualTo(i2.hiddenSuggestionCount);
|
||||||
} else {
|
} else {
|
||||||
assertThat(mDashboardDataWithOneConditions.getItemEntityByPosition(i))
|
assertThat(item).isSameAs(expectedObjects[i]);
|
||||||
.isSameAs(expectedObjects[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +148,7 @@ public class DashboardDataTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testGetPositionByEntity_selfInstance_returnPositionFound() {
|
public void testGetPositionByEntity_selfInstance_returnPositionFound() {
|
||||||
final int position = mDashboardDataWithOneConditions
|
final int position = mDashboardDataWithOneConditions
|
||||||
.getPositionByEntity(mTestCondition);
|
.getPositionByEntity(mDashboardDataWithOneConditions.getConditions());
|
||||||
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
|
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,11 +190,17 @@ public class DashboardDataTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDiffUtil_InsertOneCondition_ResultDataOneInserted() {
|
public void testDiffUtil_InsertOneCondition_ResultDataTwoChanged() {
|
||||||
//Build testResultData
|
//Build testResultData
|
||||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
||||||
|
// Item in position 1 is the header, which contains the number of conditions, changed from
|
||||||
|
// 1 to 2
|
||||||
testResultData.add(new ListUpdateResult.ResultData(
|
testResultData.add(new ListUpdateResult.ResultData(
|
||||||
ListUpdateResult.ResultData.TYPE_OPERATION_INSERT, 2, 1));
|
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 1, 1));
|
||||||
|
// Item in position 3 is the condition container containing the list of conditions, which
|
||||||
|
// gets 1 more item
|
||||||
|
testResultData.add(new ListUpdateResult.ResultData(
|
||||||
|
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 3, 1));
|
||||||
|
|
||||||
testDiffUtil(mDashboardDataWithOneConditions,
|
testDiffUtil(mDashboardDataWithOneConditions,
|
||||||
mDashboardDataWithTwoConditions, testResultData);
|
mDashboardDataWithTwoConditions, testResultData);
|
||||||
@@ -196,31 +216,6 @@ public class DashboardDataTest {
|
|||||||
testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
|
testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPayload_ItemConditionCard_returnNotNull() {
|
|
||||||
final DashboardData.ItemsDataDiffCallback callback = new DashboardData
|
|
||||||
.ItemsDataDiffCallback(
|
|
||||||
mDashboardDataWithOneConditions.getItemList(),
|
|
||||||
mDashboardDataWithOneConditions.getItemList());
|
|
||||||
|
|
||||||
// Item in position 1 is condition card, which payload should not be null
|
|
||||||
assertThat(callback.getChangePayload(1, 1)).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPayload_ItemNotConditionCard_returnNull() {
|
|
||||||
final DashboardData.ItemsDataDiffCallback callback = new DashboardData
|
|
||||||
.ItemsDataDiffCallback(
|
|
||||||
mDashboardDataWithOneConditions.getItemList(),
|
|
||||||
mDashboardDataWithOneConditions.getItemList());
|
|
||||||
|
|
||||||
// Position 0 is spacer, 1 is condition card, so others' payload should be null
|
|
||||||
for (int i = 2; i < mDashboardDataWithOneConditions.getItemList().size(); i++) {
|
|
||||||
assertThat(callback.getChangePayload(i, i)).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test when using the
|
* Test when using the
|
||||||
* {@link com.android.settings.dashboard.DashboardData.ItemsDataDiffCallback}
|
* {@link com.android.settings.dashboard.DashboardData.ItemsDataDiffCallback}
|
||||||
|
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* 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.conditional;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsRobolectricTestRunner;
|
||||||
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.dashboard.DashboardAdapter;
|
||||||
|
import com.android.settings.dashboard.DashboardData;
|
||||||
|
import com.android.settings.dashboard.conditional.Condition;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||||
|
public class ConditionAdapterTest {
|
||||||
|
@Mock
|
||||||
|
private Condition mCondition1;
|
||||||
|
@Mock
|
||||||
|
private Condition mCondition2;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private ConditionAdapter mConditionAdapter;
|
||||||
|
private List<Condition> mOneCondition;
|
||||||
|
private List<Condition> mTwoConditions;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mContext = RuntimeEnvironment.application;
|
||||||
|
final CharSequence[] actions = new CharSequence[2];
|
||||||
|
when(mCondition1.getActions()).thenReturn(actions);
|
||||||
|
when(mCondition1.shouldShow()).thenReturn(true);
|
||||||
|
mOneCondition = new ArrayList<>();
|
||||||
|
mOneCondition.add(mCondition1);
|
||||||
|
mTwoConditions = new ArrayList<>();
|
||||||
|
mTwoConditions.add(mCondition1);
|
||||||
|
mTwoConditions.add(mCondition2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getItemCount_notFullyExpanded_shouldReturn0() {
|
||||||
|
mConditionAdapter = new ConditionAdapter(
|
||||||
|
mContext, mOneCondition, DashboardData.HEADER_MODE_DEFAULT);
|
||||||
|
assertThat(mConditionAdapter.getItemCount()).isEqualTo(0);
|
||||||
|
|
||||||
|
mConditionAdapter = new ConditionAdapter(
|
||||||
|
mContext, mOneCondition, DashboardData.HEADER_MODE_SUGGESTION_EXPANDED);
|
||||||
|
assertThat(mConditionAdapter.getItemCount()).isEqualTo(0);
|
||||||
|
|
||||||
|
mConditionAdapter = new ConditionAdapter(
|
||||||
|
mContext, mOneCondition, DashboardData.HEADER_MODE_COLLAPSED);
|
||||||
|
assertThat(mConditionAdapter.getItemCount()).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getItemCount_fullyExpanded_shouldReturnListSize() {
|
||||||
|
mConditionAdapter = new ConditionAdapter(
|
||||||
|
mContext, mOneCondition, DashboardData.HEADER_MODE_FULLY_EXPANDED);
|
||||||
|
assertThat(mConditionAdapter.getItemCount()).isEqualTo(1);
|
||||||
|
|
||||||
|
mConditionAdapter = new ConditionAdapter(
|
||||||
|
mContext, mTwoConditions, DashboardData.HEADER_MODE_FULLY_EXPANDED);
|
||||||
|
assertThat(mConditionAdapter.getItemCount()).isEqualTo(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getItemViewType_shouldReturnConditionTile() {
|
||||||
|
mConditionAdapter = new ConditionAdapter(
|
||||||
|
mContext, mTwoConditions, DashboardData.HEADER_MODE_FULLY_EXPANDED);
|
||||||
|
assertThat(mConditionAdapter.getItemViewType(0)).isEqualTo(R.layout.condition_tile_new_ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindViewHolder_shouldSetListener() {
|
||||||
|
final View view = LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.condition_tile_new_ui, new LinearLayout(mContext), true);
|
||||||
|
final DashboardAdapter.DashboardItemHolder viewHolder =
|
||||||
|
new DashboardAdapter.DashboardItemHolder(view);
|
||||||
|
mConditionAdapter = new ConditionAdapter(
|
||||||
|
mContext, mOneCondition, DashboardData.HEADER_MODE_SUGGESTION_EXPANDED);
|
||||||
|
|
||||||
|
mConditionAdapter.onBindViewHolder(viewHolder, 0);
|
||||||
|
final View card = view.findViewById(R.id.content);
|
||||||
|
assertThat(card.hasOnClickListeners()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void viewClick_shouldInvokeConditionPrimaryClick() {
|
||||||
|
final View view = LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.condition_tile_new_ui, new LinearLayout(mContext), true);
|
||||||
|
final DashboardAdapter.DashboardItemHolder viewHolder =
|
||||||
|
new DashboardAdapter.DashboardItemHolder(view);
|
||||||
|
mConditionAdapter = new ConditionAdapter(
|
||||||
|
mContext, mOneCondition, DashboardData.HEADER_MODE_SUGGESTION_EXPANDED);
|
||||||
|
|
||||||
|
mConditionAdapter.onBindViewHolder(viewHolder, 0);
|
||||||
|
final View card = view.findViewById(R.id.content);
|
||||||
|
card.performClick();
|
||||||
|
verify(mCondition1).onPrimaryClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* 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.graphics.drawable.Icon;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsRobolectricTestRunner;
|
||||||
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.dashboard.DashboardAdapter;
|
||||||
|
import com.android.settingslib.drawer.Tile;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||||
|
public class SuggestionAdapterTest {
|
||||||
|
@Mock
|
||||||
|
private Tile mSuggestion1;
|
||||||
|
@Mock
|
||||||
|
private Tile mSuggestion2;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private SuggestionAdapter mSuggestionAdapter;
|
||||||
|
private List<Tile> mOneSuggestion;
|
||||||
|
private List<Tile> mTwoSuggestions;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mContext = RuntimeEnvironment.application;
|
||||||
|
mSuggestion1.title = "Test Suggestion 1";
|
||||||
|
mSuggestion1.icon = mock(Icon.class);
|
||||||
|
mSuggestion2.title = "Test Suggestion 2";
|
||||||
|
mSuggestion2.icon = mock(Icon.class);
|
||||||
|
mOneSuggestion = new ArrayList<>();
|
||||||
|
mOneSuggestion.add(mSuggestion1);
|
||||||
|
mTwoSuggestions = new ArrayList<>();
|
||||||
|
mTwoSuggestions.add(mSuggestion1);
|
||||||
|
mTwoSuggestions.add(mSuggestion2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getItemCount_shouldReturnListSize() {
|
||||||
|
mSuggestionAdapter = new SuggestionAdapter(mContext, mOneSuggestion, new ArrayList<>());
|
||||||
|
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
|
||||||
|
|
||||||
|
mSuggestionAdapter = new SuggestionAdapter(mContext, mTwoSuggestions, new ArrayList<>());
|
||||||
|
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getItemViewType_shouldReturnSuggestionTile() {
|
||||||
|
mSuggestionAdapter = new SuggestionAdapter(mContext, mOneSuggestion, new ArrayList<>());
|
||||||
|
assertThat(mSuggestionAdapter.getItemViewType(0))
|
||||||
|
.isEqualTo(R.layout.suggestion_tile_new_ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindViewHolder_shouldSetListener() {
|
||||||
|
final View view = spy(LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.suggestion_tile_new_ui, new LinearLayout(mContext), true));
|
||||||
|
final DashboardAdapter.DashboardItemHolder viewHolder =
|
||||||
|
new DashboardAdapter.DashboardItemHolder(view);
|
||||||
|
mSuggestionAdapter = new SuggestionAdapter(mContext, mOneSuggestion, new ArrayList<>());
|
||||||
|
|
||||||
|
mSuggestionAdapter.onBindViewHolder(viewHolder, 0);
|
||||||
|
|
||||||
|
verify(view).setOnClickListener(any(View.OnClickListener.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user