diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-02-17 03:36:29 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-02-17 03:36:29 +0000 |
commit | 03408d4677953576fafb754e9037541c68d4a41b (patch) | |
tree | 837e2b957b7ea9572732fa4af1d95d0cf1a0011a | |
parent | 9ba0d75bfd57a46d8abaf0f11b81c543e87ab489 (diff) | |
parent | 4995a2ba9c0f260f383667cd389f2cdda7326eca (diff) | |
download | setupwizard-android13-frc-scheduling-release.tar.gz |
Snap for 8191477 from 4995a2ba9c0f260f383667cd389f2cdda7326eca to tm-frc-scheduling-releaset_frc_sch_330443040t_frc_sch_330443010android13-frc-scheduling-release
Change-Id: I827707c28075f8ae18968f2a0ac0011a352566a3
59 files changed, 2285 insertions, 327 deletions
diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..9f39796 --- /dev/null +++ b/build.gradle @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 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. + */ + +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + mavenCentral() + + } + dependencies { + // Before upgrading this version, make sure you can close android + // studio, run git clean -fxd, and then reopen this folder in android + // studio and have it work. Sometimes android studio's built in gradle + // wrapper seems to lag behind the version required by the android + // plugin. + classpath 'com.android.tools.build:gradle:4.1.3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..2a103f9 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,32 @@ +# +# Copyright (C) 2021 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. + +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true diff --git a/library/main/Android.bp b/library/main/Android.bp index f99ecdd..506dee5 100644 --- a/library/main/Android.bp +++ b/library/main/Android.bp @@ -22,7 +22,11 @@ android_library { srcs: ["src/**/*.java"], resource_dirs: ["res"], libs: ["android.car-system-stubs"], - static_libs: ["androidx.car_car-resources-partially-dejetified"], + static_libs: [ + "androidx.car_car-resources-partially-dejetified", + "car-ui-lib", + "androidx.test.core" + ], optimize: { enabled: false, }, diff --git a/library/main/AndroidManifest.xml b/library/main/AndroidManifest.xml index 9bd2e16..e330c33 100644 --- a/library/main/AndroidManifest.xml +++ b/library/main/AndroidManifest.xml @@ -18,6 +18,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.car.setupwizardlib"> <uses-sdk - android:minSdkVersion="24" - android:targetSdkVersion="26" /> + android:minSdkVersion="29" + android:targetSdkVersion="31" /> </manifest> diff --git a/library/main/build.gradle b/library/main/build.gradle index 412e3b0..17ab59a 100644 --- a/library/main/build.gradle +++ b/library/main/build.gradle @@ -78,6 +78,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.core:core:1.3.2' implementation 'androidx.annotation:annotation:1.2.0' + implementation 'androidx.test:core:1.4.0' testImplementation 'com.google.truth:truth:0.41' testImplementation 'org.mockito:mockito-core:3.6.0' diff --git a/library/main/res/color/button_text_color.xml b/library/main/res/color/button_text_color.xml new file mode 100644 index 0000000..f4ed692 --- /dev/null +++ b/library/main/res/color/button_text_color.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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_focused="true" android:color="@android:color/white"/> + <item android:state_enabled="false" + android:alpha="0.5" + android:color="@android:color/black"/> + <item android:color="@android:color/black"/> +</selector>
\ No newline at end of file diff --git a/library/main/res/drawable/flat_button_background.xml b/library/main/res/drawable/flat_button_background.xml new file mode 100644 index 0000000..ff5dbdc --- /dev/null +++ b/library/main/res/drawable/flat_button_background.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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_focused="true" android:state_pressed="true"> + <shape android:shape="rectangle"> + <solid android:color="@color/suw_rotary_focus_pressed_fill_color"/> + <stroke android:width="@dimen/suw_rotary_focus_pressed_stroke_width" + android:color="@color/suw_rotary_focus_pressed_stroke_color" /> + <corners android:radius="@dimen/car_button_radius"/> + </shape> + </item> + <item android:state_focused="true"> + <shape android:shape="rectangle"> + <solid android:color="@color/suw_rotary_focus_fill_color"/> + <stroke android:width="@dimen/suw_rotary_focus_stroke_width" + android:color="@color/suw_rotary_focus_stroke_color" /> + <corners android:radius="@dimen/car_button_radius"/> + </shape> + </item> + <item> + <ripple android:color="@color/car_grey_300"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/car_button_radius"/> + <solid android:color="@color/car_card_ripple_background_light"/> + </shape> + </item> + </ripple> + </item> +</selector> diff --git a/library/main/res/drawable/list_item_background.xml b/library/main/res/drawable/list_item_background.xml new file mode 100644 index 0000000..9f871dd --- /dev/null +++ b/library/main/res/drawable/list_item_background.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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_focused="true" android:state_pressed="true"> + <shape android:shape="rectangle"> + <solid android:color="@color/suw_rotary_focus_pressed_fill_color"/> + <stroke android:width="@dimen/suw_rotary_focus_pressed_stroke_width" + android:color="@color/suw_rotary_focus_pressed_stroke_color"/> + </shape> + </item> + <item android:state_focused="true"> + <shape android:shape="rectangle"> + <solid android:color="@color/suw_rotary_focus_fill_color"/> + <stroke android:width="@dimen/suw_rotary_focus_stroke_width" + android:color="@color/suw_rotary_focus_stroke_color"/> + </shape> + </item> + <item android:state_activated="true"> + <shape android:shape="rectangle"> + <solid android:color="?android:attr/colorControlHighlight"/> + </shape> + </item> + <item> + <ripple android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <solid android:color="@color/car_card_ripple_background_light"/> + </shape> + </item> + </ripple> + </item> +</selector>
\ No newline at end of file diff --git a/library/main/res/drawable/primary_button_background.xml b/library/main/res/drawable/primary_button_background.xml new file mode 100644 index 0000000..e353b9c --- /dev/null +++ b/library/main/res/drawable/primary_button_background.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2021 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_focused="true" android:state_pressed="true"> + <shape android:shape="rectangle"> + <solid android:color="@color/suw_rotary_focus_pressed_fill_color"/> + <stroke android:width="@dimen/suw_rotary_focus_pressed_stroke_width" + android:color="@color/suw_rotary_focus_pressed_stroke_color" /> + <corners android:radius="@dimen/car_button_radius"/> + </shape> + </item> + <item android:state_focused="true"> + <shape android:shape="rectangle"> + <solid android:color="@color/suw_rotary_focus_fill_color"/> + <stroke android:width="@dimen/suw_rotary_focus_stroke_width" + android:color="@color/suw_rotary_focus_stroke_color" /> + <corners android:radius="@dimen/car_button_radius"/> + </shape> + </item> + <item> + <ripple android:color="?attr/colorControlHighlight"> + <item> + <selector> + <item android:state_enabled="false"> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/car_button_radius"/> + <solid android:color="@color/car_grey_300"/> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/car_button_radius"/> + <solid android:color="?android:attr/colorButtonNormal"/> + </shape> + </item> + </selector> + </item> + </ripple> + </item> +</selector>
\ No newline at end of file diff --git a/library/main/res/drawable/round_button_background.xml b/library/main/res/drawable/round_button_background.xml new file mode 100644 index 0000000..eedce1f --- /dev/null +++ b/library/main/res/drawable/round_button_background.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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_focused="true" android:state_pressed="true"> + <shape android:shape="oval"> + <solid android:color="@color/suw_rotary_focus_pressed_fill_color"/> + <stroke android:width="@dimen/suw_rotary_focus_pressed_stroke_width" + android:color="@color/suw_rotary_focus_pressed_stroke_color"/> + </shape> + </item> + <item android:state_focused="true"> + <shape android:shape="oval"> + <solid android:color="@color/suw_rotary_focus_fill_color"/> + <stroke android:width="@dimen/suw_rotary_focus_stroke_width" + android:color="@color/suw_rotary_focus_stroke_color"/> + </shape> + </item> + <item android:state_activated="true"> + <shape android:shape="oval"> + <solid android:color="?android:attr/colorControlHighlight"/> + </shape> + </item> + <item> + <ripple android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <shape android:shape="oval"> + <solid android:color="@color/car_card_ripple_background_light"/> + </shape> + </item> + </ripple> + </item> +</selector>
\ No newline at end of file diff --git a/library/main/res/layout-land/action_bar.xml b/library/main/res/layout-land/action_bar.xml new file mode 100644 index 0000000..71929a5 --- /dev/null +++ b/library/main/res/layout-land/action_bar.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/button_container" + android:layout_width="match_parent" + android:layout_height="@dimen/actionbar_height" + android:gravity="top" + android:paddingTop="@dimen/suw_padding_4" + android:paddingHorizontal="@dimen/suw_padding_7" + android:orientation="horizontal"> + <ViewStub + android:id="@+id/primary_toolbar_button_stub" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:inflatedId="@+id/primary_toolbar_button" + android:layout="@layout/primary_button"/> + <ViewStub + android:id="@+id/secondary_toolbar_button_stub" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/suw_padding_7" + android:inflatedId="@+id/secondary_toolbar_button" + android:layout="@layout/flat_button"/> +</LinearLayout>
\ No newline at end of file diff --git a/library/main/res/layout-land/rotary_action_bar.xml b/library/main/res/layout-land/rotary_action_bar.xml new file mode 100644 index 0000000..8f02f29 --- /dev/null +++ b/library/main/res/layout-land/rotary_action_bar.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<com.android.car.ui.FocusArea xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/button_container" + android:layout_width="match_parent" + android:layout_height="@dimen/actionbar_height" + android:gravity="top" + android:paddingTop="@dimen/suw_padding_4" + android:paddingHorizontal="@dimen/suw_padding_7" + android:orientation="horizontal"> + <ViewStub + android:id="@+id/primary_toolbar_button_stub" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:inflatedId="@+id/primary_toolbar_button" + android:layout="@layout/primary_button"/> + <ViewStub + android:id="@+id/secondary_toolbar_button_stub" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/suw_padding_7" + android:inflatedId="@+id/secondary_toolbar_button" + android:layout="@layout/flat_button"/> +</com.android.car.ui.FocusArea>
\ No newline at end of file diff --git a/library/main/res/layout-land/rotary_split_nav_layout.xml b/library/main/res/layout-land/rotary_split_nav_layout.xml new file mode 100644 index 0000000..b69c5b2 --- /dev/null +++ b/library/main/res/layout-land/rotary_split_nav_layout.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <com.android.car.ui.FocusParkingView + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <com.android.car.ui.FocusArea + android:id="@+id/application_bar" + android:layout_width="@dimen/sidebar_width" + android:layout_height="match_parent" + android:gravity="top" + android:paddingTop="@dimen/suw_padding_3" + android:orientation="vertical"> + + <ImageView + android:id="@+id/back_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:background="@drawable/round_button_background" + android:contentDescription="@string/back_button_content_description" + android:padding="@dimen/car_padding_2" + android:src="@drawable/car_ic_arrow_back"/> + + <ImageView + android:id="@+id/close_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:background="@drawable/round_button_background" + android:contentDescription="@string/close_button_content_description" + android:padding="@dimen/car_padding_2" + android:src="@drawable/car_ic_close"/> + </com.android.car.ui.FocusArea> + + <LinearLayout + android:id="@+id/content_container" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:orientation="vertical"> + <include layout="@layout/progressbar"/> + + <com.android.car.ui.FocusArea + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + <ViewStub + android:id="@+id/layout_content_stub" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + </com.android.car.ui.FocusArea> + + <include layout="@layout/rotary_action_bar"/> + </LinearLayout> +</merge>
\ No newline at end of file diff --git a/library/main/res/layout-land/split_nav_layout.xml b/library/main/res/layout-land/split_nav_layout.xml new file mode 100644 index 0000000..16a017f --- /dev/null +++ b/library/main/res/layout-land/split_nav_layout.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <FrameLayout + android:id="@+id/application_bar" + android:layout_width="@dimen/sidebar_width" + android:layout_height="match_parent" + android:gravity="top" + android:paddingTop="@dimen/suw_padding_3"> + + <ImageView + android:id="@+id/back_button" + android:layout_width="@dimen/car_primary_icon_size" + android:layout_height="@dimen/car_primary_icon_size" + android:layout_gravity="center_horizontal" + android:background="@drawable/button_ripple_bg" + android:contentDescription="@string/back_button_content_description" + android:src="@drawable/car_ic_arrow_back"/> + + <ImageView + android:id="@+id/close_button" + android:layout_width="@dimen/car_primary_icon_size" + android:layout_height="@dimen/car_primary_icon_size" + android:layout_gravity="center_horizontal" + android:background="@drawable/button_ripple_bg" + android:contentDescription="@string/close_button_content_description" + android:src="@drawable/car_ic_close"/> + </FrameLayout> + + <LinearLayout + android:id="@+id/content_container" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:orientation="vertical"> + <include layout="@layout/progressbar"/> + + <ViewStub + android:id="@+id/layout_content_stub" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"/> + + <include layout="@layout/action_bar"/> + </LinearLayout> +</merge>
\ No newline at end of file diff --git a/library/main/res/layout-w1760dp-land/rotary_split_nav_layout.xml b/library/main/res/layout-w1760dp-land/rotary_split_nav_layout.xml new file mode 100644 index 0000000..8ff4854 --- /dev/null +++ b/library/main/res/layout-w1760dp-land/rotary_split_nav_layout.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <com.android.car.ui.FocusParkingView + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + <com.android.car.ui.FocusArea + android:id="@+id/application_bar" + android:layout_width="@dimen/sidebar_width" + android:layout_height="match_parent" + android:gravity="top" + android:paddingTop="@dimen/suw_padding_3" + android:orientation="vertical"> + <ImageView + android:id="@+id/back_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:background="@drawable/round_button_background" + android:contentDescription="@string/back_button_content_description" + android:padding="@dimen/car_padding_2" + android:src="@drawable/car_ic_arrow_back"/> + <ImageView + android:id="@+id/close_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:background="@drawable/round_button_background" + android:contentDescription="@string/close_button_content_description" + android:padding="@dimen/car_padding_2" + android:src="@drawable/car_ic_close"/> + </com.android.car.ui.FocusArea> + <LinearLayout + android:id="@+id/ultra_wide_content_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + <include layout="@layout/progressbar"/> + <com.android.car.ui.FocusArea + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + <ViewStub + android:id="@+id/layout_content_stub" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + </com.android.car.ui.FocusArea> + <include layout="@layout/rotary_action_bar"/> + </LinearLayout> + <FrameLayout + android:id="@+id/ultra_wide_space_filler" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:background="@color/suw_color_background"/> +</merge> diff --git a/library/main/res/layout-w1760dp-land/split_nav_layout.xml b/library/main/res/layout-w1760dp-land/split_nav_layout.xml new file mode 100644 index 0000000..c118c66 --- /dev/null +++ b/library/main/res/layout-w1760dp-land/split_nav_layout.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <FrameLayout + android:id="@+id/application_bar" + android:layout_width="@dimen/sidebar_width" + android:layout_height="match_parent" + android:gravity="top" + android:paddingTop="@dimen/suw_padding_3"> + <ImageView + android:id="@+id/back_button" + android:layout_width="@dimen/car_primary_icon_size" + android:layout_height="@dimen/car_primary_icon_size" + android:layout_gravity="center_horizontal" + android:background="@drawable/button_ripple_bg" + android:contentDescription="@string/back_button_content_description" + android:src="@drawable/car_ic_arrow_back"/> + <ImageView + android:id="@+id/close_button" + android:layout_width="@dimen/car_primary_icon_size" + android:layout_height="@dimen/car_primary_icon_size" + android:layout_gravity="center_horizontal" + android:background="@drawable/button_ripple_bg" + android:contentDescription="@string/close_button_content_description" + android:src="@drawable/car_ic_close"/> + </FrameLayout> + <LinearLayout + android:id="@+id/ultra_wide_content_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + <include layout="@layout/progressbar"/> + <ViewStub + android:id="@+id/layout_content_stub" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"/> + <include layout="@layout/action_bar"/> + </LinearLayout> + <FrameLayout + android:id="@+id/ultra_wide_space_filler" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:background="@color/suw_color_background"/> +</merge> diff --git a/library/main/res/layout/action_bar.xml b/library/main/res/layout/action_bar.xml new file mode 100644 index 0000000..0920bb1 --- /dev/null +++ b/library/main/res/layout/action_bar.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/button_container" + android:layout_width="match_parent" + android:layout_height="@dimen/actionbar_height" + android:paddingHorizontal="@dimen/suw_padding_7"> + <ViewStub + android:id="@+id/primary_toolbar_button_stub" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:inflatedId="@+id/primary_toolbar_button" + android:layout="@layout/primary_button"/> + <ViewStub + android:id="@+id/secondary_toolbar_button_stub" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:inflatedId="@+id/secondary_toolbar_button" + android:layout="@layout/flat_button"/> +</RelativeLayout>
\ No newline at end of file diff --git a/library/main/res/layout/car_setup_wizard_toolbar.xml b/library/main/res/layout/car_setup_wizard_toolbar.xml index 5ae4e11..ff397d5 100644 --- a/library/main/res/layout/car_setup_wizard_toolbar.xml +++ b/library/main/res/layout/car_setup_wizard_toolbar.xml @@ -31,20 +31,22 @@ <ImageView android:id="@+id/back_button" - android:layout_width="@dimen/car_primary_icon_size" - android:layout_height="@dimen/car_primary_icon_size" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:layout_gravity="center" - android:background="@drawable/button_ripple_bg" + android:background="@drawable/round_button_background" android:contentDescription="@string/back_button_content_description" + android:padding="@dimen/car_padding_2" android:src="@drawable/car_ic_arrow_back"/> <ImageView android:id="@+id/close_button" - android:layout_width="@dimen/car_primary_icon_size" - android:layout_height="@dimen/car_primary_icon_size" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:layout_gravity="center" - android:background="@drawable/button_ripple_bg" + android:background="@drawable/round_button_background" android:contentDescription="@string/close_button_content_description" + android:padding="@dimen/car_padding_2" android:src="@drawable/car_ic_close"/> </FrameLayout> diff --git a/library/main/res/layout/empty_fragment_frame_layout.xml b/library/main/res/layout/empty_fragment_frame_layout.xml new file mode 100644 index 0000000..0ea8da7 --- /dev/null +++ b/library/main/res/layout/empty_fragment_frame_layout.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/empty_fragment_frame_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" />
\ No newline at end of file diff --git a/library/main/res/layout/progressbar.xml b/library/main/res/layout/progressbar.xml new file mode 100644 index 0000000..3056662 --- /dev/null +++ b/library/main/res/layout/progressbar.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/progress_bar" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="match_parent" + android:layout_height="2dp" + android:minHeight="0dp" + android:maxHeight="20dp" + android:indeterminateDrawable="@drawable/progress_indeterminate_horizontal_material_trimmed" + android:indeterminateTint="?android:attr/colorAccent" + android:indeterminateTintMode="src_in"/>
\ No newline at end of file diff --git a/library/main/res/layout/rotary_car_setup_wizard_layout.xml b/library/main/res/layout/rotary_car_setup_wizard_layout.xml new file mode 100644 index 0000000..9e9819f --- /dev/null +++ b/library/main/res/layout/rotary_car_setup_wizard_layout.xml @@ -0,0 +1,39 @@ +<?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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <com.android.car.ui.FocusParkingView + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <com.android.car.ui.FocusArea + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <include + android:id="@+id/application_bar" + layout="@layout/car_setup_wizard_toolbar"/> + </com.android.car.ui.FocusArea> + + <com.android.car.ui.FocusArea + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + <ViewStub + android:id="@+id/layout_content_stub" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + </com.android.car.ui.FocusArea> +</merge> diff --git a/library/main/res/layout/rotary_split_nav_layout.xml b/library/main/res/layout/rotary_split_nav_layout.xml new file mode 100644 index 0000000..1a8a004 --- /dev/null +++ b/library/main/res/layout/rotary_split_nav_layout.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <com.android.car.ui.FocusParkingView + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <com.android.car.ui.FocusArea + android:id="@+id/application_bar" + android:layout_width="match_parent" + android:layout_height="@dimen/suw_padding_8" + android:paddingStart="@dimen/navbar_padding_start" + android:gravity="start"> + + <ImageView + android:id="@+id/back_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:background="@drawable/round_button_background" + android:contentDescription="@string/back_button_content_description" + android:padding="@dimen/car_padding_2" + android:src="@drawable/car_ic_arrow_back"/> + + <ImageView + android:id="@+id/close_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:background="@drawable/round_button_background" + android:contentDescription="@string/close_button_content_description" + android:padding="@dimen/car_padding_2" + android:src="@drawable/car_ic_close"/> + </com.android.car.ui.FocusArea> + + <include layout="@layout/progressbar"/> + + <com.android.car.ui.FocusArea + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + <ViewStub + android:id="@+id/layout_content_stub" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + </com.android.car.ui.FocusArea> + + <com.android.car.ui.FocusArea + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <include layout="@layout/action_bar"/> + </com.android.car.ui.FocusArea> +</merge>
\ No newline at end of file diff --git a/library/main/res/layout/split_nav_compat_activity.xml b/library/main/res/layout/split_nav_compat_activity.xml new file mode 100644 index 0000000..ee4b0eb --- /dev/null +++ b/library/main/res/layout/split_nav_compat_activity.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<com.android.car.setupwizardlib.CarSetupWizardCompatLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/car_setup_wizard_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:showBackButton="true" + app:showPrimaryToolbarButton="true" + app:primaryToolbarButtonEnabled="true" + app:supportsSplitNavLayout="true" + app:supportsRotaryControl="true"/>
\ No newline at end of file diff --git a/library/main/res/layout/split_nav_design_activity.xml b/library/main/res/layout/split_nav_design_activity.xml new file mode 100644 index 0000000..19454c6 --- /dev/null +++ b/library/main/res/layout/split_nav_design_activity.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 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. + --> +<com.android.car.setupwizardlib.CarSetupWizardDesignLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/car_setup_wizard_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:showBackButton="true" + app:showPrimaryToolbarButton="true" + app:primaryToolbarButtonEnabled="true" + app:supportsSplitNavLayout="true" + app:supportsRotaryControl="true"/> diff --git a/library/main/res/layout/split_nav_layout.xml b/library/main/res/layout/split_nav_layout.xml new file mode 100644 index 0000000..03ee676 --- /dev/null +++ b/library/main/res/layout/split_nav_layout.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <FrameLayout + android:id="@+id/application_bar" + android:layout_width="match_parent" + android:layout_height="@dimen/suw_padding_8" + android:paddingStart="@dimen/navbar_padding_start" + android:gravity="start"> + + <ImageView + android:id="@+id/back_button" + android:layout_width="@dimen/car_primary_icon_size" + android:layout_height="@dimen/car_primary_icon_size" + android:layout_gravity="center_vertical" + android:background="@drawable/button_ripple_bg" + android:contentDescription="@string/back_button_content_description" + android:src="@drawable/car_ic_arrow_back"/> + + <ImageView + android:id="@+id/close_button" + android:layout_width="@dimen/car_primary_icon_size" + android:layout_height="@dimen/car_primary_icon_size" + android:layout_gravity="center_vertical" + android:background="@drawable/button_ripple_bg" + android:contentDescription="@string/close_button_content_description" + android:src="@drawable/car_ic_close"/> + </FrameLayout> + + <include layout="@layout/progressbar"/> + + <ViewStub + android:id="@+id/layout_content_stub" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"/> + + <include layout="@layout/action_bar"/> +</merge>
\ No newline at end of file diff --git a/library/main/res/values/attrs.xml b/library/main/res/values/attrs.xml index 31d2f5b..d5fd07b 100644 --- a/library/main/res/values/attrs.xml +++ b/library/main/res/values/attrs.xml @@ -90,6 +90,11 @@ of the progress bar --> <attr name="showProgressBar"/> <attr name="indeterminateProgressBar"/> + + <!-- Attributes related to the split nav layout --> + <attr name="supportsSplitNavLayout" format="boolean"/> + <!-- Attributes related to the rotary support --> + <attr name="supportsRotaryControl" format="boolean"/> </declare-styleable> </resources> diff --git a/library/main/res/values/colors.xml b/library/main/res/values/colors.xml index a3df84d..77a2748 100644 --- a/library/main/res/values/colors.xml +++ b/library/main/res/values/colors.xml @@ -36,4 +36,12 @@ <color name="suw_color_divider">#3C4043</color> <!-- Material grey900 --> <color name="suw_color_background">#202124</color> + <!-- Material grey868 --> + <color name="suw_color_split_nav_toolbar_background">#282B2D</color> + + <!-- Rotary focus highlight --> + <color name="suw_rotary_focus_stroke_color">#94CBFF</color> + <color name="suw_rotary_focus_fill_color">#3D94CBFF</color> + <color name="suw_rotary_focus_pressed_stroke_color">#94CBFF</color> + <color name="suw_rotary_focus_pressed_fill_color">#8A94CBFF</color> </resources> diff --git a/library/main/res/values/dimens.xml b/library/main/res/values/dimens.xml index db6c833..6c2b322 100644 --- a/library/main/res/values/dimens.xml +++ b/library/main/res/values/dimens.xml @@ -27,4 +27,12 @@ <dimen name="suw_page_margin_horizontal">112dp</dimen> <!-- The column inner padding of a two-column layout. N/A in portrait --> <dimen name="suw_column_inner_padding_horizontal">0dp</dimen> + + <dimen name="sidebar_width">@dimen/suw_padding_8</dimen> + <dimen name="navbar_padding_start">10dp</dimen> + <dimen name="actionbar_height">@dimen/suw_padding_9</dimen> + + <!-- Rotary focus highlight --> + <dimen name="suw_rotary_focus_stroke_width">8dp</dimen> + <dimen name="suw_rotary_focus_pressed_stroke_width">4dp</dimen> </resources>
\ No newline at end of file diff --git a/library/main/res/values/styles.xml b/library/main/res/values/styles.xml index 6ab739d..8155505 100644 --- a/library/main/res/values/styles.xml +++ b/library/main/res/values/styles.xml @@ -18,11 +18,13 @@ <style name="Widget.Car.SetupWizard.Button" parent="Widget.Car.Button.DarkText"> <item name="android:fontFamily">@font/sans_medium</item> <item name="android:textAllCaps">false</item> + <item name="android:background">@drawable/primary_button_background</item> </style> <style name="Widget.Car.SetupWizard.Button.Borderless.Colored" parent="Widget.Car.Button.Borderless.Colored"> <item name="android:fontFamily">@font/sans_medium</item> <item name="android:textAllCaps">false</item> + <item name="android:background">@drawable/flat_button_background</item> </style> </resources> diff --git a/library/main/res/values/themes.xml b/library/main/res/values/themes.xml index b24738a..ac71ded 100644 --- a/library/main/res/values/themes.xml +++ b/library/main/res/values/themes.xml @@ -23,7 +23,13 @@ <item name="android:colorControlNormal">?android:attr/colorAccent</item> <item name="android:colorControlActivated">?android:attr/colorAccent</item> <item name="android:buttonStyle">@style/Widget.Car.SetupWizard.Button</item> - <item name="android:borderlessButtonStyle">@style/Widget.Car.SetupWizard.Button.Borderless.Colored</item> + <item name="buttonStyle">@style/Widget.Car.SetupWizard.Button</item> + <item name="materialButtonStyle">@style/Widget.Car.SetupWizard.Button</item> + <item name="android:borderlessButtonStyle"> + @style/Widget.Car.SetupWizard.Button.Borderless.Colored + </item> + <item name="borderlessButtonStyle">@style/Widget.Car.SetupWizard.Button.Borderless.Colored + </item> </style> <style name="Theme.Car.SetupWizard.NoActionBar.Accent" parent="Theme.Car.SetupWizard.NoActionBar"> diff --git a/library/main/src/com/android/car/setupwizardlib/BaseCompatActivity.java b/library/main/src/com/android/car/setupwizardlib/BaseCompatActivity.java index c8bcf4b..5cca557 100644 --- a/library/main/src/com/android/car/setupwizardlib/BaseCompatActivity.java +++ b/library/main/src/com/android/car/setupwizardlib/BaseCompatActivity.java @@ -24,7 +24,8 @@ public class BaseCompatActivity extends BaseSetupWizardActivity { @Override @LayoutRes int getLayout() { - return R.layout.base_compat_activity; + return isSplitNavLayoutSupported() + ? R.layout.split_nav_compat_activity : R.layout.base_compat_activity; } @Override diff --git a/library/main/src/com/android/car/setupwizardlib/BaseDesignActivity.java b/library/main/src/com/android/car/setupwizardlib/BaseDesignActivity.java index a7288b5..5650f71 100644 --- a/library/main/src/com/android/car/setupwizardlib/BaseDesignActivity.java +++ b/library/main/src/com/android/car/setupwizardlib/BaseDesignActivity.java @@ -24,7 +24,8 @@ public class BaseDesignActivity extends BaseSetupWizardActivity { @Override @LayoutRes int getLayout() { - return R.layout.base_design_activity; + return isSplitNavLayoutSupported() + ? R.layout.split_nav_design_activity : R.layout.base_design_activity; } @Override diff --git a/library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java b/library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java index 36caae0..f8d9342 100644 --- a/library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java +++ b/library/main/src/com/android/car/setupwizardlib/BaseSetupWizardActivity.java @@ -19,7 +19,9 @@ package com.android.car.setupwizardlib; import android.content.Intent; import android.os.Bundle; import android.util.Log; +import android.view.KeyEvent; import android.view.View; +import android.view.ViewStub; import androidx.annotation.CallSuper; import androidx.annotation.LayoutRes; @@ -48,6 +50,8 @@ import com.android.car.setupwizardlib.util.CarWizardManagerHelper; * component attributes */ abstract class BaseSetupWizardActivity extends FragmentActivity { + private static final String TAG = BaseSetupWizardActivity.class.getSimpleName(); + @VisibleForTesting static final String CONTENT_FRAGMENT_TAG = "CONTENT_FRAGMENT_TAG"; /** @@ -91,13 +95,9 @@ abstract class BaseSetupWizardActivity extends FragmentActivity { mCarSetupWizardLayout = findViewById(R.id.car_setup_wizard_layout); - mCarSetupWizardLayout.setBackButtonListener(v -> { - if (!handleBackButton()) { - finish(); - } - }); + mCarSetupWizardLayout.setBackButtonListener(v -> handleBackButtonEvent()); - mCarSetupWizardLayout.setCloseButtonListener(v-> handleCloseButton()); + mCarSetupWizardLayout.setCloseButtonListener(v -> handleCloseButton()); resetPrimaryToolbarButtonOnClickListener(); resetSecondaryToolbarButtonOnClickListener(); @@ -176,8 +176,9 @@ abstract class BaseSetupWizardActivity extends FragmentActivity { @CallSuper protected void setContentFragmentWithBackstack(Fragment fragment) { if (mAllowFragmentCommits) { + inflateEmptyFragmentFrameLayout(); getSupportFragmentManager().beginTransaction() - .replace(R.id.car_setup_wizard_layout, fragment, CONTENT_FRAGMENT_TAG) + .replace(getFragmentContainerViewId(), fragment, CONTENT_FRAGMENT_TAG) .addToBackStack(null) .commit(); getSupportFragmentManager().executePendingTransactions(); @@ -199,13 +200,14 @@ abstract class BaseSetupWizardActivity extends FragmentActivity { @CallSuper protected void setContentFragment(Fragment fragment) { if (mAllowFragmentCommits) { + inflateEmptyFragmentFrameLayout(); getSupportFragmentManager().beginTransaction() .setCustomAnimations( android.R.animator.fade_in, android.R.animator.fade_out, android.R.animator.fade_in, android.R.animator.fade_out) - .replace(R.id.car_setup_wizard_layout, fragment, CONTENT_FRAGMENT_TAG) + .replace(getFragmentContainerViewId(), fragment, CONTENT_FRAGMENT_TAG) .commitNow(); onContentFragmentSet(getContentFragment()); } @@ -242,10 +244,25 @@ abstract class BaseSetupWizardActivity extends FragmentActivity { */ @CallSuper protected View setContentLayout(@LayoutRes int id) { + ViewStub viewStub = findViewById(R.id.layout_content_stub); + if (viewStub != null) { + viewStub.setLayoutResource(id); + return viewStub.inflate(); + } return getLayoutInflater().inflate(id, mCarSetupWizardLayout); } /** + * Method used when back button is pressed, if the back button wasn't handled by the activity + * finish() will be called. + */ + private void handleBackButtonEvent() { + if (!handleBackButton()) { + finish(); + } + } + + /** * Method to be overwritten by subclasses wanting to implement their own back behavior. * Default behavior is to pop a fragment off of the backstack if one exists, otherwise call * finish() @@ -256,6 +273,16 @@ abstract class BaseSetupWizardActivity extends FragmentActivity { return popBackStackImmediate(); } + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK + && event.getAction() == KeyEvent.ACTION_UP) { + handleBackButtonEvent(); + return true; + } + return super.dispatchKeyEvent(event); + } + /** * Method to be overwritten by subclasses wanting to implement their own close behavior. * Default behavior is finishAction. @@ -486,6 +513,10 @@ abstract class BaseSetupWizardActivity extends FragmentActivity { mCarSetupWizardLayout.setProgressBarVisible(visible); } + protected boolean isSplitNavLayoutSupported() { + return false; + } + private void launchNextAction(int resultCode, Intent data, boolean forResult) { if (resultCode == RESULT_CANCELED) { throw new IllegalArgumentException("Cannot call nextAction with RESULT_CANCELED"); @@ -509,6 +540,30 @@ abstract class BaseSetupWizardActivity extends FragmentActivity { } @VisibleForTesting + int getFragmentContainerViewId() { + // Check if the inflated frame layout is still available. Add the fragment to frame + // layout only if it's available. Otherwise, fall back to default layout to attach + // fragment. Note that frame layout might not be available if hosting activity inflated + // viewStub already with other views than before calling setFragmentContent(). + View frameLayout = findViewById(R.id.empty_fragment_frame_layout); + int containerViewId = frameLayout == null ? R.id.car_setup_wizard_layout : + R.id.empty_fragment_frame_layout; + Log.v(TAG, "fragmentContainerViewId: " + + getResources().getResourceEntryName(containerViewId)); + return containerViewId; + + } + + @VisibleForTesting + void inflateEmptyFragmentFrameLayout() { + ViewStub viewStub = findViewById(R.id.layout_content_stub); + if (viewStub != null) { + viewStub.setLayoutResource(R.layout.empty_fragment_frame_layout); + viewStub.inflate(); + } + } + + @VisibleForTesting boolean getAllowFragmentCommits() { return mAllowFragmentCommits; } diff --git a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java index 715315a..4fd1808 100644 --- a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java +++ b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardBaseLayout.java @@ -18,6 +18,7 @@ package com.android.car.setupwizardlib; import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Rect; import android.graphics.Typeface; @@ -39,14 +40,12 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; - import androidx.annotation.Nullable; import androidx.annotation.StyleRes; import androidx.annotation.VisibleForTesting; - import com.android.car.setupwizardlib.partner.PartnerConfig; import com.android.car.setupwizardlib.partner.PartnerConfigHelper; - +import com.android.car.setupwizardlib.util.FeatureResolver; import java.util.Locale; import java.util.Objects; @@ -56,17 +55,20 @@ import java.util.Objects; * done through methods provided by this class unless that is not possible so as to keep the state * internally consistent. */ -class CarSetupWizardBaseLayout extends LinearLayout { +class CarSetupWizardBaseLayout extends LinearLayout implements CarSetupWizardLayoutInterface { private static final String TAG = CarSetupWizardBaseLayout.class.getSimpleName(); private static final int INVALID_COLOR = 0; // For mirroring an image private static final float IMAGE_MIRROR_ROTATION = 180.0f; + private static final float MIN_ULTRA_WIDE_CONTENT_WIDTH = 1240.0f; private View mBackButton; private View mCloseButton; private View mTitleBar; private TextView mToolbarTitle; private PartnerConfigHelper mPartnerConfigHelper; + private boolean mSupportsSplitNavLayout; + private boolean mSupportsRotaryControl; /* <p>The Primary Toolbar Button should always be used when there is only a single action that * moves the wizard to the next screen (e.g. Only need a 'Skip' button). @@ -95,7 +97,7 @@ class CarSetupWizardBaseLayout extends LinearLayout { } CarSetupWizardBaseLayout(Context context, @Nullable AttributeSet attrs, - int defStyleAttr) { + int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } @@ -104,7 +106,7 @@ class CarSetupWizardBaseLayout extends LinearLayout { * the custom views that can be set by the user (e.g. back button, continue button). */ CarSetupWizardBaseLayout(Context context, @Nullable AttributeSet attrs, - int defStyleAttr, int defStyleRes) { + int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mPartnerConfigHelper = PartnerConfigHelper.get(context); @@ -164,12 +166,19 @@ class CarSetupWizardBaseLayout extends LinearLayout { R.styleable.CarSetupWizardBaseLayout_showProgressBar, false); indeterminateProgressBar = attrArray.getBoolean( R.styleable.CarSetupWizardBaseLayout_indeterminateProgressBar, true); + mSupportsSplitNavLayout = attrArray.getBoolean( + R.styleable.CarSetupWizardBaseLayout_supportsSplitNavLayout, false); + mSupportsRotaryControl = attrArray.getBoolean( + R.styleable.CarSetupWizardBaseLayout_supportsRotaryControl, false); } finally { attrArray.recycle(); } LayoutInflater inflater = LayoutInflater.from(getContext()); - inflater.inflate(R.layout.car_setup_wizard_layout, this); + inflater.inflate(getLayoutResourceId(), this); + + maybeSetUltraWideScreenContentWidth(); + View toolbar = findViewById(R.id.application_bar); // The toolbar will not be mirrored in RTL toolbar.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); @@ -206,10 +215,15 @@ class CarSetupWizardBaseLayout extends LinearLayout { // Se the title bar. setTitleBar(findViewById(R.id.application_bar)); - int toolbarBgColor = - mPartnerConfigHelper.getColor(getContext(), PartnerConfig.CONFIG_TOOLBAR_BG_COLOR); - if (toolbarBgColor != 0) { - mTitleBar.setBackgroundColor(toolbarBgColor); + if (isSplitNavLayoutUsed()) { + mTitleBar.setBackgroundColor(getResources() + .getColor(R.color.suw_color_split_nav_toolbar_background, null)); + } else { + int toolbarBgColor = mPartnerConfigHelper.getColor( + getContext(), PartnerConfig.CONFIG_TOOLBAR_BG_COLOR); + if (toolbarBgColor != 0) { + mTitleBar.setBackgroundColor(toolbarBgColor); + } } // Set the toolbar title visibility and text based on the custom attributes. @@ -264,7 +278,25 @@ class CarSetupWizardBaseLayout extends LinearLayout { initDivider(); // Set orientation programmatically since the inflated layout uses <merge> - setOrientation(LinearLayout.VERTICAL); + if (isSplitNavLayoutUsed() && getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE) { + setOrientation(LinearLayout.HORIZONTAL); + // The vertical bar will not be mirrored in RTL + setLayoutDirection(View.LAYOUT_DIRECTION_LTR); + + View contentContainer = getContentContainer(); + if (contentContainer != null) { + // The content should be mirrored in RTL + contentContainer.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE); + } + View actionBar = findViewById(R.id.button_container); + if (actionBar != null) { + // The action bar will not be mirrored in RTL + actionBar.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); + } + } else { + setOrientation(LinearLayout.VERTICAL); + } } /** @@ -400,6 +432,9 @@ class CarSetupWizardBaseLayout extends LinearLayout { * Sets the header title visibility to given value. */ public void setToolbarTitleVisible(boolean visible) { + if (mToolbarTitle == null) { + return; + } setViewVisible(mToolbarTitle, visible); } @@ -407,6 +442,9 @@ class CarSetupWizardBaseLayout extends LinearLayout { * Sets the header title text to the provided text. */ public void setToolbarTitleText(String text) { + if (mToolbarTitle == null) { + return; + } mToolbarTitle.setText(text); } @@ -414,6 +452,9 @@ class CarSetupWizardBaseLayout extends LinearLayout { * Sets the style for the toolbar title. */ public void setToolbarTitleStyle(@StyleRes int style) { + if (mToolbarTitle == null) { + return; + } mToolbarTitle.setTextAppearance(style); } @@ -557,28 +598,109 @@ class CarSetupWizardBaseLayout extends LinearLayout { return mProgressBar; } - /** - * Sets the progress bar visibility to the given visibility. - */ + @Override public void setProgressBarVisible(boolean visible) { setViewVisible(mProgressBar, visible); } - /** - * Sets the progress bar indeterminate/determinate state. - */ + @Override public void setProgressBarIndeterminate(boolean indeterminate) { mProgressBar.setIndeterminate(indeterminate); } - /** - * Sets the progress bar's progress. - */ + @Override public void setProgressBarProgress(int progress) { setProgressBarIndeterminate(false); mProgressBar.setProgress(progress); } + @Override + public Button getPrimaryActionButton() { + return getPrimaryToolbarButton(); + } + + @Override + public void setPrimaryActionButtonVisible(boolean visible) { + setPrimaryToolbarButtonVisible(visible); + } + + @Override + public void setPrimaryActionButtonEnabled(boolean enabled) { + setPrimaryToolbarButtonEnabled(enabled); + } + + @Override + public void setPrimaryActionButtonText(String text) { + setPrimaryToolbarButtonText(text); + } + + @Override + public void setPrimaryActionButtonListener(@Nullable View.OnClickListener listener) { + setPrimaryToolbarButtonListener(listener); + } + + @Override + public void setPrimaryActionButtonFlat(boolean isFlat) { + setPrimaryToolbarButtonFlat(isFlat); + } + + @Override + public boolean isPrimaryActionButtonFlat() { + return getPrimaryToolbarButtonFlat(); + } + + @Override + public Button getSecondaryActionButton() { + return getSecondaryToolbarButton(); + } + + @Override + public void setSecondaryActionButtonVisible(boolean visible) { + setSecondaryToolbarButtonVisible(visible); + } + + @Override + public void setSecondaryActionButtonEnabled(boolean enabled) { + setSecondaryToolbarButtonEnabled(enabled); + } + + @Override + public void setSecondaryActionButtonText(String text) { + setSecondaryToolbarButtonText(text); + } + + @Override + public void setSecondaryActionButtonListener(@Nullable View.OnClickListener listener) { + setSecondaryToolbarButtonListener(listener); + } + + /** + * Returns whether split-nav layout is currently being used. Do not use this API to determine + * whether content ViewStub should be inflated. Use {@code getContentViewStub} for that purpose. + */ + public boolean isSplitNavLayoutUsed() { + boolean isSplitNavLayoutEnabled = FeatureResolver.get(getContext()) + .isSplitNavLayoutFeatureEnabled(); + return mSupportsSplitNavLayout && isSplitNavLayoutEnabled; + } + + /** + * Returns the content ViewStub of the split-nav layout. + * + * @deprecated Use {@code getContentViewStub}. + */ + @Deprecated + public ViewStub getSplitNavContentViewStub() { + return findViewById(R.id.layout_content_stub); + } + + /** + * Returns the content ViewStub when split-nav layout is used or rotary control is supported + */ + public ViewStub getContentViewStub() { + return findViewById(R.id.layout_content_stub); + } + /** * Sets the locale to be used for rendering. */ @@ -588,8 +710,10 @@ class CarSetupWizardBaseLayout extends LinearLayout { } int direction = TextUtils.getLayoutDirectionFromLocale(locale); - mToolbarTitle.setTextLocale(locale); - mToolbarTitle.setLayoutDirection(direction); + if (mToolbarTitle != null) { + mToolbarTitle.setTextLocale(locale); + mToolbarTitle.setLayoutDirection(direction); + } mPrimaryToolbarButton.setTextLocale(locale); mPrimaryToolbarButton.setLayoutDirection(direction); @@ -641,7 +765,9 @@ class CarSetupWizardBaseLayout extends LinearLayout { } } - /** Sets button text color using partner overlay if exists */ + /** + * Sets button text color using partner overlay if exists + */ @VisibleForTesting void setButtonTextColor(TextView button, PartnerConfig config) { ColorStateList colorStateList = @@ -669,7 +795,9 @@ class CarSetupWizardBaseLayout extends LinearLayout { } } - /** Sets button background color using partner overlay if exists */ + /** + * Sets button background color using partner overlay if exists + */ @VisibleForTesting void setBackgroundColor(View button, PartnerConfig config) { ColorStateList color = mPartnerConfigHelper.getColorStateList(getContext(), config); @@ -678,7 +806,9 @@ class CarSetupWizardBaseLayout extends LinearLayout { } } - /** Sets button text size using partner overlay if exists */ + /** + * Sets button text size using partner overlay if exists + */ @VisibleForTesting void setButtonTextSize(TextView button) { float dimension = mPartnerConfigHelper.getDimension( @@ -692,13 +822,13 @@ class CarSetupWizardBaseLayout extends LinearLayout { @VisibleForTesting boolean shouldMirrorNavIcons() { return getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL - && mPartnerConfigHelper.getBoolean( - getContext(), - PartnerConfig.CONFIG_TOOLBAR_NAV_ICON_MIRRORING_IN_RTL, - true); + && mPartnerConfigHelper.getBoolean( + getContext(), PartnerConfig.CONFIG_TOOLBAR_NAV_ICON_MIRRORING_IN_RTL, true); } - /** Sets button type face with partner overlay if exists */ + /** + * Sets button type face with partner overlay if exists + */ private void setButtonTypeFace(TextView button) { String fontFamily = mPartnerConfigHelper.getString( getContext(), @@ -716,7 +846,9 @@ class CarSetupWizardBaseLayout extends LinearLayout { button.setTypeface(typeface); } - /** Sets button radius using partner overlay if exists */ + /** + * Sets button radius using partner overlay if exists + */ private void setButtonRadius(Button button) { float radius = mPartnerConfigHelper.getDimension( getContext(), @@ -760,8 +892,23 @@ class CarSetupWizardBaseLayout extends LinearLayout { setButtonTextColor(primaryButton, textColorConfig); } + private int getLayoutResourceId() { + if (isSplitNavLayoutUsed()) { + return mSupportsRotaryControl + ? R.layout.rotary_split_nav_layout + : R.layout.split_nav_layout; + } + + return mSupportsRotaryControl + ? R.layout.rotary_car_setup_wizard_layout + : R.layout.car_setup_wizard_layout; + } + private void initDivider() { mDivider = findViewById(R.id.divider); + if (mDivider == null) { + return; + } float dividerHeight = mPartnerConfigHelper.getDimension( getContext(), PartnerConfig.CONFIG_TOOLBAR_DIVIDER_LINE_WEIGHT); @@ -791,7 +938,9 @@ class CarSetupWizardBaseLayout extends LinearLayout { if (drawable instanceof InsetDrawable) { return getGradientDrawableFromInsetDrawable((InsetDrawable) drawable); } - return (GradientDrawable) drawable; + if (drawable instanceof GradientDrawable) { + return (GradientDrawable) drawable; + } } return null; @@ -803,4 +952,53 @@ class CarSetupWizardBaseLayout extends LinearLayout { } return null; } + + private void maybeSetUltraWideScreenContentWidth() { + View contentContainer = findViewById(R.id.ultra_wide_content_container); + if (contentContainer == null) { + return; + } + + float configurableContentWidth = mPartnerConfigHelper.getDimension( + getContext(), + PartnerConfig.CONFIG_ULTRA_WIDE_SCREEN_CONTENT_WIDTH); + + float pxMinWidth = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + MIN_ULTRA_WIDE_CONTENT_WIDTH, + getResources().getDisplayMetrics()); + + if (configurableContentWidth >= pxMinWidth) { + ViewGroup.LayoutParams layoutParams = contentContainer.getLayoutParams(); + layoutParams.width = (int) configurableContentWidth; + Log.d(TAG, String.format("Applying content width %f px", configurableContentWidth)); + contentContainer.setLayoutParams(layoutParams); + } else { + if (configurableContentWidth != 0) { + Log.w(TAG, String.format("The minimum ultra wide screen content width is %d dp", + (int) MIN_ULTRA_WIDE_CONTENT_WIDTH)); + } + + LinearLayout.LayoutParams contentParams = new LinearLayout.LayoutParams( + 0, LayoutParams.MATCH_PARENT); + contentParams.weight = 1; + contentContainer.setLayoutParams(contentParams); + + LinearLayout.LayoutParams fillerParams = new LinearLayout.LayoutParams( + 0, LayoutParams.MATCH_PARENT); + fillerParams.weight = 0; + View filler = findViewById(R.id.ultra_wide_space_filler); + filler.setLayoutParams(fillerParams); + } + } + + private View getContentContainer() { + View contentContainer = findViewById(R.id.content_container); + if (contentContainer == null) { + // Try ultra-wide container + return findViewById(R.id.ultra_wide_content_container); + + } + return contentContainer; + } } diff --git a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayout.java b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayout.java index f5191cc..72f4f1b 100644 --- a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayout.java +++ b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayout.java @@ -18,7 +18,6 @@ package com.android.car.setupwizardlib; import android.content.Context; import android.util.AttributeSet; - import androidx.annotation.Nullable; /** @@ -43,5 +42,7 @@ public class CarSetupWizardCompatLayout extends CarSetupWizardBaseLayout { public CarSetupWizardCompatLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + + setBackgroundColor(getResources().getColor(R.color.suw_color_background, null)); } } diff --git a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardDesignLayout.java b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardDesignLayout.java index 92269f0..c0a1831 100644 --- a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardDesignLayout.java +++ b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardDesignLayout.java @@ -18,9 +18,7 @@ package com.android.car.setupwizardlib; import android.content.Context; import android.util.AttributeSet; - import androidx.annotation.Nullable; - import com.android.car.setupwizardlib.partner.PartnerConfig; import com.android.car.setupwizardlib.partner.PartnerConfigHelper; diff --git a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java index 19a1923..67bb795 100644 --- a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java +++ b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardLayout.java @@ -38,14 +38,11 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; - import androidx.annotation.Nullable; import androidx.annotation.StyleRes; import androidx.annotation.VisibleForTesting; - import com.android.car.setupwizardlib.partner.PartnerConfig; import com.android.car.setupwizardlib.partner.PartnerConfigHelper; - import java.util.Locale; import java.util.Objects; diff --git a/library/main/src/com/android/car/setupwizardlib/CarSetupWizardLayoutInterface.java b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardLayoutInterface.java new file mode 100644 index 0000000..7126560 --- /dev/null +++ b/library/main/src/com/android/car/setupwizardlib/CarSetupWizardLayoutInterface.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2021 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.car.setupwizardlib; + +import android.view.View; +import android.widget.Button; +import androidx.annotation.Nullable; + +/** + * The interface defines the functionality of the layouts used in CarSetupWizard. It + * makes it easy to switch to a different layout. + */ +public interface CarSetupWizardLayoutInterface { + + /** Returns the primary action button. */ + Button getPrimaryActionButton(); + + /** Sets whether the primary action button is visible. */ + void setPrimaryActionButtonVisible(boolean visible); + + /** Sets whether the primary action button is enabled. */ + void setPrimaryActionButtonEnabled(boolean enabled); + + /** Sets the text of the primary action button. */ + void setPrimaryActionButtonText(String text); + + /** + * Sets the onClick listener of the primary action button. + * + * @param listener the listener to be set. When it's null, the previously set listener will be + * removed + */ + void setPrimaryActionButtonListener(@Nullable View.OnClickListener listener); + + /** Sets whether the primary action button is flat which means it does not have a background. */ + void setPrimaryActionButtonFlat(boolean isFlat); + + /** Returns whether the primary action button is flat. */ + boolean isPrimaryActionButtonFlat(); + + /** Returns the secondary action button. */ + Button getSecondaryActionButton(); + + /** Sets whether the secondary action button is visible. */ + void setSecondaryActionButtonVisible(boolean visible); + + /** Sets whether the secondary action button is enabled. */ + void setSecondaryActionButtonEnabled(boolean enabled); + + /** Sets the text of the secondary action button. */ + void setSecondaryActionButtonText(String text); + + /** + * Sets the onClick listener of the secondary action button. + * + * @param listener the listener to be set. When it's null, the previously set listener will be + * removed + */ + void setSecondaryActionButtonListener(@Nullable View.OnClickListener listener); + + /** Sets whether the progress bar is visible. */ + void setProgressBarVisible(boolean visible); + + /** Sets whether the progress bar is indeterminate. */ + void setProgressBarIndeterminate(boolean indeterminate); + + /** Sets the progress bar's progress. */ + void setProgressBarProgress(int progress); +} diff --git a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java index 54d1e6e..2ee8d07 100644 --- a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java +++ b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfig.java @@ -83,7 +83,10 @@ public enum PartnerConfig { PartnerConfigKey.KEY_LOADING_INDICATOR_LINE_WEIGHT, ResourceType.DIMENSION), CONFIG_LAYOUT_BG_COLOR( - PartnerConfigKey.KEY_LAYOUT_BG_COLOR, ResourceType.COLOR); + PartnerConfigKey.KEY_LAYOUT_BG_COLOR, ResourceType.COLOR), + + CONFIG_ULTRA_WIDE_SCREEN_CONTENT_WIDTH( + PartnerConfigKey.KEY_ULTRA_WIDE_SCREEN_CONTENT_WIDTH, ResourceType.DIMENSION); public enum ResourceType { COLOR, diff --git a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java index dfb134f..093961b 100644 --- a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java +++ b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigHelper.java @@ -27,12 +27,10 @@ import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.util.TypedValue; - import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; - import java.util.EnumMap; /** The helper reads and caches the partner configurations from Car Setup Wizard. */ diff --git a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java index 7f9dadd..e7800d1 100644 --- a/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java +++ b/library/main/src/com/android/car/setupwizardlib/partner/PartnerConfigKey.java @@ -44,7 +44,8 @@ import java.lang.annotation.RetentionPolicy; PartnerConfigKey.KEY_TOOLBAR_DIVIDER_LINE_WEIGHT, PartnerConfigKey.KEY_LOADING_INDICATOR_COLOR, PartnerConfigKey.KEY_LOADING_INDICATOR_LINE_WEIGHT, - PartnerConfigKey.KEY_LAYOUT_BG_COLOR + PartnerConfigKey.KEY_LAYOUT_BG_COLOR, + PartnerConfigKey.KEY_ULTRA_WIDE_SCREEN_CONTENT_WIDTH }) /** Resource names that can be customized by partner overlay APK. */ @@ -96,4 +97,6 @@ public @interface PartnerConfigKey { String KEY_LOADING_INDICATOR_LINE_WEIGHT = "suw_compat_loading_indicator_line_weight"; String KEY_LAYOUT_BG_COLOR = "suw_design_layout_bg_color"; + + String KEY_ULTRA_WIDE_SCREEN_CONTENT_WIDTH = "suw_compat_ultra_wide_screen_content_width"; } diff --git a/library/main/src/com/android/car/setupwizardlib/partner/ResourceEntry.java b/library/main/src/com/android/car/setupwizardlib/partner/ResourceEntry.java index 6d5d39b..456f1e6 100644 --- a/library/main/src/com/android/car/setupwizardlib/partner/ResourceEntry.java +++ b/library/main/src/com/android/car/setupwizardlib/partner/ResourceEntry.java @@ -18,7 +18,6 @@ package com.android.car.setupwizardlib.partner; import android.content.pm.PackageManager; import android.os.Bundle; - import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; diff --git a/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java b/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java index 2dfb04b..84842f3 100644 --- a/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java +++ b/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java @@ -27,9 +27,7 @@ import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; - import androidx.annotation.Nullable; - import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -151,11 +149,13 @@ public class PartnerSummaryActionsCollector { String completedDescription = summaryStateBundle.getString(EXTRA_SUMMARY_COMPLETED_DESCRIPTION, description); + SummaryActionState completeState = completed + ? SummaryActionState.COMPLETED : SummaryActionState.NOT_COMPLETED; return new SummaryAction( title, description, requiresNetwork, - completed, + completeState, priority, scriptUri, hasUnfinishedDependency, diff --git a/library/main/src/com/android/car/setupwizardlib/summary/SummaryAction.java b/library/main/src/com/android/car/setupwizardlib/summary/SummaryAction.java index 6f9e626..55d71d7 100644 --- a/library/main/src/com/android/car/setupwizardlib/summary/SummaryAction.java +++ b/library/main/src/com/android/car/setupwizardlib/summary/SummaryAction.java @@ -28,7 +28,7 @@ public class SummaryAction implements Comparable<SummaryAction> { public final int priority; public final boolean hasUnfinishedDependency; public final String dependencyDescription; - public final boolean completed; + public final SummaryActionState completeState; public final String iconResourceName; public final String completedDescription; @@ -36,7 +36,7 @@ public class SummaryAction implements Comparable<SummaryAction> { String actionTitle, String actionDescription, boolean requiresNetwork, - boolean completed, + SummaryActionState completeState, int priority, String scriptUri, boolean hasUnfinishedDependency, @@ -46,7 +46,7 @@ public class SummaryAction implements Comparable<SummaryAction> { this.actionTitle = actionTitle; this.actionDescription = actionDescription; this.requiresNetwork = requiresNetwork; - this.completed = completed; + this.completeState = completeState; this.priority = priority; this.scriptUri = scriptUri; this.hasUnfinishedDependency = hasUnfinishedDependency; @@ -55,6 +55,11 @@ public class SummaryAction implements Comparable<SummaryAction> { this.completedDescription = completedDescription; } + /** Returns {@code true} if the action is completed. */ + public boolean isCompleted() { + return completeState == SummaryActionState.COMPLETED; + } + @Override public int compareTo(@NonNull SummaryAction o) { if (o == null) { diff --git a/library/main/src/com/android/car/setupwizardlib/summary/SummaryActionState.java b/library/main/src/com/android/car/setupwizardlib/summary/SummaryActionState.java new file mode 100644 index 0000000..2b7daf2 --- /dev/null +++ b/library/main/src/com/android/car/setupwizardlib/summary/SummaryActionState.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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.car.setupwizardlib.summary; + +/** Complete state of the action. */ +public enum SummaryActionState { + COMPLETED, + NOT_COMPLETED, + WAITING_TO_RESOLVE +} diff --git a/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java b/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java index c58e352..9528502 100644 --- a/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java +++ b/library/main/src/com/android/car/setupwizardlib/util/CarDrivingStateMonitor.java @@ -33,13 +33,12 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.util.Log; - import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; /** * Monitor that listens for changes in the driving state so that it can trigger an exit of the - * setup wizard when {@link CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP} + * setup wizard when {@link CarUxRestrictions#UX_RESTRICTIONS_NO_SETUP} * is active. */ public class CarDrivingStateMonitor implements diff --git a/library/main/src/com/android/car/setupwizardlib/util/CarHelperRegistry.java b/library/main/src/com/android/car/setupwizardlib/util/CarHelperRegistry.java index 01447aa..148ba32 100644 --- a/library/main/src/com/android/car/setupwizardlib/util/CarHelperRegistry.java +++ b/library/main/src/com/android/car/setupwizardlib/util/CarHelperRegistry.java @@ -17,11 +17,9 @@ package com.android.car.setupwizardlib.util; import android.content.Context; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; - import java.util.concurrent.ConcurrentHashMap; /** A registry of singleton-like helpers, which can be injected by the application for testing. */ diff --git a/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java b/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java index 4e1d8ca..2740455 100644 --- a/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java +++ b/library/main/src/com/android/car/setupwizardlib/util/CarSetupWizardUiUtils.java @@ -38,6 +38,9 @@ public final class CarSetupWizardUiUtils { public static final String EXTRA_NEW_LANDSCAPE_LAYOUT_SUPPORTED = "extra_new_landscape_layout_supported"; + /* Setup Wizard Package Name **/ + public static final String SETUP_WIZARD_PACKAGE = "com.google.android.car.setupwizard"; + /** Hide system UI */ public static void hideSystemUI(Activity activity) { maybeHideSystemUI(activity); diff --git a/library/main/src/com/android/car/setupwizardlib/util/CarWizardManagerHelper.java b/library/main/src/com/android/car/setupwizardlib/util/CarWizardManagerHelper.java index ff83b55..8f67220 100644 --- a/library/main/src/com/android/car/setupwizardlib/util/CarWizardManagerHelper.java +++ b/library/main/src/com/android/car/setupwizardlib/util/CarWizardManagerHelper.java @@ -20,153 +20,144 @@ import android.content.Context; import android.content.Intent; import android.provider.Settings; -/** - * <p>Derived from {@code com.android.setupwizardlib/WizardManagerHelper.java} - */ +/** Derived from {@code com.android.setupwizardlib/WizardManagerHelper.java} */ public final class CarWizardManagerHelper { - public static final String EXTRA_WIZARD_BUNDLE = "wizardBundle"; - public static final String EXTRA_IS_FIRST_RUN = "firstRun"; - public static final String EXTRA_IS_DEALER = "dealer"; - public static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup"; - private static final String ACTION_NEXT = "com.android.wizard.NEXT"; - private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode"; - - private CarWizardManagerHelper() { - } + public static final String EXTRA_WIZARD_BUNDLE = "wizardBundle"; + public static final String EXTRA_IS_FIRST_RUN = "firstRun"; + public static final String EXTRA_IS_DEALER = "dealer"; + public static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup"; + private static final String ACTION_NEXT = "com.android.wizard.NEXT"; + private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode"; - /** - * Get an intent that will invoke the next step of setup wizard. - * - * @param originalIntent The original intent that was used to start the step, usually via - * {@link android.app.Activity#getIntent()}. - * @param resultCode The result code of the step. See {@link ResultCodes}. - * @return A new intent that can be used with - * {@link android.app.Activity#startActivityForResult(Intent, int)} to start the next - * step of the setup flow. - */ - public static Intent getNextIntent(Intent originalIntent, int resultCode) { - return getNextIntent(originalIntent, resultCode, null); - } + private CarWizardManagerHelper() {} - /** - * Get an intent that will invoke the next step of setup wizard. - * - * @param originalIntent The original intent that was used to start the step, usually via - * {@link android.app.Activity#getIntent()}. - * @param resultCode The result code of the step. See {@link ResultCodes}. - * @param data An intent containing extra result data. - * @return A new intent that can be used with - * {@link android.app.Activity#startActivityForResult(Intent, int)} to start the next - * step of the setup flow. - */ - public static Intent getNextIntent(Intent originalIntent, int resultCode, Intent data) { - Intent intent = new Intent(ACTION_NEXT); - copyWizardManagerExtras(originalIntent, intent); - intent.putExtra(EXTRA_RESULT_CODE, resultCode); - if (data != null && data.getExtras() != null) { - intent.putExtras(data.getExtras()); - } + /** + * Get an intent that will invoke the next step of setup wizard. + * + * @param originalIntent The original intent that was used to start the step, usually via {@link + * android.app.Activity#getIntent()}. + * @param resultCode The result code of the step. See {@link ResultCodes}. + * @return A new intent that can be used with {@link + * android.app.Activity#startActivityForResult(Intent, int)} to start the next step of the + * setup flow. + */ + public static Intent getNextIntent(Intent originalIntent, int resultCode) { + return getNextIntent(originalIntent, resultCode, null); + } - return intent; + /** + * Get an intent that will invoke the next step of setup wizard. + * + * @param originalIntent The original intent that was used to start the step, usually via {@link + * android.app.Activity#getIntent()}. + * @param resultCode The result code of the step. See {@link ResultCodes}. + * @param data An intent containing extra result data. + * @return A new intent that can be used with {@link + * android.app.Activity#startActivityForResult(Intent, int)} to start the next step of the + * setup flow. + */ + public static Intent getNextIntent(Intent originalIntent, int resultCode, Intent data) { + Intent intent = new Intent(ACTION_NEXT); + copyWizardManagerExtras(originalIntent, intent); + intent.putExtra(EXTRA_RESULT_CODE, resultCode); + if (data != null && data.getExtras() != null) { + intent.putExtras(data.getExtras()); } - /** - * Copy the internal extras used by setup wizard from one intent to another. For low-level use - * only, such as when using {@link Intent#FLAG_ACTIVITY_FORWARD_RESULT} to relay to another - * intent. - * - * @param srcIntent Intent to get the wizard manager extras from. - * @param dstIntent Intent to copy the wizard manager extras to. - */ - public static void copyWizardManagerExtras(Intent srcIntent, Intent dstIntent) { - dstIntent.putExtra(EXTRA_WIZARD_BUNDLE, srcIntent.getBundleExtra(EXTRA_WIZARD_BUNDLE)); - dstIntent.putExtra(EXTRA_IS_FIRST_RUN, - srcIntent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false)); - dstIntent.putExtra(EXTRA_IS_DEALER, - srcIntent.getBooleanExtra(EXTRA_IS_DEALER, false)); - dstIntent.putExtra(EXTRA_IS_DEFERRED_SETUP, - srcIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false)); - } + return intent; + } - /** - * Check whether an intent is intended to be used within the setup wizard flow. - * - * @param intent The intent to be checked, usually from - * {@link android.app.Activity#getIntent()}. - * @return true if the intent passed in was intended to be used with setup wizard. - */ - public static boolean isSetupWizardIntent(Intent intent) { - return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); - } + /** + * Copy the internal extras used by setup wizard from one intent to another. For low-level use + * only, such as when using {@link Intent#FLAG_ACTIVITY_FORWARD_RESULT} to relay to another + * intent. + * + * @param srcIntent Intent to get the wizard manager extras from. + * @param dstIntent Intent to copy the wizard manager extras to. + */ + public static void copyWizardManagerExtras(Intent srcIntent, Intent dstIntent) { + dstIntent.putExtra(EXTRA_WIZARD_BUNDLE, srcIntent.getBundleExtra(EXTRA_WIZARD_BUNDLE)); + dstIntent.putExtra(EXTRA_IS_FIRST_RUN, srcIntent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false)); + dstIntent.putExtra(EXTRA_IS_DEALER, srcIntent.getBooleanExtra(EXTRA_IS_DEALER, false)); + dstIntent.putExtra( + EXTRA_IS_DEFERRED_SETUP, srcIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false)); + } - /** - * Checks whether an intent is running in the deferred setup wizard flow. - * - * @param intent The intent to be checked, usually from - * {@link android.app.Activity#getIntent()}. - * @return true if the intent passed in was running in deferred setup wizard. - */ - public static boolean isDeferredIntent(Intent intent) { - return intent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false); - } + /** + * Check whether an intent is intended to be used within the setup wizard flow. + * + * @param intent The intent to be checked, usually from {@link android.app.Activity#getIntent()}. + * @return true if the intent passed in was intended to be used with setup wizard. + */ + public static boolean isSetupWizardIntent(Intent intent) { + return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); + } - /** - * Check whether an intent is intended for the dealer. - * - * @param intent The intent to be checked, usually from - * {@link android.app.Activity#getIntent()}. - * @return true if the intent passed in was intended to be used with setup wizard. - */ - public static boolean isDealerIntent(Intent intent) { - return intent.getBooleanExtra(EXTRA_IS_DEALER, false); - } + /** + * Checks whether an intent is running in the deferred setup wizard flow. + * + * @param intent The intent to be checked, usually from {@link android.app.Activity#getIntent()}. + * @return true if the intent passed in was running in deferred setup wizard. + */ + public static boolean isDeferredIntent(Intent intent) { + return intent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false); + } - /** - * Checks whether the current user has completed Setup Wizard. This is true if the current user - * has gone through Setup Wizard. The current user may or may not be the device owner and the - * device owner may have already completed setup wizard. - * - * @param context The context to retrieve the settings. - * @return true if the current user has completed Setup Wizard. - * @see #isDeviceProvisioned(Context) - */ - public static boolean isUserSetupComplete(Context context) { - return Settings.Secure.getInt(context.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, 0) == 1; - } + /** + * Check whether an intent is intended for the dealer. + * + * @param intent The intent to be checked, usually from {@link android.app.Activity#getIntent()}. + * @return true if the intent passed in was intended to be used with setup wizard. + */ + public static boolean isDealerIntent(Intent intent) { + return intent.getBooleanExtra(EXTRA_IS_DEALER, false); + } - /** - * Checks whether the device is provisioned. This means that the device has gone through Setup - * Wizard at least once. Note that the user can still be in Setup Wizard even if this is true, - * for a secondary user profile triggered through Settings > Add account. - * - * @param context The context to retrieve the settings. - * @return true if the device is provisioned. - * @see #isUserSetupComplete(Context) - */ - public static boolean isDeviceProvisioned(Context context) { - return Settings.Global.getInt(context.getContentResolver(), - Settings.Global.DEVICE_PROVISIONED, 0) == 1; - } - /** - * Checks whether an intent is running in the initial setup wizard flow. - * - * @param intent The intent to be checked, usually from {@link Activity#getIntent()}. - * @return true if the intent passed in was intended to be used with setup wizard. - */ - public static boolean isInitialSetupWizard(Intent intent) { - return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); - } + /** + * Checks whether the current user has completed Setup Wizard. This is true if the current user + * has gone through Setup Wizard. The current user may or may not be the device owner and the + * device owner may have already completed setup wizard. + * + * @param context The context to retrieve the settings. + * @return true if the current user has completed Setup Wizard. + * @see #isDeviceProvisioned(Context) + */ + public static boolean isUserSetupComplete(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0) == 1; + } - /** - * Checks whether an intent is running in the deferred setup wizard flow. - * - * @param originalIntent The original intent that was used to start the step, usually via {@link - * Activity#getIntent()}. - * @return true if the intent passed in was running in deferred setup wizard. - */ - public static boolean isDeferredSetupWizard(Intent originalIntent) { - return originalIntent != null && originalIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, - false); - } + /** + * Checks whether the device is provisioned. This means that the device has gone through Setup + * Wizard at least once. Note that the user can still be in Setup Wizard even if this is true, for + * a secondary user profile triggered through Settings > Add account. + * + * @param context The context to retrieve the settings. + * @return true if the device is provisioned. + * @see #isUserSetupComplete(Context) + */ + public static boolean isDeviceProvisioned(Context context) { + return Settings.Global.getInt( + context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) + == 1; + } + /** + * Checks whether an intent is running in the initial setup wizard flow. + * + * @param intent The intent to be checked, usually from {@link android.app.Activity#getIntent()} + * @return true if the intent passed in was intended to be used with setup wizard. + */ + public static boolean isInitialSetupWizard(Intent intent) { + return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); + } + + /** + * Checks whether an intent is running in the deferred setup wizard flow. + * + * @param originalIntent The original intent that was used to start the step, usually via {@link + * android.app.Activity#getIntent()}. + * @return true if the intent passed in was running in deferred setup wizard. + */ + public static boolean isDeferredSetupWizard(Intent originalIntent) { + return originalIntent != null && originalIntent.getBooleanExtra(EXTRA_IS_DEFERRED_SETUP, false); + } } diff --git a/library/main/src/com/android/car/setupwizardlib/util/FeatureResolver.java b/library/main/src/com/android/car/setupwizardlib/util/FeatureResolver.java new file mode 100644 index 0000000..8f0114f --- /dev/null +++ b/library/main/src/com/android/car/setupwizardlib/util/FeatureResolver.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 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.car.setupwizardlib.util; + +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * A class to resolve feature enablement and versions + */ +public class FeatureResolver { + + private static final String TAG = FeatureResolver.class.getSimpleName(); + private static volatile FeatureResolver sInstance = null; + + static final String AUTHORITY = + CarSetupWizardUiUtils.SETUP_WIZARD_PACKAGE + ".feature_management"; + static final String GET_FEATURE_VERSION_METHOD = "getFeatureVersion"; + static final String SPLIT_NAV_LAYOUT_FEATURE = "split_nav_layout"; + static final String G_MODAL_FEATURE = "g_modal"; + static final String VALUE = "value"; + + private Context mContext; + + private Map<String, Bundle> mResultMap = new HashMap<>(); + + /** + * Factory method to get an instance + */ + public static FeatureResolver get(@NonNull Context context) { + if (sInstance == null) { + synchronized (FeatureResolver.class) { + if (sInstance == null) { + sInstance = new FeatureResolver(context); + } + } + } + return sInstance; + } + + /** + * Returns whether the alternative layout feature is enabled + */ + public boolean isSplitNavLayoutFeatureEnabled() { + Bundle bundle; + if (mResultMap.containsKey(SPLIT_NAV_LAYOUT_FEATURE)) { + bundle = mResultMap.get(SPLIT_NAV_LAYOUT_FEATURE); + } else { + bundle = getFeatureBundle(SPLIT_NAV_LAYOUT_FEATURE); + mResultMap.put(SPLIT_NAV_LAYOUT_FEATURE, bundle); + } + boolean isSplitNavLayoutFeatureEnabled = bundle != null + && bundle.getBoolean(VALUE, false); + Log.v(TAG, String.format("isSplitNavLayoutEnabled: %s", isSplitNavLayoutFeatureEnabled)); + return isSplitNavLayoutFeatureEnabled; + } + + /** + * Returns the gModalVersion + */ + public int getGModalVersion() { + Bundle bundle; + if (mResultMap.containsKey(G_MODAL_FEATURE)) { + bundle = mResultMap.get(G_MODAL_FEATURE); + } else { + bundle = getFeatureBundle(G_MODAL_FEATURE); + mResultMap.put(G_MODAL_FEATURE, bundle); + } + + int gModalVersion = bundle != null ? bundle.getInt(VALUE, 0) : 0; + Log.v(TAG, String.format("gModalVersion: %s", gModalVersion)); + return gModalVersion; + } + + private Bundle getFeatureBundle(String feature) { + try { + Uri contentUri = + new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(AUTHORITY) + .appendPath(GET_FEATURE_VERSION_METHOD) + .build(); + return mContext.getContentResolver().call( + contentUri, + GET_FEATURE_VERSION_METHOD, + feature, + /* extras= */ null); + } catch (IllegalArgumentException exception) { + Log.w(TAG, String.format("Fail to resolve %s feature from suw provider", feature)); + return null; + } + } + + private FeatureResolver(Context context) { + this.mContext = context; + } +} diff --git a/library/main/tests/robotests/Android.bp b/library/main/tests/robotests/Android.bp index d5cf533..9d91639 100644 --- a/library/main/tests/robotests/Android.bp +++ b/library/main/tests/robotests/Android.bp @@ -14,7 +14,7 @@ android_app { privileged: true, - libs: ["android.car"], + libs: ["android.car-system-stubs"], static_libs: ["car-setup-wizard-lib"], } @@ -30,7 +30,7 @@ android_robolectric_test { java_resource_dirs: ["config"], libs: [ - "android.car", + "android.car-system-stubs", ], instrumentation_for: "CarSetupWizardLib", diff --git a/library/main/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml b/library/main/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml index a9bcfbe..4509b0a 100644 --- a/library/main/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml +++ b/library/main/tests/robotests/res/layout/car_setup_wizard_layout_test_activity.xml @@ -16,7 +16,9 @@ --> <com.android.car.setupwizardlib.CarSetupWizardCompatLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/car_setup_wizard_layout" android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="match_parent"/> + android:layout_height="match_parent" + app:supportsSplitNavLayout="true"/> diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/BaseCompatActivityTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/BaseCompatActivityTest.java index 7209fc9..b504625 100644 --- a/library/main/tests/robotests/src/com/android/car/setupwizardlib/BaseCompatActivityTest.java +++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/BaseCompatActivityTest.java @@ -19,8 +19,10 @@ package com.android.car.setupwizardlib; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.Activity; import android.car.Car; @@ -28,8 +30,11 @@ import android.car.CarNotConnectedException; import android.car.drivingstate.CarUxRestrictions; import android.car.drivingstate.CarUxRestrictionsManager; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewStub; import android.widget.Button; +import android.widget.FrameLayout; import android.widget.ImageView; import androidx.annotation.StyleRes; @@ -38,7 +43,6 @@ import androidx.fragment.app.Fragment; import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest; import com.android.car.setupwizardlib.robolectric.TestHelper; import com.android.car.setupwizardlib.shadows.ShadowCar; -import com.android.car.setupwizardlib.test.R; import org.junit.Before; import org.junit.Test; @@ -194,6 +198,7 @@ public class BaseCompatActivityTest extends BaseRobolectricTest { .isEqualTo(0); // Verify that onContentFragmentSet is called with the test fragment verify(spyBaseCompatActivity).onContentFragmentSet(fragment); + verify(spyBaseCompatActivity).getFragmentContainerViewId(); } /** @@ -213,6 +218,7 @@ public class BaseCompatActivityTest extends BaseRobolectricTest { .isEqualTo(0); // Verify that onContentFragmentSet is not called verify(spyBaseCompatActivity, times(0)).onContentFragmentSet(fragment); + verify(spyBaseCompatActivity, times(0)).getFragmentContainerViewId(); } /** @@ -232,6 +238,7 @@ public class BaseCompatActivityTest extends BaseRobolectricTest { .isEqualTo(1); // Verify that onContentFragmentSet is called with the test fragment verify(spyBaseCompatActivity).onContentFragmentSet(fragment); + verify(spyBaseCompatActivity).getFragmentContainerViewId(); } /** @@ -251,6 +258,7 @@ public class BaseCompatActivityTest extends BaseRobolectricTest { .isEqualTo(0); // Verify that onContentFragmentSet is not called verify(spyBaseCompatActivity, times(0)).onContentFragmentSet(fragment); + verify(spyBaseCompatActivity, times(0)).getFragmentContainerViewId(); } /** @@ -294,12 +302,50 @@ public class BaseCompatActivityTest extends BaseRobolectricTest { */ @Test public void testSetContentLayout() { - mBaseCompatActivity.setContentLayout(R.layout.base_activity_test_layout); - View contentLayout = mBaseCompatActivity.findViewById(R.id.content_layout); + BaseCompatActivity spyBaseCompatActivity = Mockito.spy(mBaseCompatActivity); + + spyBaseCompatActivity.setContentLayout(R.layout.base_activity_test_layout); + View contentLayout = spyBaseCompatActivity.findViewById(R.id.content_layout); + assertThat(contentLayout).isNotNull(); } /** + * Test that {@link BaseSetupWizardActivity#setContentLayout(int)} adds the specified layout + * to the main content when layout_content_stub is null. + */ + @Test + public void testSetContentLayoutWhenViewStubIsNull() { + BaseCompatActivity spyBaseCompatActivity = Mockito.spy(mBaseCompatActivity); + LayoutInflater mockLayoutInflater = mock(LayoutInflater.class); + when(spyBaseCompatActivity.findViewById(R.id.layout_content_stub)).thenReturn(null); + when(spyBaseCompatActivity.getLayoutInflater()).thenReturn(mockLayoutInflater); + + spyBaseCompatActivity.setContentLayout(R.layout.base_activity_test_layout); + + verify(mockLayoutInflater, times(1)) + .inflate(R.layout.base_activity_test_layout, + mBaseCompatActivity.getCarSetupWizardLayout()); + } + + /** + * Test that {@link BaseCompatActivity#setContentLayout(int)} adds the specified layout to + * the main content when layout_content_stub is not null. + */ + @Test + public void testSetContentLayoutWhenViewStubNotNull() { + BaseCompatActivity spyBaseCompatActivity = Mockito.spy(mBaseCompatActivity); + ViewStub mockViewStub = mock(ViewStub.class); + when(spyBaseCompatActivity.findViewById(R.id.layout_content_stub)).thenReturn(mockViewStub); + + spyBaseCompatActivity.setContentLayout(R.layout.base_activity_test_layout); + + verify(mockViewStub, times(1)) + .setLayoutResource(R.layout.base_activity_test_layout); + verify(mockViewStub, times(1)).inflate(); + } + + /** * Test that {@link BaseCompatActivity#finishAction()} results in a call to * {@link BaseCompatActivity#finish}. */ @@ -581,4 +627,61 @@ public class BaseCompatActivityTest extends BaseRobolectricTest { spyBaseCompatActivity.nextAction(Activity.RESULT_OK); verify(spyBaseCompatActivity, times(2)).startActivity(Mockito.any()); } + + /** + * Test that {@link BaseCompatActivity#getFragmentContainerViewId()} returns + * layout_content_fragment when split-nav is enabled and view stub is not inflated yet. + */ + @Test + public void testGetFragmentContainerViewId_viewStubNotInflated() { + BaseCompatActivity spyBaseCompatActivity = createSpyBaseCompatActivity(); + FrameLayout mockFrameLayout = mock(FrameLayout.class); + ViewStub mockViewStub = mock(ViewStub.class); + when(spyBaseCompatActivity.findViewById(R.id.empty_fragment_frame_layout)) + .thenReturn(mockFrameLayout); + when(spyBaseCompatActivity.findViewById(R.id.layout_content_stub)).thenReturn(mockViewStub); + + int fragmentContainerViewId = spyBaseCompatActivity.getFragmentContainerViewId(); + + verify(mockViewStub, times(1)).inflate(); + assertThat(fragmentContainerViewId).isEqualTo(R.id.empty_fragment_frame_layout); + } + + /** + * Test that {@link BaseCompatActivity#getFragmentContainerViewId()} returns + * layout_content_fragment when split-nav is enabled and view stub is already inflated + * with frame layout for fragment attachment. + */ + @Test + public void testGetFragmentContainerViewId_viewStubInflatedFrameLayout() { + BaseCompatActivity spyBaseCompatActivity = createSpyBaseCompatActivity(); + FrameLayout mockFrameLayout = mock(FrameLayout.class); + when(spyBaseCompatActivity.findViewById(R.id.empty_fragment_frame_layout)) + .thenReturn(mockFrameLayout); + when(spyBaseCompatActivity.findViewById(R.id.layout_content_stub)) + .thenReturn(null); + + int fragmentContainerViewId = spyBaseCompatActivity.getFragmentContainerViewId(); + + assertThat(fragmentContainerViewId).isEqualTo(R.id.empty_fragment_frame_layout); + } + + /** + * Test that {@link BaseCompatActivity#getFragmentContainerViewId()} returns + * layout_content_fragment when split-nav is enabled and view stub is already inflated + * with some other view than fragment layout. The frame layout is not available for fragment + * attachment. + */ + @Test + public void testGetFragmentContainerViewId_frameLayoutNotAvailable() { + BaseCompatActivity spyBaseCompatActivity = createSpyBaseCompatActivity(); + when(spyBaseCompatActivity.findViewById(R.id.layout_content_stub)) + .thenReturn(null); + when(spyBaseCompatActivity.findViewById(R.id.empty_fragment_frame_layout)) + .thenReturn(null); + + int fragmentContainerViewId = spyBaseCompatActivity.getFragmentContainerViewId(); + + assertThat(fragmentContainerViewId).isEqualTo(R.id.car_setup_wizard_layout); + } } diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayoutTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayoutTest.java index 3ed02c0..9371947 100644 --- a/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayoutTest.java +++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/CarSetupWizardCompatLayoutTest.java @@ -24,6 +24,7 @@ import android.app.Activity; import android.content.res.ColorStateList; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.TextView; @@ -65,6 +66,7 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { private static final Locale LOCALE_IW_IL = new Locale("iw", "IL"); private CarSetupWizardCompatLayout mCarSetupWizardCompatLayout; + private CarSetupWizardLayoutInterface mCarSetupWizardLayoutInterface; private static final String TEST_PACKAGE_NAME = "test.packageName"; @@ -85,9 +87,11 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { } mCarSetupWizardCompatLayout = createCarSetupWizardCompatLayout(); - // Have to make this call first to ensure secondaryToolbar button is created from stub. - mCarSetupWizardCompatLayout.setSecondaryToolbarButtonVisible(true); - mCarSetupWizardCompatLayout.setSecondaryToolbarButtonVisible(false); + mCarSetupWizardLayoutInterface = + (CarSetupWizardLayoutInterface) mCarSetupWizardCompatLayout; + // Have to make this call first to ensure secondaryActionButton is created from stub. + mCarSetupWizardLayoutInterface.setSecondaryActionButtonVisible(true); + mCarSetupWizardLayoutInterface.setSecondaryActionButtonVisible(false); } /** @@ -240,75 +244,108 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { } /** - * Test that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonVisible} does set the view + * Test that any call to setToolbarTitle calls toolbar's setText when split-nav is enabled. + */ + @Test + public void testSetToolbarTitleWhenSplitNavEnabled() { + CarSetupWizardCompatLayout spyCarSetupWizardCompatLayout = + Mockito.spy(mCarSetupWizardCompatLayout); + TextView spyToolbar = Mockito.spy(mCarSetupWizardCompatLayout.getToolbarTitle()); + spyCarSetupWizardCompatLayout.setToolbarTitle(null); + + spyCarSetupWizardCompatLayout.setToolbarTitleText("test title"); + + Mockito.verify(spyToolbar, Mockito.never()).setText("test title"); + } + + /** + * Test that any call to setToolbarTitleStyle calls toolbar's setTextAppearance when split-nav + * is enabled. + */ + @Test + public void testSetToolbarStyleWhenSplitNavEnabled() { + @StyleRes int newStyle = R.style.TextAppearance_Car_Body2; + CarSetupWizardCompatLayout spyCarSetupWizardCompatLayout = + Mockito.spy(mCarSetupWizardCompatLayout); + TextView spyToolbar = Mockito.spy(mCarSetupWizardCompatLayout.getToolbarTitle()); + spyCarSetupWizardCompatLayout.setToolbarTitle(null); + + spyCarSetupWizardCompatLayout.setToolbarTitleStyle(newStyle); + + Mockito.verify(spyToolbar, Mockito.never()).setTextAppearance(newStyle); + } + + /** + * Test that {@link CarSetupWizardCompatLayout#setPrimaryActionButtonVisible} does set the view * visible/not visible. */ @Test - public void testSetPrimaryToolbarButtonVisibleTrue() { - View toolbarTitle = mCarSetupWizardCompatLayout.getPrimaryToolbarButton(); + public void testSetPrimaryActionButtonVisibleTrue() { + View primaryButton = mCarSetupWizardLayoutInterface.getPrimaryActionButton(); - mCarSetupWizardCompatLayout.setPrimaryToolbarButtonVisible(true); - TestHelper.assertViewVisible(toolbarTitle); + mCarSetupWizardLayoutInterface.setPrimaryActionButtonVisible(true); + TestHelper.assertViewVisible(primaryButton); } /** - * Test that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonVisible} does set the view + * Test that {@link CarSetupWizardCompatLayout#setPrimaryActionButtonVisible} does set the view * visible/not visible. */ @Test - public void testSetPrimaryToolbarButtonVisibleFalse() { - View toolbarTitle = mCarSetupWizardCompatLayout.getPrimaryToolbarButton(); + public void testSetPrimaryActionButtonVisibleFalse() { + View primaryButton = mCarSetupWizardLayoutInterface.getPrimaryActionButton(); - mCarSetupWizardCompatLayout.setPrimaryToolbarButtonVisible(false); - TestHelper.assertViewNotVisible(toolbarTitle); + mCarSetupWizardLayoutInterface.setPrimaryActionButtonVisible(false); + TestHelper.assertViewNotVisible(primaryButton); } /** - * Test that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonEnabled} does set the view + * Test that {@link CarSetupWizardCompatLayout#setPrimaryActionButtonEnabled} does set the view * enabled/not enabled. */ @Test - public void testSetPrimaryToolbarButtonEnabledTrue() { - View toolbarTitle = mCarSetupWizardCompatLayout.getPrimaryToolbarButton(); + public void testSetPrimaryActionButtonEnabledTrue() { + View primaryButton = mCarSetupWizardLayoutInterface.getPrimaryActionButton(); - mCarSetupWizardCompatLayout.setPrimaryToolbarButtonEnabled(true); - TestHelper.assertViewEnabled(toolbarTitle); + mCarSetupWizardLayoutInterface.setPrimaryActionButtonEnabled(true); + TestHelper.assertViewEnabled(primaryButton); } /** - * Test that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonEnabled} does set the view + * Test that {@link CarSetupWizardCompatLayout#setPrimaryActionButtonEnabled} does set the view * enabled/not enabled. */ @Test - public void testSetPrimaryToolbarButtonEnabledFalse() { - View toolbarTitle = mCarSetupWizardCompatLayout.getPrimaryToolbarButton(); + public void testSetPrimaryActionButtonEnabledFalse() { + View primaryButton = mCarSetupWizardLayoutInterface.getPrimaryActionButton(); - mCarSetupWizardCompatLayout.setPrimaryToolbarButtonEnabled(false); - TestHelper.assertViewNotEnabled(toolbarTitle); + mCarSetupWizardLayoutInterface.setPrimaryActionButtonEnabled(false); + TestHelper.assertViewNotEnabled(primaryButton); } /** - * Tests that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonText(String)} does set - * the primary toolbar button text. + * Tests that {@link CarSetupWizardCompatLayout#setPrimaryActionButtonText(String)} does set + * the primary action button text. */ @Test - public void testSetPrimaryToolbarButtonText() { - mCarSetupWizardCompatLayout.setPrimaryToolbarButtonText("test title"); + public void testSetPrimaryActionButtonText() { + mCarSetupWizardLayoutInterface.setPrimaryActionButtonText("test title"); TestHelper.assertTextEqual( - mCarSetupWizardCompatLayout.getPrimaryToolbarButton(), "test title"); + mCarSetupWizardLayoutInterface.getPrimaryActionButton(), "test title"); } /** - * Test that {@link CarSetupWizardCompatLayout#setPrimaryToolbarButtonListener} does set the - * primary toolbar button listener. + * Test that {@link CarSetupWizardCompatLayout#setPrimaryActionButtonListener} does set the + * primary action button listener. */ @Test - public void testSetPrimaryToolbarButtonListener() { + public void testSetPrimaryActionButtonListener() { View.OnClickListener spyListener = TestHelper.createSpyListener(); - mCarSetupWizardCompatLayout.setPrimaryToolbarButtonListener(spyListener); - mCarSetupWizardCompatLayout.getPrimaryToolbarButton().performClick(); - Mockito.verify(spyListener).onClick(mCarSetupWizardCompatLayout.getPrimaryToolbarButton()); + mCarSetupWizardLayoutInterface.setPrimaryActionButtonListener(spyListener); + mCarSetupWizardLayoutInterface.getPrimaryActionButton().performClick(); + Mockito.verify(spyListener).onClick( + mCarSetupWizardLayoutInterface.getPrimaryActionButton()); } /** @@ -317,89 +354,89 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { */ @Test public void testCreatePrimaryButtonTrue() { - Button currPrimaryToolbarButton = mCarSetupWizardCompatLayout.getPrimaryToolbarButton(); - Button primaryToolbarButton = mCarSetupWizardCompatLayout.createPrimaryToolbarButton(true); + Button currPrimaryActionButton = mCarSetupWizardLayoutInterface.getPrimaryActionButton(); + Button primaryActionButton = mCarSetupWizardCompatLayout.createPrimaryToolbarButton(true); - assertThat(primaryToolbarButton.getVisibility()).isEqualTo( - currPrimaryToolbarButton.getVisibility()); - assertThat(primaryToolbarButton.isEnabled()).isEqualTo( - currPrimaryToolbarButton.isEnabled()); - assertThat(primaryToolbarButton.getText()).isEqualTo(currPrimaryToolbarButton.getText()); - assertThat(primaryToolbarButton.getLayoutParams()).isEqualTo( - currPrimaryToolbarButton.getLayoutParams()); + assertThat(primaryActionButton.getVisibility()).isEqualTo( + currPrimaryActionButton.getVisibility()); + assertThat(primaryActionButton.isEnabled()).isEqualTo( + currPrimaryActionButton.isEnabled()); + assertThat(primaryActionButton.getText()).isEqualTo(currPrimaryActionButton.getText()); + assertThat(primaryActionButton.getLayoutParams()).isEqualTo( + currPrimaryActionButton.getLayoutParams()); } /** - * Test that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonVisible} does set the + * Test that {@link CarSetupWizardCompatLayout#setSecondaryActionButtonVisible} does set the * view visible/not visible. */ @Test - public void testSetSecondaryToolbarButtonVisibleTrue() { - View toolbarTitle = mCarSetupWizardCompatLayout.getSecondaryToolbarButton(); + public void testSetSecondaryActionButtonVisibleTrue() { + View secondaryButton = mCarSetupWizardLayoutInterface.getSecondaryActionButton(); - mCarSetupWizardCompatLayout.setSecondaryToolbarButtonVisible(true); - TestHelper.assertViewVisible(toolbarTitle); + mCarSetupWizardLayoutInterface.setSecondaryActionButtonVisible(true); + TestHelper.assertViewVisible(secondaryButton); } /** - * Test that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonVisible} does set the + * Test that {@link CarSetupWizardCompatLayout#setSecondaryActionButtonVisible} does set the * view visible/not visible. */ @Test - public void testSetSecondaryToolbarButtonVisibleFalse() { - View toolbarTitle = mCarSetupWizardCompatLayout.getSecondaryToolbarButton(); + public void testSetSecondaryActionButtonVisibleFalse() { + View secondaryButton = mCarSetupWizardLayoutInterface.getSecondaryActionButton(); - mCarSetupWizardCompatLayout.setSecondaryToolbarButtonVisible(false); - TestHelper.assertViewNotVisible(toolbarTitle); + mCarSetupWizardLayoutInterface.setSecondaryActionButtonVisible(false); + TestHelper.assertViewNotVisible(secondaryButton); } /** - * Test that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonEnabled} does set the + * Test that {@link CarSetupWizardCompatLayout#setSecondaryActionButtonEnabled} does set the * view enabled/not enabled. */ @Test - public void testSetSecondaryToolbarButtonEnabledTrue() { - View toolbarTitle = mCarSetupWizardCompatLayout.getSecondaryToolbarButton(); + public void testSetSecondaryActionButtonEnabledTrue() { + View secondaryButton = mCarSetupWizardLayoutInterface.getSecondaryActionButton(); - mCarSetupWizardCompatLayout.setSecondaryToolbarButtonEnabled(true); - TestHelper.assertViewEnabled(toolbarTitle); + mCarSetupWizardLayoutInterface.setSecondaryActionButtonEnabled(true); + TestHelper.assertViewEnabled(secondaryButton); } /** - * Test that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonEnabled} does set the + * Test that {@link CarSetupWizardCompatLayout#setSecondaryActionButtonEnabled} does set the * view enabled/not enabled. */ @Test - public void testSetSecondaryToolbarButtonEnabledFalse() { - View toolbarTitle = mCarSetupWizardCompatLayout.getSecondaryToolbarButton(); + public void testSetSecondaryActionButtonEnabledFalse() { + View secondaryButton = mCarSetupWizardLayoutInterface.getSecondaryActionButton(); - mCarSetupWizardCompatLayout.setSecondaryToolbarButtonEnabled(false); - TestHelper.assertViewNotEnabled(toolbarTitle); + mCarSetupWizardLayoutInterface.setSecondaryActionButtonEnabled(false); + TestHelper.assertViewNotEnabled(secondaryButton); } /** - * Tests that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonText(String)} does set - * the secondary toolbar button text. + * Tests that {@link CarSetupWizardCompatLayout#setSecondaryActionButtonText(String)} does set + * the secondary action button text. */ @Test - public void testSetSecondaryToolbarButtonText() { - mCarSetupWizardCompatLayout.setSecondaryToolbarButtonText("test title"); + public void testSetSecondaryActionButtonText() { + mCarSetupWizardLayoutInterface.setSecondaryActionButtonText("test title"); TestHelper.assertTextEqual( - mCarSetupWizardCompatLayout.getSecondaryToolbarButton(), "test title"); + mCarSetupWizardLayoutInterface.getSecondaryActionButton(), "test title"); } /** - * Test that {@link CarSetupWizardCompatLayout#setSecondaryToolbarButtonListener} does set the - * secondary toolbar button listener. + * Test that {@link CarSetupWizardCompatLayout#setSecondaryActionButtonListener} does set the + * secondary action button listener. */ @Test - public void testSetSecondaryToolbarButtonListener() { + public void testSetSecondaryActionButtonListener() { View.OnClickListener spyListener = TestHelper.createSpyListener(); - mCarSetupWizardCompatLayout.setSecondaryToolbarButtonListener(spyListener); - mCarSetupWizardCompatLayout.getSecondaryToolbarButton().performClick(); + mCarSetupWizardLayoutInterface.setSecondaryActionButtonListener(spyListener); + mCarSetupWizardLayoutInterface.getSecondaryActionButton().performClick(); Mockito.verify(spyListener) - .onClick(mCarSetupWizardCompatLayout.getSecondaryToolbarButton()); + .onClick(mCarSetupWizardLayoutInterface.getSecondaryActionButton()); } /** @@ -408,10 +445,10 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { */ @Test public void testSetProgressBarVisibleTrue() { - View toolbarTitle = mCarSetupWizardCompatLayout.getProgressBar(); + View progressBar = mCarSetupWizardCompatLayout.getProgressBar(); - mCarSetupWizardCompatLayout.setProgressBarVisible(true); - TestHelper.assertViewVisible(toolbarTitle); + mCarSetupWizardLayoutInterface.setProgressBarVisible(true); + TestHelper.assertViewVisible(progressBar); } /** @@ -420,10 +457,10 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { */ @Test public void testSetProgressBarVisibleFalse() { - View toolbarTitle = mCarSetupWizardCompatLayout.getProgressBar(); + View progressBar = mCarSetupWizardCompatLayout.getProgressBar(); - mCarSetupWizardCompatLayout.setProgressBarVisible(false); - TestHelper.assertViewNotVisible(toolbarTitle); + mCarSetupWizardLayoutInterface.setProgressBarVisible(false); + TestHelper.assertViewNotVisible(progressBar); } /** @@ -432,7 +469,7 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { */ @Test public void testSetProgressBarIndeterminateTrue() { - mCarSetupWizardCompatLayout.setProgressBarIndeterminate(true); + mCarSetupWizardLayoutInterface.setProgressBarIndeterminate(true); assertThat(mCarSetupWizardCompatLayout.getProgressBar().isIndeterminate()).isTrue(); } @@ -442,7 +479,7 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { */ @Test public void testSetProgressBarIndeterminateFalse() { - mCarSetupWizardCompatLayout.setProgressBarIndeterminate(false); + mCarSetupWizardLayoutInterface.setProgressBarIndeterminate(false); assertThat(mCarSetupWizardCompatLayout.getProgressBar().isIndeterminate()).isFalse(); } @@ -451,7 +488,7 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { */ @Test public void testSetProgressBarProgress() { - mCarSetupWizardCompatLayout.setProgressBarProgress(80); + mCarSetupWizardLayoutInterface.setProgressBarProgress(80); assertThat(mCarSetupWizardCompatLayout.getProgressBar().getProgress()).isEqualTo(80); } @@ -459,22 +496,36 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { public void testApplyUpdatedLocale() { mCarSetupWizardCompatLayout.applyLocale(LOCALE_IW_IL); TextView toolbarTitle = mCarSetupWizardCompatLayout.getToolbarTitle(); - Button primaryToolbarButton = mCarSetupWizardCompatLayout.getPrimaryToolbarButton(); - Button secondaryToolbarButton = mCarSetupWizardCompatLayout.getSecondaryToolbarButton(); + Button primaryActionButton = mCarSetupWizardLayoutInterface.getPrimaryActionButton(); + Button secondaryActionButton = mCarSetupWizardLayoutInterface.getSecondaryActionButton(); assertThat(toolbarTitle.getTextLocale()).isEqualTo(LOCALE_IW_IL); - assertThat(primaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_IW_IL); - assertThat(secondaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_IW_IL); + assertThat(primaryActionButton.getTextLocale()).isEqualTo(LOCALE_IW_IL); + assertThat(secondaryActionButton.getTextLocale()).isEqualTo(LOCALE_IW_IL); mCarSetupWizardCompatLayout.applyLocale(LOCALE_EN_US); assertThat(toolbarTitle.getTextLocale()).isEqualTo(LOCALE_EN_US); - assertThat(primaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_EN_US); - assertThat(secondaryToolbarButton.getTextLocale()).isEqualTo(LOCALE_EN_US); + assertThat(primaryActionButton.getTextLocale()).isEqualTo(LOCALE_EN_US); + assertThat(secondaryActionButton.getTextLocale()).isEqualTo(LOCALE_EN_US); + } + + @Test + public void testApplyUpdatedLocaleWhenSplitNavEnabled() { + CarSetupWizardCompatLayout spyCarSetupWizardCompatLayout = + Mockito.spy(mCarSetupWizardCompatLayout); + TextView spyToolbar = Mockito.spy(mCarSetupWizardCompatLayout.getToolbarTitle()); + spyCarSetupWizardCompatLayout.setToolbarTitle(null); + + spyCarSetupWizardCompatLayout.applyLocale(LOCALE_EN_US); + + Mockito.verify(spyToolbar, Mockito.never()).setTextLocale(LOCALE_EN_US); + Mockito.verify(spyToolbar, Mockito.never()) + .setLayoutDirection(TextUtils.getLayoutDirectionFromLocale(LOCALE_EN_US)); } @Test public void testGetBackButton() { - assertThat(mCarSetupWizardCompatLayout.getPrimaryToolbarButton()).isEqualTo( + assertThat(mCarSetupWizardLayoutInterface.getPrimaryActionButton()).isEqualTo( mCarSetupWizardCompatLayout.findViewById(R.id.primary_toolbar_button)); } @@ -485,14 +536,14 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { } @Test - public void testGetPrimaryToolBarButton() { - assertThat(mCarSetupWizardCompatLayout.getPrimaryToolbarButton()).isEqualTo( + public void testGetPrimaryActionButton() { + assertThat(mCarSetupWizardLayoutInterface.getPrimaryActionButton()).isEqualTo( mCarSetupWizardCompatLayout.findViewById(R.id.primary_toolbar_button)); } @Test - public void testGetSecondaryToolBarButton() { - assertThat(mCarSetupWizardCompatLayout.getSecondaryToolbarButton()).isEqualTo( + public void testGetSecondaryActionButton() { + assertThat(mCarSetupWizardLayoutInterface.getSecondaryActionButton()).isEqualTo( mCarSetupWizardCompatLayout.findViewById(R.id.secondary_toolbar_button)); } @@ -509,10 +560,10 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { CarSetupWizardCompatLayout layout = createCarSetupWizardCompatLayout(); // Verify primary button background - Button primary = layout.getPrimaryToolbarButton(); + Button primary = layout.getPrimaryActionButton(); Drawable expected = application.getResources().getDrawable(R.drawable.button_ripple_bg); - assertThat(getDrawbleDefaultColor(primary.getBackground())) - .isEqualTo(getDrawbleDefaultColor(expected)); + assertThat(getDrawableDefaultColor(primary.getBackground())) + .isEqualTo(getDrawableDefaultColor(expected)); // Verify primary button text size assertThat(primary.getTextSize()) @@ -538,14 +589,15 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { CarSetupWizardCompatLayout layout = createCarSetupWizardCompatLayout(); ColorDrawable bg = (ColorDrawable) layout.getBackground(); - assertThat(bg).isNull(); + assertThat(bg.getColor()).isEqualTo( + application.getResources().getColor(R.color.suw_color_background)); } @Test public void testSetButtonTextColor() { setupFakeContentProvider(); CarSetupWizardCompatLayout layout = createCarSetupWizardCompatLayout(); - Button primary = layout.getPrimaryToolbarButton(); + Button primary = layout.getPrimaryActionButton(); layout.setButtonTextColor( primary, PartnerConfig.CONFIG_LAYOUT_BG_COLOR); @@ -558,8 +610,8 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { public void testSetBackground() { setupFakeContentProvider(); CarSetupWizardCompatLayout layout = createCarSetupWizardCompatLayout(); - layout.setSecondaryToolbarButtonVisible(true); - Button secondary = layout.getSecondaryToolbarButton(); + layout.setSecondaryActionButtonVisible(true); + Button secondary = layout.getSecondaryActionButton(); layout.setBackground( secondary, @@ -567,14 +619,14 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_BG_COLOR); Drawable expected = application.getResources().getDrawable(R.drawable.button_ripple_bg); - assertThat(getDrawbleDefaultColor(secondary.getBackground())) - .isEqualTo(getDrawbleDefaultColor(expected)); + assertThat(getDrawableDefaultColor(secondary.getBackground())) + .isEqualTo(getDrawableDefaultColor(expected)); } @Test public void test_bothButtons_areStyled_inDefaultLayout() { - Button primaryButton = mCarSetupWizardCompatLayout.getPrimaryToolbarButton(); - Button secondaryButton = mCarSetupWizardCompatLayout.getSecondaryToolbarButton(); + Button primaryButton = mCarSetupWizardLayoutInterface.getPrimaryActionButton(); + Button secondaryButton = mCarSetupWizardLayoutInterface.getSecondaryActionButton(); assertThat(primaryButton.getTextSize()).isWithin(TOLERANCE).of(EXCEPTED_TEXT_SIZE); assertThat(secondaryButton.getTextSize()).isWithin(TOLERANCE).of(EXCEPTED_TEXT_SIZE); @@ -588,8 +640,8 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { .get(); CarSetupWizardCompatLayout layout = activity.findViewById(R.id.car_setup_wizard_layout); - Button primaryButton = layout.getPrimaryToolbarButton(); - Button secondaryButton = layout.getSecondaryToolbarButton(); + Button primaryButton = layout.getPrimaryActionButton(); + Button secondaryButton = layout.getSecondaryActionButton(); assertThat(primaryButton.getTextSize()).isWithin(TOLERANCE).of(EXCEPTED_TEXT_SIZE); assertThat(secondaryButton.getTextSize()).isWithin(TOLERANCE).of(EXCEPTED_TEXT_SIZE); @@ -615,7 +667,7 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { CarSetupWizardCompatLayout layout = activity.findViewById(R.id.car_setup_wizard_layout); View toolbar = layout.findViewById(R.id.application_bar); - assertThat(toolbar.getTextDirection()).isEqualTo(View.TEXT_DIRECTION_LTR); + assertThat(toolbar.getLayoutDirection()).isEqualTo(View.LAYOUT_DIRECTION_LTR); assertThat(layout.shouldMirrorNavIcons()).isTrue(); } @@ -632,7 +684,7 @@ public class CarSetupWizardCompatLayoutTest extends BaseRobolectricTest { return activity.findViewById(R.id.car_setup_wizard_layout); } - private @ColorRes int getDrawbleDefaultColor(Drawable drawable) { + private @ColorRes int getDrawableDefaultColor(Drawable drawable) { Drawable.ConstantState state = drawable.getConstantState(); ColorStateList colorStateList = ReflectionHelpers.getField(state, "mColor"); return colorStateList.getDefaultColor(); diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/FakeFeatureManagementProvider.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/FakeFeatureManagementProvider.java new file mode 100644 index 0000000..273f88e --- /dev/null +++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/FakeFeatureManagementProvider.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2021 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.car.setupwizardlib; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; + +import org.robolectric.Robolectric; + +/** + * An implementation of + * {@link com.google.android.car.setupwizard.common.config.FeatureManagementProvider} for + * Robolectric tests. + */ +public class FakeFeatureManagementProvider extends ContentProvider { + private static final String SUW_AUTHORITY = + "com.google.android.car.setupwizard.feature_management"; + private static final String GET_FEATURE_VERSION_METHOD = "getFeatureVersion"; + private static final String SPLIT_NAV_LAYOUT = "split_nav_layout"; + private static final String TYPE = "type"; + private static final String BOOLEAN_TYPE = "BOOLEAN"; + private static final String VALUE = "value"; + + public static FakeFeatureManagementProvider installProvider() { + return Robolectric.setupContentProvider(FakeFeatureManagementProvider.class, SUW_AUTHORITY); + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query( + Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + throw new UnsupportedOperationException("query operation not supported currently."); + } + + @Override + public String getType(Uri uri) { + throw new UnsupportedOperationException("getType operation not supported currently."); + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + throw new UnsupportedOperationException("insert operation not supported currently."); + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("delete operation not supported currently."); + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("update operation not supported currently."); + } + + @Override + public Bundle call(String method, String feature, Bundle extras) { + Bundle bundle = new Bundle(); + if (!GET_FEATURE_VERSION_METHOD.equals(method)) { + return bundle; + } + + switch(feature) { + case SPLIT_NAV_LAYOUT: + bundle.putString(TYPE, BOOLEAN_TYPE); + bundle.putBoolean(VALUE, true); + break; + default: + // Do nothing + } + return bundle; + } +} diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/SplitNavLayoutTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/SplitNavLayoutTest.java new file mode 100644 index 0000000..34f5a46 --- /dev/null +++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/SplitNavLayoutTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2021 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.car.setupwizardlib; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Activity; +import android.view.View; +import android.view.ViewGroup; + +import com.android.car.setupwizardlib.partner.ExternalResources; +import com.android.car.setupwizardlib.partner.FakeOverrideContentProvider; +import com.android.car.setupwizardlib.partner.PartnerConfig; +import com.android.car.setupwizardlib.partner.ResourceEntry; +import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest; +import com.android.car.setupwizardlib.shadows.ShadowConfiguration; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.util.Arrays; +import java.util.List; + +/** + * Tests for split-nav layout + */ +@RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowConfiguration.class) +public class SplitNavLayoutTest extends BaseRobolectricTest { + private static final String TEST_PACKAGE_NAME = "test.packageName"; + + private static final PartnerConfig ULTRA_WIDE_SCREEN_CONTENT_WIDTH_RESOURCE_NAME = + PartnerConfig.CONFIG_ULTRA_WIDE_SCREEN_CONTENT_WIDTH; + + private static final int PARTNER_CONTENT_WIDTH = 1500; + + private FakeOverrideContentProvider mFakeOverrideContentProvider; + + @Before + public void setUp() { + mFakeOverrideContentProvider = FakeOverrideContentProvider.installEmptyProvider(); + FakeFeatureManagementProvider.installProvider(); + } + + @Test + @Config(qualifiers = "w1760dp-land") + public void test_UltraWideContentWidth_isSetToCustomWidth() { + List<ResourceEntry> resourceEntries = prepareCustomContentWidth(); + for (ResourceEntry entry : resourceEntries) { + mFakeOverrideContentProvider.injectResourceEntry(entry); + } + + Activity activity = Robolectric.buildActivity(CarSetupWizardLayoutTestActivity.class) + .create() + .get(); + View contentContainer = activity.findViewById(R.id.ultra_wide_content_container); + ViewGroup.LayoutParams layoutParams = contentContainer.getLayoutParams(); + assertThat(layoutParams.width).isEqualTo(PARTNER_CONTENT_WIDTH); + } + + @Test + @Config(qualifiers = "w1760dp-land") + public void test_UltraWideContentWidthIsSetTo0_withoutCustomValue() { + Activity activity = Robolectric.buildActivity(CarSetupWizardLayoutTestActivity.class) + .create() + .get(); + + View contentContainer = activity.findViewById(R.id.ultra_wide_content_container); + ViewGroup.LayoutParams layoutParams = contentContainer.getLayoutParams(); + assertThat(layoutParams.width).isEqualTo(0); + } + + @Test + @Config(qualifiers = "iw-w1250dp-land") + public void test_layoutDirectionIsLtr_inRtrLocale() { + Activity activity = Robolectric.buildActivity(CarSetupWizardLayoutTestActivity.class) + .create() + .get(); + + View layout = activity.findViewById(R.id.car_setup_wizard_layout); + assertThat(layout.getLayoutDirection()).isEqualTo(View.LAYOUT_DIRECTION_LTR); + + View buttonContainer = activity.findViewById(R.id.button_container); + assertThat(buttonContainer.getLayoutDirection()).isEqualTo(View.LAYOUT_DIRECTION_LTR); + } + + @Test + @Config(qualifiers = "iw-w1760dp-land") + public void test_layoutDirectionIsLtrInUltraWide_isRtrLocale() { + Activity activity = Robolectric.buildActivity(CarSetupWizardLayoutTestActivity.class) + .create() + .get(); + + View layout = activity.findViewById(R.id.car_setup_wizard_layout); + assertThat(layout.getLayoutDirection()).isEqualTo(View.LAYOUT_DIRECTION_LTR); + + View buttonContainer = activity.findViewById(R.id.button_container); + assertThat(buttonContainer.getLayoutDirection()).isEqualTo(View.LAYOUT_DIRECTION_LTR); + } + + private List<ResourceEntry> prepareCustomContentWidth() { + ExternalResources.Resources testResources = + ExternalResources.injectExternalResources(TEST_PACKAGE_NAME); + + testResources.putDimension( + ULTRA_WIDE_SCREEN_CONTENT_WIDTH_RESOURCE_NAME.getResourceName(), + PARTNER_CONTENT_WIDTH); + + return Arrays.asList( + new ResourceEntry( + TEST_PACKAGE_NAME, + ULTRA_WIDE_SCREEN_CONTENT_WIDTH_RESOURCE_NAME.getResourceName(), + testResources.getIdentifier( + ULTRA_WIDE_SCREEN_CONTENT_WIDTH_RESOURCE_NAME.getResourceName(), + /* defType= */ "dimen", + TEST_PACKAGE_NAME)) + ); + } +} diff --git a/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/FeatureResolverTest.java b/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/FeatureResolverTest.java new file mode 100644 index 0000000..6951030 --- /dev/null +++ b/library/main/tests/robotests/src/com/android/car/setupwizardlib/util/FeatureResolverTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2021 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.car.setupwizardlib.util; + +import static com.android.car.setupwizardlib.util.FeatureResolver.VALUE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.car.setupwizardlib.robolectric.BaseRobolectricTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.robolectric.RobolectricTestRunner; + +import java.lang.reflect.Constructor; + +@RunWith(RobolectricTestRunner.class) +public class FeatureResolverTest extends BaseRobolectricTest { + + @Mock + private ContentResolver mContentResolver; + + private FeatureResolver mFeatureResolver; + private Context mContext; + + @Before + public void setUp() throws Exception { + Constructor<FeatureResolver> constructor = FeatureResolver.class + .getDeclaredConstructor(Context.class); + constructor.setAccessible(true); + mContext = spy(ApplicationProvider.getApplicationContext()); + mFeatureResolver = constructor.newInstance(mContext); + doReturn(mContentResolver).when(mContext).getContentResolver(); + } + + @Test + public void testFeatureResolverInstance() { + assertThat(FeatureResolver.get(mContext)).isNotNull(); + } + + @Test + public void testFeatureResolverSingletonInstance() { + FeatureResolver instance = FeatureResolver.get(mContext); + + assertThat(instance).isEqualTo(FeatureResolver.get(mContext)); + } + + @Test + public void testIsSplitNavLayoutFeatureEnabled_whenReturnsTrue() { + Bundle bundle = new Bundle(); + bundle.putBoolean(VALUE, true); + doReturn(bundle).when(mContentResolver).call((Uri) any(), any(), any(), any()); + + boolean isSplitNavLayoutFeatureEnabled = mFeatureResolver.isSplitNavLayoutFeatureEnabled(); + + assertThat(isSplitNavLayoutFeatureEnabled).isTrue(); + } + + @Test + public void testIsSplitNavLayoutFeatureEnabled_whenReturnsFalse() { + Bundle bundle = new Bundle(); + bundle.putBoolean(VALUE, false); + doReturn(bundle).when(mContentResolver).call((Uri) any(), any(), any(), any()); + + boolean isSplitNavLayoutFeatureEnabled = mFeatureResolver.isSplitNavLayoutFeatureEnabled(); + + assertThat(isSplitNavLayoutFeatureEnabled).isFalse(); + } + + @Test + public void testIsSplitNavLayoutFeatureEnabled_whenReturnsBundleNull() { + doReturn(null).when(mContentResolver).call((Uri) any(), any(), any(), any()); + + boolean isSplitNavLayoutFeatureEnabled = mFeatureResolver.isSplitNavLayoutFeatureEnabled(); + + assertThat(isSplitNavLayoutFeatureEnabled).isFalse(); + } + + @Test + public void testGetGModalVersion_whenVersionNumber1() { + Bundle bundle = new Bundle(); + bundle.putInt(VALUE, 1); + doReturn(bundle).when(mContentResolver).call((Uri) any(), any(), any(), any()); + + int splitNavLayoutFeatureVersion = mFeatureResolver.getGModalVersion(); + + assertThat(splitNavLayoutFeatureVersion).isEqualTo(1); + } + + @Test + public void testGModalVersion_whenVersionNumber0() { + Bundle bundle = new Bundle(); + bundle.putInt(VALUE, 0); + doReturn(bundle).when(mContentResolver).call((Uri) any(), any(), any(), any()); + + int splitNavLayoutFeatureVersion = mFeatureResolver.getGModalVersion(); + + assertThat(splitNavLayoutFeatureVersion).isEqualTo(0); + } + + @Test + public void testGetGModalVersion_whenReturnsBundleNull() { + doReturn(null).when(mContentResolver).call((Uri) any(), any(), any(), any()); + + int splitNavLayoutFeatureVersion = mFeatureResolver.getGModalVersion(); + + assertThat(splitNavLayoutFeatureVersion).isEqualTo(0); + } +} diff --git a/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupClient.java b/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupClient.java index 6c7830f..171710c 100644 --- a/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupClient.java +++ b/library/utils/src/com/android/car/setupwizardlib/InitialLockSetupClient.java @@ -316,7 +316,7 @@ public class InitialLockSetupClient implements ServiceConnection { new LockConfig( /* enabled= */ true, km.getMinLockLength( - /* isPin= */ true, PasswordComplexity.PASSWORD_COMPLEXITY_LOW)); + /* isPin= */ true, PasswordComplexity.PASSWORD_COMPLEXITY_MEDIUM)); patternConfig = new LockConfig( /* enabled= */ true, diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..d8c51f0 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2021 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. + */ + +include ':car-setup-wizard-lib' +project(':car-setup-wizard-lib').projectDir = new File('./library/main') + +rootProject.name='CarSetupWizard' |