diff options
author | Googler <noreply@google.com> | 2022-01-27 22:54:19 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-01-27 22:54:19 +0000 |
commit | c319e39461f262deef11f47c30c01fddc719d030 (patch) | |
tree | fb9949c0f45f0543a43aedeac231aa6a799a8fa5 | |
parent | 5ef0c1063d237a4f08110623cbef00e9445c4cb6 (diff) | |
parent | eccda78bf3a3df753ef9d3cdc119e0d9d84b8f89 (diff) | |
download | setupwizard-c319e39461f262deef11f47c30c01fddc719d030.tar.gz |
Changes for Car Setup Wizard Support Library: am: 15612ebde2 am: f941384091 am: eccda78bf3
Original change: https://android-review.googlesource.com/c/platform/frameworks/opt/car/setupwizard/+/1959684
Change-Id: Ib055569f3327236a37251f08e1df5c4e0e12d99d
45 files changed, 1533 insertions, 209 deletions
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/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/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; + } +} |