Snap for 4994968 from 76e91c8cdd to qt-release

Change-Id: Ife829128becef12f89eb10cc76b02b7dd3e1e86f
This commit is contained in:
android-build-team Robot
2018-09-06 03:08:14 +00:00
30 changed files with 943 additions and 483 deletions

View File

@@ -29,22 +29,6 @@
column="9"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" app:barPredictionColor=&quot;@color/material_empty_color_light&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/layout/battery_history_chart.xml"
line="38"
column="9"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
@@ -2425,7 +2409,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rXC/strings.xml"
line="2559"
line="2524"
column="168"/>
</issue>
@@ -2441,7 +2425,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rAU/strings.xml"
line="2560"
line="2525"
column="64"/>
</issue>
@@ -2457,7 +2441,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rCA/strings.xml"
line="2560"
line="2525"
column="64"/>
</issue>
@@ -2473,7 +2457,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rGB/strings.xml"
line="2560"
line="2525"
column="64"/>
</issue>
@@ -2489,7 +2473,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rIN/strings.xml"
line="2560"
line="2525"
column="64"/>
</issue>
@@ -2505,7 +2489,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/strings.xml"
line="5898"
line="5865"
column="36"/>
</issue>
@@ -2701,6 +2685,22 @@
column="49"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" &lt;item name=&quot;android:navigationBarDividerColor&quot;>#1f000000&lt;/item>"
errorLine2=" ^">
<location
file="res/values/themes.xml"
line="175"
column="56"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"

View File

@@ -0,0 +1,25 @@
<?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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_checked="true"
android:color="?android:attr/colorAccent" />
<item
android:state_checked="false"
android:color="?android:attr/textColorPrimary" />
</selector>

View File

@@ -1,24 +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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:attr/colorAccent"
android:pathData="M4,13L4,13c0.55,0 1,-0.45 1,-1v0c0,-0.55 -0.45,-1 -1,-1h0c-0.55,0 -1,0.45 -1,1v0C3,12.55 3.45,13 4,13zM4,17L4,17c0.55,0 1,-0.45 1,-1v0c0,-0.55 -0.45,-1 -1,-1h0c-0.55,0 -1,0.45 -1,1v0C3,16.55 3.45,17 4,17zM4,9L4,9c0.55,0 1,-0.45 1,-1v0c0,-0.55 -0.45,-1 -1,-1h0C3.45,7 3,7.45 3,8v0C3,8.55 3.45,9 4,9zM7,13h14v-2H7V13zM7,17h14v-2H7V17zM7,7v2h14V7H7z"/>
</vector>

View File

@@ -0,0 +1,26 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3,12L3,3h9,9v9,9L12,21 3,21ZM19,12L19,5L12,5 5,5v7,7h7,7zM7,16c0,-0.55 0.45,-1 1,-1 0.55,0 1,0.45 1,1 0,0.55 -0.45,1 -1,1 -0.55,0 -1,-0.45 -1,-1zM11,16c0,-0.55 1.35,-1 3,-1 1.65,0 3,0.45 3,1 0,0.55 -1.35,1 -3,1 -1.65,0 -3,-0.45 -3,-1zM7,12c0,-0.55 0.45,-1 1,-1 0.55,0 1,0.45 1,1 0,0.55 -0.45,1 -1,1 -0.55,0 -1,-0.45 -1,-1zM11,12c0,-0.55 1.35,-1 3,-1 1.65,0 3,0.45 3,1 0,0.55 -1.35,1 -3,1 -1.65,0 -3,-0.45 -3,-1zM7,8C7,7.45 7.45,7 8,7 8.55,7 9,7.45 9,8 9,8.55 8.55,9 8,9 7.45,9 7,8.55 7,8ZM11,8c0,-0.55 1.35,-1 3,-1 1.65,0 3,0.45 3,1 0,0.55 -1.35,1 -3,1 -1.65,0 -3,-0.45 -3,-1z"
android:fillColor="#000000"/>
</vector>

View File

@@ -0,0 +1,28 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M13.85,22.24h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.52c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.89 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73C7.91,5.2 8.17,5.05 8.43,4.91L8.7,3.01c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.7 14.59,22.24 13.85,22.24zM13.32,20.72c0,0.01 0,0.01 0,0.02V20.72zM10.68,20.69v0.02C10.69,20.72 10.69,20.7 10.68,20.69zM10.62,20.24h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96L15.6,7.28c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.69 8.84,6.94 8.38,7.3L7.93,7.62L5.55,6.67L4.16,9.06l2.03,1.58L6.12,11.2c-0.03,0.26 -0.06,0.53 -0.06,0.79s0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.24zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.7l0.01,0.02C5.78,17.72 5.77,17.7 5.77,17.7zM3.93,9.47L3.93,9.47L3.93,9.47zM18.22,6.26c0,0.01 0.01,0.02 0.01,0.02L18.22,6.26zM5.79,6.24L5.78,6.26C5.78,6.26 5.79,6.26 5.79,6.24zM13.31,3.27c0,0.01 0,0.01 0,0.02V3.27zM10.69,3.25v0.02C10.69,3.26 10.69,3.26 10.69,3.25z"/>
<path
android:fillColor="#FF000000"
android:pathData="M13.56,10.44l-1.56,-3.44l-1.56,3.44l-3.44,1.56l3.44,1.56l1.56,3.44l1.56,-3.44l3.44,-1.56z"/>
</vector>

View File

@@ -0,0 +1,29 @@
<!--
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.
-->
<androidx.cardview.widget.CardView
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"
app:cardUseCompatPadding="true"
app:cardElevation="@dimen/condition_card_elevation"
app:cardCornerRadius="@dimen/suggestion_card_corner_radius">
<include layout="@layout/condition_tile"/>
</androidx.cardview.widget.CardView>

View File

@@ -15,62 +15,8 @@
limitations under the License.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/card_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<RelativeLayout
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:minHeight="@dimen/homepage_bottomsheet_height"
app:layout_behavior="@string/bottom_sheet_behavior"
app:behavior_peekHeight="@dimen/homepage_bottomsheet_height">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/bottom_area"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/search_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_search_floating_24dp"
app:backgroundTint="@android:color/white"
app:layout_anchor="@id/bar"/>
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bar"
android:layout_width="match_parent"
android:layout_height="@dimen/homepage_bottombar_height"
android:layout_alignParentTop="true"
android:layout_marginTop="@dimen/homepage_bottombar_top_margin"
android:clickable="true"
app:fabAttached="true"
app:fabAlignmentMode="end"
app:fabCradleDiameter="@dimen/homepage_bottombar_fab_cradle"
app:navigationIcon="@drawable/ic_list_24dp"
style="@style/Widget.MaterialComponents.BottomAppBar"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<include layout="@layout/search_bar"
android:visibility="invisible"/>
<FrameLayout
android:id="@+id/bottom_sheet_fragment"
android:layout_below="@id/bottom_area"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
android:layout_height="wrap_content" />

View File

@@ -15,8 +15,38 @@
limitations under the License.
-->
<FrameLayout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/main_content"
android:layout_height="match_parent"
android:layout_width="match_parent"/>
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="0dp"
android:layout_marginStart="0dp"
android:background="?android:attr/windowBackground"
android:layout_alignParentBottom="true"
app:itemIconTint="@color/bottom_navigation_colors"
app:itemTextColor="@color/bottom_navigation_colors"
app:menu="@menu/home_bottom_navigation" />
<FrameLayout
android:id="@id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/bottom_nav" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/search_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_search_24dp"
android:layout_margin="24dp"
android:layout_above="@id/bottom_nav"
android:layout_alignParentRight="true"
app:backgroundTint="?android:attr/colorAccent"
app:tint="@android:color/white" />
</RelativeLayout>

View File

@@ -0,0 +1,27 @@
<?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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/homepage_personal_settings"
android:icon="@drawable/ic_settings_personal"
android:title="@string/homepage_personal_settings" />
<item
android:id="@+id/homepage_all_settings"
android:icon="@drawable/ic_settings_all"
android:title="@string/homepage_all_settings" />
</menu>

View File

@@ -312,6 +312,9 @@
<dimen name="suggestion_card_button_top_margin">16dp</dimen>
<dimen name="suggestion_card_button_bottom_margin">18dp</dimen>
<!-- Condition cards size and padding -->
<dimen name="condition_card_elevation">2dp</dimen>
<!-- Padding for the reset screens -->
<dimen name="reset_checkbox_padding_end">8dp</dimen>
<dimen name="reset_checkbox_title_padding_top">12dp</dimen>

View File

@@ -9471,9 +9471,6 @@
<!-- [CHAR LIMIT=25] Title of developer tile to toggle winscope trace -->
<string name="winscope_trace_quick_settings_title">Winscope Trace</string>
<!-- Template for formatting country and language. eg Canada - French [CHAR LIMIT=NONE]-->
<string name="support_country_format"><xliff:g id="country" example="Canada">%1$s</xliff:g> - <xliff:g id="language" example="French">%2$s</xliff:g></string>
<!-- [CHAR LIMIT=60] Title of work profile setting page -->
<string name="managed_profile_settings_title">Work profile settings</string>
<!-- [CHAR LIMIT=60] The preference title for enabling cross-profile remote contact search -->
@@ -10078,4 +10075,10 @@
<!-- UI debug setting: Enable High Refresh Rate virtual panel [CHAR LIMIT=50] -->
<string name="high_frequency_display_device_summary">Enable Virtual High Frequency Panel</string>
<!-- Homepage bottom menu. Title for display all Settings [CHAR LIMIT=30] -->
<string name="homepage_all_settings">All Settings</string>
<!-- Homepage bottom menu. Title for display personalized Settings [CHAR LIMIT=30] -->
<string name="homepage_personal_settings">Your Settings</string>
</resources>

View File

@@ -167,6 +167,7 @@
<style name="Theme.Settings.Home" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Color names copied from frameworks/base/core/res/res/values/themes_device_defaults.xml -->
<!-- TODO (b/113964702) : fix theme color -->
<item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
<item name="colorPrimaryDark">@*android:color/primary_dark_device_default_settings_light</item>
<item name="colorAccent">@*android:color/accent_device_default_light</item>

View File

@@ -22,15 +22,24 @@ import android.os.Bundle;
import android.util.FeatureFlagUtils;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.homepage.HomepageFragment;
import com.android.settings.dashboard.DashboardSummary;
import com.android.settings.homepage.PersonalSettingsFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.SearchFeatureProvider;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
public class SettingsHomepageActivity extends SettingsBaseActivity {
private static final String ALL_SETTINGS_TAG = "all_settings";
private static final String PERSONAL_SETTINGS_TAG = "personal_settings";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -42,24 +51,52 @@ public class SettingsHomepageActivity extends SettingsBaseActivity {
finish();
return;
}
setContentView(R.layout.settings_homepage_container);
if (savedInstanceState == null) {
switchToFragment(this, R.id.main_content, HomepageFragment.class.getName());
}
final FloatingActionButton searchButton = findViewById(R.id.search_fab);
FeatureFactory.getFactory(this).getSearchFeatureProvider()
.initSearchToolbar(this, searchButton);
final BottomNavigationView navigation = (BottomNavigationView) findViewById(
R.id.bottom_nav);
navigation.setOnNavigationItemSelectedListener(item -> {
switch (item.getItemId()) {
case R.id.homepage_personal_settings:
switchFragment(PersonalSettingsFragment.class.getName(), PERSONAL_SETTINGS_TAG,
ALL_SETTINGS_TAG);
return true;
case R.id.homepage_all_settings:
switchFragment(DashboardSummary.class.getName(), ALL_SETTINGS_TAG,
PERSONAL_SETTINGS_TAG);
return true;
}
return false;
});
}
public static boolean isDynamicHomepageEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlags.DYNAMIC_HOMEPAGE);
}
/**
* Switch to a specific Fragment
*/
public static void switchToFragment(FragmentActivity activity, int id, String fragmentName) {
final Fragment f = Fragment.instantiate(activity, fragmentName, null /* args */);
private void switchFragment(String fragmentName, String showFragmentTag,
String hideFragmentTag) {
final FragmentManager fragmentManager = getSupportFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
FragmentManager manager = activity.getSupportFragmentManager();
manager.beginTransaction().replace(id, f).commitAllowingStateLoss();
manager.executePendingTransactions();
final Fragment hideFragment = fragmentManager.findFragmentByTag(hideFragmentTag);
if (hideFragment != null) {
fragmentTransaction.hide(hideFragment);
}
Fragment showFragment = fragmentManager.findFragmentByTag(showFragmentTag);
if (showFragment == null) {
showFragment = Fragment.instantiate(this, fragmentName, null /* args */);
fragmentTransaction.add(R.id.main_content, showFragment, showFragmentTag);
} else {
fragmentTransaction.show(showFragment);
}
fragmentTransaction.commit();
}
}

View File

@@ -73,8 +73,8 @@ public class ApplicationViewHolder extends RecyclerView.ViewHolder {
static View newView(ViewGroup parent, boolean twoTarget) {
ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext())
.inflate(R.layout.preference_app, parent, false);
final ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame);
if (twoTarget) {
final ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame);
if (widgetFrame != null) {
LayoutInflater.from(parent.getContext())
.inflate(R.layout.preference_widget_master_switch, widgetFrame, true);
@@ -84,6 +84,8 @@ public class ApplicationViewHolder extends RecyclerView.ViewHolder {
// second to last, before widget frame
view.addView(divider, view.getChildCount() - 1);
}
} else if (widgetFrame != null) {
widgetFrame.setVisibility(View.GONE);
}
return view;
}

View File

@@ -21,6 +21,9 @@ import android.util.Log;
import androidx.collection.ArraySet;
import com.android.settings.homepage.conditional.ConditionHomepageCardController;
import com.android.settings.homepage.conditional.ConditionHomepageCardRenderer;
import java.util.Set;
/**
@@ -80,19 +83,16 @@ public class ControllerRendererPool {
private HomepageCardController createCardController(Context context,
Class<? extends HomepageCardController> clz) {
/*
if (ConditionHomepageCardController.class == clz) {
return new ConditionHomepageCardController(context);
}
*/
return null;
}
private HomepageCardRenderer createCardRenderer(Context context, Class<?> clz) {
//if (ConditionHomepageCardRenderer.class == clz) {
// return new ConditionHomepageCardRenderer(context, this /*controllerRendererPool*/);
//}
if (ConditionHomepageCardRenderer.class == clz) {
return new ConditionHomepageCardRenderer(context, this /*controllerRendererPool*/);
}
return null;
}

View File

@@ -65,79 +65,79 @@ public class HomepageCard {
return mName;
}
int getCardType() {
public int getCardType() {
return mCardType;
}
double getRankingScore() {
public double getRankingScore() {
return mRankingScore;
}
String getTextSliceUri() {
public String getTextSliceUri() {
return mSliceUri;
}
Uri getSliceUri() {
public Uri getSliceUri() {
return Uri.parse(mSliceUri);
}
int getCategory() {
public int getCategory() {
return mCategory;
}
String getLocalizedToLocale() {
public String getLocalizedToLocale() {
return mLocalizedToLocale;
}
String getPackageName() {
public String getPackageName() {
return mPackageName;
}
String getAppVersion() {
public String getAppVersion() {
return mAppVersion;
}
String getTitleResName() {
public String getTitleResName() {
return mTitleResName;
}
String getTitleText() {
public String getTitleText() {
return mTitleText;
}
String getSummaryResName() {
public String getSummaryResName() {
return mSummaryResName;
}
String getSummaryText() {
public String getSummaryText() {
return mSummaryText;
}
String getIconResName() {
public String getIconResName() {
return mIconResName;
}
int getIconResId() {
public int getIconResId() {
return mIconResId;
}
int getCardAction() {
public int getCardAction() {
return mCardAction;
}
long getExpireTimeMS() {
public long getExpireTimeMS() {
return mExpireTimeMS;
}
Drawable getIconDrawable() {
public Drawable getIconDrawable() {
return mIconDrawable;
}
boolean isHalfWidth() {
public boolean isHalfWidth() {
return mIsHalfWidth;
}
HomepageCard(Builder builder) {
public HomepageCard(Builder builder) {
mName = builder.mName;
mCardType = builder.mCardType;
mRankingScore = builder.mRankingScore;
@@ -179,7 +179,7 @@ public class HomepageCard {
return TextUtils.equals(mName, that.mName);
}
static class Builder {
public static class Builder {
private String mName;
private int mCardType;
private double mRankingScore;

View File

@@ -18,8 +18,6 @@ package com.android.settings.homepage;
import java.util.List;
//TODO(b/111821137): add test cases
/**
* Data controller for {@link HomepageCard}.
*/

View File

@@ -17,6 +17,8 @@
package com.android.settings.homepage;
import com.android.settings.homepage.HomepageCard.CardType;
import com.android.settings.homepage.conditional.ConditionHomepageCardController;
import com.android.settings.homepage.conditional.ConditionHomepageCardRenderer;
import java.util.Set;
import java.util.TreeSet;
@@ -43,10 +45,10 @@ public class HomepageCardLookupTable {
}
}
private static final Set<HomepageMapping> LOOKUP_TABLE = new TreeSet<HomepageMapping>() {
//add(new HomepageMapping(CardType.CONDITIONAL, ConditionHomepageCardController.class,
// ConditionHomepageCardRenderer.class));
};
private static final Set<HomepageMapping> LOOKUP_TABLE = new TreeSet<HomepageMapping>() {{
add(new HomepageMapping(CardType.CONDITIONAL, ConditionHomepageCardController.class,
ConditionHomepageCardRenderer.class));
}};
public static Class<? extends HomepageCardController> getCardControllerClass(
@CardType int cardType) {

View File

@@ -26,6 +26,6 @@ import java.util.List;
* {@link HomepageManager} will notify the listeners registered, {@link HomepageAdapter} in this
* case.
*/
interface HomepageCardUpdateListener {
public interface HomepageCardUpdateListener {
void onHomepageCardUpdated(int cardType, List<HomepageCard> updateList);
}

View File

@@ -1,196 +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.homepage;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsHomepageActivity;
import com.android.settings.Utils;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.dashboard.DashboardSummary;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.SearchFeatureProvider;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
public class HomepageFragment extends InstrumentedFragment {
private static final String TAG = "HomepageFragment";
private static final String SAVE_BOTTOMBAR_STATE = "bottombar_state";
private static final String SAVE_BOTTOM_FRAGMENT_LOADED = "bottom_fragment_loaded";
private RecyclerView mCardsContainer;
private HomepageAdapter mHomepageAdapter;
private LinearLayoutManager mLayoutManager;
private FloatingActionButton mSearchButton;
private BottomSheetBehavior mBottomSheetBehavior;
private View mBottomBar;
private View mSearchBar;
private boolean mBottomFragmentLoaded;
private HomepageManager mHomepageManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHomepageManager = new HomepageManager(getContext(), getSettingsLifecycle());
mHomepageManager.startCardContentLoading();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.settings_homepage,
container, false);
mCardsContainer = (RecyclerView) rootView.findViewById(R.id.card_container);
//TODO(b/111822407): May have to swap to GridLayoutManager
mLayoutManager = new LinearLayoutManager(getActivity());
mCardsContainer.setLayoutManager(mLayoutManager);
mHomepageAdapter = new HomepageAdapter(getContext(), mHomepageManager);
mCardsContainer.setAdapter(mHomepageAdapter);
mHomepageManager.setListener(mHomepageAdapter);
return rootView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setupBottomBar();
setupSearchBar();
if (savedInstanceState != null) {
final int bottombarState = savedInstanceState.getInt(SAVE_BOTTOMBAR_STATE);
mBottomFragmentLoaded = savedInstanceState.getBoolean(SAVE_BOTTOM_FRAGMENT_LOADED);
mBottomSheetBehavior.setState(bottombarState);
setBarState(bottombarState);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mBottomSheetBehavior != null) {
outState.putInt(SAVE_BOTTOMBAR_STATE, mBottomSheetBehavior.getState());
outState.putBoolean(SAVE_BOTTOM_FRAGMENT_LOADED, mBottomFragmentLoaded);
}
}
@Override
public int getMetricsCategory() {
return MetricsEvent.SETTINGS_HOMEPAGE;
}
private void setupBottomBar() {
final Activity activity = getActivity();
mSearchButton = activity.findViewById(R.id.search_fab);
mSearchButton.setOnClickListener(v -> {
final Intent intent = SearchFeatureProvider.SEARCH_UI_INTENT;
intent.setPackage(FeatureFactory.getFactory(activity)
.getSearchFeatureProvider().getSettingsIntelligencePkgName());
startActivityForResult(intent, 0 /* requestCode */);
});
mBottomSheetBehavior = BottomSheetBehavior.from(activity.findViewById(R.id.bottom_sheet));
mSearchBar = activity.findViewById(R.id.search_bar_container);
mBottomBar = activity.findViewById(R.id.bar);
mBottomBar.setOnClickListener(v -> {
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
});
final int screenWidthpx = getResources().getDisplayMetrics().widthPixels;
final Toolbar searchActionBar = activity.findViewById(R.id.search_action_bar);
searchActionBar.setNavigationIcon(R.drawable.ic_search_floating_24dp);
mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (!mBottomFragmentLoaded) {
// TODO(b/110405144): Switch to {@link TopLevelSettings} when it's ready.
SettingsHomepageActivity.switchToFragment(getActivity(),
R.id.bottom_sheet_fragment, DashboardSummary.class.getName());
mBottomFragmentLoaded = true;
}
setBarState(newState);
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
mBottomBar.setAlpha(1 - slideOffset);
mSearchButton.setAlpha(1 - slideOffset);
mSearchBar.setAlpha(slideOffset);
mSearchBar.setPadding((int) (screenWidthpx * (1 - slideOffset)), 0, 0, 0);
}
});
}
private void setBarState(int bottomSheetState) {
if (bottomSheetState == BottomSheetBehavior.STATE_EXPANDED) {
mBottomBar.setVisibility(View.INVISIBLE);
mSearchBar.setVisibility(View.VISIBLE);
mSearchButton.setVisibility(View.GONE);
} else if (bottomSheetState == BottomSheetBehavior.STATE_COLLAPSED) {
mBottomBar.setVisibility(View.VISIBLE);
mSearchBar.setVisibility(View.INVISIBLE);
mSearchButton.setVisibility(View.VISIBLE);
} else if (bottomSheetState == BottomSheetBehavior.STATE_SETTLING) {
mBottomBar.setVisibility(View.VISIBLE);
mSearchBar.setVisibility(View.VISIBLE);
mSearchButton.setVisibility(View.VISIBLE);
}
}
//TODO(110767984), copied from settingsActivity. We have to merge them
private void setupSearchBar() {
final Activity activity = getActivity();
final Toolbar toolbar = activity.findViewById(R.id.search_action_bar);
FeatureFactory.getFactory(activity).getSearchFeatureProvider()
.initSearchToolbar(activity, toolbar);
activity.setActionBar(toolbar);
// Please forgive me for what I am about to do.
//
// Need to make the navigation icon non-clickable so that the entire card is clickable
// and goes to the search UI. Also set the background to null so there's no ripple.
final View navView = toolbar.getNavigationView();
navView.setClickable(false);
navView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
navView.setBackground(null);
final ActionBar actionBar = activity.getActionBar();
if (actionBar != null) {
boolean deviceProvisioned = Utils.isDeviceProvisioned(activity);
actionBar.setDisplayHomeAsUpEnabled(deviceProvisioned);
actionBar.setHomeButtonEnabled(deviceProvisioned);
actionBar.setDisplayShowTitleEnabled(false);
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.homepage;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment;
public class PersonalSettingsFragment extends InstrumentedFragment {
private static final String TAG = "PersonalSettingsFragment";
private RecyclerView mCardsContainer;
//TODO(b/113966426): rename
private HomepageAdapter mHomepageAdapter;
private LinearLayoutManager mLayoutManager;
//TODO(b/113966426): rename
private HomepageManager mHomepageManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHomepageManager = new HomepageManager(getContext(), getSettingsLifecycle());
mHomepageManager.startCardContentLoading();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.settings_homepage,
container, false);
mCardsContainer = (RecyclerView) rootView.findViewById(R.id.card_container);
//TODO(b/111822407): May have to swap to GridLayoutManager
mLayoutManager = new LinearLayoutManager(getActivity());
mCardsContainer.setLayoutManager(mLayoutManager);
mHomepageAdapter = new HomepageAdapter(getContext(), mHomepageManager);
mCardsContainer.setAdapter(mHomepageAdapter);
mHomepageManager.setListener(mHomepageAdapter);
return rootView;
}
@Override
public int getMetricsCategory() {
return MetricsEvent.SETTINGS_HOMEPAGE;
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.homepage.conditional;
import com.android.settings.homepage.HomepageCard;
/**
* Data class representing a {@link ConditionCard}.
*
* Use this class to store additional attributes on top of {@link HomepageCard} for
* {@link ConditionalCard}.
*/
public class ConditionCard extends HomepageCard {
private final long mConditionId;
private final int mMetricsConstant;
private final CharSequence mActionText;
private ConditionCard(Builder builder) {
super(builder);
mConditionId = builder.mConditionId;
mMetricsConstant = builder.mMetricsConstant;
mActionText = builder.mActionText;
}
public long getConditionId() {
return mConditionId;
}
public int getMetricsConstant() {
return mMetricsConstant;
}
public CharSequence getActionText() {
return mActionText;
}
static class Builder extends HomepageCard.Builder {
private long mConditionId;
private int mMetricsConstant;
private CharSequence mActionText;
public Builder setConditionId(long id) {
mConditionId = id;
return this;
}
public Builder setMetricsConstant(int metricsConstant) {
mMetricsConstant = metricsConstant;
return this;
}
public Builder setActionText(CharSequence actionText) {
mActionText = actionText;
return this;
}
public ConditionCard build() {
return new ConditionCard(this);
}
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.homepage.conditional;
import android.content.Context;
import com.android.settings.homepage.HomepageCard;
import com.android.settings.homepage.HomepageCardController;
import com.android.settings.homepage.HomepageCardUpdateListener;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.ArrayList;
import java.util.List;
/**
* This controller triggers the loading of conditional cards and monitors state changes to
* update the homepage.
*/
public class ConditionHomepageCardController implements HomepageCardController, ConditionListener,
LifecycleObserver, OnStart, OnStop {
private final Context mContext;
private final ConditionManager mConditionManager;
private HomepageCardUpdateListener mListener;
public ConditionHomepageCardController(Context context) {
mContext = context;
mConditionManager = new ConditionManager(context.getApplicationContext(), this);
mConditionManager.startMonitoringStateChange();
}
@Override
public void setHomepageCardUpdateListener(HomepageCardUpdateListener listener) {
mListener = listener;
}
@Override
public int getCardType() {
return HomepageCard.CardType.CONDITIONAL;
}
@Override
public void onDataUpdated(List<HomepageCard> cardList) {
mListener.onHomepageCardUpdated(getCardType(), cardList);
}
@Override
public void onStart() {
mConditionManager.startMonitoringStateChange();
}
@Override
public void onStop() {
mConditionManager.stopMonitoringStateChange();
}
@Override
public void onPrimaryClick(HomepageCard homepageCard) {
final ConditionCard card = (ConditionCard) homepageCard;
mConditionManager.onPrimaryClick(mContext, card.getConditionId());
}
@Override
public void onActionClick(HomepageCard homepageCard) {
final ConditionCard card = (ConditionCard) homepageCard;
mConditionManager.onActionClick(card.getConditionId());
}
@Override
public void onConditionsChanged() {
final List<HomepageCard> conditionCards = new ArrayList<>();
final List<ConditionalCard> conditionList = mConditionManager.getDisplayableCards();
for (ConditionalCard condition : conditionList) {
final ConditionCard conditionCard = ((ConditionCard.Builder) new ConditionCard.Builder()
.setConditionId(condition.getId())
.setMetricsConstant(condition.getMetricsConstant())
.setActionText(condition.getActionText())
.setName(mContext.getPackageName() + "/" + condition.getTitle().toString())
.setCardType(HomepageCard.CardType.CONDITIONAL)
.setTitleText(condition.getTitle().toString())
.setSummaryText(condition.getSummary().toString())
.setIconDrawable(condition.getIcon()))
.build();
conditionCards.add(conditionCard);
}
if (mListener != null) {
onDataUpdated(conditionCards);
}
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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.homepage.conditional;
import android.content.Context;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.homepage.ControllerRendererPool;
import com.android.settings.homepage.HomepageCard;
import com.android.settings.homepage.HomepageCardRenderer;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/**
* Card renderer for {@link ConditionCard}.
*/
public class ConditionHomepageCardRenderer implements HomepageCardRenderer {
private final Context mContext;
private final ControllerRendererPool mControllerRendererPool;
public ConditionHomepageCardRenderer(Context context,
ControllerRendererPool controllerRendererPool) {
mContext = context;
mControllerRendererPool = controllerRendererPool;
}
@Override
public int getViewType() {
return R.layout.homepage_condition_tile;
}
@Override
public RecyclerView.ViewHolder createViewHolder(View view) {
return new ConditionalCardHolder(view);
}
@Override
public void bindView(RecyclerView.ViewHolder holder, HomepageCard homepageCard) {
final ConditionalCardHolder view = (ConditionalCardHolder) holder;
final ConditionCard card = (ConditionCard) homepageCard;
final MetricsFeatureProvider metricsFeatureProvider = FeatureFactory.getFactory(
mContext).getMetricsFeatureProvider();
metricsFeatureProvider.visible(mContext, MetricsProto.MetricsEvent.SETTINGS_HOMEPAGE,
card.getMetricsConstant());
initializePrimaryClick(view, card, metricsFeatureProvider);
initializeView(view, card);
initializeActionButton(view, card, metricsFeatureProvider);
}
private void initializePrimaryClick(ConditionalCardHolder view, ConditionCard card,
MetricsFeatureProvider metricsFeatureProvider) {
view.itemView.findViewById(R.id.content).setOnClickListener(
v -> {
metricsFeatureProvider.action(mContext,
MetricsProto.MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
card.getMetricsConstant());
mControllerRendererPool.getController(mContext,
card.getCardType()).onPrimaryClick(card);
});
}
private void initializeView(ConditionalCardHolder view, ConditionCard card) {
view.icon.setImageDrawable(card.getIconDrawable());
view.title.setText(card.getTitleText());
view.summary.setText(card.getSummaryText());
setViewVisibility(view.itemView, R.id.divider, false);
}
private void initializeActionButton(ConditionalCardHolder view, ConditionCard card,
MetricsFeatureProvider metricsFeatureProvider) {
final CharSequence action = card.getActionText();
final boolean hasButtons = !TextUtils.isEmpty(action);
setViewVisibility(view.itemView, R.id.buttonBar, hasButtons);
final Button button = view.itemView.findViewById(R.id.first_action);
if (hasButtons) {
button.setVisibility(View.VISIBLE);
button.setText(action);
button.setOnClickListener(v -> {
final Context viewContext = v.getContext();
metricsFeatureProvider.action(
viewContext, MetricsProto.MetricsEvent.ACTION_SETTINGS_CONDITION_BUTTON,
card.getMetricsConstant());
mControllerRendererPool.getController(mContext, card.getCardType()).onActionClick(
card);
});
} else {
button.setVisibility(View.GONE);
}
}
private void setViewVisibility(View containerView, int viewId, boolean visible) {
View view = containerView.findViewById(viewId);
if (view != null) {
view.setVisibility(visible ? View.VISIBLE : View.GONE);
}
}
public static class ConditionalCardHolder extends RecyclerView.ViewHolder {
public final ImageView icon;
public final TextView title;
public final TextView summary;
public ConditionalCardHolder(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);
}
}
}

View File

@@ -16,55 +16,17 @@
package com.android.settings.overlay;
import android.accounts.Account;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.Activity;
import android.content.Context;
import com.android.settings.support.SupportPhone;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Feature provider for support tab.
*/
public interface SupportFeatureProvider {
@IntDef({SupportType.EMAIL, SupportType.PHONE, SupportType.CHAT})
@Retention(RetentionPolicy.SOURCE)
@interface SupportType {
int EMAIL = 1;
int PHONE = 2;
int CHAT = 3;
}
/**
* Refreshes all operation rules.
*/
void refreshOperationRules();
/**
* Returns the current country code if it has a operation config, otherwise returns null.
*/
String getCurrentCountryCodeIfHasConfig(@SupportType int type);
/**
* Returns a support phone for specified country.
*/
SupportPhone getSupportPhones(String countryCode, boolean isTollfree);
/**
* Returns array of {@link Account} that's eligible for support options.
*/
@NonNull
Account[] getSupportEligibleAccounts(Context context);
/**
* Starts support v2, invokes the support home page. Will no-op if support v2 is not enabled.
* Starts support, invokes the support home page.
*
* @param activity Calling activity.
*/
void startSupportV2(Activity activity);
void startSupport(Activity activity);
}

View File

@@ -21,7 +21,7 @@ import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.widget.Toolbar;
import android.view.View;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.search.SearchIndexableResources;
@@ -55,11 +55,11 @@ public interface SearchFeatureProvider {
/**
* Initializes the search toolbar.
*/
default void initSearchToolbar(Activity activity, Toolbar toolbar) {
if (activity == null || toolbar == null) {
default void initSearchToolbar(Activity activity, View view) {
if (activity == null || view == null) {
return;
}
toolbar.setOnClickListener(tb -> {
view.setOnClickListener(tb -> {
final Intent intent = SEARCH_UI_INTENT;
intent.setPackage(getSettingsIntelligencePkgName());

View File

@@ -43,9 +43,9 @@ public class SupportDashboardActivity extends Activity implements Indexable {
SupportFeatureProvider supportFeatureProvider = FeatureFactory.getFactory(this)
.getSupportFeatureProvider(this);
// try to launch support v2 if we have the feature provider
// try to launch support if we have the feature provider
if (supportFeatureProvider != null) {
supportFeatureProvider.startSupportV2(this);
supportFeatureProvider.startSupport(this);
finish();
}
}

View File

@@ -1,84 +0,0 @@
/*
* Copyright (C) 2016 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.support;
import android.content.Intent;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.text.ParseException;
/**
* Data model for a support phone number.
*/
public final class SupportPhone implements Parcelable {
public final String language;
public final String number;
public final boolean isTollFree;
public SupportPhone(String config) throws ParseException {
// Config follows this format: language:[tollfree|tolled]:number
final String[] tokens = config.split(":");
if (tokens.length != 3) {
throw new ParseException("Phone config is invalid " + config, 0);
}
language = tokens[0];
isTollFree = TextUtils.equals(tokens[1], "tollfree");
number = tokens[2];
}
protected SupportPhone(Parcel in) {
language = in.readString();
number = in.readString();
isTollFree = in.readInt() != 0;
}
public Intent getDialIntent() {
return new Intent(Intent.ACTION_DIAL)
.setData(new Uri.Builder()
.scheme("tel")
.appendPath(number)
.build());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(language);
dest.writeString(number);
dest.writeInt(isTollFree ? 1 : 0);
}
public static final Creator<SupportPhone> CREATOR = new Creator<SupportPhone>() {
@Override
public SupportPhone createFromParcel(Parcel in) {
return new SupportPhone(in);
}
@Override
public SupportPhone[] newArray(int size) {
return new SupportPhone[size];
}
};
}

View File

@@ -0,0 +1,138 @@
/*
* 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.homepage.conditional;
import static org.mockito.ArgumentMatchers.any;
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.content.Context;
import android.graphics.drawable.Drawable;
import com.android.settings.R;
import com.android.settings.homepage.HomepageCardUpdateListener;
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.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
public class ConditionHomepageCardControllerTest {
@Mock
private ConditionManager mConditionManager;
@Mock
private HomepageCardUpdateListener mListener;
private Context mContext;
private ConditionHomepageCardController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mController = spy(new ConditionHomepageCardController(mContext));
ReflectionHelpers.setField(mController, "mConditionManager", mConditionManager);
}
@Test
public void onStart_shouldStartMonitoring() {
mController.onStart();
verify(mConditionManager).startMonitoringStateChange();
}
@Test
public void onStop_shouldStopMonitoring() {
mController.onStop();
verify(mConditionManager).stopMonitoringStateChange();
}
@Test
public void onConditionsChanged_listenerIsSet_shouldUpdateData() {
final FakeConditionalCard fakeConditionalCard = new FakeConditionalCard(mContext);
final List<ConditionalCard> conditionalCards = new ArrayList<>();
conditionalCards.add(fakeConditionalCard);
when(mConditionManager.getDisplayableCards()).thenReturn(conditionalCards);
mController.setHomepageCardUpdateListener(mListener);
mController.onConditionsChanged();
verify(mController).onDataUpdated(any());
}
@Test
public void onConditionsChanged_listenerNotSet_shouldNotUpdateData() {
final FakeConditionalCard fakeConditionalCard = new FakeConditionalCard(mContext);
final List<ConditionalCard> conditionalCards = new ArrayList<>();
conditionalCards.add(fakeConditionalCard);
when(mConditionManager.getDisplayableCards()).thenReturn(conditionalCards);
mController.onConditionsChanged();
verify(mController, never()).onDataUpdated(any());
}
private class FakeConditionalCard implements ConditionalCard {
private final Context mContext;
public FakeConditionalCard(Context context) {
mContext = context;
}
@Override
public long getId() {
return 100;
}
@Override
public CharSequence getActionText() {
return "action_text_test";
}
@Override
public int getMetricsConstant() {
return 1;
}
@Override
public Drawable getIcon() {
return mContext.getDrawable(R.drawable.ic_do_not_disturb_on_24dp);
}
@Override
public CharSequence getTitle() {
return "title_text_test";
}
@Override
public CharSequence getSummary() {
return "summary_text_test";
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.homepage.conditional;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.homepage.ControllerRendererPool;
import com.android.settings.homepage.HomepageCard;
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;
@RunWith(SettingsRobolectricTestRunner.class)
public class ConditionHomepageCardRendererTest {
@Mock
private ControllerRendererPool mControllerRendererPool;
@Mock
private ConditionHomepageCardController mController;
private Context mContext;
private ConditionHomepageCardRenderer mRenderer;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mRenderer = new ConditionHomepageCardRenderer(mContext, mControllerRendererPool);
}
@Test
public void bindView_shouldSetListener() {
final int viewType = mRenderer.getViewType();
final RecyclerView recyclerView = new RecyclerView(mContext);
recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
final View view = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false);
final RecyclerView.ViewHolder viewHolder = mRenderer.createViewHolder(view);
final View card = view.findViewById(R.id.content);
when(mControllerRendererPool.getController(mContext,
HomepageCard.CardType.CONDITIONAL)).thenReturn(mController);
mRenderer.bindView(viewHolder, getHomepageCard());
assertThat(card).isNotNull();
assertThat(card.hasOnClickListeners()).isTrue();
}
@Test
public void viewClick_shouldInvokeControllerPrimaryClick() {
final int viewType = mRenderer.getViewType();
final RecyclerView recyclerView = new RecyclerView(mContext);
recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
final View view = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false);
final RecyclerView.ViewHolder viewHolder = mRenderer.createViewHolder(view);
final View card = view.findViewById(R.id.content);
when(mControllerRendererPool.getController(mContext,
HomepageCard.CardType.CONDITIONAL)).thenReturn(mController);
mRenderer.bindView(viewHolder, getHomepageCard());
assertThat(card).isNotNull();
card.performClick();
verify(mController).onPrimaryClick(any(HomepageCard.class));
}
private HomepageCard getHomepageCard() {
ConditionCard conditionCard = ((ConditionCard.Builder) new ConditionCard.Builder()
.setConditionId(123)
.setMetricsConstant(1)
.setActionText("test_action")
.setName("test_name")
.setCardType(HomepageCard.CardType.CONDITIONAL)
.setTitleText("test_title")
.setSummaryText("test_summary")
.setIconDrawable(mContext.getDrawable(R.drawable.ic_do_not_disturb_on_24dp))
.setIsHalfWidth(true))
.build();
return conditionCard;
}
}