Remove suggestion UI v1 codes.
- remove the check for feature flag for suggestion UI v2 and switch to use the v2 codes. - remove all code related to v1 of suggestion UI Fixes: 70573674 Test: make RunSettingsRoboTests Change-Id: I99ab318c1c0192508a9c5e9e708e86319120d55b
This commit is contained in:
@@ -1,43 +0,0 @@
|
|||||||
<?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="5dp"
|
|
||||||
android:paddingEnd="5dp"
|
|
||||||
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:background="@color/material_grey_300"
|
|
||||||
android:scrollbars="none"/>
|
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
@@ -14,51 +14,69 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<LinearLayout
|
<android.support.v7.widget.CardView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/suggestion_card"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
app:cardPreventCornerOverlap="false"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardCornerRadius="@dimen/suggestion_card_corner_radius">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@android:color/white"
|
android:minHeight="112dp"
|
||||||
android:gravity="center_vertical"
|
android:orientation="vertical">
|
||||||
android:orientation="horizontal"
|
|
||||||
android:minHeight="@dimen/dashboard_tile_minimum_height">
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@android:id/icon"
|
android:id="@android:id/icon"
|
||||||
android:layout_width="@dimen/suggestion_card_icon_size"
|
android:layout_width="@dimen/suggestion_card_icon_size"
|
||||||
android:layout_height="@dimen/suggestion_card_icon_size"
|
android:layout_height="@dimen/suggestion_card_icon_size"
|
||||||
android:layout_marginStart="14dp"
|
style="@style/SuggestionCardIcon"
|
||||||
android:layout_marginEnd="24dp" />
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
<LinearLayout
|
<ImageView
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/close_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="18dp"
|
||||||
android:orientation="vertical">
|
android:layout_height="18dp"
|
||||||
|
android:alpha="0.54"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:src="@drawable/ic_suggestion_close_button"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@android:id/title"
|
android:id="@android:id/title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/SuggestionCardText"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAppearance="@style/TextAppearance.TileTitle"
|
android:textAppearance="@style/TextAppearance.SuggestionTitleV2"
|
||||||
android:ellipsize="marquee"
|
android:ellipsize="end"
|
||||||
android:fadingEdge="horizontal" />
|
android:fadingEdge="horizontal" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@android:id/summary"
|
android:id="@android:id/summary"
|
||||||
android:textAppearance="@style/TextAppearance.SuggestionSummary"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content" />
|
style="@style/SuggestionCardText"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.SuggestionSummary" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
<include layout="@layout/horizontal_divider" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
@@ -1,82 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2018 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/suggestion_card"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardPreventCornerOverlap="false"
|
|
||||||
app:cardUseCompatPadding="true"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:cardCornerRadius="@dimen/suggestion_card_corner_radius">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="112dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@android:id/icon"
|
|
||||||
android:layout_width="@dimen/suggestion_card_icon_size"
|
|
||||||
android:layout_height="@dimen/suggestion_card_icon_size"
|
|
||||||
style="@style/SuggestionCardIcon"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginBottom="8dp" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/close_button"
|
|
||||||
android:layout_width="18dp"
|
|
||||||
android:layout_height="18dp"
|
|
||||||
android:alpha="0.54"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:src="@drawable/ic_suggestion_close_button"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/SuggestionCardText"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="@style/TextAppearance.SuggestionTitleV2"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:fadingEdge="horizontal" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/summary"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/SuggestionCardText"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.SuggestionSummary" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
|
@@ -14,58 +14,78 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<LinearLayout
|
<android.support.v7.widget.CardView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/suggestion_card"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardPreventCornerOverlap="false"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardCornerRadius="@dimen/suggestion_card_corner_radius">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@android:color/white"
|
android:minHeight="112dp"
|
||||||
android:clipChildren="false"
|
android:orientation="vertical">
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingStart="16dp"
|
<RelativeLayout
|
||||||
android:paddingEnd="12dp"
|
android:layout_width="match_parent"
|
||||||
android:paddingBottom="20dp"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="16dp"
|
android:orientation="horizontal">
|
||||||
android:orientation="horizontal"
|
|
||||||
android:minHeight="@dimen/dashboard_tile_minimum_height">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@android:id/icon"
|
android:id="@android:id/icon"
|
||||||
android:layout_width="@dimen/dashboard_tile_image_size"
|
android:layout_width="@dimen/suggestion_card_icon_size"
|
||||||
android:layout_height="@dimen/dashboard_tile_image_size" />
|
android:layout_height="@dimen/suggestion_card_icon_size"
|
||||||
|
style="@style/SuggestionCardIcon"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
<LinearLayout
|
<ImageView
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/close_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="18dp"
|
||||||
android:layout_gravity="start"
|
android:layout_height="18dp"
|
||||||
android:layout_marginStart="18dp"
|
android:alpha="0.54"
|
||||||
android:layout_marginTop="2dp"
|
android:layout_alignParentEnd="true"
|
||||||
android:clipChildren="false"
|
android:layout_marginTop="8dp"
|
||||||
android:clipToPadding="false"
|
android:layout_marginEnd="8dp"
|
||||||
android:orientation="vertical">
|
android:src="@drawable/ic_suggestion_close_button"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@android:id/title"
|
android:id="@android:id/title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="6dp"
|
style="@style/SuggestionCardText"
|
||||||
android:textAppearance="@style/TextAppearance.TileTitle" />
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.SuggestionTitleV2"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:fadingEdge="horizontal" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@android:id/summary"
|
android:id="@android:id/summary"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="12dp"
|
style="@style/SuggestionCardText"
|
||||||
android:layout_marginStart="6dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginEnd="50dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:textAppearance="@style/TextAppearance.SuggestionSummary" />
|
android:textAppearance="@style/TextAppearance.SuggestionSummary" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@android:id/primary"
|
android:id="@android:id/primary"
|
||||||
style="@style/ActionPrimaryButton"
|
style="@style/ActionPrimaryButton"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:text="@string/suggestion_button_text" />
|
android:text="@string/suggestion_button_text" />
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</android.support.v7.widget.CardView>
|
||||||
|
@@ -1,91 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2018 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/suggestion_card"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardPreventCornerOverlap="false"
|
|
||||||
app:cardUseCompatPadding="true"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:cardCornerRadius="@dimen/suggestion_card_corner_radius">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="112dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@android:id/icon"
|
|
||||||
android:layout_width="@dimen/suggestion_card_icon_size"
|
|
||||||
android:layout_height="@dimen/suggestion_card_icon_size"
|
|
||||||
style="@style/SuggestionCardIcon"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginBottom="8dp" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/close_button"
|
|
||||||
android:layout_width="18dp"
|
|
||||||
android:layout_height="18dp"
|
|
||||||
android:alpha="0.54"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:src="@drawable/ic_suggestion_close_button"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/SuggestionCardText"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAppearance="@style/TextAppearance.SuggestionTitleV2"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:fadingEdge="horizontal" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/summary"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/SuggestionCardText"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.SuggestionSummary" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@android:id/primary"
|
|
||||||
style="@style/ActionPrimaryButton"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="@string/suggestion_button_text" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
|
@@ -24,7 +24,6 @@ public class FeatureFlags {
|
|||||||
public static final String BATTERY_SETTINGS_V2 = "settings_battery_v2";
|
public static final String BATTERY_SETTINGS_V2 = "settings_battery_v2";
|
||||||
public static final String BATTERY_DISPLAY_APP_LIST = "settings_battery_display_app_list";
|
public static final String BATTERY_DISPLAY_APP_LIST = "settings_battery_display_app_list";
|
||||||
public static final String ZONE_PICKER_V2 = "settings_zone_picker_v2";
|
public static final String ZONE_PICKER_V2 = "settings_zone_picker_v2";
|
||||||
public static final String SUGGESTION_UI_V2 = "settings_suggestion_ui_v2";
|
|
||||||
public static final String ABOUT_PHONE_V2 = "settings_about_phone_v2";
|
public static final String ABOUT_PHONE_V2 = "settings_about_phone_v2";
|
||||||
public static final String BLUETOOTH_WHILE_DRIVING = "settings_bluetooth_while_driving";
|
public static final String BLUETOOTH_WHILE_DRIVING = "settings_bluetooth_while_driving";
|
||||||
}
|
}
|
||||||
|
@@ -17,9 +17,6 @@ package com.android.settings.dashboard;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -41,44 +38,37 @@ 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.R.id;
|
||||||
import com.android.settings.dashboard.DashboardData.SuggestionConditionHeaderData;
|
import com.android.settings.dashboard.DashboardData.ConditionHeaderData;
|
||||||
import com.android.settings.dashboard.conditional.Condition;
|
import com.android.settings.dashboard.conditional.Condition;
|
||||||
import com.android.settings.dashboard.conditional.ConditionAdapter;
|
import com.android.settings.dashboard.conditional.ConditionAdapter;
|
||||||
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
|
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
|
||||||
import com.android.settings.dashboard.suggestions.SuggestionDismissController;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.Utils;
|
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||||
|
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
import com.android.settingslib.drawer.DashboardCategory;
|
||||||
import com.android.settingslib.drawer.Tile;
|
import com.android.settingslib.drawer.Tile;
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixin;
|
import com.android.settingslib.suggestions.SuggestionControllerMixin;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder>
|
public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.DashboardItemHolder>
|
||||||
implements SummaryLoader.SummaryConsumer {
|
implements SummaryLoader.SummaryConsumer, SuggestionAdapter.Callback, LifecycleObserver,
|
||||||
|
OnSaveInstanceState {
|
||||||
public static final String TAG = "DashboardAdapter";
|
public static final String TAG = "DashboardAdapter";
|
||||||
private static final String STATE_SUGGESTION_LIST = "suggestion_list";
|
|
||||||
private static final String STATE_CATEGORY_LIST = "category_list";
|
private static final String STATE_CATEGORY_LIST = "category_list";
|
||||||
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String STATE_SUGGESTION_CONDITION_MODE = "suggestion_condition_mode";
|
static final String STATE_CONDITION_EXPANDED = "condition_expanded";
|
||||||
@VisibleForTesting
|
|
||||||
static final int SUGGESTION_CONDITION_HEADER_POSITION = 0;
|
|
||||||
|
|
||||||
private final IconCache mCache;
|
private final IconCache mCache;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final SuggestionControllerMixin mSuggestionControllerMixin;
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
private final DashboardFeatureProvider mDashboardFeatureProvider;
|
private final DashboardFeatureProvider mDashboardFeatureProvider;
|
||||||
private final ArrayList<String> mSuggestionsShownLogged;
|
|
||||||
private boolean mFirstFrameDrawn;
|
private boolean mFirstFrameDrawn;
|
||||||
private RecyclerView mRecyclerView;
|
private RecyclerView mRecyclerView;
|
||||||
private SuggestionAdapter mSuggestionAdapter;
|
private SuggestionAdapter mSuggestionAdapter;
|
||||||
private SuggestionDismissController mSuggestionDismissHandler;
|
|
||||||
private SuggestionDismissController.Callback mCallback;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
DashboardData mDashboardData;
|
DashboardData mDashboardData;
|
||||||
@@ -93,38 +83,36 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
|
|
||||||
public DashboardAdapter(Context context, Bundle savedInstanceState,
|
public DashboardAdapter(Context context, Bundle savedInstanceState,
|
||||||
List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
|
List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
|
||||||
SuggestionDismissController.Callback callback) {
|
Lifecycle lifecycle) {
|
||||||
|
|
||||||
List<Suggestion> suggestions = null;
|
|
||||||
DashboardCategory category = null;
|
DashboardCategory category = null;
|
||||||
int suggestionConditionMode = DashboardData.HEADER_MODE_DEFAULT;
|
boolean conditionExpanded = false;
|
||||||
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
||||||
mSuggestionControllerMixin = suggestionControllerMixin;
|
|
||||||
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
||||||
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
|
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
|
||||||
mCache = new IconCache(context);
|
mCache = new IconCache(context);
|
||||||
mCallback = callback;
|
mSuggestionAdapter = new SuggestionAdapter(mContext, suggestionControllerMixin,
|
||||||
|
savedInstanceState, this /* callback */, lifecycle);
|
||||||
|
|
||||||
setHasStableIds(true);
|
setHasStableIds(true);
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
suggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
|
|
||||||
category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
|
category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
|
||||||
suggestionConditionMode = savedInstanceState.getInt(
|
conditionExpanded = savedInstanceState.getBoolean(
|
||||||
STATE_SUGGESTION_CONDITION_MODE, suggestionConditionMode);
|
STATE_CONDITION_EXPANDED, conditionExpanded);
|
||||||
mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
|
}
|
||||||
STATE_SUGGESTIONS_SHOWN_LOGGED);
|
|
||||||
} else {
|
if (lifecycle != null) {
|
||||||
mSuggestionsShownLogged = new ArrayList<>();
|
lifecycle.addObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
mDashboardData = new DashboardData.Builder()
|
mDashboardData = new DashboardData.Builder()
|
||||||
.setConditions(conditions)
|
.setConditions(conditions)
|
||||||
.setSuggestions(suggestions)
|
.setSuggestions(mSuggestionAdapter.getSuggestions())
|
||||||
.setCategory(category)
|
.setCategory(category)
|
||||||
.setSuggestionConditionMode(suggestionConditionMode)
|
.setConditionExpanded(conditionExpanded)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +125,6 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setCategory(DashboardCategory category) {
|
public void setCategory(DashboardCategory category) {
|
||||||
tintIcons(category, null);
|
|
||||||
final DashboardData prevData = mDashboardData;
|
final DashboardData prevData = mDashboardData;
|
||||||
Log.d(TAG, "adapter setCategory called");
|
Log.d(TAG, "adapter setCategory called");
|
||||||
mDashboardData = new DashboardData.Builder(prevData)
|
mDashboardData = new DashboardData.Builder(prevData)
|
||||||
@@ -155,7 +142,8 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
notifyDashboardDataChanged(prevData);
|
notifyDashboardDataChanged(prevData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSuggestionDismissed(Suggestion suggestion) {
|
@Override
|
||||||
|
public void onSuggestionClosed(Suggestion suggestion) {
|
||||||
final List<Suggestion> list = mDashboardData.getSuggestions();
|
final List<Suggestion> list = mDashboardData.getSuggestions();
|
||||||
if (list == null || list.size() == 0) {
|
if (list == null || list.size() == 0) {
|
||||||
return;
|
return;
|
||||||
@@ -163,13 +151,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
if (list.size() == 1) {
|
if (list.size() == 1) {
|
||||||
// The only suggestion is dismissed, and the the empty suggestion container will
|
// The only suggestion is dismissed, and the the empty suggestion container will
|
||||||
// remain as the dashboard item. Need to refresh the dashboard list.
|
// remain as the dashboard item. Need to refresh the dashboard list.
|
||||||
final DashboardData prevData = mDashboardData;
|
setSuggestions(null);
|
||||||
mDashboardData = new DashboardData.Builder(prevData)
|
|
||||||
.setSuggestions(null)
|
|
||||||
.build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
} else {
|
} else {
|
||||||
mSuggestionAdapter.removeSuggestion(suggestion);
|
mSuggestionAdapter.removeSuggestion(suggestion);
|
||||||
|
notifyItemChanged(0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,11 +171,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) {
|
||||||
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
|
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
|
||||||
if (viewType == R.layout.suggestion_condition_header) {
|
if (viewType == R.layout.condition_header) {
|
||||||
return new SuggestionAndConditionHeaderHolder(view);
|
return new ConditionHeaderHolder(view);
|
||||||
}
|
}
|
||||||
if (viewType == R.layout.suggestion_condition_container) {
|
if (viewType == R.layout.condition_container) {
|
||||||
return new SuggestionAndConditionContainerHolder(view);
|
return new ConditionContainerHolder(view);
|
||||||
|
}
|
||||||
|
if (viewType == R.layout.suggestion_container) {
|
||||||
|
return new SuggestionContainerHolder(view);
|
||||||
}
|
}
|
||||||
return new DashboardItemHolder(view);
|
return new DashboardItemHolder(view);
|
||||||
}
|
}
|
||||||
@@ -205,24 +193,25 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
holder.itemView.setTag(tile);
|
holder.itemView.setTag(tile);
|
||||||
holder.itemView.setOnClickListener(mTileClickListener);
|
holder.itemView.setOnClickListener(mTileClickListener);
|
||||||
break;
|
break;
|
||||||
case R.layout.suggestion_condition_container:
|
case R.layout.suggestion_container:
|
||||||
onBindConditionAndSuggestion(
|
onBindSuggestion((SuggestionContainerHolder) holder, position);
|
||||||
(SuggestionAndConditionContainerHolder) holder, position);
|
|
||||||
break;
|
break;
|
||||||
case R.layout.suggestion_condition_header:
|
case R.layout.condition_container:
|
||||||
onBindSuggestionConditionHeader((SuggestionAndConditionHeaderHolder) holder,
|
onBindCondition((ConditionContainerHolder) holder, position);
|
||||||
(SuggestionConditionHeaderData)
|
|
||||||
mDashboardData.getItemEntityByPosition(position));
|
|
||||||
break;
|
break;
|
||||||
case R.layout.suggestion_condition_footer:
|
case R.layout.condition_header:
|
||||||
|
onBindConditionHeader((ConditionHeaderHolder) holder,
|
||||||
|
(ConditionHeaderData) mDashboardData.getItemEntityByPosition(position));
|
||||||
|
break;
|
||||||
|
case R.layout.condition_footer:
|
||||||
holder.itemView.setOnClickListener(v -> {
|
holder.itemView.setOnClickListener(v -> {
|
||||||
mMetricsFeatureProvider.action(mContext,
|
mMetricsFeatureProvider.action(mContext,
|
||||||
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
|
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
|
||||||
DashboardData prevData = mDashboardData;
|
DashboardData prevData = mDashboardData;
|
||||||
mDashboardData = new DashboardData.Builder(prevData).setSuggestionConditionMode(
|
mDashboardData = new DashboardData.Builder(prevData).
|
||||||
DashboardData.HEADER_MODE_COLLAPSED).build();
|
setConditionExpanded(false).build();
|
||||||
notifyDashboardDataChanged(prevData);
|
notifyDashboardDataChanged(prevData);
|
||||||
mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);
|
scrollToTopOfConditions();
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -272,120 +261,66 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void onBindSuggestionConditionHeader(final SuggestionAndConditionHeaderHolder holder,
|
void onBindConditionHeader(final ConditionHeaderHolder holder, ConditionHeaderData data) {
|
||||||
SuggestionConditionHeaderData data) {
|
|
||||||
final int curMode = mDashboardData.getSuggestionConditionMode();
|
|
||||||
final int nextMode = data.hiddenSuggestionCount > 0
|
|
||||||
&& data.conditionCount > 0
|
|
||||||
&& curMode != DashboardData.HEADER_MODE_SUGGESTION_EXPANDED
|
|
||||||
? DashboardData.HEADER_MODE_SUGGESTION_EXPANDED
|
|
||||||
: DashboardData.HEADER_MODE_FULLY_EXPANDED;
|
|
||||||
|
|
||||||
final boolean hasConditions = data.conditionCount > 0;
|
|
||||||
if (data.conditionCount > 0) {
|
|
||||||
holder.icon.setImageIcon(data.conditionIcons.get(0));
|
holder.icon.setImageIcon(data.conditionIcons.get(0));
|
||||||
holder.icon.setVisibility(View.VISIBLE);
|
|
||||||
if (data.conditionCount == 1) {
|
if (data.conditionCount == 1) {
|
||||||
holder.title.setText(data.title);
|
holder.title.setText(data.title);
|
||||||
holder.title.setTextColor(Utils.getColorAccent(mContext));
|
holder.summary.setText(null);
|
||||||
holder.icons.setVisibility(View.INVISIBLE);
|
holder.icons.setVisibility(View.INVISIBLE);
|
||||||
} else {
|
} else {
|
||||||
holder.title.setText(null);
|
holder.title.setText(null);
|
||||||
|
holder.summary.setText(
|
||||||
|
mContext.getString(R.string.condition_summary, data.conditionCount));
|
||||||
updateConditionIcons(data.conditionIcons, holder.icons);
|
updateConditionIcons(data.conditionIcons, holder.icons);
|
||||||
holder.icons.setVisibility(View.VISIBLE);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Resources res = mContext.getResources();
|
|
||||||
final int padding = res.getDimensionPixelOffset(
|
|
||||||
curMode == DashboardData.HEADER_MODE_COLLAPSED
|
|
||||||
? R.dimen.suggestion_condition_header_padding_collapsed
|
|
||||||
: R.dimen.suggestion_condition_header_padding_expanded);
|
|
||||||
holder.itemView.setPadding(0, padding, 0, padding);
|
|
||||||
|
|
||||||
holder.itemView.setOnClickListener(v -> {
|
holder.itemView.setOnClickListener(v -> {
|
||||||
if (hasConditions) {
|
|
||||||
mMetricsFeatureProvider.action(mContext,
|
mMetricsFeatureProvider.action(mContext,
|
||||||
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
|
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
|
||||||
}
|
final DashboardData prevData = mDashboardData;
|
||||||
DashboardData prevData = mDashboardData;
|
|
||||||
final boolean wasCollapsed = curMode == DashboardData.HEADER_MODE_COLLAPSED;
|
|
||||||
mDashboardData = new DashboardData.Builder(prevData)
|
mDashboardData = new DashboardData.Builder(prevData)
|
||||||
.setSuggestionConditionMode(nextMode).build();
|
.setConditionExpanded(true).build();
|
||||||
notifyDashboardDataChanged(prevData);
|
notifyDashboardDataChanged(prevData);
|
||||||
if (wasCollapsed) {
|
scrollToTopOfConditions();
|
||||||
mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void onBindConditionAndSuggestion(final SuggestionAndConditionContainerHolder holder,
|
void onBindCondition(final ConditionContainerHolder holder, int position) {
|
||||||
int position) {
|
final ConditionAdapter adapter = new ConditionAdapter(mContext,
|
||||||
// If there is suggestions to show, it will be at position 0 as we don't show the suggestion
|
|
||||||
// header anymore.
|
|
||||||
final List<Suggestion> suggestions = mDashboardData.getSuggestions();
|
|
||||||
|
|
||||||
boolean conditionOnly = true;
|
|
||||||
if (position == SUGGESTION_CONDITION_HEADER_POSITION) {
|
|
||||||
if (suggestions != null && suggestions.size() > 0) {
|
|
||||||
conditionOnly = false;
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
|
||||||
(List<Suggestion>) mDashboardData.getItemEntityByPosition(position),
|
|
||||||
mSuggestionsShownLogged);
|
|
||||||
mSuggestionDismissHandler = new SuggestionDismissController(mContext,
|
|
||||||
holder.data, mSuggestionControllerMixin, mCallback);
|
|
||||||
holder.data.setAdapter(mSuggestionAdapter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (conditionOnly) {
|
|
||||||
ConditionAdapter adapter = new ConditionAdapter(mContext,
|
|
||||||
(List<Condition>) mDashboardData.getItemEntityByPosition(position),
|
(List<Condition>) mDashboardData.getItemEntityByPosition(position),
|
||||||
mDashboardData.getSuggestionConditionMode());
|
mDashboardData.isConditionExpanded());
|
||||||
adapter.addDismissHandling(holder.data);
|
adapter.addDismissHandling(holder.data);
|
||||||
holder.data.setAdapter(adapter);
|
holder.data.setAdapter(adapter);
|
||||||
}
|
|
||||||
holder.data.setLayoutManager(new LinearLayoutManager(mContext));
|
holder.data.setLayoutManager(new LinearLayoutManager(mContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onBindTile(DashboardItemHolder holder, Tile tile) {
|
@VisibleForTesting
|
||||||
holder.icon.setImageDrawable(mCache.getIcon(tile.icon));
|
void onBindSuggestion(final SuggestionContainerHolder holder, int position) {
|
||||||
|
// If there is suggestions to show, it will be at position 0 as we don't show the suggestion
|
||||||
|
// header anymore.
|
||||||
|
final List<Suggestion> suggestions =
|
||||||
|
(List<Suggestion>) mDashboardData.getItemEntityByPosition(position);
|
||||||
|
final int suggestionCount = suggestions.size();
|
||||||
|
if (suggestions != null && suggestionCount > 0) {
|
||||||
|
holder.summary.setText("" + suggestionCount);
|
||||||
|
mSuggestionAdapter.setSuggestions(suggestions);
|
||||||
|
holder.data.setAdapter(mSuggestionAdapter);
|
||||||
|
}
|
||||||
|
final LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
|
||||||
|
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
||||||
|
holder.data.setLayoutManager(layoutManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void onBindTile(DashboardItemHolder holder, Tile tile) {
|
||||||
|
Drawable icon = mCache.getIcon(tile.icon);
|
||||||
|
if (!TextUtils.equals(tile.icon.getResPackage(), mContext.getPackageName())) {
|
||||||
|
icon = new RoundedHomepageIcon(mContext, icon);
|
||||||
|
mCache.updateIcon(tile.icon, icon);
|
||||||
|
}
|
||||||
|
holder.icon.setImageDrawable(icon);
|
||||||
holder.title.setText(tile.title);
|
holder.title.setText(tile.title);
|
||||||
if (!TextUtils.isEmpty(tile.summary)) {
|
if (!TextUtils.isEmpty(tile.summary)) {
|
||||||
holder.summary.setText(tile.summary);
|
holder.summary.setText(tile.summary);
|
||||||
@@ -395,45 +330,13 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tintIcons(DashboardCategory category, List<Tile> suggestions) {
|
@Override
|
||||||
if (!mDashboardFeatureProvider.shouldTintIcon()) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO: Better place for tinting?
|
|
||||||
final TypedArray a = mContext.obtainStyledAttributes(new int[]{
|
|
||||||
android.R.attr.colorControlNormal});
|
|
||||||
final int tintColor = a.getColor(0, mContext.getColor(R.color.fallback_tintColor));
|
|
||||||
a.recycle();
|
|
||||||
if (category != null) {
|
|
||||||
for (Tile tile : category.getTiles()) {
|
|
||||||
if (tile.isIconTintable) {
|
|
||||||
// If this drawable is tintable, tint it to match the color.
|
|
||||||
tile.icon.setTint(tintColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (suggestions != null) {
|
|
||||||
for (Tile suggestion : suggestions) {
|
|
||||||
if (suggestion.isIconTintable) {
|
|
||||||
suggestion.icon.setTint(tintColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onSaveInstanceState(Bundle outState) {
|
|
||||||
final DashboardCategory category = mDashboardData.getCategory();
|
final DashboardCategory category = mDashboardData.getCategory();
|
||||||
final List<Suggestion> suggestions = mDashboardData.getSuggestions();
|
|
||||||
if (suggestions != null) {
|
|
||||||
outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
|
|
||||||
new ArrayList<>(suggestions));
|
|
||||||
}
|
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
outState.putParcelable(STATE_CATEGORY_LIST, category);
|
outState.putParcelable(STATE_CATEGORY_LIST, category);
|
||||||
}
|
}
|
||||||
outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
|
outState.putBoolean(STATE_CONDITION_EXPANDED, mDashboardData.isConditionExpanded());
|
||||||
outState.putInt(STATE_SUGGESTION_CONDITION_MODE,
|
|
||||||
mDashboardData.getSuggestionConditionMode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateConditionIcons(List<Icon> icons, ViewGroup parent) {
|
private void updateConditionIcons(List<Icon> icons, ViewGroup parent) {
|
||||||
@@ -452,6 +355,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
parent.setVisibility(View.VISIBLE);
|
parent.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scrollToTopOfConditions() {
|
||||||
|
mRecyclerView.scrollToPosition(mDashboardData.hasSuggestion() ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
public static class IconCache {
|
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<>();
|
||||||
@@ -467,10 +374,14 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
Drawable drawable = mMap.get(icon);
|
Drawable drawable = mMap.get(icon);
|
||||||
if (drawable == null) {
|
if (drawable == null) {
|
||||||
drawable = icon.loadDrawable(mContext);
|
drawable = icon.loadDrawable(mContext);
|
||||||
mMap.put(icon, drawable);
|
updateIcon(icon, drawable);
|
||||||
}
|
}
|
||||||
return drawable;
|
return drawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateIcon(Icon icon, Drawable drawable) {
|
||||||
|
mMap.put(icon, drawable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DashboardItemHolder extends RecyclerView.ViewHolder {
|
public static class DashboardItemHolder extends RecyclerView.ViewHolder {
|
||||||
@@ -486,24 +397,33 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SuggestionAndConditionHeaderHolder extends DashboardItemHolder {
|
public static class ConditionHeaderHolder extends DashboardItemHolder {
|
||||||
public final LinearLayout icons;
|
public final LinearLayout icons;
|
||||||
public final ImageView expandIndicator;
|
public final ImageView expandIndicator;
|
||||||
|
|
||||||
public SuggestionAndConditionHeaderHolder(View itemView) {
|
public ConditionHeaderHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
icons = itemView.findViewById(id.additional_icons);
|
icons = itemView.findViewById(id.additional_icons);
|
||||||
expandIndicator = itemView.findViewById(id.expand_indicator);
|
expandIndicator = itemView.findViewById(id.expand_indicator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SuggestionAndConditionContainerHolder extends DashboardItemHolder {
|
public static class ConditionContainerHolder extends DashboardItemHolder {
|
||||||
public final RecyclerView data;
|
public final RecyclerView data;
|
||||||
|
|
||||||
public SuggestionAndConditionContainerHolder(View itemView) {
|
public ConditionContainerHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
data = itemView.findViewById(id.data);
|
data = itemView.findViewById(id.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class SuggestionContainerHolder extends DashboardItemHolder {
|
||||||
|
public final RecyclerView data;
|
||||||
|
|
||||||
|
public SuggestionContainerHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
data = itemView.findViewById(id.suggestion_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,429 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.dashboard;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.support.annotation.VisibleForTesting;
|
|
||||||
import android.support.v7.util.DiffUtil;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.R.id;
|
|
||||||
import com.android.settings.dashboard.DashboardDataV2.ConditionHeaderData;
|
|
||||||
import com.android.settings.dashboard.conditional.Condition;
|
|
||||||
import com.android.settings.dashboard.conditional.ConditionAdapterV2;
|
|
||||||
import com.android.settings.dashboard.suggestions.SuggestionAdapterV2;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
|
||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
|
||||||
import com.android.settingslib.drawer.Tile;
|
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixin;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DashboardAdapterV2 extends RecyclerView.Adapter<DashboardAdapterV2.DashboardItemHolder>
|
|
||||||
implements SummaryLoader.SummaryConsumer, SuggestionAdapterV2.Callback, LifecycleObserver,
|
|
||||||
OnSaveInstanceState {
|
|
||||||
public static final String TAG = "DashboardAdapterV2";
|
|
||||||
private static final String STATE_CATEGORY_LIST = "category_list";
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static final String STATE_CONDITION_EXPANDED = "condition_expanded";
|
|
||||||
|
|
||||||
private final IconCache mCache;
|
|
||||||
private final Context mContext;
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
|
||||||
private final DashboardFeatureProvider mDashboardFeatureProvider;
|
|
||||||
private boolean mFirstFrameDrawn;
|
|
||||||
private RecyclerView mRecyclerView;
|
|
||||||
private SuggestionAdapterV2 mSuggestionAdapter;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
DashboardDataV2 mDashboardData;
|
|
||||||
|
|
||||||
private View.OnClickListener mTileClickListener = new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
//TODO: get rid of setTag/getTag
|
|
||||||
mDashboardFeatureProvider.openTileIntent((Activity) mContext, (Tile) v.getTag());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public DashboardAdapterV2(Context context, Bundle savedInstanceState,
|
|
||||||
List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
|
|
||||||
Lifecycle lifecycle) {
|
|
||||||
|
|
||||||
DashboardCategory category = null;
|
|
||||||
boolean conditionExpanded = false;
|
|
||||||
|
|
||||||
mContext = context;
|
|
||||||
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
|
||||||
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
|
||||||
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
|
|
||||||
mCache = new IconCache(context);
|
|
||||||
mSuggestionAdapter = new SuggestionAdapterV2(mContext, suggestionControllerMixin,
|
|
||||||
savedInstanceState, this /* callback */, lifecycle);
|
|
||||||
|
|
||||||
setHasStableIds(true);
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
|
|
||||||
conditionExpanded = savedInstanceState.getBoolean(
|
|
||||||
STATE_CONDITION_EXPANDED, conditionExpanded);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lifecycle != null) {
|
|
||||||
lifecycle.addObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
mDashboardData = new DashboardDataV2.Builder()
|
|
||||||
.setConditions(conditions)
|
|
||||||
.setSuggestions(mSuggestionAdapter.getSuggestions())
|
|
||||||
.setCategory(category)
|
|
||||||
.setConditionExpanded(conditionExpanded)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSuggestions(List<Suggestion> data) {
|
|
||||||
final DashboardDataV2 prevData = mDashboardData;
|
|
||||||
mDashboardData = new DashboardDataV2.Builder(prevData)
|
|
||||||
.setSuggestions(data)
|
|
||||||
.build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCategory(DashboardCategory category) {
|
|
||||||
final DashboardDataV2 prevData = mDashboardData;
|
|
||||||
Log.d(TAG, "adapter setCategory called");
|
|
||||||
mDashboardData = new DashboardDataV2.Builder(prevData)
|
|
||||||
.setCategory(category)
|
|
||||||
.build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setConditions(List<Condition> conditions) {
|
|
||||||
final DashboardDataV2 prevData = mDashboardData;
|
|
||||||
Log.d(TAG, "adapter setConditions called");
|
|
||||||
mDashboardData = new DashboardDataV2.Builder(prevData)
|
|
||||||
.setConditions(conditions)
|
|
||||||
.build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuggestionClosed(Suggestion suggestion) {
|
|
||||||
final List<Suggestion> list = mDashboardData.getSuggestions();
|
|
||||||
if (list == null || list.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (list.size() == 1) {
|
|
||||||
// The only suggestion is dismissed, and the the empty suggestion container will
|
|
||||||
// remain as the dashboard item. Need to refresh the dashboard list.
|
|
||||||
setSuggestions(null);
|
|
||||||
} else {
|
|
||||||
mSuggestionAdapter.removeSuggestion(suggestion);
|
|
||||||
notifyItemChanged(0, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void notifySummaryChanged(Tile tile) {
|
|
||||||
final int position = mDashboardData.getPositionByTile(tile);
|
|
||||||
if (position != DashboardDataV2.POSITION_NOT_FOUND) {
|
|
||||||
// Since usually tile in parameter and tile in mCategories are same instance,
|
|
||||||
// which is hard to be detected by DiffUtil, so we notifyItemChanged directly.
|
|
||||||
notifyItemChanged(position, mDashboardData.getItemTypeByPosition(position));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
|
|
||||||
if (viewType == R.layout.suggestion_condition_header) {
|
|
||||||
return new ConditionHeaderHolder(view);
|
|
||||||
}
|
|
||||||
if (viewType == R.layout.condition_container) {
|
|
||||||
return new ConditionContainerHolder(view);
|
|
||||||
}
|
|
||||||
if (viewType == R.layout.suggestion_container) {
|
|
||||||
return new SuggestionContainerHolder(view);
|
|
||||||
}
|
|
||||||
return new DashboardItemHolder(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(DashboardItemHolder holder, int position) {
|
|
||||||
final int type = mDashboardData.getItemTypeByPosition(position);
|
|
||||||
switch (type) {
|
|
||||||
case R.layout.dashboard_tile:
|
|
||||||
final Tile tile = (Tile) mDashboardData.getItemEntityByPosition(position);
|
|
||||||
onBindTile(holder, tile);
|
|
||||||
holder.itemView.setTag(tile);
|
|
||||||
holder.itemView.setOnClickListener(mTileClickListener);
|
|
||||||
break;
|
|
||||||
case R.layout.suggestion_container:
|
|
||||||
onBindSuggestion((SuggestionContainerHolder) holder, position);
|
|
||||||
break;
|
|
||||||
case R.layout.condition_container:
|
|
||||||
onBindCondition((ConditionContainerHolder) holder, position);
|
|
||||||
break;
|
|
||||||
case R.layout.suggestion_condition_header:
|
|
||||||
onBindConditionHeader((ConditionHeaderHolder) holder,
|
|
||||||
(ConditionHeaderData) mDashboardData.getItemEntityByPosition(position));
|
|
||||||
break;
|
|
||||||
case R.layout.suggestion_condition_footer:
|
|
||||||
holder.itemView.setOnClickListener(v -> {
|
|
||||||
mMetricsFeatureProvider.action(mContext,
|
|
||||||
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
|
|
||||||
DashboardDataV2 prevData = mDashboardData;
|
|
||||||
mDashboardData = new DashboardDataV2.Builder(prevData).
|
|
||||||
setConditionExpanded(false).build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
scrollToTopOfConditions();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return mDashboardData.getItemIdByPosition(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position) {
|
|
||||||
return mDashboardData.getItemTypeByPosition(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return mDashboardData.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
|
|
||||||
super.onAttachedToRecyclerView(recyclerView);
|
|
||||||
// save the view so that we can scroll it when expanding/collapsing the suggestion and
|
|
||||||
// conditions.
|
|
||||||
mRecyclerView = recyclerView;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getItem(long itemId) {
|
|
||||||
return mDashboardData.getItemEntityById(itemId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Suggestion getSuggestion(int position) {
|
|
||||||
return mSuggestionAdapter.getSuggestion(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void notifyDashboardDataChanged(DashboardDataV2 prevData) {
|
|
||||||
if (mFirstFrameDrawn && prevData != null) {
|
|
||||||
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardDataV2
|
|
||||||
.ItemsDataDiffCallback(prevData.getItemList(), mDashboardData.getItemList()));
|
|
||||||
diffResult.dispatchUpdatesTo(this);
|
|
||||||
} else {
|
|
||||||
mFirstFrameDrawn = true;
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void onBindConditionHeader(final ConditionHeaderHolder holder, ConditionHeaderData data) {
|
|
||||||
holder.icon.setImageIcon(data.conditionIcons.get(0));
|
|
||||||
if (data.conditionCount == 1) {
|
|
||||||
holder.title.setText(data.title);
|
|
||||||
holder.summary.setText(null);
|
|
||||||
holder.icons.setVisibility(View.INVISIBLE);
|
|
||||||
} else {
|
|
||||||
holder.title.setText(null);
|
|
||||||
holder.summary.setText(
|
|
||||||
mContext.getString(R.string.condition_summary, data.conditionCount));
|
|
||||||
updateConditionIcons(data.conditionIcons, holder.icons);
|
|
||||||
holder.icons.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.itemView.setOnClickListener(v -> {
|
|
||||||
mMetricsFeatureProvider.action(mContext,
|
|
||||||
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
|
|
||||||
final DashboardDataV2 prevData = mDashboardData;
|
|
||||||
mDashboardData = new DashboardDataV2.Builder(prevData)
|
|
||||||
.setConditionExpanded(true).build();
|
|
||||||
notifyDashboardDataChanged(prevData);
|
|
||||||
scrollToTopOfConditions();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void onBindCondition(final ConditionContainerHolder holder, int position) {
|
|
||||||
final ConditionAdapterV2 adapter = new ConditionAdapterV2(mContext,
|
|
||||||
(List<Condition>) mDashboardData.getItemEntityByPosition(position),
|
|
||||||
mDashboardData.isConditionExpanded());
|
|
||||||
adapter.addDismissHandling(holder.data);
|
|
||||||
holder.data.setAdapter(adapter);
|
|
||||||
holder.data.setLayoutManager(new LinearLayoutManager(mContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void onBindSuggestion(final SuggestionContainerHolder holder, int position) {
|
|
||||||
// If there is suggestions to show, it will be at position 0 as we don't show the suggestion
|
|
||||||
// header anymore.
|
|
||||||
final List<Suggestion> suggestions =
|
|
||||||
(List<Suggestion>) mDashboardData.getItemEntityByPosition(position);
|
|
||||||
final int suggestionCount = suggestions.size();
|
|
||||||
if (suggestions != null && suggestionCount > 0) {
|
|
||||||
holder.summary.setText("" + suggestionCount);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
holder.data.setAdapter(mSuggestionAdapter);
|
|
||||||
}
|
|
||||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
|
|
||||||
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
|
||||||
holder.data.setLayoutManager(layoutManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void onBindTile(DashboardItemHolder holder, Tile tile) {
|
|
||||||
Drawable icon = mCache.getIcon(tile.icon);
|
|
||||||
if (!TextUtils.equals(tile.icon.getResPackage(), mContext.getPackageName())) {
|
|
||||||
icon = new RoundedHomepageIcon(mContext, icon);
|
|
||||||
mCache.updateIcon(tile.icon, icon);
|
|
||||||
}
|
|
||||||
holder.icon.setImageDrawable(icon);
|
|
||||||
holder.title.setText(tile.title);
|
|
||||||
if (!TextUtils.isEmpty(tile.summary)) {
|
|
||||||
holder.summary.setText(tile.summary);
|
|
||||||
holder.summary.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
holder.summary.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
final DashboardCategory category = mDashboardData.getCategory();
|
|
||||||
if (category != null) {
|
|
||||||
outState.putParcelable(STATE_CATEGORY_LIST, category);
|
|
||||||
}
|
|
||||||
outState.putBoolean(STATE_CONDITION_EXPANDED, mDashboardData.isConditionExpanded());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateConditionIcons(List<Icon> icons, ViewGroup parent) {
|
|
||||||
if (icons == null || icons.size() < 2) {
|
|
||||||
parent.setVisibility(View.INVISIBLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
|
||||||
parent.removeAllViews();
|
|
||||||
for (int i = 1, size = icons.size(); i < size; i++) {
|
|
||||||
ImageView icon = (ImageView) inflater.inflate(
|
|
||||||
R.layout.condition_header_icon, parent, false);
|
|
||||||
icon.setImageIcon(icons.get(i));
|
|
||||||
parent.addView(icon);
|
|
||||||
}
|
|
||||||
parent.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scrollToTopOfConditions() {
|
|
||||||
mRecyclerView.scrollToPosition(mDashboardData.hasSuggestion() ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class IconCache {
|
|
||||||
private final Context mContext;
|
|
||||||
private final ArrayMap<Icon, Drawable> mMap = new ArrayMap<>();
|
|
||||||
|
|
||||||
public IconCache(Context context) {
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Drawable getIcon(Icon icon) {
|
|
||||||
if (icon == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Drawable drawable = mMap.get(icon);
|
|
||||||
if (drawable == null) {
|
|
||||||
drawable = icon.loadDrawable(mContext);
|
|
||||||
updateIcon(icon, drawable);
|
|
||||||
}
|
|
||||||
return drawable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateIcon(Icon icon, Drawable drawable) {
|
|
||||||
mMap.put(icon, drawable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DashboardItemHolder extends RecyclerView.ViewHolder {
|
|
||||||
public final ImageView icon;
|
|
||||||
public final TextView title;
|
|
||||||
public final TextView summary;
|
|
||||||
|
|
||||||
public DashboardItemHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
icon = itemView.findViewById(android.R.id.icon);
|
|
||||||
title = itemView.findViewById(android.R.id.title);
|
|
||||||
summary = itemView.findViewById(android.R.id.summary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ConditionHeaderHolder extends DashboardItemHolder {
|
|
||||||
public final LinearLayout icons;
|
|
||||||
public final ImageView expandIndicator;
|
|
||||||
|
|
||||||
public ConditionHeaderHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
icons = itemView.findViewById(id.additional_icons);
|
|
||||||
expandIndicator = itemView.findViewById(id.expand_indicator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ConditionContainerHolder extends DashboardItemHolder {
|
|
||||||
public final RecyclerView data;
|
|
||||||
|
|
||||||
public ConditionContainerHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
data = itemView.findViewById(id.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SuggestionContainerHolder extends DashboardItemHolder {
|
|
||||||
public final RecyclerView data;
|
|
||||||
|
|
||||||
public SuggestionContainerHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
data = itemView.findViewById(id.suggestion_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -40,29 +40,17 @@ 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 {
|
||||||
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 MAX_SUGGESTION_COUNT = 4;
|
||||||
|
|
||||||
// stable id for different type of items.
|
// stable id for different type of items.
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int STABLE_ID_SUGGESTION_CONDITION_TOP_HEADER = 0;
|
static final int STABLE_ID_SUGGESTION_CONTAINER = 0;
|
||||||
|
static final int STABLE_ID_SUGGESTION_CONDITION_DIVIDER = 1;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER = 1;
|
static final int STABLE_ID_CONDITION_HEADER = 2;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int STABLE_ID_SUGGESTION_CONDITION_FOOTER = 2;
|
static final int STABLE_ID_CONDITION_FOOTER = 3;
|
||||||
@VisibleForTesting
|
|
||||||
static final int STABLE_ID_SUGGESTION_CONTAINER = 3;
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int STABLE_ID_CONDITION_CONTAINER = 4;
|
static final int STABLE_ID_CONDITION_CONTAINER = 4;
|
||||||
|
|
||||||
@@ -70,15 +58,13 @@ public class DashboardData {
|
|||||||
private final DashboardCategory mCategory;
|
private final DashboardCategory mCategory;
|
||||||
private final List<Condition> mConditions;
|
private final List<Condition> mConditions;
|
||||||
private final List<Suggestion> mSuggestions;
|
private final List<Suggestion> mSuggestions;
|
||||||
@HeaderMode
|
private final boolean mConditionExpanded;
|
||||||
private final int mSuggestionConditionMode;
|
|
||||||
|
|
||||||
private DashboardData(Builder builder) {
|
private DashboardData(Builder builder) {
|
||||||
mCategory = builder.mCategory;
|
mCategory = builder.mCategory;
|
||||||
mConditions = builder.mConditions;
|
mConditions = builder.mConditions;
|
||||||
mSuggestions = builder.mSuggestionsV2;
|
mSuggestions = builder.mSuggestions;
|
||||||
mSuggestionConditionMode = builder.mSuggestionConditionMode;
|
mConditionExpanded = builder.mConditionExpanded;
|
||||||
|
|
||||||
mItems = new ArrayList<>();
|
mItems = new ArrayList<>();
|
||||||
|
|
||||||
buildItemsData();
|
buildItemsData();
|
||||||
@@ -125,8 +111,12 @@ public class DashboardData {
|
|||||||
return mSuggestions;
|
return mSuggestions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSuggestionConditionMode() {
|
public boolean hasSuggestion() {
|
||||||
return mSuggestionConditionMode;
|
return sizeOf(mSuggestions) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConditionExpanded() {
|
||||||
|
return mConditionExpanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -188,69 +178,42 @@ public class DashboardData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the mItems list using mConditions, mSuggestions, mCategories data
|
* Build the mItems list using mConditions, mSuggestions, mCategories data
|
||||||
* and mIsShowingAll, mSuggestionConditionMode flag.
|
* and mIsShowingAll, mConditionExpanded flag.
|
||||||
*/
|
*/
|
||||||
private void buildItemsData() {
|
private void buildItemsData() {
|
||||||
final boolean hasSuggestions = sizeOf(mSuggestions) > 0;
|
|
||||||
final List<Condition> conditions = getConditionsToShow(mConditions);
|
final List<Condition> conditions = getConditionsToShow(mConditions);
|
||||||
final boolean hasConditions = sizeOf(conditions) > 0;
|
final boolean hasConditions = sizeOf(conditions) > 0;
|
||||||
|
|
||||||
final List<Suggestion> suggestions = getSuggestionsToShow(mSuggestions);
|
final List<Suggestion> suggestions = getSuggestionsToShow(mSuggestions);
|
||||||
|
final boolean hasSuggestions = sizeOf(suggestions) > 0;
|
||||||
|
|
||||||
final int hiddenSuggestion = hasSuggestions
|
/* Suggestion container. This is the card view that contains the list of suggestions.
|
||||||
? sizeOf(mSuggestions) - sizeOf(suggestions)
|
* This will be added whenever the suggestion list is not empty */
|
||||||
: 0;
|
addToItemList(suggestions, R.layout.suggestion_container,
|
||||||
|
STABLE_ID_SUGGESTION_CONTAINER, hasSuggestions);
|
||||||
|
|
||||||
final boolean hasSuggestionAndCollapsed = hasSuggestions
|
/* Divider between suggestion and conditions if both are present. */
|
||||||
&& mSuggestionConditionMode == HEADER_MODE_COLLAPSED;
|
addToItemList(null /* item */, R.layout.horizontal_divider,
|
||||||
final boolean onlyHasConditionAndCollapsed = !hasSuggestions
|
STABLE_ID_SUGGESTION_CONDITION_DIVIDER, hasSuggestions && hasConditions);
|
||||||
&& hasConditions
|
|
||||||
&& mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED;
|
|
||||||
|
|
||||||
/* Top suggestion/condition header. This will be present when there is any suggestion
|
/* Condition header. This will be present when there is condition and it is collapsed */
|
||||||
* and the mode is collapsed */
|
addToItemList(new ConditionHeaderData(conditions),
|
||||||
addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
|
R.layout.condition_header,
|
||||||
R.layout.suggestion_condition_header,
|
STABLE_ID_CONDITION_HEADER, hasConditions && !mConditionExpanded);
|
||||||
STABLE_ID_SUGGESTION_CONDITION_TOP_HEADER, hasSuggestionAndCollapsed);
|
|
||||||
|
|
||||||
/* Use mid header if there is only condition & it's in collapsed mode */
|
|
||||||
addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
|
|
||||||
R.layout.suggestion_condition_header,
|
|
||||||
STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER, onlyHasConditionAndCollapsed);
|
|
||||||
|
|
||||||
addToItemList(suggestions, R.layout.suggestion_condition_container,
|
|
||||||
STABLE_ID_SUGGESTION_CONTAINER, sizeOf(suggestions) > 0);
|
|
||||||
|
|
||||||
/* Second suggestion/condition header. This will be added when there is at least one
|
|
||||||
* suggestion or condition that is not currently displayed, and the user can expand the
|
|
||||||
* section to view more items. */
|
|
||||||
addToItemList(new SuggestionConditionHeaderData(conditions, hiddenSuggestion),
|
|
||||||
R.layout.suggestion_condition_header,
|
|
||||||
STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER,
|
|
||||||
mSuggestionConditionMode != HEADER_MODE_COLLAPSED
|
|
||||||
&& mSuggestionConditionMode != HEADER_MODE_FULLY_EXPANDED
|
|
||||||
&& (hiddenSuggestion > 0 || hasConditions && hasSuggestions));
|
|
||||||
|
|
||||||
/* Condition container. This is the card view that contains the list of conditions.
|
/* Condition container. This is the card view that contains the list of conditions.
|
||||||
* This will be added whenever the condition list is not empty */
|
* This will be added whenever the condition list is not empty and expanded */
|
||||||
addToItemList(conditions, R.layout.suggestion_condition_container,
|
addToItemList(conditions, R.layout.condition_container,
|
||||||
STABLE_ID_CONDITION_CONTAINER,
|
STABLE_ID_CONDITION_CONTAINER, hasConditions && mConditionExpanded);
|
||||||
hasConditions && mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED);
|
|
||||||
|
|
||||||
/* Suggestion/condition footer. This will be present when the section is fully expanded
|
/* Condition footer. This will be present when there is condition and it is expanded */
|
||||||
* or when there is no conditions and no hidden suggestions */
|
addToItemList(null /* item */, R.layout.condition_footer,
|
||||||
addToItemList(null /* item */, R.layout.suggestion_condition_footer,
|
STABLE_ID_CONDITION_FOOTER, hasConditions && mConditionExpanded);
|
||||||
STABLE_ID_SUGGESTION_CONDITION_FOOTER,
|
|
||||||
(hasConditions || hasSuggestions)
|
|
||||||
&& mSuggestionConditionMode == HEADER_MODE_FULLY_EXPANDED
|
|
||||||
|| hasSuggestions
|
|
||||||
&& !hasConditions
|
|
||||||
&& hiddenSuggestion == 0);
|
|
||||||
|
|
||||||
if (mCategory != null) {
|
if (mCategory != null) {
|
||||||
final List<Tile> tiles = mCategory.getTiles();
|
final List<Tile> tiles = mCategory.getTiles();
|
||||||
for (int j = 0; j < tiles.size(); j++) {
|
for (int i = 0; i < tiles.size(); i++) {
|
||||||
final Tile tile = tiles.get(j);
|
final Tile tile = tiles.get(i);
|
||||||
addToItemList(tile, R.layout.dashboard_tile, Objects.hash(tile.title),
|
addToItemList(tile, R.layout.dashboard_tile, Objects.hash(tile.title),
|
||||||
true /* add */);
|
true /* add */);
|
||||||
}
|
}
|
||||||
@@ -277,28 +240,23 @@ public class DashboardData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<Suggestion> getSuggestionsToShow(List<Suggestion> suggestions) {
|
private List<Suggestion> getSuggestionsToShow(List<Suggestion> suggestions) {
|
||||||
if (suggestions == null || mSuggestionConditionMode == HEADER_MODE_COLLAPSED) {
|
if (suggestions == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (mSuggestionConditionMode != HEADER_MODE_DEFAULT
|
if (suggestions.size() <= MAX_SUGGESTION_COUNT) {
|
||||||
|| suggestions.size() <= DEFAULT_SUGGESTION_COUNT) {
|
|
||||||
return suggestions;
|
return suggestions;
|
||||||
}
|
}
|
||||||
return suggestions.subList(0, DEFAULT_SUGGESTION_COUNT);
|
return suggestions.subList(0, MAX_SUGGESTION_COUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder used to build the ItemsData
|
* Builder used to build the ItemsData
|
||||||
* <p>
|
|
||||||
* {@link #mSuggestionConditionMode} have default value while others are not.
|
|
||||||
*/
|
*/
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
@HeaderMode
|
|
||||||
private int mSuggestionConditionMode = HEADER_MODE_DEFAULT;
|
|
||||||
|
|
||||||
private DashboardCategory mCategory;
|
private DashboardCategory mCategory;
|
||||||
private List<Condition> mConditions;
|
private List<Condition> mConditions;
|
||||||
private List<Suggestion> mSuggestionsV2;
|
private List<Suggestion> mSuggestions;
|
||||||
|
private boolean mConditionExpanded;
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
}
|
}
|
||||||
@@ -306,8 +264,8 @@ public class DashboardData {
|
|||||||
public Builder(DashboardData dashboardData) {
|
public Builder(DashboardData dashboardData) {
|
||||||
mCategory = dashboardData.mCategory;
|
mCategory = dashboardData.mCategory;
|
||||||
mConditions = dashboardData.mConditions;
|
mConditions = dashboardData.mConditions;
|
||||||
mSuggestionsV2 = dashboardData.mSuggestions;
|
mSuggestions = dashboardData.mSuggestions;
|
||||||
mSuggestionConditionMode = dashboardData.mSuggestionConditionMode;
|
mConditionExpanded = dashboardData.mConditionExpanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setCategory(DashboardCategory category) {
|
public Builder setCategory(DashboardCategory category) {
|
||||||
@@ -321,12 +279,12 @@ public class DashboardData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Builder setSuggestions(List<Suggestion> suggestions) {
|
public Builder setSuggestions(List<Suggestion> suggestions) {
|
||||||
this.mSuggestionsV2 = suggestions;
|
this.mSuggestions = suggestions;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setSuggestionConditionMode(@HeaderMode int mode) {
|
public Builder setConditionExpanded(boolean expanded) {
|
||||||
this.mSuggestionConditionMode = mode;
|
this.mConditionExpanded = expanded;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,17 +334,18 @@ public class DashboardData {
|
|||||||
static class Item {
|
static class Item {
|
||||||
// valid types in field type
|
// valid types in field type
|
||||||
private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
|
private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
|
||||||
private static final int TYPE_SUGGESTION_CONDITION_CONTAINER =
|
private static final int TYPE_SUGGESTION_CONTAINER =
|
||||||
R.layout.suggestion_condition_container;
|
R.layout.suggestion_container;
|
||||||
private static final int TYPE_SUGGESTION_CONDITION_HEADER =
|
private static final int TYPE_CONDITION_CONTAINER =
|
||||||
R.layout.suggestion_condition_header;
|
R.layout.condition_container;
|
||||||
private static final int TYPE_SUGGESTION_CONDITION_FOOTER =
|
private static final int TYPE_CONDITION_HEADER =
|
||||||
R.layout.suggestion_condition_footer;
|
R.layout.condition_header;
|
||||||
private static final int TYPE_DASHBOARD_SPACER = R.layout.dashboard_spacer;
|
private static final int TYPE_CONDITION_FOOTER =
|
||||||
|
R.layout.condition_footer;
|
||||||
|
private static final int TYPE_SUGGESTION_CONDITION_DIVIDER = R.layout.horizontal_divider;
|
||||||
|
|
||||||
@IntDef({TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_CONDITION_CONTAINER,
|
@IntDef({TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_CONTAINER, TYPE_CONDITION_CONTAINER,
|
||||||
TYPE_SUGGESTION_CONDITION_HEADER, TYPE_SUGGESTION_CONDITION_FOOTER,
|
TYPE_CONDITION_HEADER, TYPE_CONDITION_FOOTER, TYPE_SUGGESTION_CONDITION_DIVIDER})
|
||||||
TYPE_DASHBOARD_SPACER})
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface ItemTypes {
|
public @interface ItemTypes {
|
||||||
}
|
}
|
||||||
@@ -445,7 +404,8 @@ public class DashboardData {
|
|||||||
// Only check title and summary for dashboard tile
|
// Only check title and summary for dashboard tile
|
||||||
return TextUtils.equals(localTile.title, targetTile.title)
|
return TextUtils.equals(localTile.title, targetTile.title)
|
||||||
&& TextUtils.equals(localTile.summary, targetTile.summary);
|
&& TextUtils.equals(localTile.summary, targetTile.summary);
|
||||||
case TYPE_SUGGESTION_CONDITION_CONTAINER:
|
case TYPE_SUGGESTION_CONTAINER:
|
||||||
|
case TYPE_CONDITION_CONTAINER:
|
||||||
// If entity is suggestion and contains remote view, force refresh
|
// If entity is suggestion and contains remote view, force refresh
|
||||||
final List entities = (List) entity;
|
final List entities = (List) entity;
|
||||||
if (!entities.isEmpty()) {
|
if (!entities.isEmpty()) {
|
||||||
@@ -467,16 +427,13 @@ public class DashboardData {
|
|||||||
* This class contains the data needed to build the suggestion/condition header. The data can
|
* 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
|
* also be used to check the diff in DiffUtil.Callback
|
||||||
*/
|
*/
|
||||||
public static class SuggestionConditionHeaderData {
|
public static class ConditionHeaderData {
|
||||||
public final List<Icon> conditionIcons;
|
public final List<Icon> conditionIcons;
|
||||||
public final CharSequence title;
|
public final CharSequence title;
|
||||||
public final int conditionCount;
|
public final int conditionCount;
|
||||||
public final int hiddenSuggestionCount;
|
|
||||||
|
|
||||||
public SuggestionConditionHeaderData(List<Condition> conditions,
|
public ConditionHeaderData(List<Condition> conditions) {
|
||||||
int hiddenSuggestionCount) {
|
|
||||||
conditionCount = sizeOf(conditions);
|
conditionCount = sizeOf(conditions);
|
||||||
this.hiddenSuggestionCount = hiddenSuggestionCount;
|
|
||||||
title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
|
title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
|
||||||
conditionIcons = new ArrayList<>();
|
conditionIcons = new ArrayList<>();
|
||||||
for (int i = 0; conditions != null && i < conditions.size(); i++) {
|
for (int i = 0; conditions != null && i < conditions.size(); i++) {
|
||||||
|
@@ -1,446 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.dashboard;
|
|
||||||
|
|
||||||
import android.annotation.IntDef;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.support.annotation.VisibleForTesting;
|
|
||||||
import android.support.v7.util.DiffUtil;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.dashboard.conditional.Condition;
|
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
|
||||||
import com.android.settingslib.drawer.Tile;
|
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Description about data list used in the DashboardAdapter. In the data list each item can be
|
|
||||||
* Condition, suggestion or category tile.
|
|
||||||
* <p>
|
|
||||||
* ItemsData has inner class Item, which represents the Item in data list.
|
|
||||||
*/
|
|
||||||
public class DashboardDataV2 {
|
|
||||||
public static final int POSITION_NOT_FOUND = -1;
|
|
||||||
public static final int MAX_SUGGESTION_COUNT = 4;
|
|
||||||
|
|
||||||
// stable id for different type of items.
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int STABLE_ID_SUGGESTION_CONTAINER = 0;
|
|
||||||
static final int STABLE_ID_SUGGESTION_CONDITION_DIVIDER = 1;
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int STABLE_ID_CONDITION_HEADER = 2;
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int STABLE_ID_CONDITION_FOOTER = 3;
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int STABLE_ID_CONDITION_CONTAINER = 4;
|
|
||||||
|
|
||||||
private final List<Item> mItems;
|
|
||||||
private final DashboardCategory mCategory;
|
|
||||||
private final List<Condition> mConditions;
|
|
||||||
private final List<Suggestion> mSuggestions;
|
|
||||||
private final boolean mConditionExpanded;
|
|
||||||
|
|
||||||
private DashboardDataV2(Builder builder) {
|
|
||||||
mCategory = builder.mCategory;
|
|
||||||
mConditions = builder.mConditions;
|
|
||||||
mSuggestions = builder.mSuggestions;
|
|
||||||
mConditionExpanded = builder.mConditionExpanded;
|
|
||||||
mItems = new ArrayList<>();
|
|
||||||
|
|
||||||
buildItemsData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getItemIdByPosition(int position) {
|
|
||||||
return mItems.get(position).id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getItemTypeByPosition(int position) {
|
|
||||||
return mItems.get(position).type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getItemEntityByPosition(int position) {
|
|
||||||
return mItems.get(position).entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Item> getItemList() {
|
|
||||||
return mItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
return mItems.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getItemEntityById(long id) {
|
|
||||||
for (final Item item : mItems) {
|
|
||||||
if (item.id == id) {
|
|
||||||
return item.entity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DashboardCategory getCategory() {
|
|
||||||
return mCategory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Condition> getConditions() {
|
|
||||||
return mConditions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Suggestion> getSuggestions() {
|
|
||||||
return mSuggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSuggestion() {
|
|
||||||
return sizeOf(mSuggestions) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isConditionExpanded() {
|
|
||||||
return mConditionExpanded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the position of the object in mItems list, using the equals method to compare
|
|
||||||
*
|
|
||||||
* @param entity the object that need to be found in list
|
|
||||||
* @return position of the object, return POSITION_NOT_FOUND if object isn't in the list
|
|
||||||
*/
|
|
||||||
public int getPositionByEntity(Object entity) {
|
|
||||||
if (entity == null) return POSITION_NOT_FOUND;
|
|
||||||
|
|
||||||
final int size = mItems.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
final Object item = mItems.get(i).entity;
|
|
||||||
if (entity.equals(item)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return POSITION_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the position of the Tile object.
|
|
||||||
* <p>
|
|
||||||
* First, try to find the exact identical instance of the tile object, if not found,
|
|
||||||
* then try to find a tile has the same title.
|
|
||||||
*
|
|
||||||
* @param tile tile that need to be found
|
|
||||||
* @return position of the object, return INDEX_NOT_FOUND if object isn't in the list
|
|
||||||
*/
|
|
||||||
public int getPositionByTile(Tile tile) {
|
|
||||||
final int size = mItems.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
final Object entity = mItems.get(i).entity;
|
|
||||||
if (entity == tile) {
|
|
||||||
return i;
|
|
||||||
} else if (entity instanceof Tile && tile.title.equals(((Tile) entity).title)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return POSITION_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add item into list when {@paramref add} is true.
|
|
||||||
*
|
|
||||||
* @param item maybe {@link Condition}, {@link Tile}, {@link DashboardCategory} or null
|
|
||||||
* @param type type of the item, and value is the layout id
|
|
||||||
* @param stableId The stable id for this item
|
|
||||||
* @param add flag about whether to add item into list
|
|
||||||
*/
|
|
||||||
private void addToItemList(Object item, int type, int stableId, boolean add) {
|
|
||||||
if (add) {
|
|
||||||
mItems.add(new Item(item, type, stableId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the mItems list using mConditions, mSuggestions, mCategories data
|
|
||||||
* and mIsShowingAll, mConditionExpanded flag.
|
|
||||||
*/
|
|
||||||
private void buildItemsData() {
|
|
||||||
final List<Condition> conditions = getConditionsToShow(mConditions);
|
|
||||||
final boolean hasConditions = sizeOf(conditions) > 0;
|
|
||||||
|
|
||||||
final List<Suggestion> suggestions = getSuggestionsToShow(mSuggestions);
|
|
||||||
final boolean hasSuggestions = sizeOf(suggestions) > 0;
|
|
||||||
|
|
||||||
/* Suggestion container. This is the card view that contains the list of suggestions.
|
|
||||||
* This will be added whenever the suggestion list is not empty */
|
|
||||||
addToItemList(suggestions, R.layout.suggestion_container,
|
|
||||||
STABLE_ID_SUGGESTION_CONTAINER, hasSuggestions);
|
|
||||||
|
|
||||||
/* Divider between suggestion and conditions if both are present. */
|
|
||||||
addToItemList(suggestions, R.layout.horizontal_divider,
|
|
||||||
STABLE_ID_SUGGESTION_CONDITION_DIVIDER, hasSuggestions && hasConditions);
|
|
||||||
|
|
||||||
/* Condition header. This will be present when there is condition and it is collapsed */
|
|
||||||
addToItemList(new ConditionHeaderData(conditions),
|
|
||||||
R.layout.suggestion_condition_header,
|
|
||||||
STABLE_ID_CONDITION_HEADER, hasConditions && !mConditionExpanded);
|
|
||||||
|
|
||||||
/* Condition container. This is the card view that contains the list of conditions.
|
|
||||||
* This will be added whenever the condition list is not empty and expanded */
|
|
||||||
addToItemList(conditions, R.layout.condition_container,
|
|
||||||
STABLE_ID_CONDITION_CONTAINER, hasConditions && mConditionExpanded);
|
|
||||||
|
|
||||||
/* Condition footer. This will be present when there is condition and it is expanded */
|
|
||||||
addToItemList(null /* item */, R.layout.suggestion_condition_footer,
|
|
||||||
STABLE_ID_CONDITION_FOOTER, hasConditions && mConditionExpanded);
|
|
||||||
|
|
||||||
if (mCategory != null) {
|
|
||||||
final List<Tile> tiles = mCategory.getTiles();
|
|
||||||
for (int i = 0; i < tiles.size(); i++) {
|
|
||||||
final Tile tile = tiles.get(i);
|
|
||||||
addToItemList(tile, R.layout.dashboard_tile, Objects.hash(tile.title),
|
|
||||||
true /* add */);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int sizeOf(List<?> list) {
|
|
||||||
return list == null ? 0 : list.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Condition> getConditionsToShow(List<Condition> conditions) {
|
|
||||||
if (conditions == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
List<Condition> result = new ArrayList<>();
|
|
||||||
final int size = conditions == null ? 0 : conditions.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
final Condition condition = conditions.get(i);
|
|
||||||
if (condition.shouldShow()) {
|
|
||||||
result.add(condition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Suggestion> getSuggestionsToShow(List<Suggestion> suggestions) {
|
|
||||||
if (suggestions == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (suggestions.size() <= MAX_SUGGESTION_COUNT) {
|
|
||||||
return suggestions;
|
|
||||||
}
|
|
||||||
return suggestions.subList(0, MAX_SUGGESTION_COUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builder used to build the ItemsData
|
|
||||||
*/
|
|
||||||
public static class Builder {
|
|
||||||
private DashboardCategory mCategory;
|
|
||||||
private List<Condition> mConditions;
|
|
||||||
private List<Suggestion> mSuggestions;
|
|
||||||
private boolean mConditionExpanded;
|
|
||||||
|
|
||||||
public Builder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder(DashboardDataV2 dashboardData) {
|
|
||||||
mCategory = dashboardData.mCategory;
|
|
||||||
mConditions = dashboardData.mConditions;
|
|
||||||
mSuggestions = dashboardData.mSuggestions;
|
|
||||||
mConditionExpanded = dashboardData.mConditionExpanded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setCategory(DashboardCategory category) {
|
|
||||||
this.mCategory = category;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setConditions(List<Condition> conditions) {
|
|
||||||
this.mConditions = conditions;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setSuggestions(List<Suggestion> suggestions) {
|
|
||||||
this.mSuggestions = suggestions;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setConditionExpanded(boolean expanded) {
|
|
||||||
this.mConditionExpanded = expanded;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DashboardDataV2 build() {
|
|
||||||
return new DashboardDataV2(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A DiffCallback to calculate the difference between old and new Item
|
|
||||||
* List in DashboardDataV2
|
|
||||||
*/
|
|
||||||
public static class ItemsDataDiffCallback extends DiffUtil.Callback {
|
|
||||||
final private List<Item> mOldItems;
|
|
||||||
final private List<Item> mNewItems;
|
|
||||||
|
|
||||||
public ItemsDataDiffCallback(List<Item> oldItems, List<Item> newItems) {
|
|
||||||
mOldItems = oldItems;
|
|
||||||
mNewItems = newItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOldListSize() {
|
|
||||||
return mOldItems.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getNewListSize() {
|
|
||||||
return mNewItems.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
|
||||||
return mOldItems.get(oldItemPosition).id == mNewItems.get(newItemPosition).id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
|
||||||
return mOldItems.get(oldItemPosition).equals(mNewItems.get(newItemPosition));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An item contains the data needed in the DashboardDataV2.
|
|
||||||
*/
|
|
||||||
static class Item {
|
|
||||||
// valid types in field type
|
|
||||||
private static final int TYPE_DASHBOARD_TILE = R.layout.dashboard_tile;
|
|
||||||
private static final int TYPE_SUGGESTION_CONTAINER =
|
|
||||||
R.layout.suggestion_container;
|
|
||||||
private static final int TYPE_CONDITION_CONTAINER =
|
|
||||||
R.layout.condition_container;
|
|
||||||
private static final int TYPE_CONDITION_HEADER =
|
|
||||||
R.layout.suggestion_condition_header;
|
|
||||||
private static final int TYPE_CONDITION_FOOTER =
|
|
||||||
R.layout.suggestion_condition_footer;
|
|
||||||
private static final int TYPE_SUGGESTION_CONDITION_DIVIDER = R.layout.horizontal_divider;
|
|
||||||
|
|
||||||
@IntDef({TYPE_DASHBOARD_TILE, TYPE_SUGGESTION_CONTAINER, TYPE_CONDITION_CONTAINER,
|
|
||||||
TYPE_CONDITION_HEADER, TYPE_CONDITION_FOOTER, TYPE_SUGGESTION_CONDITION_DIVIDER})
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
public @interface ItemTypes {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main data object in item, usually is a {@link Tile}, {@link Condition}
|
|
||||||
* object. This object can also be null when the
|
|
||||||
* item is an divider line. Please refer to {@link #buildItemsData()} for
|
|
||||||
* detail usage of the Item.
|
|
||||||
*/
|
|
||||||
public final Object entity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of item, value inside is the layout id(e.g. R.layout.dashboard_tile)
|
|
||||||
*/
|
|
||||||
@ItemTypes
|
|
||||||
public final int type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Id of this item, used in the {@link ItemsDataDiffCallback} to identify the same item.
|
|
||||||
*/
|
|
||||||
public final int id;
|
|
||||||
|
|
||||||
public Item(Object entity, @ItemTypes int type, int id) {
|
|
||||||
this.entity = entity;
|
|
||||||
this.type = type;
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override it to make comparision in the {@link ItemsDataDiffCallback}
|
|
||||||
*
|
|
||||||
* @param obj object to compared with
|
|
||||||
* @return true if the same object or has equal value.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(obj instanceof Item)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Item targetItem = (Item) obj;
|
|
||||||
if (type != targetItem.type || id != targetItem.id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case TYPE_DASHBOARD_TILE:
|
|
||||||
final Tile localTile = (Tile) entity;
|
|
||||||
final Tile targetTile = (Tile) targetItem.entity;
|
|
||||||
|
|
||||||
// Only check title and summary for dashboard tile
|
|
||||||
return TextUtils.equals(localTile.title, targetTile.title)
|
|
||||||
&& TextUtils.equals(localTile.summary, targetTile.summary);
|
|
||||||
case TYPE_SUGGESTION_CONTAINER:
|
|
||||||
case TYPE_CONDITION_CONTAINER:
|
|
||||||
// If entity is suggestion and contains remote view, force refresh
|
|
||||||
final List entities = (List) entity;
|
|
||||||
if (!entities.isEmpty()) {
|
|
||||||
Object firstEntity = entities.get(0);
|
|
||||||
if (firstEntity instanceof Tile
|
|
||||||
&& ((Tile) firstEntity).remoteViews != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Otherwise Fall through to default
|
|
||||||
default:
|
|
||||||
return entity == null ? targetItem.entity == null
|
|
||||||
: entity.equals(targetItem.entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class contains the data needed to build the suggestion/condition header. The data can
|
|
||||||
* also be used to check the diff in DiffUtil.Callback
|
|
||||||
*/
|
|
||||||
public static class ConditionHeaderData {
|
|
||||||
public final List<Icon> conditionIcons;
|
|
||||||
public final CharSequence title;
|
|
||||||
public final int conditionCount;
|
|
||||||
|
|
||||||
public ConditionHeaderData(List<Condition> conditions) {
|
|
||||||
conditionCount = sizeOf(conditions);
|
|
||||||
title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
|
|
||||||
conditionIcons = new ArrayList<>();
|
|
||||||
for (int i = 0; conditions != null && i < conditions.size(); i++) {
|
|
||||||
final Condition condition = conditions.get(i);
|
|
||||||
conditionIcons.add(condition.getIcon());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -90,9 +90,4 @@ public interface DashboardFeatureProvider {
|
|||||||
*/
|
*/
|
||||||
void openTileIntent(Activity activity, Tile tile);
|
void openTileIntent(Activity activity, Tile tile);
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not we should use the v2 of suggestions UI.
|
|
||||||
*/
|
|
||||||
boolean useSuggestionUiV2();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -217,11 +217,6 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
|
|||||||
launchIntentOrSelectProfile(activity, tile, intent, MetricsEvent.DASHBOARD_SUMMARY);
|
launchIntentOrSelectProfile(activity, tile, intent, MetricsEvent.DASHBOARD_SUMMARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean useSuggestionUiV2() {
|
|
||||||
return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.SUGGESTION_UI_V2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindSummary(Preference preference, Tile tile) {
|
private void bindSummary(Preference preference, Tile tile) {
|
||||||
if (tile.summary != null) {
|
if (tile.summary != null) {
|
||||||
preference.setSummary(tile.summary);
|
preference.setSummary(tile.summary);
|
||||||
|
@@ -38,7 +38,6 @@ import com.android.settings.dashboard.conditional.ConditionManager;
|
|||||||
import com.android.settings.dashboard.conditional.ConditionManager.ConditionListener;
|
import com.android.settings.dashboard.conditional.ConditionManager.ConditionListener;
|
||||||
import com.android.settings.dashboard.conditional.FocusRecyclerView;
|
import com.android.settings.dashboard.conditional.FocusRecyclerView;
|
||||||
import com.android.settings.dashboard.conditional.FocusRecyclerView.FocusListener;
|
import com.android.settings.dashboard.conditional.FocusRecyclerView.FocusListener;
|
||||||
import com.android.settings.dashboard.suggestions.SuggestionDismissController;
|
|
||||||
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
|
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settings.widget.ActionBarShadowController;
|
import com.android.settings.widget.ActionBarShadowController;
|
||||||
@@ -53,8 +52,7 @@ import java.util.List;
|
|||||||
|
|
||||||
public class DashboardSummary extends InstrumentedFragment
|
public class DashboardSummary extends InstrumentedFragment
|
||||||
implements CategoryListener, ConditionListener,
|
implements CategoryListener, ConditionListener,
|
||||||
FocusListener, SuggestionDismissController.Callback,
|
FocusListener, SuggestionControllerMixin.SuggestionControllerHost {
|
||||||
SuggestionControllerMixin.SuggestionControllerHost {
|
|
||||||
public static final boolean DEBUG = false;
|
public static final boolean DEBUG = false;
|
||||||
private static final boolean DEBUG_TIMING = false;
|
private static final boolean DEBUG_TIMING = false;
|
||||||
private static final int MAX_WAIT_MILLIS = 700;
|
private static final int MAX_WAIT_MILLIS = 700;
|
||||||
@@ -66,7 +64,6 @@ public class DashboardSummary extends InstrumentedFragment
|
|||||||
|
|
||||||
private FocusRecyclerView mDashboard;
|
private FocusRecyclerView mDashboard;
|
||||||
private DashboardAdapter mAdapter;
|
private DashboardAdapter mAdapter;
|
||||||
private DashboardAdapterV2 mAdapterV2;
|
|
||||||
private SummaryLoader mSummaryLoader;
|
private SummaryLoader mSummaryLoader;
|
||||||
private ConditionManager mConditionManager;
|
private ConditionManager mConditionManager;
|
||||||
private LinearLayoutManager mLayoutManager;
|
private LinearLayoutManager mLayoutManager;
|
||||||
@@ -181,13 +178,10 @@ public class DashboardSummary extends InstrumentedFragment
|
|||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
if (mLayoutManager == null) return;
|
if (mLayoutManager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
outState.putInt(EXTRA_SCROLL_POSITION, mLayoutManager.findFirstVisibleItemPosition());
|
outState.putInt(EXTRA_SCROLL_POSITION, mLayoutManager.findFirstVisibleItemPosition());
|
||||||
if (!mDashboardFeatureProvider.useSuggestionUiV2()) {
|
|
||||||
if (mAdapter != null) {
|
|
||||||
mAdapter.onSaveInstanceState(outState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -205,17 +199,10 @@ public class DashboardSummary extends InstrumentedFragment
|
|||||||
mDashboard.setHasFixedSize(true);
|
mDashboard.setHasFixedSize(true);
|
||||||
mDashboard.setListener(this);
|
mDashboard.setListener(this);
|
||||||
mDashboard.setItemAnimator(new DashboardItemAnimator());
|
mDashboard.setItemAnimator(new DashboardItemAnimator());
|
||||||
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
|
mAdapter = new DashboardAdapter(getContext(), bundle,
|
||||||
mAdapterV2 = new DashboardAdapterV2(getContext(), bundle,
|
|
||||||
mConditionManager.getConditions(), mSuggestionControllerMixin, getLifecycle());
|
mConditionManager.getConditions(), mSuggestionControllerMixin, getLifecycle());
|
||||||
mDashboard.setAdapter(mAdapterV2);
|
|
||||||
mSummaryLoader.setSummaryConsumer(mAdapterV2);
|
|
||||||
} else {
|
|
||||||
mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions(),
|
|
||||||
mSuggestionControllerMixin, this /* SuggestionDismissController.Callback */);
|
|
||||||
mDashboard.setAdapter(mAdapter);
|
mDashboard.setAdapter(mAdapter);
|
||||||
mSummaryLoader.setSummaryConsumer(mAdapter);
|
mSummaryLoader.setSummaryConsumer(mAdapter);
|
||||||
}
|
|
||||||
ActionBarShadowController.attachToRecyclerView(
|
ActionBarShadowController.attachToRecyclerView(
|
||||||
getActivity().findViewById(R.id.search_bar_container), getLifecycle(), mDashboard);
|
getActivity().findViewById(R.id.search_bar_container), getLifecycle(), mDashboard);
|
||||||
rebuildUI();
|
rebuildUI();
|
||||||
@@ -254,11 +241,7 @@ public class DashboardSummary extends InstrumentedFragment
|
|||||||
if (mOnConditionsChangedCalled) {
|
if (mOnConditionsChangedCalled) {
|
||||||
final boolean scrollToTop =
|
final boolean scrollToTop =
|
||||||
mLayoutManager.findFirstCompletelyVisibleItemPosition() <= 1;
|
mLayoutManager.findFirstCompletelyVisibleItemPosition() <= 1;
|
||||||
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
|
|
||||||
mAdapterV2.setConditions(mConditionManager.getConditions());
|
|
||||||
} else {
|
|
||||||
mAdapter.setConditions(mConditionManager.getConditions());
|
mAdapter.setConditions(mConditionManager.getConditions());
|
||||||
}
|
|
||||||
if (scrollToTop) {
|
if (scrollToTop) {
|
||||||
mDashboard.scrollToPosition(0);
|
mDashboard.scrollToPosition(0);
|
||||||
}
|
}
|
||||||
@@ -267,31 +250,9 @@ public class DashboardSummary extends InstrumentedFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Suggestion getSuggestionAt(int position) {
|
|
||||||
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
|
|
||||||
return mAdapterV2.getSuggestion(position);
|
|
||||||
} else {
|
|
||||||
return mAdapter.getSuggestion(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuggestionDismissed(Suggestion suggestion) {
|
|
||||||
mAdapter.onSuggestionDismissed(suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuggestionReady(List<Suggestion> suggestions) {
|
public void onSuggestionReady(List<Suggestion> suggestions) {
|
||||||
mStagingSuggestions = suggestions;
|
mStagingSuggestions = suggestions;
|
||||||
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
|
|
||||||
mAdapterV2.setSuggestions(suggestions);
|
|
||||||
if (mStagingCategory != null) {
|
|
||||||
Log.d(TAG, "Category has loaded, setting category from suggestionReady");
|
|
||||||
mHandler.removeCallbacksAndMessages(null);
|
|
||||||
mAdapterV2.setCategory(mStagingCategory);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mAdapter.setSuggestions(suggestions);
|
mAdapter.setSuggestions(suggestions);
|
||||||
if (mStagingCategory != null) {
|
if (mStagingCategory != null) {
|
||||||
Log.d(TAG, "Category has loaded, setting category from suggestionReady");
|
Log.d(TAG, "Category has loaded, setting category from suggestionReady");
|
||||||
@@ -299,7 +260,6 @@ public class DashboardSummary extends InstrumentedFragment
|
|||||||
mAdapter.setCategory(mStagingCategory);
|
mAdapter.setCategory(mStagingCategory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
void updateCategory() {
|
void updateCategory() {
|
||||||
@@ -313,26 +273,14 @@ public class DashboardSummary extends InstrumentedFragment
|
|||||||
if (mSuggestionControllerMixin.isSuggestionLoaded()) {
|
if (mSuggestionControllerMixin.isSuggestionLoaded()) {
|
||||||
Log.d(TAG, "Suggestion has loaded, setting suggestion/category");
|
Log.d(TAG, "Suggestion has loaded, setting suggestion/category");
|
||||||
ThreadUtils.postOnMainThread(() -> {
|
ThreadUtils.postOnMainThread(() -> {
|
||||||
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
|
|
||||||
if (mStagingSuggestions != null) {
|
|
||||||
mAdapterV2.setSuggestions(mStagingSuggestions);
|
|
||||||
}
|
|
||||||
mAdapterV2.setCategory(mStagingCategory);
|
|
||||||
} else {
|
|
||||||
if (mStagingSuggestions != null) {
|
if (mStagingSuggestions != null) {
|
||||||
mAdapter.setSuggestions(mStagingSuggestions);
|
mAdapter.setSuggestions(mStagingSuggestions);
|
||||||
}
|
}
|
||||||
mAdapter.setCategory(mStagingCategory);
|
mAdapter.setCategory(mStagingCategory);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Suggestion NOT loaded, delaying setCategory by " + MAX_WAIT_MILLIS + "ms");
|
Log.d(TAG, "Suggestion NOT loaded, delaying setCategory by " + MAX_WAIT_MILLIS + "ms");
|
||||||
if (mDashboardFeatureProvider.useSuggestionUiV2()) {
|
|
||||||
mHandler.postDelayed(()
|
|
||||||
-> mAdapterV2.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
|
|
||||||
} else {
|
|
||||||
mHandler.postDelayed(() -> mAdapter.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
|
mHandler.postDelayed(() -> mAdapter.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@@ -27,10 +27,7 @@ import android.widget.Button;
|
|||||||
|
|
||||||
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.dashboard.DashboardAdapter;
|
|
||||||
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
|
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
|
||||||
import com.android.settings.dashboard.DashboardData;
|
|
||||||
import com.android.settings.dashboard.DashboardData.HeaderMode;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.WirelessUtils;
|
import com.android.settingslib.WirelessUtils;
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
@@ -44,7 +41,7 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
|
|||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
private List<Condition> mConditions;
|
private List<Condition> mConditions;
|
||||||
private @HeaderMode int mMode;
|
private boolean mExpanded;
|
||||||
|
|
||||||
private View.OnClickListener mConditionClickListener = new View.OnClickListener() {
|
private View.OnClickListener mConditionClickListener = new View.OnClickListener() {
|
||||||
|
|
||||||
@@ -84,10 +81,10 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public ConditionAdapter(Context context, List<Condition> conditions, @HeaderMode int mode) {
|
public ConditionAdapter(Context context, List<Condition> conditions, boolean expanded) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mConditions = conditions;
|
mConditions = conditions;
|
||||||
mMode = mode;
|
mExpanded = expanded;
|
||||||
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
|
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
|
||||||
|
|
||||||
setHasStableIds(true);
|
setHasStableIds(true);
|
||||||
@@ -126,7 +123,7 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
if (mMode == DashboardData.HEADER_MODE_FULLY_EXPANDED) {
|
if (mExpanded) {
|
||||||
return mConditions.size();
|
return mConditions.size();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -138,7 +135,7 @@ public class ConditionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void bindViews(final Condition condition,
|
private void bindViews(final Condition condition,
|
||||||
DashboardAdapter.DashboardItemHolder view, boolean isLastItem,
|
DashboardItemHolder view, boolean isLastItem,
|
||||||
View.OnClickListener onClickListener) {
|
View.OnClickListener onClickListener) {
|
||||||
if (condition instanceof AirplaneModeCondition) {
|
if (condition instanceof AirplaneModeCondition) {
|
||||||
Log.d(TAG, "Airplane mode condition has been bound with "
|
Log.d(TAG, "Airplane mode condition has been bound with "
|
||||||
|
@@ -1,186 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.dashboard.conditional;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.VisibleForTesting;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.Button;
|
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settingslib.WirelessUtils;
|
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class ConditionAdapterV2 extends RecyclerView.Adapter<DashboardItemHolder> {
|
|
||||||
public static final String TAG = "ConditionAdapter";
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
|
||||||
private List<Condition> mConditions;
|
|
||||||
private boolean mExpanded;
|
|
||||||
|
|
||||||
private View.OnClickListener mConditionClickListener = new View.OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
//TODO: get rid of setTag/getTag
|
|
||||||
Condition condition = (Condition) v.getTag();
|
|
||||||
mMetricsFeatureProvider.action(mContext,
|
|
||||||
MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
|
|
||||||
condition.getMetricsConstant());
|
|
||||||
condition.onPrimaryClick();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
ItemTouchHelper.SimpleCallback mSwipeCallback = new ItemTouchHelper.SimpleCallback(0,
|
|
||||||
ItemTouchHelper.START | ItemTouchHelper.END) {
|
|
||||||
@Override
|
|
||||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
|
|
||||||
RecyclerView.ViewHolder target) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
|
||||||
return viewHolder.getItemViewType() == R.layout.condition_tile
|
|
||||||
? super.getSwipeDirs(recyclerView, viewHolder) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
|
||||||
Object item = getItem(viewHolder.getItemId());
|
|
||||||
// item can become null when running monkey
|
|
||||||
if (item != null) {
|
|
||||||
((Condition) item).silence();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public ConditionAdapterV2(Context context, List<Condition> conditions, boolean expanded) {
|
|
||||||
mContext = context;
|
|
||||||
mConditions = conditions;
|
|
||||||
mExpanded = expanded;
|
|
||||||
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
|
|
||||||
|
|
||||||
setHasStableIds(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getItem(long itemId) {
|
|
||||||
for (Condition condition : mConditions) {
|
|
||||||
if (Objects.hash(condition.getTitle()) == itemId) {
|
|
||||||
return condition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
|
|
||||||
viewType, parent, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(DashboardItemHolder holder, int position) {
|
|
||||||
bindViews(mConditions.get(position), holder,
|
|
||||||
position == mConditions.size() - 1, mConditionClickListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return Objects.hash(mConditions.get(position).getTitle());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position) {
|
|
||||||
return R.layout.condition_tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
if (mExpanded) {
|
|
||||||
return mConditions.size();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addDismissHandling(final RecyclerView recyclerView) {
|
|
||||||
final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(mSwipeCallback);
|
|
||||||
itemTouchHelper.attachToRecyclerView(recyclerView);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindViews(final Condition condition,
|
|
||||||
DashboardItemHolder view, boolean isLastItem,
|
|
||||||
View.OnClickListener onClickListener) {
|
|
||||||
if (condition instanceof AirplaneModeCondition) {
|
|
||||||
Log.d(TAG, "Airplane mode condition has been bound with "
|
|
||||||
+ "isActive=" + condition.isActive() + ". Airplane mode is currently " +
|
|
||||||
WirelessUtils.isAirplaneModeOn(condition.mManager.getContext()));
|
|
||||||
}
|
|
||||||
View card = view.itemView.findViewById(R.id.content);
|
|
||||||
card.setTag(condition);
|
|
||||||
card.setOnClickListener(onClickListener);
|
|
||||||
view.icon.setImageIcon(condition.getIcon());
|
|
||||||
view.title.setText(condition.getTitle());
|
|
||||||
|
|
||||||
CharSequence[] actions = condition.getActions();
|
|
||||||
final boolean hasButtons = actions.length > 0;
|
|
||||||
setViewVisibility(view.itemView, R.id.buttonBar, hasButtons);
|
|
||||||
|
|
||||||
view.summary.setText(condition.getSummary());
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
Button button = (Button) view.itemView.findViewById(i == 0
|
|
||||||
? R.id.first_action : R.id.second_action);
|
|
||||||
if (actions.length > i) {
|
|
||||||
button.setVisibility(View.VISIBLE);
|
|
||||||
button.setText(actions[i]);
|
|
||||||
final int index = i;
|
|
||||||
button.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
Context context = v.getContext();
|
|
||||||
FeatureFactory.getFactory(context).getMetricsFeatureProvider()
|
|
||||||
.action(context, MetricsEvent.ACTION_SETTINGS_CONDITION_BUTTON,
|
|
||||||
condition.getMetricsConstant());
|
|
||||||
condition.onActionClick(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
button.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setViewVisibility(view.itemView, R.id.divider, !isLastItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setViewVisibility(View containerView, int viewId, boolean visible) {
|
|
||||||
View view = containerView.findViewById(viewId);
|
|
||||||
if (view != null) {
|
|
||||||
view.setVisibility(visible ? View.VISIBLE : View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -17,6 +17,10 @@ package com.android.settings.dashboard.suggestions;
|
|||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.service.settings.suggestions.Suggestion;
|
import android.service.settings.suggestions.Suggestion;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -24,37 +28,71 @@ import android.util.Log;
|
|||||||
import android.view.LayoutInflater;
|
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.LinearLayout;
|
||||||
|
|
||||||
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.dashboard.DashboardAdapter.DashboardItemHolder;
|
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
|
||||||
import com.android.settings.dashboard.DashboardAdapter.IconCache;
|
import com.android.settings.dashboard.DashboardAdapter.IconCache;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.Utils;
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||||
|
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixin;
|
import com.android.settingslib.suggestions.SuggestionControllerMixin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder> {
|
public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder> implements
|
||||||
|
LifecycleObserver, OnSaveInstanceState {
|
||||||
public static final String TAG = "SuggestionAdapter";
|
public static final String TAG = "SuggestionAdapter";
|
||||||
|
|
||||||
|
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
|
||||||
|
private static final String STATE_SUGGESTION_LIST = "suggestion_list";
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
private final List<Suggestion> mSuggestions;
|
|
||||||
private final IconCache mCache;
|
private final IconCache mCache;
|
||||||
private final List<String> mSuggestionsShownLogged;
|
private final ArrayList<String> mSuggestionsShownLogged;
|
||||||
|
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
|
||||||
private final SuggestionControllerMixin mSuggestionControllerMixin;
|
private final SuggestionControllerMixin mSuggestionControllerMixin;
|
||||||
|
private final Callback mCallback;
|
||||||
|
private final CardConfig mConfig;
|
||||||
|
|
||||||
|
private List<Suggestion> mSuggestions;
|
||||||
|
|
||||||
|
public interface Callback {
|
||||||
|
/**
|
||||||
|
* Called when the close button of the suggestion card is clicked.
|
||||||
|
*/
|
||||||
|
void onSuggestionClosed(Suggestion suggestion);
|
||||||
|
}
|
||||||
|
|
||||||
public SuggestionAdapter(Context context, SuggestionControllerMixin suggestionControllerMixin,
|
public SuggestionAdapter(Context context, SuggestionControllerMixin suggestionControllerMixin,
|
||||||
List<Suggestion> suggestions, List<String> suggestionsShownLogged) {
|
Bundle savedInstanceState, Callback callback, Lifecycle lifecycle) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mSuggestionControllerMixin = suggestionControllerMixin;
|
mSuggestionControllerMixin = suggestionControllerMixin;
|
||||||
mSuggestions = suggestions;
|
|
||||||
mSuggestionsShownLogged = suggestionsShownLogged;
|
|
||||||
mCache = new IconCache(context);
|
mCache = new IconCache(context);
|
||||||
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
||||||
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
||||||
|
mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
|
||||||
|
mCallback = callback;
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
|
||||||
|
mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
|
||||||
|
STATE_SUGGESTIONS_SHOWN_LOGGED);
|
||||||
|
} else {
|
||||||
|
mSuggestionsShownLogged = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lifecycle != null) {
|
||||||
|
lifecycle.addObserver(this);
|
||||||
|
}
|
||||||
|
mConfig = CardConfig.get(context);
|
||||||
|
|
||||||
setHasStableIds(true);
|
setHasStableIds(true);
|
||||||
}
|
}
|
||||||
@@ -67,20 +105,25 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(DashboardItemHolder holder, int position) {
|
public void onBindViewHolder(DashboardItemHolder holder, int position) {
|
||||||
bindSuggestion(holder, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindSuggestion(DashboardItemHolder holder, int position) {
|
|
||||||
final Suggestion suggestion = mSuggestions.get(position);
|
final Suggestion suggestion = mSuggestions.get(position);
|
||||||
final String id = suggestion.getId();
|
final String id = suggestion.getId();
|
||||||
|
final int suggestionCount = mSuggestions.size();
|
||||||
if (!mSuggestionsShownLogged.contains(id)) {
|
if (!mSuggestionsShownLogged.contains(id)) {
|
||||||
mMetricsFeatureProvider.action(
|
mMetricsFeatureProvider.action(
|
||||||
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, id);
|
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, id);
|
||||||
mSuggestionsShownLogged.add(id);
|
mSuggestionsShownLogged.add(id);
|
||||||
}
|
}
|
||||||
|
mConfig.setCardLayout(holder, suggestionCount, position);
|
||||||
holder.icon.setImageDrawable(mCache.getIcon(suggestion.getIcon()));
|
final Icon icon = suggestion.getIcon();
|
||||||
|
final Drawable drawable = mCache.getIcon(icon);
|
||||||
|
if (drawable != null && TextUtils.equals(icon.getResPackage(), mContext.getPackageName())) {
|
||||||
|
drawable.setTint(Utils.getColorAccent(mContext));
|
||||||
|
}
|
||||||
|
holder.icon.setImageDrawable(drawable);
|
||||||
holder.title.setText(suggestion.getTitle());
|
holder.title.setText(suggestion.getTitle());
|
||||||
|
holder.title.setSingleLine(suggestionCount == 1);
|
||||||
|
|
||||||
|
if (suggestionCount == 1) {
|
||||||
final CharSequence summary = suggestion.getSummary();
|
final CharSequence summary = suggestion.getSummary();
|
||||||
if (!TextUtils.isEmpty(summary)) {
|
if (!TextUtils.isEmpty(summary)) {
|
||||||
holder.summary.setText(summary);
|
holder.summary.setText(summary);
|
||||||
@@ -88,10 +131,23 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
|
|||||||
} else {
|
} else {
|
||||||
holder.summary.setVisibility(View.GONE);
|
holder.summary.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
final View divider = holder.itemView.findViewById(R.id.divider);
|
} else {
|
||||||
if (divider != null) {
|
// Do not show summary if there are more than 1 suggestions
|
||||||
divider.setVisibility(position < mSuggestions.size() - 1 ? View.VISIBLE : View.GONE);
|
holder.summary.setVisibility(View.GONE);
|
||||||
|
holder.title.setMaxLines(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final ImageView closeButton = holder.itemView.findViewById(R.id.close_button);
|
||||||
|
if (closeButton != null) {
|
||||||
|
closeButton.setOnClickListener(v -> {
|
||||||
|
mSuggestionFeatureProvider.dismissSuggestion(
|
||||||
|
mContext, mSuggestionControllerMixin, suggestion);
|
||||||
|
if (mCallback != null) {
|
||||||
|
mCallback.onSuggestionClosed(suggestion);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
View clickHandler = holder.itemView;
|
View clickHandler = holder.itemView;
|
||||||
// If a view with @android:id/primary is defined, use that as the click handler
|
// If a view with @android:id/primary is defined, use that as the click handler
|
||||||
// instead.
|
// instead.
|
||||||
@@ -144,7 +200,83 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removeSuggestion(Suggestion suggestion) {
|
public void removeSuggestion(Suggestion suggestion) {
|
||||||
|
final int position = mSuggestions.indexOf(suggestion);
|
||||||
mSuggestions.remove(suggestion);
|
mSuggestions.remove(suggestion);
|
||||||
notifyDataSetChanged();
|
notifyItemRemoved(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
if (mSuggestions != null) {
|
||||||
|
outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
|
||||||
|
new ArrayList<>(mSuggestions));
|
||||||
|
}
|
||||||
|
outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuggestions(List<Suggestion> suggestions) {
|
||||||
|
mSuggestions = suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Suggestion> getSuggestions() {
|
||||||
|
return mSuggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CardConfig {
|
||||||
|
// Card start/end margin
|
||||||
|
private final int mMarginInner;
|
||||||
|
private final int mMarginOuter;
|
||||||
|
// Card width for different numbers of cards
|
||||||
|
private final int mWidthSingleCard;
|
||||||
|
private final int mWidthTwoCards;
|
||||||
|
private final int mWidthMultipleCards;
|
||||||
|
// padding between icon and title
|
||||||
|
private final int mPaddingTitleTopSingleCard;
|
||||||
|
private final int mPaddingTitleTopMultipleCards;
|
||||||
|
|
||||||
|
private static CardConfig sConfig;
|
||||||
|
|
||||||
|
private CardConfig(Context context) {
|
||||||
|
final Resources res = context.getResources();
|
||||||
|
mMarginInner =
|
||||||
|
res.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin);
|
||||||
|
mMarginOuter =
|
||||||
|
res.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin);
|
||||||
|
mWidthSingleCard = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_one_card);
|
||||||
|
mWidthTwoCards = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_two_cards);
|
||||||
|
mWidthMultipleCards =
|
||||||
|
res.getDimensionPixelOffset(R.dimen.suggestion_card_width_multiple_cards);
|
||||||
|
mPaddingTitleTopSingleCard =
|
||||||
|
res.getDimensionPixelOffset(R.dimen.suggestion_card_title_padding_bottom_one_card);
|
||||||
|
mPaddingTitleTopMultipleCards = res.getDimensionPixelOffset(
|
||||||
|
R.dimen.suggestion_card_title_padding_bottom_multiple_cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CardConfig get(Context context) {
|
||||||
|
if (sConfig == null) {
|
||||||
|
sConfig = new CardConfig(context);
|
||||||
|
}
|
||||||
|
return sConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCardLayout(DashboardItemHolder holder, int suggestionCount,
|
||||||
|
int position) {
|
||||||
|
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
suggestionCount == 1
|
||||||
|
? mWidthSingleCard : suggestionCount == 2
|
||||||
|
? mWidthTwoCards : mWidthMultipleCards,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||||
|
if (suggestionCount == 1) {
|
||||||
|
params.setMarginStart(mMarginOuter);
|
||||||
|
params.setMarginEnd(mMarginOuter);
|
||||||
|
} else {
|
||||||
|
params.setMarginStart(
|
||||||
|
position == 0 ? mMarginOuter : mMarginInner);
|
||||||
|
params.setMarginEnd(position == suggestionCount - 1 ? mMarginOuter : 0);
|
||||||
|
}
|
||||||
|
holder.itemView.setLayoutParams(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,282 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.dashboard.suggestions;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.dashboard.DashboardAdapterV2.DashboardItemHolder;
|
|
||||||
import com.android.settings.dashboard.DashboardAdapterV2.IconCache;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settingslib.Utils;
|
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
|
||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixin;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class SuggestionAdapterV2 extends RecyclerView.Adapter<DashboardItemHolder> implements
|
|
||||||
LifecycleObserver, OnSaveInstanceState {
|
|
||||||
public static final String TAG = "SuggestionAdapterV2";
|
|
||||||
|
|
||||||
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
|
|
||||||
private static final String STATE_SUGGESTION_LIST = "suggestion_list";
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
|
||||||
private final IconCache mCache;
|
|
||||||
private final ArrayList<String> mSuggestionsShownLogged;
|
|
||||||
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
|
|
||||||
private final SuggestionControllerMixin mSuggestionControllerMixin;
|
|
||||||
private final Callback mCallback;
|
|
||||||
private final CardConfig mConfig;
|
|
||||||
|
|
||||||
private List<Suggestion> mSuggestions;
|
|
||||||
|
|
||||||
public interface Callback {
|
|
||||||
/**
|
|
||||||
* Called when the close button of the suggestion card is clicked.
|
|
||||||
*/
|
|
||||||
void onSuggestionClosed(Suggestion suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SuggestionAdapterV2(Context context, SuggestionControllerMixin suggestionControllerMixin,
|
|
||||||
Bundle savedInstanceState, Callback callback, Lifecycle lifecycle) {
|
|
||||||
mContext = context;
|
|
||||||
mSuggestionControllerMixin = suggestionControllerMixin;
|
|
||||||
mCache = new IconCache(context);
|
|
||||||
final FeatureFactory factory = FeatureFactory.getFactory(context);
|
|
||||||
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
|
|
||||||
mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
|
|
||||||
mCallback = callback;
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
mSuggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
|
|
||||||
mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
|
|
||||||
STATE_SUGGESTIONS_SHOWN_LOGGED);
|
|
||||||
} else {
|
|
||||||
mSuggestionsShownLogged = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lifecycle != null) {
|
|
||||||
lifecycle.addObserver(this);
|
|
||||||
}
|
|
||||||
mConfig = CardConfig.get(context);
|
|
||||||
|
|
||||||
setHasStableIds(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DashboardItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
return new DashboardItemHolder(LayoutInflater.from(parent.getContext()).inflate(
|
|
||||||
viewType, parent, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(DashboardItemHolder holder, int position) {
|
|
||||||
final Suggestion suggestion = mSuggestions.get(position);
|
|
||||||
final String id = suggestion.getId();
|
|
||||||
final int suggestionCount = mSuggestions.size();
|
|
||||||
if (!mSuggestionsShownLogged.contains(id)) {
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, id);
|
|
||||||
mSuggestionsShownLogged.add(id);
|
|
||||||
}
|
|
||||||
mConfig.setCardLayout(holder, suggestionCount, position);
|
|
||||||
final Icon icon = suggestion.getIcon();
|
|
||||||
final Drawable drawable = mCache.getIcon(icon);
|
|
||||||
if (drawable != null && TextUtils.equals(icon.getResPackage(), mContext.getPackageName())) {
|
|
||||||
drawable.setTint(Utils.getColorAccent(mContext));
|
|
||||||
}
|
|
||||||
holder.icon.setImageDrawable(drawable);
|
|
||||||
holder.title.setText(suggestion.getTitle());
|
|
||||||
holder.title.setSingleLine(suggestionCount == 1);
|
|
||||||
|
|
||||||
if (suggestionCount == 1) {
|
|
||||||
final CharSequence summary = suggestion.getSummary();
|
|
||||||
if (!TextUtils.isEmpty(summary)) {
|
|
||||||
holder.summary.setText(summary);
|
|
||||||
holder.summary.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
holder.summary.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Do not show summary if there are more than 1 suggestions
|
|
||||||
holder.summary.setVisibility(View.GONE);
|
|
||||||
holder.title.setMaxLines(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
final ImageView closeButton = holder.itemView.findViewById(R.id.close_button);
|
|
||||||
if (closeButton != null) {
|
|
||||||
closeButton.setOnClickListener(v -> {
|
|
||||||
mSuggestionFeatureProvider.dismissSuggestion(
|
|
||||||
mContext, mSuggestionControllerMixin, suggestion);
|
|
||||||
if (mCallback != null) {
|
|
||||||
mCallback.onSuggestionClosed(suggestion);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
View clickHandler = holder.itemView;
|
|
||||||
// If a view with @android:id/primary is defined, use that as the click handler
|
|
||||||
// instead.
|
|
||||||
final View primaryAction = holder.itemView.findViewById(android.R.id.primary);
|
|
||||||
if (primaryAction != null) {
|
|
||||||
clickHandler = primaryAction;
|
|
||||||
}
|
|
||||||
clickHandler.setOnClickListener(v -> {
|
|
||||||
mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_SETTINGS_SUGGESTION, id);
|
|
||||||
try {
|
|
||||||
suggestion.getPendingIntent().send();
|
|
||||||
mSuggestionControllerMixin.launchSuggestion(suggestion);
|
|
||||||
} catch (PendingIntent.CanceledException e) {
|
|
||||||
Log.w(TAG, "Failed to start suggestion " + suggestion.getTitle());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return Objects.hash(mSuggestions.get(position).getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position) {
|
|
||||||
final Suggestion suggestion = getSuggestion(position);
|
|
||||||
if ((suggestion.getFlags() & Suggestion.FLAG_HAS_BUTTON) != 0) {
|
|
||||||
return R.layout.suggestion_tile_with_button_v2;
|
|
||||||
} else {
|
|
||||||
return R.layout.suggestion_tile_v2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return mSuggestions.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Suggestion getSuggestion(int position) {
|
|
||||||
final long itemId = getItemId(position);
|
|
||||||
if (mSuggestions == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (Suggestion suggestion : mSuggestions) {
|
|
||||||
if (Objects.hash(suggestion.getId()) == itemId) {
|
|
||||||
return suggestion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeSuggestion(Suggestion suggestion) {
|
|
||||||
final int position = mSuggestions.indexOf(suggestion);
|
|
||||||
mSuggestions.remove(suggestion);
|
|
||||||
notifyItemRemoved(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
if (mSuggestions != null) {
|
|
||||||
outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
|
|
||||||
new ArrayList<>(mSuggestions));
|
|
||||||
}
|
|
||||||
outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSuggestions(List<Suggestion> suggestions) {
|
|
||||||
mSuggestions = suggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Suggestion> getSuggestions() {
|
|
||||||
return mSuggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class CardConfig {
|
|
||||||
// Card start/end margin
|
|
||||||
private final int mMarginInner;
|
|
||||||
private final int mMarginOuter;
|
|
||||||
// Card width for different numbers of cards
|
|
||||||
private final int mWidthSingleCard;
|
|
||||||
private final int mWidthTwoCards;
|
|
||||||
private final int mWidthMultipleCards;
|
|
||||||
// padding between icon and title
|
|
||||||
private final int mPaddingTitleTopSingleCard;
|
|
||||||
private final int mPaddingTitleTopMultipleCards;
|
|
||||||
|
|
||||||
private static CardConfig sConfig;
|
|
||||||
|
|
||||||
private CardConfig(Context context) {
|
|
||||||
final Resources res = context.getResources();
|
|
||||||
mMarginInner =
|
|
||||||
res.getDimensionPixelOffset(R.dimen.suggestion_card_inner_margin);
|
|
||||||
mMarginOuter =
|
|
||||||
res.getDimensionPixelOffset(R.dimen.suggestion_card_outer_margin);
|
|
||||||
mWidthSingleCard = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_one_card);
|
|
||||||
mWidthTwoCards = res.getDimensionPixelOffset(R.dimen.suggestion_card_width_two_cards);
|
|
||||||
mWidthMultipleCards =
|
|
||||||
res.getDimensionPixelOffset(R.dimen.suggestion_card_width_multiple_cards);
|
|
||||||
mPaddingTitleTopSingleCard =
|
|
||||||
res.getDimensionPixelOffset(R.dimen.suggestion_card_title_padding_bottom_one_card);
|
|
||||||
mPaddingTitleTopMultipleCards = res.getDimensionPixelOffset(
|
|
||||||
R.dimen.suggestion_card_title_padding_bottom_multiple_cards);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CardConfig get(Context context) {
|
|
||||||
if (sConfig == null) {
|
|
||||||
sConfig = new CardConfig(context);
|
|
||||||
}
|
|
||||||
return sConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCardLayout(DashboardItemHolder holder, int suggestionCount,
|
|
||||||
int position) {
|
|
||||||
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
|
||||||
suggestionCount == 1
|
|
||||||
? mWidthSingleCard : suggestionCount == 2
|
|
||||||
? mWidthTwoCards : mWidthMultipleCards,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
if (suggestionCount == 1) {
|
|
||||||
params.setMarginStart(mMarginOuter);
|
|
||||||
params.setMarginEnd(mMarginOuter);
|
|
||||||
} else {
|
|
||||||
params.setMarginStart(
|
|
||||||
position == 0 ? mMarginOuter : mMarginInner);
|
|
||||||
params.setMarginEnd(position == suggestionCount - 1 ? mMarginOuter : 0);
|
|
||||||
}
|
|
||||||
holder.itemView.setLayoutParams(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.dashboard.suggestions;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deprecated as a close button is provided to dismiss the suggestion.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class SuggestionDismissController extends ItemTouchHelper.SimpleCallback {
|
|
||||||
|
|
||||||
public interface Callback {
|
|
||||||
/**
|
|
||||||
* Returns suggestion tile data from the callback
|
|
||||||
*/
|
|
||||||
Suggestion getSuggestionAt(int position);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a suggestion is dismissed.
|
|
||||||
*/
|
|
||||||
void onSuggestionDismissed(Suggestion suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
|
|
||||||
private final SuggestionControllerMixin mSuggestionMixin;
|
|
||||||
private final Callback mCallback;
|
|
||||||
|
|
||||||
public SuggestionDismissController(Context context, RecyclerView recyclerView,
|
|
||||||
SuggestionControllerMixin suggestionMixin, Callback callback) {
|
|
||||||
super(0, ItemTouchHelper.START | ItemTouchHelper.END);
|
|
||||||
mSuggestionMixin = suggestionMixin;
|
|
||||||
mContext = context;
|
|
||||||
mSuggestionFeatureProvider = FeatureFactory.getFactory(context)
|
|
||||||
.getSuggestionFeatureProvider(context);
|
|
||||||
mCallback = callback;
|
|
||||||
final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(this);
|
|
||||||
itemTouchHelper.attachToRecyclerView(recyclerView);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
|
|
||||||
RecyclerView.ViewHolder target) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
|
||||||
final int layoutId = viewHolder.getItemViewType();
|
|
||||||
if (layoutId == R.layout.suggestion_tile
|
|
||||||
|| layoutId == R.layout.suggestion_tile_with_button) {
|
|
||||||
// Only return swipe direction for suggestion tiles. All other types are not swipeable.
|
|
||||||
return super.getSwipeDirs(recyclerView, viewHolder);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
|
||||||
if (mCallback == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final int position = viewHolder.getAdapterPosition();
|
|
||||||
final Suggestion suggestionV2 = mCallback.getSuggestionAt(position);
|
|
||||||
mSuggestionFeatureProvider.dismissSuggestion(mContext, mSuggestionMixin, suggestionV2);
|
|
||||||
mCallback.onSuggestionDismissed(suggestionV2);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,10 +16,8 @@
|
|||||||
package com.android.settings.dashboard;
|
package com.android.settings.dashboard;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.reset;
|
import static org.mockito.Mockito.reset;
|
||||||
@@ -30,14 +28,14 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.service.settings.suggestions.Suggestion;
|
import android.service.settings.suggestions.Suggestion;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SettingsActivity;
|
import com.android.settings.SettingsActivity;
|
||||||
@@ -47,7 +45,6 @@ import com.android.settings.dashboard.suggestions.SuggestionAdapter;
|
|||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
|
||||||
import com.android.settingslib.drawer.Tile;
|
import com.android.settingslib.drawer.Tile;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -58,6 +55,7 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -81,8 +79,6 @@ public class DashboardAdapterTest {
|
|||||||
private Resources mResources;
|
private Resources mResources;
|
||||||
private FakeFeatureFactory mFactory;
|
private FakeFeatureFactory mFactory;
|
||||||
private DashboardAdapter mDashboardAdapter;
|
private DashboardAdapter mDashboardAdapter;
|
||||||
private DashboardAdapter.SuggestionAndConditionHeaderHolder mSuggestionHolder;
|
|
||||||
private DashboardData.SuggestionConditionHeaderData mSuggestionHeaderData;
|
|
||||||
private List<Condition> mConditionList;
|
private List<Condition> mConditionList;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -98,30 +94,17 @@ public class DashboardAdapterTest {
|
|||||||
mConditionList = new ArrayList<>();
|
mConditionList = new ArrayList<>();
|
||||||
mConditionList.add(mCondition);
|
mConditionList.add(mCondition);
|
||||||
when(mCondition.shouldShow()).thenReturn(true);
|
when(mCondition.shouldShow()).thenReturn(true);
|
||||||
mDashboardAdapter = new DashboardAdapter(mContext, null, mConditionList, null, null);
|
mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
|
||||||
mSuggestionHeaderData = new DashboardData.SuggestionConditionHeaderData(mConditionList, 1);
|
mConditionList, null /* suggestionControllerMixin */, null /* lifecycle */);
|
||||||
when(mView.getTag()).thenReturn(mCondition);
|
when(mView.getTag()).thenReturn(mCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuggestionsLogs_nullSuggestionsList_shouldNotCrash() {
|
|
||||||
setupSuggestions(makeSuggestionsV2("pkg1", "pkg2", "pkg3", "pkg4", "pkg5"));
|
|
||||||
mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
|
|
||||||
|
|
||||||
// set suggestions to null
|
|
||||||
final DashboardData prevData = mDashboardAdapter.mDashboardData;
|
|
||||||
mDashboardAdapter.mDashboardData = new DashboardData.Builder(prevData)
|
|
||||||
.setSuggestions(null)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
mSuggestionHolder.itemView.callOnClick();
|
|
||||||
// no crash
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
|
public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
|
||||||
final DashboardAdapter adapter =
|
final DashboardAdapter adapter =
|
||||||
spy(new DashboardAdapter(mContext, null, null, null, null));
|
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
|
||||||
|
null /* conditions */, null /* suggestionControllerMixin */, null /*
|
||||||
|
lifecycle */));
|
||||||
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3");
|
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3");
|
||||||
adapter.setSuggestions(suggestions);
|
adapter.setSuggestions(suggestions);
|
||||||
|
|
||||||
@@ -130,18 +113,18 @@ public class DashboardAdapterTest {
|
|||||||
when(data.getContext()).thenReturn(mContext);
|
when(data.getContext()).thenReturn(mContext);
|
||||||
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
||||||
final View itemView = mock(View.class);
|
final View itemView = mock(View.class);
|
||||||
when(itemView.findViewById(R.id.data)).thenReturn(data);
|
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
||||||
final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
|
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
|
||||||
new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
|
final DashboardAdapter.SuggestionContainerHolder holder =
|
||||||
|
new DashboardAdapter.SuggestionContainerHolder(itemView);
|
||||||
|
|
||||||
adapter.onBindConditionAndSuggestion(
|
adapter.onBindSuggestion(holder, 0);
|
||||||
holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
|
|
||||||
|
|
||||||
final DashboardData dashboardData = adapter.mDashboardData;
|
final DashboardData dashboardData = adapter.mDashboardData;
|
||||||
reset(adapter); // clear interactions tracking
|
reset(adapter); // clear interactions tracking
|
||||||
|
|
||||||
final Suggestion suggestionToRemove = suggestions.get(1);
|
final Suggestion suggestionToRemove = suggestions.get(1);
|
||||||
adapter.onSuggestionDismissed(suggestionToRemove);
|
adapter.onSuggestionClosed(suggestionToRemove);
|
||||||
|
|
||||||
assertThat(adapter.mDashboardData).isEqualTo(dashboardData);
|
assertThat(adapter.mDashboardData).isEqualTo(dashboardData);
|
||||||
assertThat(suggestions.size()).isEqualTo(2);
|
assertThat(suggestions.size()).isEqualTo(2);
|
||||||
@@ -150,25 +133,25 @@ public class DashboardAdapterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuggestionDismissed_moreThanTwoSuggestions_defaultMode_shouldNotCrash() {
|
public void testSuggestionDismissed_moreThanTwoSuggestions_shouldNotCrash() {
|
||||||
final RecyclerView data = new RecyclerView(RuntimeEnvironment.application);
|
final RecyclerView data = new RecyclerView(RuntimeEnvironment.application);
|
||||||
final View itemView = mock(View.class);
|
final View itemView = mock(View.class);
|
||||||
when(itemView.findViewById(R.id.data)).thenReturn(data);
|
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
||||||
final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
|
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
|
||||||
new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
|
final DashboardAdapter.SuggestionContainerHolder holder =
|
||||||
|
new DashboardAdapter.SuggestionContainerHolder(itemView);
|
||||||
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3", "pkg4");
|
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3", "pkg4");
|
||||||
final DashboardAdapter adapter = spy(new DashboardAdapter(mContext, null /*savedInstance */,
|
final DashboardAdapter adapter = spy(new DashboardAdapter(mContext,
|
||||||
null /* conditions */,
|
null /*savedInstance */, null /* conditions */,
|
||||||
null /* suggestionControllerMixin */, null /* callback */));
|
null /* suggestionControllerMixin */,
|
||||||
|
null /* lifecycle */));
|
||||||
adapter.setSuggestions(suggestions);
|
adapter.setSuggestions(suggestions);
|
||||||
adapter.onBindConditionAndSuggestion(
|
adapter.onBindSuggestion(holder, 0);
|
||||||
holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
|
|
||||||
// default mode, only displaying 2 suggestions
|
|
||||||
|
|
||||||
adapter.onSuggestionDismissed(suggestions.get(1));
|
adapter.onSuggestionClosed(suggestions.get(1));
|
||||||
|
|
||||||
// verify operations that access the lists will not cause ConcurrentModificationException
|
// verify operations that access the lists will not cause ConcurrentModificationException
|
||||||
assertThat(holder.data.getAdapter().getItemCount()).isEqualTo(1);
|
assertThat(holder.data.getAdapter().getItemCount()).isEqualTo(3);
|
||||||
adapter.setSuggestions(suggestions);
|
adapter.setSuggestions(suggestions);
|
||||||
// should not crash
|
// should not crash
|
||||||
}
|
}
|
||||||
@@ -176,42 +159,25 @@ public class DashboardAdapterTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testSuggestionDismissed_onlySuggestion_updateDashboardData() {
|
public void testSuggestionDismissed_onlySuggestion_updateDashboardData() {
|
||||||
DashboardAdapter adapter =
|
DashboardAdapter adapter =
|
||||||
spy(new DashboardAdapter(mContext, null, null, null, null));
|
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
|
||||||
|
null /* conditions */, null /* suggestionControllerMixin */, null /*
|
||||||
|
lifecycle */));
|
||||||
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
|
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
|
||||||
adapter.setSuggestions(suggestions);
|
adapter.setSuggestions(suggestions);
|
||||||
final DashboardData dashboardData = adapter.mDashboardData;
|
final DashboardData dashboardData = adapter.mDashboardData;
|
||||||
reset(adapter); // clear interactions tracking
|
reset(adapter); // clear interactions tracking
|
||||||
|
|
||||||
adapter.onSuggestionDismissed(suggestions.get(0));
|
adapter.onSuggestionClosed(suggestions.get(0));
|
||||||
|
|
||||||
assertThat(adapter.mDashboardData).isNotEqualTo(dashboardData);
|
assertThat(adapter.mDashboardData).isNotEqualTo(dashboardData);
|
||||||
verify(adapter).notifyDashboardDataChanged(any());
|
verify(adapter).notifyDashboardDataChanged(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetCategories_iconTinted() {
|
public void testBindSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
|
||||||
TypedArray mockTypedArray = mock(TypedArray.class);
|
mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
|
||||||
doReturn(mockTypedArray).when(mContext).obtainStyledAttributes(any(int[].class));
|
null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
|
||||||
doReturn(0x89000000).when(mockTypedArray).getColor(anyInt(), anyInt());
|
|
||||||
|
|
||||||
final DashboardCategory category = new DashboardCategory();
|
|
||||||
final Icon mockIcon = mock(Icon.class);
|
|
||||||
final Tile tile = new Tile();
|
|
||||||
tile.isIconTintable = true;
|
|
||||||
tile.icon = mockIcon;
|
|
||||||
category.addTile(tile);
|
|
||||||
|
|
||||||
mDashboardAdapter.setCategory(category);
|
|
||||||
|
|
||||||
verify(mockIcon).setTint(eq(0x89000000));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBindConditionAndSuggestion_v2_shouldSetSuggestionAdapterAndNoCrash() {
|
|
||||||
mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null);
|
|
||||||
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
|
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
|
||||||
final DashboardCategory category = new DashboardCategory();
|
|
||||||
category.addTile(mock(Tile.class));
|
|
||||||
|
|
||||||
mDashboardAdapter.setSuggestions(suggestions);
|
mDashboardAdapter.setSuggestions(suggestions);
|
||||||
|
|
||||||
@@ -220,17 +186,88 @@ public class DashboardAdapterTest {
|
|||||||
when(data.getContext()).thenReturn(mContext);
|
when(data.getContext()).thenReturn(mContext);
|
||||||
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
||||||
final View itemView = mock(View.class);
|
final View itemView = mock(View.class);
|
||||||
when(itemView.findViewById(R.id.data)).thenReturn(data);
|
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
||||||
final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
|
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
|
||||||
new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
|
final DashboardAdapter.SuggestionContainerHolder holder =
|
||||||
|
new DashboardAdapter.SuggestionContainerHolder(itemView);
|
||||||
|
|
||||||
mDashboardAdapter.onBindConditionAndSuggestion(
|
mDashboardAdapter.onBindSuggestion(holder, 0);
|
||||||
holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
|
|
||||||
|
|
||||||
verify(data).setAdapter(any(SuggestionAdapter.class));
|
verify(data).setAdapter(any(SuggestionAdapter.class));
|
||||||
// should not crash
|
// should not crash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBindSuggestion_shouldSetSummary() {
|
||||||
|
mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
|
||||||
|
null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
|
||||||
|
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
|
||||||
|
|
||||||
|
mDashboardAdapter.setSuggestions(suggestions);
|
||||||
|
|
||||||
|
final RecyclerView data = mock(RecyclerView.class);
|
||||||
|
when(data.getResources()).thenReturn(mResources);
|
||||||
|
when(data.getContext()).thenReturn(mContext);
|
||||||
|
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
||||||
|
final View itemView = mock(View.class);
|
||||||
|
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
||||||
|
final TextView summary = mock(TextView.class);
|
||||||
|
when(itemView.findViewById(android.R.id.summary)).thenReturn(summary);
|
||||||
|
final DashboardAdapter.SuggestionContainerHolder holder =
|
||||||
|
new DashboardAdapter.SuggestionContainerHolder(itemView);
|
||||||
|
|
||||||
|
mDashboardAdapter.onBindSuggestion(holder, 0);
|
||||||
|
|
||||||
|
verify(summary).setText("1");
|
||||||
|
|
||||||
|
suggestions.addAll(makeSuggestionsV2("pkg2", "pkg3", "pkg4"));
|
||||||
|
mDashboardAdapter.setSuggestions(suggestions);
|
||||||
|
|
||||||
|
mDashboardAdapter.onBindSuggestion(holder, 0);
|
||||||
|
|
||||||
|
verify(summary).setText("4");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindTile_internalTile_shouldNotUseGenericBackgroundIcon() {
|
||||||
|
final Context context = RuntimeEnvironment.application;
|
||||||
|
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
|
||||||
|
final DashboardAdapter.DashboardItemHolder holder =
|
||||||
|
new DashboardAdapter.DashboardItemHolder(view);
|
||||||
|
final Tile tile = new Tile();
|
||||||
|
tile.icon = Icon.createWithResource(context, R.drawable.ic_settings);
|
||||||
|
final DashboardAdapter.IconCache iconCache = mock(DashboardAdapter.IconCache.class);
|
||||||
|
when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
|
||||||
|
|
||||||
|
mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
|
||||||
|
null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
|
||||||
|
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
|
||||||
|
mDashboardAdapter.onBindTile(holder, tile);
|
||||||
|
|
||||||
|
verify(iconCache, never()).updateIcon(any(Icon.class), any(Drawable.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindTile_externalTile_shouldNotUseGenericBackgroundIcon() {
|
||||||
|
final Context context = RuntimeEnvironment.application;
|
||||||
|
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
|
||||||
|
final DashboardAdapter.DashboardItemHolder holder =
|
||||||
|
new DashboardAdapter.DashboardItemHolder(view);
|
||||||
|
final Tile tile = new Tile();
|
||||||
|
tile.icon = mock(Icon.class);
|
||||||
|
when(tile.icon.getResPackage()).thenReturn("another.package");
|
||||||
|
|
||||||
|
final DashboardAdapter.IconCache iconCache = mock(DashboardAdapter.IconCache.class);
|
||||||
|
when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
|
||||||
|
|
||||||
|
mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
|
||||||
|
null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
|
||||||
|
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
|
||||||
|
mDashboardAdapter.onBindTile(holder, tile);
|
||||||
|
|
||||||
|
verify(iconCache).updateIcon(eq(tile.icon), any(RoundedHomepageIcon.class));
|
||||||
|
}
|
||||||
|
|
||||||
private List<Suggestion> makeSuggestionsV2(String... pkgNames) {
|
private List<Suggestion> makeSuggestionsV2(String... pkgNames) {
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
final List<Suggestion> suggestions = new ArrayList<>();
|
||||||
for (String pkgName : pkgNames) {
|
for (String pkgName : pkgNames) {
|
||||||
@@ -245,8 +282,5 @@ public class DashboardAdapterTest {
|
|||||||
private void setupSuggestions(List<Suggestion> suggestions) {
|
private void setupSuggestions(List<Suggestion> suggestions) {
|
||||||
final Context context = RuntimeEnvironment.application;
|
final Context context = RuntimeEnvironment.application;
|
||||||
mDashboardAdapter.setSuggestions(suggestions);
|
mDashboardAdapter.setSuggestions(suggestions);
|
||||||
mSuggestionHolder = new DashboardAdapter.SuggestionAndConditionHeaderHolder(
|
|
||||||
LayoutInflater.from(context).inflate(
|
|
||||||
R.layout.suggestion_condition_header, new RelativeLayout(context), true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,286 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.dashboard;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.Mockito.any;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.reset;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.util.DisplayMetrics;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.SettingsActivity;
|
|
||||||
import com.android.settings.TestConfig;
|
|
||||||
import com.android.settings.dashboard.conditional.Condition;
|
|
||||||
import com.android.settings.dashboard.suggestions.SuggestionAdapterV2;
|
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
|
||||||
import com.android.settingslib.drawer.Tile;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Answers;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
import org.robolectric.annotation.Config;
|
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
@Config(manifest = TestConfig.MANIFEST_PATH,
|
|
||||||
sdk = TestConfig.SDK_VERSION,
|
|
||||||
shadows = {
|
|
||||||
SettingsShadowResources.class,
|
|
||||||
SettingsShadowResources.SettingsShadowTheme.class,
|
|
||||||
})
|
|
||||||
public class DashboardAdapterV2Test {
|
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
|
||||||
private SettingsActivity mContext;
|
|
||||||
@Mock
|
|
||||||
private View mView;
|
|
||||||
@Mock
|
|
||||||
private Condition mCondition;
|
|
||||||
@Mock
|
|
||||||
private Resources mResources;
|
|
||||||
private FakeFeatureFactory mFactory;
|
|
||||||
private DashboardAdapterV2 mDashboardAdapter;
|
|
||||||
private List<Condition> mConditionList;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
mFactory = FakeFeatureFactory.setupForTest();
|
|
||||||
when(mFactory.dashboardFeatureProvider.shouldTintIcon()).thenReturn(true);
|
|
||||||
|
|
||||||
when(mContext.getResources()).thenReturn(mResources);
|
|
||||||
when(mResources.getQuantityString(any(int.class), any(int.class), any()))
|
|
||||||
.thenReturn("");
|
|
||||||
|
|
||||||
mConditionList = new ArrayList<>();
|
|
||||||
mConditionList.add(mCondition);
|
|
||||||
when(mCondition.shouldShow()).thenReturn(true);
|
|
||||||
mDashboardAdapter = new DashboardAdapterV2(mContext, null /* savedInstanceState */,
|
|
||||||
mConditionList, null /* suggestionControllerMixin */, null /* lifecycle */);
|
|
||||||
when(mView.getTag()).thenReturn(mCondition);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
|
|
||||||
final DashboardAdapterV2 adapter =
|
|
||||||
spy(new DashboardAdapterV2(mContext, null /* savedInstanceState */,
|
|
||||||
null /* conditions */, null /* suggestionControllerMixin */, null /*
|
|
||||||
lifecycle */));
|
|
||||||
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3");
|
|
||||||
adapter.setSuggestions(suggestions);
|
|
||||||
|
|
||||||
final RecyclerView data = mock(RecyclerView.class);
|
|
||||||
when(data.getResources()).thenReturn(mResources);
|
|
||||||
when(data.getContext()).thenReturn(mContext);
|
|
||||||
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
|
||||||
final View itemView = mock(View.class);
|
|
||||||
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
|
||||||
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
|
|
||||||
final DashboardAdapterV2.SuggestionContainerHolder holder =
|
|
||||||
new DashboardAdapterV2.SuggestionContainerHolder(itemView);
|
|
||||||
|
|
||||||
adapter.onBindSuggestion(holder, 0);
|
|
||||||
|
|
||||||
final DashboardDataV2 dashboardData = adapter.mDashboardData;
|
|
||||||
reset(adapter); // clear interactions tracking
|
|
||||||
|
|
||||||
final Suggestion suggestionToRemove = suggestions.get(1);
|
|
||||||
adapter.onSuggestionClosed(suggestionToRemove);
|
|
||||||
|
|
||||||
assertThat(adapter.mDashboardData).isEqualTo(dashboardData);
|
|
||||||
assertThat(suggestions.size()).isEqualTo(2);
|
|
||||||
assertThat(suggestions.contains(suggestionToRemove)).isFalse();
|
|
||||||
verify(adapter, never()).notifyDashboardDataChanged(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuggestionDismissed_moreThanTwoSuggestions_shouldNotCrash() {
|
|
||||||
final RecyclerView data = new RecyclerView(RuntimeEnvironment.application);
|
|
||||||
final View itemView = mock(View.class);
|
|
||||||
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
|
||||||
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
|
|
||||||
final DashboardAdapterV2.SuggestionContainerHolder holder =
|
|
||||||
new DashboardAdapterV2.SuggestionContainerHolder(itemView);
|
|
||||||
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3", "pkg4");
|
|
||||||
final DashboardAdapterV2 adapter = spy(new DashboardAdapterV2(mContext,
|
|
||||||
null /*savedInstance */, null /* conditions */,
|
|
||||||
null /* suggestionControllerMixin */,
|
|
||||||
null /* lifecycle */));
|
|
||||||
adapter.setSuggestions(suggestions);
|
|
||||||
adapter.onBindSuggestion(holder, 0);
|
|
||||||
|
|
||||||
adapter.onSuggestionClosed(suggestions.get(1));
|
|
||||||
|
|
||||||
// verify operations that access the lists will not cause ConcurrentModificationException
|
|
||||||
assertThat(holder.data.getAdapter().getItemCount()).isEqualTo(3);
|
|
||||||
adapter.setSuggestions(suggestions);
|
|
||||||
// should not crash
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuggestionDismissed_onlySuggestion_updateDashboardData() {
|
|
||||||
DashboardAdapterV2 adapter =
|
|
||||||
spy(new DashboardAdapterV2(mContext, null /* savedInstanceState */,
|
|
||||||
null /* conditions */, null /* suggestionControllerMixin */, null /*
|
|
||||||
lifecycle */));
|
|
||||||
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
|
|
||||||
adapter.setSuggestions(suggestions);
|
|
||||||
final DashboardDataV2 dashboardData = adapter.mDashboardData;
|
|
||||||
reset(adapter); // clear interactions tracking
|
|
||||||
|
|
||||||
adapter.onSuggestionClosed(suggestions.get(0));
|
|
||||||
|
|
||||||
assertThat(adapter.mDashboardData).isNotEqualTo(dashboardData);
|
|
||||||
verify(adapter).notifyDashboardDataChanged(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBindSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
|
|
||||||
mDashboardAdapter = new DashboardAdapterV2(mContext, null /* savedInstanceState */,
|
|
||||||
null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
|
|
||||||
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
|
|
||||||
|
|
||||||
mDashboardAdapter.setSuggestions(suggestions);
|
|
||||||
|
|
||||||
final RecyclerView data = mock(RecyclerView.class);
|
|
||||||
when(data.getResources()).thenReturn(mResources);
|
|
||||||
when(data.getContext()).thenReturn(mContext);
|
|
||||||
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
|
||||||
final View itemView = mock(View.class);
|
|
||||||
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
|
||||||
when(itemView.findViewById(android.R.id.summary)).thenReturn(mock(TextView.class));
|
|
||||||
final DashboardAdapterV2.SuggestionContainerHolder holder =
|
|
||||||
new DashboardAdapterV2.SuggestionContainerHolder(itemView);
|
|
||||||
|
|
||||||
mDashboardAdapter.onBindSuggestion(holder, 0);
|
|
||||||
|
|
||||||
verify(data).setAdapter(any(SuggestionAdapterV2.class));
|
|
||||||
// should not crash
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBindSuggestion_shouldSetSummary() {
|
|
||||||
mDashboardAdapter = new DashboardAdapterV2(mContext, null /* savedInstanceState */,
|
|
||||||
null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
|
|
||||||
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
|
|
||||||
|
|
||||||
mDashboardAdapter.setSuggestions(suggestions);
|
|
||||||
|
|
||||||
final RecyclerView data = mock(RecyclerView.class);
|
|
||||||
when(data.getResources()).thenReturn(mResources);
|
|
||||||
when(data.getContext()).thenReturn(mContext);
|
|
||||||
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
|
|
||||||
final View itemView = mock(View.class);
|
|
||||||
when(itemView.findViewById(R.id.suggestion_list)).thenReturn(data);
|
|
||||||
final TextView summary = mock(TextView.class);
|
|
||||||
when(itemView.findViewById(android.R.id.summary)).thenReturn(summary);
|
|
||||||
final DashboardAdapterV2.SuggestionContainerHolder holder =
|
|
||||||
new DashboardAdapterV2.SuggestionContainerHolder(itemView);
|
|
||||||
|
|
||||||
mDashboardAdapter.onBindSuggestion(holder, 0);
|
|
||||||
|
|
||||||
verify(summary).setText("1");
|
|
||||||
|
|
||||||
suggestions.addAll(makeSuggestionsV2("pkg2", "pkg3", "pkg4"));
|
|
||||||
mDashboardAdapter.setSuggestions(suggestions);
|
|
||||||
|
|
||||||
mDashboardAdapter.onBindSuggestion(holder, 0);
|
|
||||||
|
|
||||||
verify(summary).setText("4");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindTile_internalTile_shouldNotUseGenericBackgroundIcon() {
|
|
||||||
final Context context = RuntimeEnvironment.application;
|
|
||||||
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
|
|
||||||
final DashboardAdapterV2.DashboardItemHolder holder =
|
|
||||||
new DashboardAdapterV2.DashboardItemHolder(view);
|
|
||||||
final Tile tile = new Tile();
|
|
||||||
tile.icon = Icon.createWithResource(context, R.drawable.ic_settings);
|
|
||||||
final DashboardAdapterV2.IconCache iconCache = mock(DashboardAdapterV2.IconCache.class);
|
|
||||||
when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
|
|
||||||
|
|
||||||
mDashboardAdapter = new DashboardAdapterV2(context, null /* savedInstanceState */,
|
|
||||||
null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
|
|
||||||
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
|
|
||||||
mDashboardAdapter.onBindTile(holder, tile);
|
|
||||||
|
|
||||||
verify(iconCache, never()).updateIcon(any(Icon.class), any(Drawable.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindTile_externalTile_shouldNotUseGenericBackgroundIcon() {
|
|
||||||
final Context context = RuntimeEnvironment.application;
|
|
||||||
final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
|
|
||||||
final DashboardAdapterV2.DashboardItemHolder holder =
|
|
||||||
new DashboardAdapterV2.DashboardItemHolder(view);
|
|
||||||
final Tile tile = new Tile();
|
|
||||||
tile.icon = mock(Icon.class);
|
|
||||||
when(tile.icon.getResPackage()).thenReturn("another.package");
|
|
||||||
|
|
||||||
final DashboardAdapterV2.IconCache iconCache = mock(DashboardAdapterV2.IconCache.class);
|
|
||||||
when(iconCache.getIcon(tile.icon)).thenReturn(context.getDrawable(R.drawable.ic_settings));
|
|
||||||
|
|
||||||
mDashboardAdapter = new DashboardAdapterV2(context, null /* savedInstanceState */,
|
|
||||||
null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
|
|
||||||
ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
|
|
||||||
mDashboardAdapter.onBindTile(holder, tile);
|
|
||||||
|
|
||||||
verify(iconCache).updateIcon(eq(tile.icon), any(RoundedHomepageIcon.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Suggestion> makeSuggestionsV2(String... pkgNames) {
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
for (String pkgName : pkgNames) {
|
|
||||||
final Suggestion suggestion = new Suggestion.Builder(pkgName)
|
|
||||||
.setPendingIntent(mock(PendingIntent.class))
|
|
||||||
.build();
|
|
||||||
suggestions.add(suggestion);
|
|
||||||
}
|
|
||||||
return suggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupSuggestions(List<Suggestion> suggestions) {
|
|
||||||
final Context context = RuntimeEnvironment.application;
|
|
||||||
mDashboardAdapter.setSuggestions(suggestions);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -17,8 +17,9 @@
|
|||||||
package com.android.settings.dashboard;
|
package com.android.settings.dashboard;
|
||||||
|
|
||||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_CONTAINER;
|
import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_CONTAINER;
|
||||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONDITION_FOOTER;
|
import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_FOOTER;
|
||||||
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONTAINER;
|
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONTAINER;
|
||||||
|
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONDITION_DIVIDER;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -103,14 +104,14 @@ public class DashboardDataTest {
|
|||||||
.setConditions(oneItemConditions)
|
.setConditions(oneItemConditions)
|
||||||
.setCategory(mDashboardCategory)
|
.setCategory(mDashboardCategory)
|
||||||
.setSuggestions(suggestions)
|
.setSuggestions(suggestions)
|
||||||
.setSuggestionConditionMode(DashboardData.HEADER_MODE_FULLY_EXPANDED)
|
.setConditionExpanded(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
mDashboardDataWithTwoConditions = new DashboardData.Builder()
|
mDashboardDataWithTwoConditions = new DashboardData.Builder()
|
||||||
.setConditions(twoItemsConditions)
|
.setConditions(twoItemsConditions)
|
||||||
.setCategory(mDashboardCategory)
|
.setCategory(mDashboardCategory)
|
||||||
.setSuggestions(suggestions)
|
.setSuggestions(suggestions)
|
||||||
.setSuggestionConditionMode(DashboardData.HEADER_MODE_FULLY_EXPANDED)
|
.setConditionExpanded(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
mDashboardDataWithNoItems = new DashboardData.Builder()
|
mDashboardDataWithNoItems = new DashboardData.Builder()
|
||||||
@@ -124,21 +125,23 @@ public class DashboardDataTest {
|
|||||||
public void testBuildItemsData_shouldSetstableId() {
|
public void testBuildItemsData_shouldSetstableId() {
|
||||||
final List<DashboardData.Item> items = mDashboardDataWithOneConditions.getItemList();
|
final List<DashboardData.Item> items = mDashboardDataWithOneConditions.getItemList();
|
||||||
|
|
||||||
// Header, suggestion, condition, footer, 1 tile
|
// suggestion, seperator, condition, footer, 1 tile
|
||||||
assertThat(items).hasSize(4);
|
assertThat(items).hasSize(5);
|
||||||
|
|
||||||
assertThat(items.get(0).id).isEqualTo(STABLE_ID_SUGGESTION_CONTAINER);
|
assertThat(items.get(0).id).isEqualTo(STABLE_ID_SUGGESTION_CONTAINER);
|
||||||
assertThat(items.get(1).id).isEqualTo(STABLE_ID_CONDITION_CONTAINER);
|
assertThat(items.get(1).id).isEqualTo(STABLE_ID_SUGGESTION_CONDITION_DIVIDER);
|
||||||
assertThat(items.get(2).id).isEqualTo(STABLE_ID_SUGGESTION_CONDITION_FOOTER);
|
assertThat(items.get(2).id).isEqualTo(STABLE_ID_CONDITION_CONTAINER);
|
||||||
assertThat(items.get(3).id).isEqualTo(Objects.hash(mTestCategoryTile.title));
|
assertThat(items.get(3).id).isEqualTo(STABLE_ID_CONDITION_FOOTER);
|
||||||
|
assertThat(items.get(4).id).isEqualTo(Objects.hash(mTestCategoryTile.title));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBuildItemsData_containsAllData() {
|
public void testBuildItemsData_containsAllData() {
|
||||||
final Object[] expectedObjects = {
|
final Object[] expectedObjects = {
|
||||||
mDashboardDataWithOneConditions.getSuggestions(),
|
mDashboardDataWithOneConditions.getSuggestions(),
|
||||||
|
null /* divider */,
|
||||||
mDashboardDataWithOneConditions.getConditions(),
|
mDashboardDataWithOneConditions.getConditions(),
|
||||||
null, mTestCategoryTile};
|
null /* footer */, mTestCategoryTile};
|
||||||
final int expectedSize = expectedObjects.length;
|
final int expectedSize = expectedObjects.length;
|
||||||
|
|
||||||
assertThat(mDashboardDataWithOneConditions.getItemList()).hasSize(expectedSize);
|
assertThat(mDashboardDataWithOneConditions.getItemList()).hasSize(expectedSize);
|
||||||
@@ -147,14 +150,13 @@ public class DashboardDataTest {
|
|||||||
final Object item = mDashboardDataWithOneConditions.getItemEntityByPosition(i);
|
final Object item = mDashboardDataWithOneConditions.getItemEntityByPosition(i);
|
||||||
if (item instanceof List) {
|
if (item instanceof List) {
|
||||||
assertThat(item).isEqualTo(expectedObjects[i]);
|
assertThat(item).isEqualTo(expectedObjects[i]);
|
||||||
} else if (item instanceof DashboardData.SuggestionConditionHeaderData) {
|
} else if (item instanceof DashboardData.ConditionHeaderData) {
|
||||||
DashboardData.SuggestionConditionHeaderData i1 =
|
DashboardData.ConditionHeaderData i1 =
|
||||||
(DashboardData.SuggestionConditionHeaderData) item;
|
(DashboardData.ConditionHeaderData) item;
|
||||||
DashboardData.SuggestionConditionHeaderData i2 =
|
DashboardData.ConditionHeaderData i2 =
|
||||||
(DashboardData.SuggestionConditionHeaderData) expectedObjects[i];
|
(DashboardData.ConditionHeaderData) expectedObjects[i];
|
||||||
assertThat(i1.title).isEqualTo(i2.title);
|
assertThat(i1.title).isEqualTo(i2.title);
|
||||||
assertThat(i1.conditionCount).isEqualTo(i2.conditionCount);
|
assertThat(i1.conditionCount).isEqualTo(i2.conditionCount);
|
||||||
assertThat(i1.hiddenSuggestionCount).isEqualTo(i2.hiddenSuggestionCount);
|
|
||||||
} else {
|
} else {
|
||||||
assertThat(item).isSameAs(expectedObjects[i]);
|
assertThat(item).isSameAs(expectedObjects[i]);
|
||||||
}
|
}
|
||||||
@@ -209,10 +211,10 @@ public class DashboardDataTest {
|
|||||||
public void testDiffUtil_InsertOneCondition_ResultDataOneChanged() {
|
public void testDiffUtil_InsertOneCondition_ResultDataOneChanged() {
|
||||||
//Build testResultData
|
//Build testResultData
|
||||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
||||||
// Item in position 2 is the condition container containing the list of conditions, which
|
// Item in position 3 is the condition container containing the list of conditions, which
|
||||||
// gets 1 more item
|
// gets 1 more item
|
||||||
testResultData.add(new ListUpdateResult.ResultData(
|
testResultData.add(new ListUpdateResult.ResultData(
|
||||||
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 1, 1));
|
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 2, 1));
|
||||||
|
|
||||||
testDiffUtil(mDashboardDataWithOneConditions,
|
testDiffUtil(mDashboardDataWithOneConditions,
|
||||||
mDashboardDataWithTwoConditions, testResultData);
|
mDashboardDataWithTwoConditions, testResultData);
|
||||||
@@ -222,10 +224,11 @@ public class DashboardDataTest {
|
|||||||
public void testDiffUtil_RemoveOneSuggestion_causeItemRemoveAndChange() {
|
public void testDiffUtil_RemoveOneSuggestion_causeItemRemoveAndChange() {
|
||||||
//Build testResultData
|
//Build testResultData
|
||||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
||||||
|
// removed suggestion and the divider
|
||||||
testResultData.add(new ListUpdateResult.ResultData(
|
testResultData.add(new ListUpdateResult.ResultData(
|
||||||
ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 1));
|
ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 2));
|
||||||
testResultData.add(new ListUpdateResult.ResultData(
|
testResultData.add(new ListUpdateResult.ResultData(
|
||||||
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 1, 1));
|
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 2, 1));
|
||||||
// Build DashboardData
|
// Build DashboardData
|
||||||
final List<Condition> oneItemConditions = new ArrayList<>();
|
final List<Condition> oneItemConditions = new ArrayList<>();
|
||||||
when(mTestCondition.shouldShow()).thenReturn(true);
|
when(mTestCondition.shouldShow()).thenReturn(true);
|
||||||
@@ -237,13 +240,13 @@ public class DashboardDataTest {
|
|||||||
.setConditions(oneItemConditions)
|
.setConditions(oneItemConditions)
|
||||||
.setCategory(mDashboardCategory)
|
.setCategory(mDashboardCategory)
|
||||||
.setSuggestions(suggestions)
|
.setSuggestions(suggestions)
|
||||||
.setSuggestionConditionMode(DashboardData.HEADER_MODE_DEFAULT)
|
.setConditionExpanded(false)
|
||||||
.build();
|
.build();
|
||||||
final DashboardData newData = new DashboardData.Builder()
|
final DashboardData newData = new DashboardData.Builder()
|
||||||
.setConditions(oneItemConditions)
|
.setConditions(oneItemConditions)
|
||||||
.setSuggestions(null)
|
.setSuggestions(null)
|
||||||
.setCategory(mDashboardCategory)
|
.setCategory(mDashboardCategory)
|
||||||
.setSuggestionConditionMode(DashboardData.HEADER_MODE_DEFAULT)
|
.setConditionExpanded(false)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
testDiffUtil(oldData, newData, testResultData);
|
testDiffUtil(oldData, newData, testResultData);
|
||||||
@@ -254,7 +257,7 @@ public class DashboardDataTest {
|
|||||||
//Build testResultData
|
//Build testResultData
|
||||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
||||||
testResultData.add(new ListUpdateResult.ResultData(
|
testResultData.add(new ListUpdateResult.ResultData(
|
||||||
ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 4));
|
ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 5));
|
||||||
|
|
||||||
testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
|
testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
|
||||||
}
|
}
|
||||||
|
@@ -164,10 +164,4 @@ public class DashboardSummaryTest {
|
|||||||
mSummary.onCategoriesChanged();
|
mSummary.onCategoriesChanged();
|
||||||
verify(mSummary).rebuildUI();
|
verify(mSummary).rebuildUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onSuggestionDismissed_shouldNotRebuildUI() {
|
|
||||||
mSummary.onSuggestionDismissed(mock(Suggestion.class));
|
|
||||||
verify(mSummary, never()).rebuildUI();
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -15,6 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.settings.dashboard.conditional;
|
package com.android.settings.dashboard.conditional;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -22,13 +27,9 @@ import android.view.View;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
import com.android.settings.TestConfig;
|
import com.android.settings.TestConfig;
|
||||||
import com.android.settings.dashboard.DashboardAdapter;
|
import com.android.settings.dashboard.DashboardAdapter;
|
||||||
import com.android.settings.dashboard.DashboardData;
|
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -38,9 +39,8 @@ import org.mockito.MockitoAnnotations;
|
|||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import java.util.ArrayList;
|
||||||
import static org.mockito.Mockito.verify;
|
import java.util.List;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||||
@@ -70,35 +70,23 @@ public class ConditionAdapterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getItemCount_notFullyExpanded_shouldReturn0() {
|
public void getItemCount_notExpanded_shouldReturn0() {
|
||||||
mConditionAdapter = new ConditionAdapter(
|
mConditionAdapter = new ConditionAdapter(mContext, mOneCondition, false);
|
||||||
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);
|
assertThat(mConditionAdapter.getItemCount()).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getItemCount_fullyExpanded_shouldReturnListSize() {
|
public void getItemCount_expanded_shouldReturnListSize() {
|
||||||
mConditionAdapter = new ConditionAdapter(
|
mConditionAdapter = new ConditionAdapter(mContext, mOneCondition, true);
|
||||||
mContext, mOneCondition, DashboardData.HEADER_MODE_FULLY_EXPANDED);
|
|
||||||
assertThat(mConditionAdapter.getItemCount()).isEqualTo(1);
|
assertThat(mConditionAdapter.getItemCount()).isEqualTo(1);
|
||||||
|
|
||||||
mConditionAdapter = new ConditionAdapter(
|
mConditionAdapter = new ConditionAdapter(mContext, mTwoConditions, true);
|
||||||
mContext, mTwoConditions, DashboardData.HEADER_MODE_FULLY_EXPANDED);
|
|
||||||
assertThat(mConditionAdapter.getItemCount()).isEqualTo(2);
|
assertThat(mConditionAdapter.getItemCount()).isEqualTo(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getItemViewType_shouldReturnConditionTile() {
|
public void getItemViewType_shouldReturnConditionTile() {
|
||||||
mConditionAdapter = new ConditionAdapter(
|
mConditionAdapter = new ConditionAdapter(mContext, mTwoConditions, true);
|
||||||
mContext, mTwoConditions, DashboardData.HEADER_MODE_FULLY_EXPANDED);
|
|
||||||
assertThat(mConditionAdapter.getItemViewType(0)).isEqualTo(R.layout.condition_tile);
|
assertThat(mConditionAdapter.getItemViewType(0)).isEqualTo(R.layout.condition_tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +96,7 @@ public class ConditionAdapterTest {
|
|||||||
R.layout.condition_tile, new LinearLayout(mContext), true);
|
R.layout.condition_tile, new LinearLayout(mContext), true);
|
||||||
final DashboardAdapter.DashboardItemHolder viewHolder =
|
final DashboardAdapter.DashboardItemHolder viewHolder =
|
||||||
new DashboardAdapter.DashboardItemHolder(view);
|
new DashboardAdapter.DashboardItemHolder(view);
|
||||||
mConditionAdapter = new ConditionAdapter(
|
mConditionAdapter = new ConditionAdapter(mContext, mOneCondition, true);
|
||||||
mContext, mOneCondition, DashboardData.HEADER_MODE_SUGGESTION_EXPANDED);
|
|
||||||
|
|
||||||
mConditionAdapter.onBindViewHolder(viewHolder, 0);
|
mConditionAdapter.onBindViewHolder(viewHolder, 0);
|
||||||
final View card = view.findViewById(R.id.content);
|
final View card = view.findViewById(R.id.content);
|
||||||
@@ -122,8 +109,7 @@ public class ConditionAdapterTest {
|
|||||||
R.layout.condition_tile, new LinearLayout(mContext), true);
|
R.layout.condition_tile, new LinearLayout(mContext), true);
|
||||||
final DashboardAdapter.DashboardItemHolder viewHolder =
|
final DashboardAdapter.DashboardItemHolder viewHolder =
|
||||||
new DashboardAdapter.DashboardItemHolder(view);
|
new DashboardAdapter.DashboardItemHolder(view);
|
||||||
mConditionAdapter = new ConditionAdapter(
|
mConditionAdapter = new ConditionAdapter(mContext, mOneCondition, true);
|
||||||
mContext, mOneCondition, DashboardData.HEADER_MODE_SUGGESTION_EXPANDED);
|
|
||||||
|
|
||||||
mConditionAdapter.onBindViewHolder(viewHolder, 0);
|
mConditionAdapter.onBindViewHolder(viewHolder, 0);
|
||||||
final View card = view.findViewById(R.id.content);
|
final View card = view.findViewById(R.id.content);
|
||||||
@@ -138,8 +124,7 @@ public class ConditionAdapterTest {
|
|||||||
R.layout.condition_tile, new LinearLayout(mContext), true);
|
R.layout.condition_tile, new LinearLayout(mContext), true);
|
||||||
final DashboardAdapter.DashboardItemHolder viewHolder =
|
final DashboardAdapter.DashboardItemHolder viewHolder =
|
||||||
new DashboardAdapter.DashboardItemHolder(view);
|
new DashboardAdapter.DashboardItemHolder(view);
|
||||||
mConditionAdapter = new ConditionAdapter(
|
mConditionAdapter = new ConditionAdapter(mContext, mOneCondition, true);
|
||||||
mContext, mOneCondition, DashboardData.HEADER_MODE_SUGGESTION_EXPANDED);
|
|
||||||
mConditionAdapter.addDismissHandling(recyclerView);
|
mConditionAdapter.addDismissHandling(recyclerView);
|
||||||
|
|
||||||
// do not bind viewholder to simulate the null condition scenario
|
// do not bind viewholder to simulate the null condition scenario
|
||||||
|
@@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.dashboard.conditional;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.TestConfig;
|
|
||||||
import com.android.settings.dashboard.DashboardAdapterV2;
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
|
|
||||||
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 java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
|
||||||
public class ConditionAdapterV2Test {
|
|
||||||
@Mock
|
|
||||||
private Condition mCondition1;
|
|
||||||
@Mock
|
|
||||||
private Condition mCondition2;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private ConditionAdapterV2 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_notExpanded_shouldReturn0() {
|
|
||||||
mConditionAdapter = new ConditionAdapterV2(mContext, mOneCondition, false);
|
|
||||||
assertThat(mConditionAdapter.getItemCount()).isEqualTo(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemCount_expanded_shouldReturnListSize() {
|
|
||||||
mConditionAdapter = new ConditionAdapterV2(mContext, mOneCondition, true);
|
|
||||||
assertThat(mConditionAdapter.getItemCount()).isEqualTo(1);
|
|
||||||
|
|
||||||
mConditionAdapter = new ConditionAdapterV2(mContext, mTwoConditions, true);
|
|
||||||
assertThat(mConditionAdapter.getItemCount()).isEqualTo(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemViewType_shouldReturnConditionTile() {
|
|
||||||
mConditionAdapter = new ConditionAdapterV2(mContext, mTwoConditions, true);
|
|
||||||
assertThat(mConditionAdapter.getItemViewType(0)).isEqualTo(R.layout.condition_tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_shouldSetListener() {
|
|
||||||
final View view = LayoutInflater.from(mContext).inflate(
|
|
||||||
R.layout.condition_tile, new LinearLayout(mContext), true);
|
|
||||||
final DashboardAdapterV2.DashboardItemHolder viewHolder =
|
|
||||||
new DashboardAdapterV2.DashboardItemHolder(view);
|
|
||||||
mConditionAdapter = new ConditionAdapterV2(mContext, mOneCondition, true);
|
|
||||||
|
|
||||||
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 LinearLayout(mContext), true);
|
|
||||||
final DashboardAdapterV2.DashboardItemHolder viewHolder =
|
|
||||||
new DashboardAdapterV2.DashboardItemHolder(view);
|
|
||||||
mConditionAdapter = new ConditionAdapterV2(mContext, mOneCondition, true);
|
|
||||||
|
|
||||||
mConditionAdapter.onBindViewHolder(viewHolder, 0);
|
|
||||||
final View card = view.findViewById(R.id.content);
|
|
||||||
card.performClick();
|
|
||||||
verify(mCondition1).onPrimaryClick();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onSwiped_nullCondition_shouldNotCrash() {
|
|
||||||
final RecyclerView recyclerView = new RecyclerView(mContext);
|
|
||||||
final View view = LayoutInflater.from(mContext).inflate(
|
|
||||||
R.layout.condition_tile, new LinearLayout(mContext), true);
|
|
||||||
final DashboardAdapterV2.DashboardItemHolder viewHolder =
|
|
||||||
new DashboardAdapterV2.DashboardItemHolder(view);
|
|
||||||
mConditionAdapter = new ConditionAdapterV2(mContext, mOneCondition, true);
|
|
||||||
mConditionAdapter.addDismissHandling(recyclerView);
|
|
||||||
|
|
||||||
// do not bind viewholder to simulate the null condition scenario
|
|
||||||
mConditionAdapter.mSwipeCallback.onSwiped(viewHolder, 0);
|
|
||||||
// no crash
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -16,12 +16,19 @@
|
|||||||
package com.android.settings.dashboard.suggestions;
|
package com.android.settings.dashboard.suggestions;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.service.settings.suggestions.Suggestion;
|
import android.service.settings.suggestions.Suggestion;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -46,6 +53,7 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -87,18 +95,19 @@ public class SuggestionAdapterTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getItemCount_shouldReturnListSize() {
|
public void getItemCount_shouldReturnListSize() {
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
||||||
mOneSuggestion, new ArrayList<>());
|
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||||
|
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
||||||
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
|
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
|
||||||
|
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
mSuggestionAdapter.setSuggestions(mTwoSuggestions);
|
||||||
mTwoSuggestions, new ArrayList<>());
|
|
||||||
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
|
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getItemViewType_shouldReturnSuggestionTile() {
|
public void getItemViewType_shouldReturnSuggestionTile() {
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
||||||
mOneSuggestion, new ArrayList<>());
|
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||||
|
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
||||||
assertThat(mSuggestionAdapter.getItemViewType(0))
|
assertThat(mSuggestionAdapter.getItemViewType(0))
|
||||||
.isEqualTo(R.layout.suggestion_tile);
|
.isEqualTo(R.layout.suggestion_tile);
|
||||||
}
|
}
|
||||||
@@ -112,7 +121,8 @@ public class SuggestionAdapterTest {
|
|||||||
.setSummary("456")
|
.setSummary("456")
|
||||||
.build());
|
.build());
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
||||||
suggestions, new ArrayList<>());
|
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||||
|
mSuggestionAdapter.setSuggestions(suggestions);
|
||||||
|
|
||||||
assertThat(mSuggestionAdapter.getItemViewType(0))
|
assertThat(mSuggestionAdapter.getItemViewType(0))
|
||||||
.isEqualTo(R.layout.suggestion_tile_with_button);
|
.isEqualTo(R.layout.suggestion_tile_with_button);
|
||||||
@@ -124,7 +134,8 @@ public class SuggestionAdapterTest {
|
|||||||
R.layout.suggestion_tile, new LinearLayout(mContext), true));
|
R.layout.suggestion_tile, new LinearLayout(mContext), true));
|
||||||
mSuggestionHolder = new DashboardAdapter.DashboardItemHolder(view);
|
mSuggestionHolder = new DashboardAdapter.DashboardItemHolder(view);
|
||||||
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
||||||
mOneSuggestion, new ArrayList<>());
|
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||||
|
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
||||||
|
|
||||||
// Bind twice
|
// Bind twice
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||||
@@ -149,6 +160,31 @@ public class SuggestionAdapterTest {
|
|||||||
verify(suggestions.get(0).getPendingIntent()).send();
|
verify(suggestions.get(0).getPendingIntent()).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindViewHolder_hasButton_buttonShouldHandleClick()
|
||||||
|
throws PendingIntent.CanceledException {
|
||||||
|
final List<Suggestion> suggestions = new ArrayList<>();
|
||||||
|
final PendingIntent pendingIntent = mock(PendingIntent.class);
|
||||||
|
suggestions.add(new Suggestion.Builder("id")
|
||||||
|
.setFlags(Suggestion.FLAG_HAS_BUTTON)
|
||||||
|
.setTitle("123")
|
||||||
|
.setSummary("456")
|
||||||
|
.setPendingIntent(pendingIntent)
|
||||||
|
.build());
|
||||||
|
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
|
||||||
|
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||||
|
mSuggestionAdapter.setSuggestions(suggestions);
|
||||||
|
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
||||||
|
new FrameLayout(RuntimeEnvironment.application),
|
||||||
|
mSuggestionAdapter.getItemViewType(0));
|
||||||
|
|
||||||
|
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||||
|
mSuggestionHolder.itemView.findViewById(android.R.id.primary).performClick();
|
||||||
|
|
||||||
|
verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
|
||||||
|
verify(pendingIntent).send();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getSuggestions_shouldReturnSuggestionWhenMatch() {
|
public void getSuggestions_shouldReturnSuggestionWhenMatch() {
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
||||||
@@ -157,9 +193,92 @@ public class SuggestionAdapterTest {
|
|||||||
assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
|
assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindViewHolder_closeButtonShouldHandleClick()
|
||||||
|
throws PendingIntent.CanceledException {
|
||||||
|
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
||||||
|
final SuggestionAdapter.Callback callback = mock(SuggestionAdapter.Callback.class);
|
||||||
|
mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
|
||||||
|
null /* savedInstanceState */, callback, null /* lifecycle */);
|
||||||
|
mSuggestionAdapter.setSuggestions(suggestions);
|
||||||
|
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
||||||
|
new FrameLayout(RuntimeEnvironment.application),
|
||||||
|
mSuggestionAdapter.getItemViewType(0));
|
||||||
|
|
||||||
|
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||||
|
mSuggestionHolder.itemView.findViewById(R.id.close_button).performClick();
|
||||||
|
|
||||||
|
final Suggestion suggestion = suggestions.get(0);
|
||||||
|
verify(mFeatureFactory.suggestionsFeatureProvider).dismissSuggestion(
|
||||||
|
mActivity, mSuggestionControllerMixin, suggestion);
|
||||||
|
verify(callback).onSuggestionClosed(suggestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindViewHolder_differentPackage_shouldNotTintIcon()
|
||||||
|
throws PendingIntent.CanceledException {
|
||||||
|
final Icon icon = mock(Icon.class);
|
||||||
|
when(icon.getResPackage()).thenReturn("pkg1");
|
||||||
|
when(mActivity.getPackageName()).thenReturn("pkg2");
|
||||||
|
final Suggestion suggestion = new Suggestion.Builder("pkg1")
|
||||||
|
.setPendingIntent(mock(PendingIntent.class))
|
||||||
|
.setIcon(icon)
|
||||||
|
.build();
|
||||||
|
final List<Suggestion> suggestions = new ArrayList<>();
|
||||||
|
suggestions.add(suggestion);
|
||||||
|
mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
|
||||||
|
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||||
|
mSuggestionAdapter.setSuggestions(suggestions);
|
||||||
|
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
||||||
|
new FrameLayout(RuntimeEnvironment.application),
|
||||||
|
mSuggestionAdapter.getItemViewType(0));
|
||||||
|
DashboardAdapter.IconCache cache = mock(DashboardAdapter.IconCache.class);
|
||||||
|
final Drawable drawable = mock(Drawable.class);
|
||||||
|
when(cache.getIcon(icon)).thenReturn(drawable);
|
||||||
|
ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
|
||||||
|
|
||||||
|
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||||
|
|
||||||
|
verify(drawable, never()).setTint(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindViewHolder_samePackage_shouldTintIcon()
|
||||||
|
throws PendingIntent.CanceledException {
|
||||||
|
final Icon icon = mock(Icon.class);
|
||||||
|
final String packageName = "pkg1";
|
||||||
|
when(icon.getResPackage()).thenReturn(packageName);
|
||||||
|
when(mActivity.getPackageName()).thenReturn(packageName);
|
||||||
|
final Suggestion suggestion = new Suggestion.Builder(packageName)
|
||||||
|
.setPendingIntent(mock(PendingIntent.class))
|
||||||
|
.setIcon(icon)
|
||||||
|
.build();
|
||||||
|
final List<Suggestion> suggestions = new ArrayList<>();
|
||||||
|
suggestions.add(suggestion);
|
||||||
|
mSuggestionAdapter = new SuggestionAdapter(mActivity, mSuggestionControllerMixin,
|
||||||
|
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||||
|
mSuggestionAdapter.setSuggestions(suggestions);
|
||||||
|
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
||||||
|
new FrameLayout(RuntimeEnvironment.application),
|
||||||
|
mSuggestionAdapter.getItemViewType(0));
|
||||||
|
DashboardAdapter.IconCache cache = mock(DashboardAdapter.IconCache.class);
|
||||||
|
final Drawable drawable = mock(Drawable.class);
|
||||||
|
when(cache.getIcon(icon)).thenReturn(drawable);
|
||||||
|
ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
|
||||||
|
TypedArray typedArray = mock(TypedArray.class);
|
||||||
|
final int colorAccent = 1234;
|
||||||
|
when(mActivity.obtainStyledAttributes(any())).thenReturn(typedArray);
|
||||||
|
when(typedArray.getColor(anyInt(), anyInt())).thenReturn(colorAccent);
|
||||||
|
|
||||||
|
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
||||||
|
|
||||||
|
verify(drawable).setTint(colorAccent);
|
||||||
|
}
|
||||||
|
|
||||||
private void setupSuggestions(Context context, List<Suggestion> suggestions) {
|
private void setupSuggestions(Context context, List<Suggestion> suggestions) {
|
||||||
mSuggestionAdapter = new SuggestionAdapter(context, mSuggestionControllerMixin,
|
mSuggestionAdapter = new SuggestionAdapter(context, mSuggestionControllerMixin,
|
||||||
suggestions, new ArrayList<>());
|
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
||||||
|
mSuggestionAdapter.setSuggestions(suggestions);
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
new FrameLayout(RuntimeEnvironment.application),
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
mSuggestionAdapter.getItemViewType(0));
|
||||||
|
@@ -1,297 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.dashboard.suggestions;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.Icon;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import com.android.internal.logging.nano.MetricsProto;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.SettingsActivity;
|
|
||||||
import com.android.settings.TestConfig;
|
|
||||||
import com.android.settings.dashboard.DashboardAdapterV2;
|
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixin;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Answers;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
import org.robolectric.annotation.Config;
|
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
|
||||||
public class SuggestionAdapterV2Test {
|
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
|
||||||
private SettingsActivity mActivity;
|
|
||||||
@Mock
|
|
||||||
private SuggestionControllerMixin mSuggestionControllerMixin;
|
|
||||||
private FakeFeatureFactory mFeatureFactory;
|
|
||||||
private Context mContext;
|
|
||||||
private SuggestionAdapterV2 mSuggestionAdapter;
|
|
||||||
private DashboardAdapterV2.DashboardItemHolder mSuggestionHolder;
|
|
||||||
private List<Suggestion> mOneSuggestion;
|
|
||||||
private List<Suggestion> mTwoSuggestions;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
mContext = RuntimeEnvironment.application;
|
|
||||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
|
||||||
|
|
||||||
final Suggestion suggestion1 = new Suggestion.Builder("id1")
|
|
||||||
.setTitle("Test suggestion 1")
|
|
||||||
.build();
|
|
||||||
final Suggestion suggestion2 = new Suggestion.Builder("id2")
|
|
||||||
.setTitle("Test suggestion 2")
|
|
||||||
.build();
|
|
||||||
mOneSuggestion = new ArrayList<>();
|
|
||||||
mOneSuggestion.add(suggestion1);
|
|
||||||
mTwoSuggestions = new ArrayList<>();
|
|
||||||
mTwoSuggestions.add(suggestion1);
|
|
||||||
mTwoSuggestions.add(suggestion2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemCount_shouldReturnListSize() {
|
|
||||||
mSuggestionAdapter = new SuggestionAdapterV2(mContext, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
|
||||||
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
|
|
||||||
|
|
||||||
mSuggestionAdapter.setSuggestions(mTwoSuggestions);
|
|
||||||
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemViewType_shouldReturnSuggestionTile() {
|
|
||||||
mSuggestionAdapter = new SuggestionAdapterV2(mContext, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
|
||||||
assertThat(mSuggestionAdapter.getItemViewType(0))
|
|
||||||
.isEqualTo(R.layout.suggestion_tile_v2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getItemType_hasButton_shouldReturnSuggestionWithButton() {
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
suggestions.add(new Suggestion.Builder("id")
|
|
||||||
.setFlags(Suggestion.FLAG_HAS_BUTTON)
|
|
||||||
.setTitle("123")
|
|
||||||
.setSummary("456")
|
|
||||||
.build());
|
|
||||||
mSuggestionAdapter = new SuggestionAdapterV2(mContext, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
|
|
||||||
assertThat(mSuggestionAdapter.getItemViewType(0))
|
|
||||||
.isEqualTo(R.layout.suggestion_tile_with_button_v2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_shouldLog() {
|
|
||||||
final View view = spy(LayoutInflater.from(mContext).inflate(
|
|
||||||
R.layout.suggestion_tile, new LinearLayout(mContext), true));
|
|
||||||
mSuggestionHolder = new DashboardAdapterV2.DashboardItemHolder(view);
|
|
||||||
mSuggestionAdapter = new SuggestionAdapterV2(mContext, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(mOneSuggestion);
|
|
||||||
|
|
||||||
// Bind twice
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
|
|
||||||
// Log once
|
|
||||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
|
||||||
mContext, MetricsProto.MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
|
|
||||||
mOneSuggestion.get(0).getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_itemViewShouldHandleClick()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
setupSuggestions(mActivity, suggestions);
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
mSuggestionHolder.itemView.performClick();
|
|
||||||
|
|
||||||
verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
|
|
||||||
verify(suggestions.get(0).getPendingIntent()).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_hasButton_buttonShouldHandleClick()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
final PendingIntent pendingIntent = mock(PendingIntent.class);
|
|
||||||
suggestions.add(new Suggestion.Builder("id")
|
|
||||||
.setFlags(Suggestion.FLAG_HAS_BUTTON)
|
|
||||||
.setTitle("123")
|
|
||||||
.setSummary("456")
|
|
||||||
.setPendingIntent(pendingIntent)
|
|
||||||
.build());
|
|
||||||
mSuggestionAdapter = new SuggestionAdapterV2(mContext, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
mSuggestionHolder.itemView.findViewById(android.R.id.primary).performClick();
|
|
||||||
|
|
||||||
verify(mSuggestionControllerMixin).launchSuggestion(suggestions.get(0));
|
|
||||||
verify(pendingIntent).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getSuggestions_shouldReturnSuggestionWhenMatch() {
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
setupSuggestions(mActivity, suggestions);
|
|
||||||
|
|
||||||
assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_closeButtonShouldHandleClick()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final List<Suggestion> suggestions = makeSuggestions("pkg1");
|
|
||||||
final SuggestionAdapterV2.Callback callback = mock(SuggestionAdapterV2.Callback.class);
|
|
||||||
mSuggestionAdapter = new SuggestionAdapterV2(mActivity, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, callback, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
mSuggestionHolder.itemView.findViewById(R.id.close_button).performClick();
|
|
||||||
|
|
||||||
final Suggestion suggestion = suggestions.get(0);
|
|
||||||
verify(mFeatureFactory.suggestionsFeatureProvider).dismissSuggestion(
|
|
||||||
mActivity, mSuggestionControllerMixin, suggestion);
|
|
||||||
verify(callback).onSuggestionClosed(suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_differentPackage_shouldNotTintIcon()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final Icon icon = mock(Icon.class);
|
|
||||||
when(icon.getResPackage()).thenReturn("pkg1");
|
|
||||||
when(mActivity.getPackageName()).thenReturn("pkg2");
|
|
||||||
final Suggestion suggestion = new Suggestion.Builder("pkg1")
|
|
||||||
.setPendingIntent(mock(PendingIntent.class))
|
|
||||||
.setIcon(icon)
|
|
||||||
.build();
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
suggestions.add(suggestion);
|
|
||||||
mSuggestionAdapter = new SuggestionAdapterV2(mActivity, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
|
||||||
DashboardAdapterV2.IconCache cache = mock(DashboardAdapterV2.IconCache.class);
|
|
||||||
final Drawable drawable = mock(Drawable.class);
|
|
||||||
when(cache.getIcon(icon)).thenReturn(drawable);
|
|
||||||
ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
|
|
||||||
verify(drawable, never()).setTint(anyInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onBindViewHolder_samePackage_shouldTintIcon()
|
|
||||||
throws PendingIntent.CanceledException {
|
|
||||||
final Icon icon = mock(Icon.class);
|
|
||||||
final String packageName = "pkg1";
|
|
||||||
when(icon.getResPackage()).thenReturn(packageName);
|
|
||||||
when(mActivity.getPackageName()).thenReturn(packageName);
|
|
||||||
final Suggestion suggestion = new Suggestion.Builder(packageName)
|
|
||||||
.setPendingIntent(mock(PendingIntent.class))
|
|
||||||
.setIcon(icon)
|
|
||||||
.build();
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
suggestions.add(suggestion);
|
|
||||||
mSuggestionAdapter = new SuggestionAdapterV2(mActivity, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
|
||||||
DashboardAdapterV2.IconCache cache = mock(DashboardAdapterV2.IconCache.class);
|
|
||||||
final Drawable drawable = mock(Drawable.class);
|
|
||||||
when(cache.getIcon(icon)).thenReturn(drawable);
|
|
||||||
ReflectionHelpers.setField(mSuggestionAdapter, "mCache", cache);
|
|
||||||
TypedArray typedArray = mock(TypedArray.class);
|
|
||||||
final int colorAccent = 1234;
|
|
||||||
when(mActivity.obtainStyledAttributes(any())).thenReturn(typedArray);
|
|
||||||
when(typedArray.getColor(anyInt(), anyInt())).thenReturn(colorAccent);
|
|
||||||
|
|
||||||
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
|
|
||||||
|
|
||||||
verify(drawable).setTint(colorAccent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupSuggestions(Context context, List<Suggestion> suggestions) {
|
|
||||||
mSuggestionAdapter = new SuggestionAdapterV2(context, mSuggestionControllerMixin,
|
|
||||||
null /* savedInstanceState */, null /* callback */, null /* lifecycle */);
|
|
||||||
mSuggestionAdapter.setSuggestions(suggestions);
|
|
||||||
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
|
|
||||||
new FrameLayout(RuntimeEnvironment.application),
|
|
||||||
mSuggestionAdapter.getItemViewType(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Suggestion> makeSuggestions(String... pkgNames) {
|
|
||||||
final List<Suggestion> suggestions = new ArrayList<>();
|
|
||||||
for (String pkgName : pkgNames) {
|
|
||||||
final Suggestion suggestion = new Suggestion.Builder(pkgName)
|
|
||||||
.setPendingIntent(mock(PendingIntent.class))
|
|
||||||
.build();
|
|
||||||
suggestions.add(suggestion);
|
|
||||||
}
|
|
||||||
return suggestions;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.dashboard.suggestions;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
import static org.mockito.ArgumentMatchers.nullable;
|
|
||||||
import static org.mockito.Matchers.anyInt;
|
|
||||||
import static org.mockito.Matchers.eq;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.service.settings.suggestions.Suggestion;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.TestConfig;
|
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
import com.android.settingslib.suggestions.SuggestionControllerMixin;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Answers;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.robolectric.annotation.Config;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
|
||||||
public class SuggestionDismissControllerTest {
|
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
|
||||||
private Context mContext;
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
|
||||||
private RecyclerView mRecyclerView;
|
|
||||||
@Mock
|
|
||||||
private SuggestionControllerMixin mSuggestionControllerMixin;
|
|
||||||
@Mock
|
|
||||||
private SuggestionDismissController.Callback mCallback;
|
|
||||||
|
|
||||||
private FakeFeatureFactory mFactory;
|
|
||||||
private SuggestionDismissController mController;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
mFactory = FakeFeatureFactory.setupForTest();
|
|
||||||
|
|
||||||
when(mRecyclerView.getResources().getDimension(anyInt())).thenReturn(50F);
|
|
||||||
|
|
||||||
mController = new SuggestionDismissController(mContext, mRecyclerView,
|
|
||||||
mSuggestionControllerMixin, mCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onMove_alwaysReturnTrue() {
|
|
||||||
assertThat(mController.onMove(null, null, null)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getSwipeDirs_isSuggestionTile_shouldReturnDirection() {
|
|
||||||
final RecyclerView.ViewHolder vh = mock(RecyclerView.ViewHolder.class);
|
|
||||||
when(vh.getItemViewType()).thenReturn(R.layout.suggestion_tile);
|
|
||||||
|
|
||||||
assertThat(mController.getSwipeDirs(mRecyclerView, vh))
|
|
||||||
.isEqualTo(ItemTouchHelper.START | ItemTouchHelper.END);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getSwipeDirs_isSuggestionTileCard_shouldReturnDirection() {
|
|
||||||
final RecyclerView.ViewHolder vh = mock(RecyclerView.ViewHolder.class);
|
|
||||||
when(vh.getItemViewType()).thenReturn(R.layout.suggestion_tile_with_button);
|
|
||||||
|
|
||||||
assertThat(mController.getSwipeDirs(mRecyclerView, vh))
|
|
||||||
.isEqualTo(ItemTouchHelper.START | ItemTouchHelper.END);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getSwipeDirs_isNotSuggestionTile_shouldReturn0() {
|
|
||||||
final RecyclerView.ViewHolder vh = mock(RecyclerView.ViewHolder.class);
|
|
||||||
when(vh.getItemViewType()).thenReturn(R.layout.condition_tile);
|
|
||||||
|
|
||||||
assertThat(mController.getSwipeDirs(mRecyclerView, vh))
|
|
||||||
.isEqualTo(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onSwiped_shouldTriggerDismissSuggestion() {
|
|
||||||
final RecyclerView.ViewHolder vh = mock(RecyclerView.ViewHolder.class);
|
|
||||||
when(mCallback.getSuggestionAt(anyInt())).thenReturn(
|
|
||||||
new Suggestion.Builder("id").build());
|
|
||||||
|
|
||||||
mController.onSwiped(vh, ItemTouchHelper.START);
|
|
||||||
|
|
||||||
verify(mFactory.suggestionsFeatureProvider).dismissSuggestion(
|
|
||||||
eq(mContext), eq(mSuggestionControllerMixin), nullable(Suggestion.class));
|
|
||||||
verify(mCallback).onSuggestionDismissed(nullable(Suggestion.class));
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user