diff options
author | Matt Lee <matthewhlee@google.com> | 2023-03-02 14:24:25 -0800 |
---|---|---|
committer | Matt Lee <matthewhlee@google.com> | 2023-03-02 14:24:25 -0800 |
commit | 7840d3a5c52f290606c2bd48e667e749a7ae42b5 (patch) | |
tree | e8550954cf53c8ad3bce5250ebb98338f509bbdf | |
parent | 26b234f41e2d398d68661185ccf68a06b8c616b7 (diff) | |
parent | 20ba591019b8d4364ac74723b08a7b530a169c8a (diff) |
Merge t-qpr-2023-03
Change-Id: Ie89a9aa3cb885a45f8afec559919a626c7389a25
253 files changed, 7028 insertions, 1209 deletions
@@ -43,6 +43,7 @@ android_library { "SettingsLibCollapsingToolbarBaseActivity", "subsampling-scale-image-view", "SystemUISharedLib", + "SystemUICustomizationLib", "volley", "SettingsLibActivityEmbedding", ], @@ -61,7 +62,7 @@ android_library { visibility: [ ":__subpackages__", - "//packages/apps/ThemePicker", + "//packages/apps/ThemePicker:__subpackages__", "//vendor:__subpackages__", ], } @@ -107,12 +108,8 @@ java_defaults { name: "WallpaperPicker2_defaults", static_libs: [ - "renderscript_toolkit", "wallpaper-common-deps", - ], - - jni_libs: [ - "librenderscript-toolkit", + "monet", ], srcs: [ diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 40722275..62708365 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -14,7 +14,10 @@ <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> + <uses-permission android:name="android.permission.READ_WALLPAPER_INTERNAL" /> <uses-permission android:name="android.permission.SET_WALLPAPER"/> + <uses-permission android:name="android.permission.SET_WALLPAPER_COMPONENT" /> + <uses-permission android:name="android.permission.BIND_WALLPAPER" /> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="com.android.wallpaper.NOTIFY_ROTATING_WALLPAPER_CHANGED"/> @@ -41,6 +44,11 @@ <intent> <action android:name="com.android.launcher3.action.PARTNER_CUSTOMIZATION" /> </intent> + <!-- Intent filter used to query the launcher Activity for grid preview metadata --> + <intent> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.HOME" /> + </intent> </queries> <application diff --git a/ktfmt_includes.txt b/ktfmt_includes.txt index 37bbd6aa..112b4a10 100644 --- a/ktfmt_includes.txt +++ b/ktfmt_includes.txt @@ -1,9 +1,3 @@ +src/ +src_override/ +tests/ --src/com/android/wallpaper/model/WallpaperColorsViewModel.kt --src/com/android/wallpaper/model/WorkspaceViewModel.kt --src/com/android/wallpaper/module/LargeScreenMultiPanesChecker.kt --src/com/android/wallpaper/module/MultiPanesChecker.kt --src/com/android/wallpaper/util/DisplayUtils.kt --src/com/android/wallpaper/widget/GridRowSpacerDecoration.kt diff --git a/res/color-night/separated_tabs_indicator_color.xml b/res/color-night/separated_tabs_indicator_color.xml index 7313a16a..e0f225e0 100644 --- a/res/color-night/separated_tabs_indicator_color.xml +++ b/res/color-night/separated_tabs_indicator_color.xml @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> - <item android:color="?androidprv:attr/colorAccentSecondary" /> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="@color/color_accent_secondary" /> </selector> diff --git a/res/color/bottom_action_button_color_tint.xml b/res/color/bottom_action_button_color_tint.xml index 8381c2f4..c139748a 100644 --- a/res/color/bottom_action_button_color_tint.xml +++ b/res/color/bottom_action_button_color_tint.xml @@ -14,12 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="false" android:color="?android:textColorTertiary" /> <item android:state_selected="true" - android:color="?androidprv:attr/textColorOnAccent" /> + android:color="@color/text_color_on_accent" /> </selector>
\ No newline at end of file diff --git a/res/color/separated_tabs_indicator_color.xml b/res/color/separated_tabs_indicator_color.xml index cbce7d01..28f443bd 100644 --- a/res/color/separated_tabs_indicator_color.xml +++ b/res/color/separated_tabs_indicator_color.xml @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> - <item android:color="?androidprv:attr/colorAccentPrimary" /> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="@color/color_accent_primary" /> </selector>
\ No newline at end of file diff --git a/res/color/tab_text_color.xml b/res/color/tab_text_color.xml new file mode 100644 index 00000000..9ef5cad3 --- /dev/null +++ b/res/color/tab_text_color.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2022 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_selected="true" android:color="@color/text_color_on_accent" /> + <item android:color="@color/text_color_secondary" /> +</selector> diff --git a/res/color/text_color_on_accent.xml b/res/color/text_color_on_accent.xml new file mode 100644 index 00000000..27800d9a --- /dev/null +++ b/res/color/text_color_on_accent.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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. + --> +<!-- Please see primary_text_material_light.xml --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:color="@android:color/system_neutral1_400"/> + <item android:color="@android:color/system_neutral1_900"/> +</selector>
\ No newline at end of file diff --git a/res/color/wallpaper_control_button_ic_color_tint.xml b/res/color/wallpaper_control_button_ic_color_tint.xml new file mode 100644 index 00000000..84a7fe95 --- /dev/null +++ b/res/color/wallpaper_control_button_ic_color_tint.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true" android:color="@color/text_color_on_accent" /> + <item android:state_checked="false" android:color="?android:textColorPrimary" /> +</selector>
\ No newline at end of file diff --git a/res/drawable/bottom_action_button_background.xml b/res/drawable/bottom_action_button_background.xml index e1b3554b..7b235ca2 100644 --- a/res/drawable/bottom_action_button_background.xml +++ b/res/drawable/bottom_action_button_background.xml @@ -15,7 +15,6 @@ limitations under the License. --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:color="?android:colorControlHighlight"> <item> <selector> @@ -27,7 +26,7 @@ </item> <item android:state_selected="true"> <shape> - <solid android:color="?androidprv:attr/colorAccentPrimary" /> + <solid android:color="@color/color_accent_primary" /> <corners android:radius="@dimen/bottom_action_button_radius" /> </shape> </item> diff --git a/res/drawable/bottom_apply_button_background.xml b/res/drawable/bottom_apply_button_background.xml index f43ec1fc..2bb371f5 100644 --- a/res/drawable/bottom_apply_button_background.xml +++ b/res/drawable/bottom_apply_button_background.xml @@ -14,11 +14,10 @@ limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> <corners android:radius="@dimen/apply_button_background_height" /> <size android:width="@dimen/apply_button_background_width" android:height="@dimen/apply_button_background_height" /> - <solid android:color="?androidprv:attr/colorAccentPrimaryVariant" /> + <solid android:color="@color/color_accent_primary_variant" /> </shape>
\ No newline at end of file diff --git a/res/drawable/bottom_sheet_background.xml b/res/drawable/bottom_sheet_background.xml index c0c36680..8edea28d 100644 --- a/res/drawable/bottom_sheet_background.xml +++ b/res/drawable/bottom_sheet_background.xml @@ -16,8 +16,7 @@ limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurface"/> + <solid android:color="@color/color_surface"/> <corners android:radius="@dimen/preview_bottom_sheet_corner_radius"/> </shape> diff --git a/res/drawable/check_circle_accent_24dp.xml b/res/drawable/check_circle_accent_24dp.xml index 01723d9a..4372a27d 100644 --- a/res/drawable/check_circle_accent_24dp.xml +++ b/res/drawable/check_circle_accent_24dp.xml @@ -17,7 +17,7 @@ <item> <shape android:shape="oval"> <size android:width="24dp" android:height="24dp" /> - <solid android:color="?android:colorAccent" /> + <solid android:color="@color/color_accent_primary" /> </shape> </item> <item android:drawable="@drawable/ic_check_24dp" android:gravity="fill" /> diff --git a/res/drawable/dialog_option_background.xml b/res/drawable/dialog_option_background.xml index efb58916..4d23500b 100644 --- a/res/drawable/dialog_option_background.xml +++ b/res/drawable/dialog_option_background.xml @@ -15,11 +15,10 @@ limitations under the License. --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:color="?android:colorControlHighlight"> <item android:id="@android:id/background"> <shape> - <solid android:color="?androidprv:attr/colorAccentPrimary" /> + <solid android:color="@color/color_accent_primary" /> <corners android:radius="@dimen/set_wallpaper_dialog_item_inner_corner_radius"/> </shape> </item> diff --git a/res/drawable/duo_tabs_button_background.xml b/res/drawable/duo_tabs_button_background.xml new file mode 100644 index 00000000..b06f36f2 --- /dev/null +++ b/res/drawable/duo_tabs_button_background.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:colorControlHighlight"> + + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/duo_tabs_button_corner_radius" /> + <solid android:color="?android:colorControlHighlight" /> + </shape> + </item> + + <item android:drawable="@drawable/duo_tabs_button_background_base" /> +</ripple>
\ No newline at end of file diff --git a/res/drawable/duo_tabs_button_background_base.xml b/res/drawable/duo_tabs_button_background_base.xml new file mode 100644 index 00000000..20ab7a17 --- /dev/null +++ b/res/drawable/duo_tabs_button_background_base.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="@dimen/duo_tabs_button_corner_radius" /> + <solid android:color="@color/color_surface" /> +</shape>
\ No newline at end of file diff --git a/res/drawable/duo_tabs_button_indicator_background.xml b/res/drawable/duo_tabs_button_indicator_background.xml new file mode 100644 index 00000000..86841c56 --- /dev/null +++ b/res/drawable/duo_tabs_button_indicator_background.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:colorControlHighlight"> + + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/duo_tabs_button_corner_radius" /> + <solid android:color="?android:colorControlHighlight" /> + </shape> + </item> + + <item android:drawable="@drawable/duo_tabs_button_indicator_background_base" /> +</ripple>
\ No newline at end of file diff --git a/res/drawable/duo_tabs_button_indicator_background_base.xml b/res/drawable/duo_tabs_button_indicator_background_base.xml new file mode 100644 index 00000000..a7693bde --- /dev/null +++ b/res/drawable/duo_tabs_button_indicator_background_base.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="@dimen/duo_tabs_button_corner_radius" /> + <solid android:color="@color/color_accent_primary" /> +</shape>
\ No newline at end of file diff --git a/res/drawable/duo_tabs_divider.xml b/res/drawable/duo_tabs_divider.xml new file mode 100644 index 00000000..8325048c --- /dev/null +++ b/res/drawable/duo_tabs_divider.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <size + android:height="0dp" + android:width="@dimen/duo_tabs_divider_space" /> +</shape>
\ No newline at end of file diff --git a/res/drawable/floating_sheet_background.xml b/res/drawable/floating_sheet_background.xml new file mode 100644 index 00000000..d4caa2b3 --- /dev/null +++ b/res/drawable/floating_sheet_background.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + + Copyright (C) 2022 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/color_surface"/> + <corners android:radius="@dimen/floating_sheet_corner_radius"/> +</shape> diff --git a/res/drawable/fullscreen_button_background.xml b/res/drawable/fullscreen_button_background.xml index 87a3e1e7..6de7663e 100644 --- a/res/drawable/fullscreen_button_background.xml +++ b/res/drawable/fullscreen_button_background.xml @@ -15,11 +15,10 @@ limitations under the License. --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:color="?android:colorControlHighlight"> <item> <shape android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorAccentPrimary" /> + <solid android:color="@color/color_accent_primary" /> <corners android:radius="@dimen/separated_tabs_corner_radius" /> </shape> </item> diff --git a/res/drawable/horizontal_divider_8dp.xml b/res/drawable/horizontal_divider_8dp.xml new file mode 100644 index 00000000..b45de7bd --- /dev/null +++ b/res/drawable/horizontal_divider_8dp.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <size + android:width="8dp" + android:height="0dp" /> +</shape> diff --git a/res/drawable/ic_check_wallpaper_24dp.xml b/res/drawable/ic_check_wallpaper_24dp.xml index eeee07e5..75cec263 100644 --- a/res/drawable/ic_check_wallpaper_24dp.xml +++ b/res/drawable/ic_check_wallpaper_24dp.xml @@ -14,12 +14,11 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <path - android:fillColor="?androidprv:attr/textColorOnAccent" + android:fillColor="@color/text_color_on_accent" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41L9,16.17z"/> </vector> diff --git a/res/drawable/ic_delete.xml b/res/drawable/ic_delete.xml new file mode 100644 index 00000000..6b68d7dd --- /dev/null +++ b/res/drawable/ic_delete.xml @@ -0,0 +1,32 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/wallpaper_control_icon_size" + android:height="@dimen/wallpaper_control_icon_size" + android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size" + android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size" + android:tintMode="multiply" + android:tint="@color/wallpaper_control_button_ic_color_tint"> + <path + android:fillColor="@android:color/white" + android:pathData="M15,4V3H9v1H4v2h1v13c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V6h1V4H15zM17,19H7V6h10V19z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M9,8h2v9h-2z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M13,8h2v9h-2z"/> +</vector> diff --git a/res/drawable/ic_download_badge.xml b/res/drawable/ic_download_badge.xml index 85e09aa9..e897c382 100644 --- a/res/drawable/ic_download_badge.xml +++ b/res/drawable/ic_download_badge.xml @@ -1,12 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape android:shape="oval"> <size android:width="@dimen/grid_item_badge_size" android:height="@dimen/grid_item_badge_size" /> - <solid android:color="?androidprv:attr/colorSurfaceVariant" /> + <solid android:color="@color/color_surface_variant" /> </shape> </item> <item diff --git a/res/drawable/ic_effect.xml b/res/drawable/ic_effect.xml new file mode 100644 index 00000000..fe4acba2 --- /dev/null +++ b/res/drawable/ic_effect.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/wallpaper_control_icon_size" + android:height="@dimen/wallpaper_control_icon_size" + android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size" + android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size" + android:tintMode="multiply" + android:tint="@color/wallpaper_control_button_ic_color_tint"> + <path + android:fillColor="@android:color/white" + android:pathData="M19.6352 8.72727L20.9984 5.72648L24 4.36364L20.9984 3.0008L19.6352 0L18.272 3.0008L15.2704 4.36364L18.272 5.72648L19.6352 8.72727ZM11.456 9.27113L8.7296 3.26953L6.0032 9.27113L0 11.9968L6.0032 14.7225L8.7296 20.7241L11.456 14.7225L17.4592 11.9968L11.456 9.27113ZM19.6352 15.2727L18.272 18.2735L15.2704 19.6364L18.272 20.9992L19.6352 24L20.9984 20.9992L24 19.6364L20.9984 18.2735L19.6352 15.2727Z" /> +</vector> diff --git a/res/drawable/ic_file_download.xml b/res/drawable/ic_file_download.xml new file mode 100644 index 00000000..78ecd17d --- /dev/null +++ b/res/drawable/ic_file_download.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/wallpaper_control_icon_size" + android:height="@dimen/wallpaper_control_icon_size" + android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size" + android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size" + android:tint="@color/wallpaper_control_button_ic_color_tint" + android:tintMode="multiply"> + <path + android:fillColor="@android:color/white" + android:pathData="M4,15h2v3h12v-3h2v3c0,1.1 -0.9,2 -2,2H6c-1.1,0 -2,-0.9 -2,-2m11.59,-8.41L13,12.17V4h-2v8.17L8.41,9.59 7,11l5,5 5,-5 -1.41,-1.41z" /> +</vector> diff --git a/res/drawable/ic_info.xml b/res/drawable/ic_info.xml new file mode 100644 index 00000000..1cbabe7f --- /dev/null +++ b/res/drawable/ic_info.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/wallpaper_control_icon_size" + android:height="@dimen/wallpaper_control_icon_size" + android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size" + android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size" + android:tintMode="multiply" + android:tint="@color/wallpaper_control_button_ic_color_tint"> + <path + android:fillColor="@android:color/white" + android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/> +</vector> diff --git a/res/drawable/ic_tune.xml b/res/drawable/ic_tune.xml new file mode 100644 index 00000000..4e2a83c0 --- /dev/null +++ b/res/drawable/ic_tune.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/wallpaper_control_icon_size" + android:height="@dimen/wallpaper_control_icon_size" + android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size" + android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size" + android:tintMode="multiply" + android:tint="@color/wallpaper_control_button_ic_color_tint"> + <path + android:fillColor="@android:color/white" + android:pathData="M3,17v2h6v-2L3,17zM3,5v2h10L13,5L3,5zM13,21v-2h8v-2h-8v-2h-2v6h2zM7,9v2L3,11v2h4v2h2L9,9L7,9zM21,13v-2L11,11v2h10zM15,9h2L17,7h4L21,5h-4L17,3h-2v6z" /> +</vector> diff --git a/res/drawable/menu_item_hide_ui_background.xml b/res/drawable/menu_item_hide_ui_background.xml index 6a0b219e..26e826df 100644 --- a/res/drawable/menu_item_hide_ui_background.xml +++ b/res/drawable/menu_item_hide_ui_background.xml @@ -14,7 +14,6 @@ limitations under the License. --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:color="?android:colorControlHighlight"> <item> <layer-list> @@ -23,7 +22,7 @@ <size android:width="@dimen/menu_item_hide_ui_background_width" android:height="@dimen/menu_item_hide_ui_background_height" /> - <solid android:color="?androidprv:attr/colorAccentSecondary" /> + <solid android:color="@color/color_accent_secondary" /> </shape> </item> <item diff --git a/res/drawable/menu_item_set_wallpaper_background.xml b/res/drawable/menu_item_set_wallpaper_background.xml index 98becc01..fbf63b80 100644 --- a/res/drawable/menu_item_set_wallpaper_background.xml +++ b/res/drawable/menu_item_set_wallpaper_background.xml @@ -14,7 +14,6 @@ limitations under the License. --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:color="?android:colorControlHighlight"> <item> <shape @@ -23,7 +22,7 @@ android:width="@dimen/menu_item_set_wallpaper_background_width" android:height="@dimen/menu_item_set_wallpaper_background_height" /> <corners android:radius="@dimen/menu_item_set_wallpaper_background_radius" /> - <solid android:color="?androidprv:attr/colorAccentPrimary" /> + <solid android:color="@color/color_accent_primary" /> </shape> </item> </ripple>
\ No newline at end of file diff --git a/res/drawable/separated_tabs_background.xml b/res/drawable/separated_tabs_background.xml index 546c568f..2737b4f4 100644 --- a/res/drawable/separated_tabs_background.xml +++ b/res/drawable/separated_tabs_background.xml @@ -14,11 +14,10 @@ limitations under the License. --> <inset xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:insetLeft="@dimen/separated_tabs_inset_horizontal" android:insetRight="@dimen/separated_tabs_inset_horizontal"> <shape android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurface" /> + <solid android:color="@color/color_surface" /> <corners android:radius="@dimen/separated_tabs_corner_radius" /> </shape> </inset> diff --git a/res/drawable/set_wallpaper_button_background.xml b/res/drawable/set_wallpaper_button_background.xml new file mode 100644 index 00000000..a19a3fc8 --- /dev/null +++ b/res/drawable/set_wallpaper_button_background.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:colorControlHighlight"> + + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/set_wallpaper_button_corner_radius" /> + <padding + android:left="@dimen/set_wallpaper_button_horizontal_padding" + android:top="@dimen/set_wallpaper_button_vertical_padding" + android:right="@dimen/set_wallpaper_button_horizontal_padding" + android:bottom="@dimen/set_wallpaper_button_vertical_padding" /> + <solid android:color="?android:colorControlHighlight" /> + </shape> + </item> + + <item android:drawable="@drawable/set_wallpaper_button_background_base" /> +</ripple>
\ No newline at end of file diff --git a/res/drawable/set_wallpaper_button_background_base.xml b/res/drawable/set_wallpaper_button_background_base.xml new file mode 100644 index 00000000..67f77760 --- /dev/null +++ b/res/drawable/set_wallpaper_button_background_base.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="@dimen/set_wallpaper_button_corner_radius" /> + <padding + android:left="@dimen/set_wallpaper_button_horizontal_padding" + android:top="@dimen/set_wallpaper_button_vertical_padding" + android:right="@dimen/set_wallpaper_button_horizontal_padding" + android:bottom="@dimen/set_wallpaper_button_vertical_padding" /> + <solid android:color="@color/color_accent_primary" /> +</shape>
\ No newline at end of file diff --git a/res/drawable/snackbar_background.xml b/res/drawable/snackbar_background.xml index e7833c07..acc51d59 100644 --- a/res/drawable/snackbar_background.xml +++ b/res/drawable/snackbar_background.xml @@ -15,8 +15,7 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> <corners android:radius="18dp" /> - <solid android:color="?androidprv:attr/colorSurface" /> + <solid android:color="@color/color_surface" /> </shape>
\ No newline at end of file diff --git a/res/drawable/tab_background.xml b/res/drawable/tab_background.xml new file mode 100644 index 00000000..2f91a22e --- /dev/null +++ b/res/drawable/tab_background.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2022 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_selected="true"> + <shape android:shape="rectangle"> + <corners android:radius="12dp" /> + <solid android:color="@color/color_accent_secondary" /> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <corners android:radius="12dp" /> + <solid android:color="@color/color_surface" /> + </shape> + </item> +</selector>
\ No newline at end of file diff --git a/res/drawable/wallpaper_check_circle_24dp.xml b/res/drawable/wallpaper_check_circle_24dp.xml index 12491115..a83842ea 100644 --- a/res/drawable/wallpaper_check_circle_24dp.xml +++ b/res/drawable/wallpaper_check_circle_24dp.xml @@ -13,12 +13,11 @@ See the License for the specific language governing permissions and limitations under the License. --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape android:shape="oval"> <size android:width="24dp" android:height="24dp" /> - <solid android:color="?androidprv:attr/colorAccentPrimary" /> + <solid android:color="@color/color_accent_primary" /> </shape> </item> <item diff --git a/res/drawable/wallpaper_control_button_background.xml b/res/drawable/wallpaper_control_button_background.xml new file mode 100644 index 00000000..e3954dc6 --- /dev/null +++ b/res/drawable/wallpaper_control_button_background.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true" + android:drawable="@drawable/wallpaper_control_button_on_background" /> + <item android:drawable="@drawable/wallpaper_control_button_off_background" /> +</selector>
\ No newline at end of file diff --git a/res/drawable/wallpaper_control_button_customize.xml b/res/drawable/wallpaper_control_button_customize.xml new file mode 100644 index 00000000..246552dd --- /dev/null +++ b/res/drawable/wallpaper_control_button_customize.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@drawable/wallpaper_control_button_background" /> + <item android:drawable="@drawable/ic_tune" /> +</layer-list>
\ No newline at end of file diff --git a/res/drawable/wallpaper_control_button_delete.xml b/res/drawable/wallpaper_control_button_delete.xml new file mode 100644 index 00000000..dfc4b106 --- /dev/null +++ b/res/drawable/wallpaper_control_button_delete.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@drawable/wallpaper_control_button_background" /> + <item android:drawable="@drawable/ic_delete" /> +</layer-list>
\ No newline at end of file diff --git a/res/drawable/wallpaper_control_button_download.xml b/res/drawable/wallpaper_control_button_download.xml new file mode 100644 index 00000000..343a0cb9 --- /dev/null +++ b/res/drawable/wallpaper_control_button_download.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@drawable/wallpaper_control_button_background" /> + <item android:drawable="@drawable/ic_file_download" /> +</layer-list>
\ No newline at end of file diff --git a/res/drawable/wallpaper_control_button_effect.xml b/res/drawable/wallpaper_control_button_effect.xml new file mode 100644 index 00000000..a3c975b4 --- /dev/null +++ b/res/drawable/wallpaper_control_button_effect.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@drawable/wallpaper_control_button_background" /> + <item android:drawable="@drawable/ic_effect" /> +</layer-list>
\ No newline at end of file diff --git a/res/drawable/wallpaper_control_button_group_divider.xml b/res/drawable/wallpaper_control_button_group_divider.xml new file mode 100644 index 00000000..b8f115cc --- /dev/null +++ b/res/drawable/wallpaper_control_button_group_divider.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <size + android:height="@dimen/wallpaper_control_button_group_divider_space" + android:width="0dp" /> +</shape>
\ No newline at end of file diff --git a/res/drawable/wallpaper_control_button_info.xml b/res/drawable/wallpaper_control_button_info.xml new file mode 100644 index 00000000..c3954c1a --- /dev/null +++ b/res/drawable/wallpaper_control_button_info.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@drawable/wallpaper_control_button_background" /> + <item android:drawable="@drawable/ic_info" /> +</layer-list>
\ No newline at end of file diff --git a/res/drawable/wallpaper_control_button_off_background.xml b/res/drawable/wallpaper_control_button_off_background.xml new file mode 100644 index 00000000..7c0b1bc7 --- /dev/null +++ b/res/drawable/wallpaper_control_button_off_background.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:colorControlHighlight"> + + <item android:id="@android:id/mask"> + <shape android:shape="oval"> + <padding + android:bottom="@dimen/wallpaper_control_button_padding" + android:left="@dimen/wallpaper_control_button_padding" + android:right="@dimen/wallpaper_control_button_padding" + android:top="@dimen/wallpaper_control_button_padding" /> + <solid android:color="?android:colorControlHighlight" /> + </shape> + </item> + + <item android:drawable="@drawable/wallpaper_control_button_off_background_base" /> +</ripple>
\ No newline at end of file diff --git a/res/drawable/wallpaper_control_button_off_background_base.xml b/res/drawable/wallpaper_control_button_off_background_base.xml new file mode 100644 index 00000000..85e3e1f4 --- /dev/null +++ b/res/drawable/wallpaper_control_button_off_background_base.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <padding + android:left="@dimen/wallpaper_control_button_padding" + android:right="@dimen/wallpaper_control_button_padding" + android:top="@dimen/wallpaper_control_button_padding" + android:bottom="@dimen/wallpaper_control_button_padding" /> + <solid android:color="@color/color_surface" /> +</shape>
\ No newline at end of file diff --git a/res/drawable/wallpaper_control_button_on_background.xml b/res/drawable/wallpaper_control_button_on_background.xml new file mode 100644 index 00000000..3e35d095 --- /dev/null +++ b/res/drawable/wallpaper_control_button_on_background.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:colorControlHighlight"> + + <item android:id="@android:id/mask"> + <shape android:shape="oval"> + <padding + android:bottom="@dimen/wallpaper_control_button_padding" + android:left="@dimen/wallpaper_control_button_padding" + android:right="@dimen/wallpaper_control_button_padding" + android:top="@dimen/wallpaper_control_button_padding" /> + <solid android:color="?android:colorControlHighlight" /> + </shape> + </item> + + <item android:drawable="@drawable/wallpaper_control_button_on_background_base" /> +</ripple>
\ No newline at end of file diff --git a/res/drawable/wallpaper_control_button_on_background_base.xml b/res/drawable/wallpaper_control_button_on_background_base.xml new file mode 100644 index 00000000..0414188e --- /dev/null +++ b/res/drawable/wallpaper_control_button_on_background_base.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:shape="oval"> + <padding + android:left="@dimen/wallpaper_control_button_padding" + android:right="@dimen/wallpaper_control_button_padding" + android:top="@dimen/wallpaper_control_button_padding" + android:bottom="@dimen/wallpaper_control_button_padding"/> + <solid android:color="@color/color_accent_primary" /> +</shape>
\ No newline at end of file diff --git a/res/drawable/wallpaper_section_background.xml b/res/drawable/wallpaper_section_background.xml index 208f5438..f59f1962 100644 --- a/res/drawable/wallpaper_section_background.xml +++ b/res/drawable/wallpaper_section_background.xml @@ -14,8 +14,7 @@ limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> <corners android:radius="28dp" /> - <solid android:color="?androidprv:attr/colorSurface" /> + <solid android:color="@color/color_surface" /> </shape> diff --git a/res/layout/button_download_wallpaper.xml b/res/layout/button_download_wallpaper.xml new file mode 100644 index 00000000..882df49b --- /dev/null +++ b/res/layout/button_download_wallpaper.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="@dimen/wallpaper_control_button_size" + android:layout_height="@dimen/wallpaper_control_button_size"> + <ToggleButton + android:id="@+id/download_button" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent" + android:contentDescription="@string/bottom_action_bar_download" + android:foreground="@drawable/wallpaper_control_button_download" + android:textOff="" + android:textOn="" /> + + <FrameLayout + android:id="@+id/action_download_progress" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/wallpaper_control_button_off_background" + android:visibility="gone"> + <ProgressBar + android:layout_width="match_parent" + android:layout_height="match_parent" + android:indeterminateTint="?android:textColorTertiary"/> + </FrameLayout> +</FrameLayout> + diff --git a/res/layout/dialog_effect_error_title.xml b/res/layout/dialog_effect_error_title.xml index 1b6557fd..90466a86 100755 --- a/res/layout/dialog_effect_error_title.xml +++ b/res/layout/dialog_effect_error_title.xml @@ -14,10 +14,9 @@ limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?androidprv:attr/colorSurface" + android:background="@color/color_surface" android:orientation="vertical"> <Space @@ -41,7 +40,7 @@ android:layout_height="wrap_content" android:fontFamily="@*android:string/config_headlineFontFamily" android:minHeight="32dp" - android:textColor="?android:attr/textColorPrimary" + android:textColor="@color/text_color_primary" android:textSize="24sp" android:gravity="center" /> diff --git a/res/layout/dialog_set_wallpaper.xml b/res/layout/dialog_set_wallpaper.xml index f1102bd6..550463f1 100755 --- a/res/layout/dialog_set_wallpaper.xml +++ b/res/layout/dialog_set_wallpaper.xml @@ -16,10 +16,9 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="?androidprv:attr/colorSurface" + android:background="@color/color_surface" android:padding="@dimen/set_wallpaper_dialog_padding"> <LinearLayout @@ -37,7 +36,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/set_wallpaper_home_screen_destination" - android:textColor="?androidprv:attr/textColorOnAccent" /> + android:textColor="@color/text_color_on_accent" /> <Button android:id="@+id/set_lock_wallpaper_button" @@ -45,7 +44,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/set_wallpaper_lock_screen_destination" - android:textColor="?androidprv:attr/textColorOnAccent" /> + android:textColor="@color/text_color_on_accent" /> <Button android:id="@+id/set_both_wallpaper_button" @@ -53,7 +52,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/set_wallpaper_both_destination" - android:textColor="?androidprv:attr/textColorOnAccent" /> + android:textColor="@color/text_color_on_accent" /> </LinearLayout> diff --git a/res/layout/dialog_set_wallpaper_title.xml b/res/layout/dialog_set_wallpaper_title.xml index 3267939b..4dbc4f2f 100755 --- a/res/layout/dialog_set_wallpaper_title.xml +++ b/res/layout/dialog_set_wallpaper_title.xml @@ -14,10 +14,9 @@ limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?androidprv:attr/colorSurface" + android:background="@color/color_surface" android:orientation="vertical"> <Space @@ -41,7 +40,7 @@ android:layout_height="wrap_content" android:fontFamily="@*android:string/config_headlineFontFamily" android:minHeight="32dp" - android:textColor="?android:attr/textColorPrimary" + android:textColor="@color/text_color_primary" android:textSize="24sp" android:gravity="center" /> diff --git a/res/layout/duo_tabs.xml b/res/layout/duo_tabs.xml new file mode 100644 index 00000000..5a253761 --- /dev/null +++ b/res/layout/duo_tabs.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:divider="@drawable/duo_tabs_divider" + android:orientation="horizontal" + android:showDividers="middle"> + + <Button + android:id="@+id/tab_primary" + android:layout_width="match_parent" + android:layout_height="@dimen/separated_tabs_height" + android:layout_weight="1" + android:background="@drawable/duo_tabs_button_background" + android:gravity="center" /> + + <Button + android:id="@+id/tab_secondary" + android:layout_width="match_parent" + android:layout_height="@dimen/separated_tabs_height" + android:layout_weight="1" + android:background="@drawable/duo_tabs_button_background" + android:gravity="center" /> +</LinearLayout> diff --git a/res/layout/floating_sheet.xml b/res/layout/floating_sheet.xml new file mode 100644 index 00000000..24aacfe2 --- /dev/null +++ b/res/layout/floating_sheet.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<androidx.coordinatorlayout.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_height="match_parent" + android:layout_width="match_parent"> + <!-- Bottom Sheet Behavior view should be a child view of CoordinatorLayout --> + <FrameLayout + android:id="@+id/floating_sheet_container" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:clickable="true" + app:behavior_hideable="true" + app:behavior_peekHeight="0dp" + app:behavior_skipCollapsed="true" + app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> + <!-- To enable a floating sheet, content and styling are included as child view --> + <FrameLayout + android:id="@+id/floating_sheet_content" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_marginBottom="@dimen/floating_sheet_margin" + android:layout_marginHorizontal="@dimen/floating_sheet_margin" + android:orientation="vertical" + android:theme="@style/WallpaperPicker.FloatingPaneStyle" /> + </FrameLayout> +</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/res/layout/floating_sheet_wallpaper_info_view.xml b/res/layout/floating_sheet_wallpaper_info_view.xml new file mode 100644 index 00000000..519a4e25 --- /dev/null +++ b/res/layout/floating_sheet_wallpaper_info_view.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + + Copyright (C) 2022 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.wallpaper.widget.floatingsheetcontent.WallpaperInfoView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="@dimen/wallpaper_info_pane_padding" + android:theme="@style/WallpaperPicker.BottomPaneStyle"> + + <TextView + android:id="@+id/wallpaper_info_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TitleTextAppearance" + android:textColor="?android:textColorPrimary" /> + + <TextView + android:id="@+id/wallpaper_info_subtitle1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/wallpaper_info_pane_subtitle1_top_margin" + android:textColor="?android:textColorSecondary" + android:textSize="14sp" + android:visibility="gone" /> + + <TextView + android:id="@+id/wallpaper_info_subtitle2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/wallpaper_info_pane_subtitle2_top_margin" + android:textColor="?android:textColorSecondary" + android:textSize="14sp" + android:visibility="gone" /> + + <Button + android:id="@+id/wallpaper_info_explore_button" + style="@style/ExploreButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/wallpaper_info_pane_explore_button_top_margin" + android:text="@string/explore" + android:visibility="gone" /> +</com.android.wallpaper.widget.floatingsheetcontent.WallpaperInfoView> diff --git a/res/layout/fragment_clock_custom_picker_demo.xml b/res/layout/fragment_clock_custom_picker_demo.xml new file mode 100644 index 00000000..29bd9413 --- /dev/null +++ b/res/layout/fragment_clock_custom_picker_demo.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <FrameLayout + android:id="@+id/section_header_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_constraintBottom_toTopOf="@+id/clock_preview_card_list_demo" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + <include layout="@layout/section_header" /> + </FrameLayout> + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/clock_preview_card_list_demo" + android:layout_width="match_parent" + android:layout_height="0dp" + app:layout_constraintBottom_toBottomOf="parent" + android:paddingHorizontal="@dimen/grid_options_container_horizontal_margin" + app:layout_constraintTop_toBottomOf="@+id/section_header_container" + android:clipToPadding="false" /> +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/res/layout/fragment_tabbed_customization_picker.xml b/res/layout/fragment_tabbed_customization_picker.xml new file mode 100644 index 00000000..e0566703 --- /dev/null +++ b/res/layout/fragment_tabbed_customization_picker.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <com.android.wallpaper.widget.DuoTabs + android:id="@+id/duo_tabs" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingHorizontal="24dp" + android:paddingVertical="16dp" /> + + <include + layout="@layout/fragment_customization_picker" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> + +</LinearLayout> diff --git a/res/layout/fragment_wallpaper_preview.xml b/res/layout/fragment_wallpaper_preview.xml new file mode 100644 index 00000000..bc73fa83 --- /dev/null +++ b/res/layout/fragment_wallpaper_preview.xml @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.android.wallpaper.picker.TouchForwardingLayout + android:id="@+id/touch_forwarding_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent"/> + + <FrameLayout + android:id="@+id/hide_floating_sheet_touch_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent" + android:clickable="true"/> + + <include layout="@layout/wallpaper_preview" /> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent" + android:fitsSystemWindows="true"> + + <Toolbar + android:id="@+id/toolbar" + android:layout_width="0dp" + android:layout_height="?android:attr/actionBarSize" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@+id/barrier_start" /> + + <Button + android:id="@+id/button_set_wallpaper" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:background="@drawable/set_wallpaper_button_background" + android:gravity="center" + android:text="@string/set_wallpaper_button_text" + android:textColor="@color/text_color_on_accent" + android:layout_marginEnd="@dimen/set_wallpaper_button_margin_end" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@+id/toolbar" + app:layout_constraintBottom_toBottomOf="@+id/toolbar" /> + + <com.android.wallpaper.widget.WallpaperDownloadButton + android:id="@+id/button_download_wallpaper" + android:layout_width="@dimen/wallpaper_control_button_size" + android:layout_height="@dimen/wallpaper_control_button_size" + android:layout_marginEnd="@dimen/wallpaper_control_button_group_margin_end" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@+id/toolbar" + app:layout_constraintBottom_toBottomOf="@+id/toolbar" + android:visibility="gone"/> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/barrier_start" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:barrierDirection="start" + app:constraint_referenced_ids="button_set_wallpaper, button_download_wallpaper" /> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/barrier_bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:barrierDirection="bottom" + app:constraint_referenced_ids="button_set_wallpaper, button_download_wallpaper" /> + + <com.android.wallpaper.widget.WallpaperControlButtonGroup + android:id="@+id/wallpaper_control_button_group" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/wallpaper_control_button_group_margin_top" + android:layout_marginEnd="@dimen/wallpaper_control_button_group_margin_end" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@+id/barrier_bottom" /> + + <com.android.wallpaper.widget.DuoTabs + android:id="@+id/overlay_tabs" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingHorizontal="@dimen/full_preview_page_tabs_horizontal_padding" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + </androidx.constraintlayout.widget.ConstraintLayout> + + <com.android.wallpaper.widget.FloatingSheet + android:id="@+id/floating_sheet" + android:layout_height="match_parent" + android:layout_width="match_parent" /> + +</FrameLayout> diff --git a/res/layout/fullscreen_buttons.xml b/res/layout/fullscreen_buttons.xml index e6a03877..d9121499 100644 --- a/res/layout/fullscreen_buttons.xml +++ b/res/layout/fullscreen_buttons.xml @@ -16,7 +16,6 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_height="@dimen/separated_tabs_height" android:layout_width="match_parent" android:orientation="horizontal" @@ -34,7 +33,7 @@ android:text="@string/hide_ui_preview_text" android:textAlignment="center" android:textAppearance="@style/SeparatedTabsTextAppearance" - android:textColor="?androidprv:attr/textColorOnAccent" + android:textColor="@color/text_color_on_accent" android:visibility="visible"> </Button> @@ -50,7 +49,7 @@ android:text="@string/set_wallpaper_button_text" android:textAlignment="center" android:textAppearance="@style/SeparatedTabsTextAppearance" - android:textColor="?androidprv:attr/textColorOnAccent" + android:textColor="@color/text_color_on_accent" android:visibility="visible"> </Button> </LinearLayout> diff --git a/res/layout/grid_item_header.xml b/res/layout/grid_item_header.xml new file mode 100644 index 00000000..eb569932 --- /dev/null +++ b/res/layout/grid_item_header.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + + Copyright (C) 2022 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. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:lineHeight="@dimen/wallpaper_header_line_height" + android:textAppearance="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" + android:textColor="?android:textColorPrimary" + android:textSize="@dimen/wallpaper_header_text_size" /> diff --git a/res/layout/lock_screen_preview.xml b/res/layout/lock_screen_preview.xml index 1387603f..32ee922a 100644 --- a/res/layout/lock_screen_preview.xml +++ b/res/layout/lock_screen_preview.xml @@ -15,7 +15,6 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/lock_overlay" android:layout_width="match_parent" diff --git a/res/layout/menu_item_hide_ui.xml b/res/layout/menu_item_hide_ui.xml index 1e8aaa25..2331efc4 100644 --- a/res/layout/menu_item_hide_ui.xml +++ b/res/layout/menu_item_hide_ui.xml @@ -15,7 +15,6 @@ limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> @@ -27,5 +26,5 @@ android:background="@drawable/menu_item_hide_ui_background" android:contentDescription="@string/hide_ui_preview_text" android:gravity="center" - android:textColor="?androidprv:attr/textColorOnAccent" /> + android:textColor="@color/text_color_on_accent" /> </LinearLayout> diff --git a/res/layout/menu_item_set_wallpaper.xml b/res/layout/menu_item_set_wallpaper.xml index e660313d..4d9af600 100644 --- a/res/layout/menu_item_set_wallpaper.xml +++ b/res/layout/menu_item_set_wallpaper.xml @@ -15,7 +15,6 @@ limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> @@ -26,6 +25,6 @@ android:background="@drawable/menu_item_set_wallpaper_background" android:gravity="center" android:text="@string/apply_btn" - android:textColor="?androidprv:attr/textColorOnAccent" + android:textColor="@color/text_color_on_accent" android:textStyle="bold" /> </LinearLayout> diff --git a/res/layout/screen_preview_section.xml b/res/layout/screen_preview_section.xml new file mode 100644 index 00000000..fde66113 --- /dev/null +++ b/res/layout/screen_preview_section.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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.wallpaper.picker.customization.ui.section.ScreenPreviewView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="16dp"> + + <com.android.wallpaper.picker.DisplayAspectRatioFrameLayout + android:layout_width="match_parent" + android:layout_height="@dimen/screen_preview_height"> + + <include + android:id="@+id/lock_preview" + layout="@layout/wallpaper_preview_card" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" /> + + <include + android:id="@+id/home_preview" + layout="@layout/wallpaper_preview_card" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" /> + + </com.android.wallpaper.picker.DisplayAspectRatioFrameLayout> + +</com.android.wallpaper.picker.customization.ui.section.ScreenPreviewView> diff --git a/res/layout/wallpaper_control_button_group.xml b/res/layout/wallpaper_control_button_group.xml new file mode 100644 index 00000000..ecc73fcb --- /dev/null +++ b/res/layout/wallpaper_control_button_group.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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/wallpaper_control_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:divider="@drawable/wallpaper_control_button_group_divider" + android:showDividers="middle"> + <ToggleButton + android:id="@+id/delete_button" + android:layout_width="@dimen/wallpaper_control_button_size" + android:layout_height="@dimen/wallpaper_control_button_size" + android:background="@android:color/transparent" + android:contentDescription="@string/delete_live_wallpaper" + android:foreground="@drawable/wallpaper_control_button_delete" + android:textOff="" + android:textOn="" + android:visibility="gone" /> + + <ToggleButton + android:id="@+id/customize_button" + android:layout_width="@dimen/wallpaper_control_button_size" + android:layout_height="@dimen/wallpaper_control_button_size" + android:background="@android:color/transparent" + android:contentDescription="@string/tab_customize" + android:foreground="@drawable/wallpaper_control_button_customize" + android:textOff="" + android:textOn="" + android:visibility="gone" /> + + <ToggleButton + android:id="@+id/effects_button" + android:layout_width="@dimen/wallpaper_control_button_size" + android:layout_height="@dimen/wallpaper_control_button_size" + android:background="@android:color/transparent" + android:contentDescription="@string/tab_effects" + android:foreground="@drawable/wallpaper_control_button_effect" + android:textOff="" + android:textOn="" + android:visibility="gone" /> + + <ToggleButton + android:id="@+id/information_button" + android:layout_width="@dimen/wallpaper_control_button_size" + android:layout_height="@dimen/wallpaper_control_button_size" + android:background="@android:color/transparent" + android:contentDescription="@string/tab_info" + android:foreground="@drawable/wallpaper_control_button_info" + android:textOff="" + android:textOn="" + android:visibility="gone" /> +</LinearLayout> + diff --git a/res/layout/wallpaper_preview.xml b/res/layout/wallpaper_preview.xml new file mode 100644 index 00000000..abf1043c --- /dev/null +++ b/res/layout/wallpaper_preview.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="@string/wallpaper_preview_card_content_description"> + + <SurfaceView + android:id="@+id/wallpaper_surface" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <SurfaceView + android:id="@+id/wallpaper_surface_cinematic" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <com.android.wallpaper.picker.FadeAnimationSurfaceView + android:id="@+id/workspace_surface" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:importantForAccessibility="noHideDescendants" /> + + <FrameLayout + android:id="@+id/lock_screen_preview_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:importantForAccessibility="noHideDescendants" /> + + <androidx.core.widget.ContentLoadingProgressBar + android:id="@+id/wallpaper_preview_spinner" + style="?android:progressBarStyleLarge" + android:background="@android:color/transparent" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:visibility="gone" /> +</FrameLayout> diff --git a/res/layout/wallpaper_section_view.xml b/res/layout/wallpaper_section_view.xml index 1c7fe247..76cfc947 100644 --- a/res/layout/wallpaper_section_view.xml +++ b/res/layout/wallpaper_section_view.xml @@ -16,7 +16,6 @@ --> <com.android.wallpaper.picker.WallpaperSectionView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -71,10 +70,10 @@ android:layout_marginVertical="@dimen/wallpaper_picker_entry_margin_vertical" android:paddingHorizontal="@dimen/wallpaper_picker_entry_horizontal_padding" android:background="@drawable/btn_transparent_background" - android:textColor="?androidprv:attr/colorAccentPrimaryVariant" + android:textColor="@color/color_accent_primary_variant" android:drawablePadding="@dimen/wallpaper_picker_entry_drawable_padding" android:drawableStart="@drawable/ic_nav_wallpaper" - android:drawableTint="?androidprv:attr/colorAccentPrimaryVariant" + android:drawableTint="@color/color_accent_primary_variant" android:text="@string/wallpaper_picker_entry_title" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" diff --git a/res/menu/undoable_customization_menu.xml b/res/menu/undoable_customization_menu.xml new file mode 100644 index 00000000..35ee682c --- /dev/null +++ b/res/menu/undoable_customization_menu.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/revert" + android:title="@string/reset" + android:showAsAction="always|withText"/> +</menu> diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 5882b0b4..3d2919f8 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Aktiveer asseblief lêers en media in instellings."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktiveer"</string> <string name="open_my_photos" msgid="4107196465713868381">"Maak My Foto\'s oop"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Sluitskerm"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Tuisskerm"</string> + <string name="reset" msgid="4945445169532850631">"Stel terug"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Is jy seker?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Dit sal alle veranderinge wat jy gemaak het, terugstel."</string> </resources> diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 04e35740..60a485a8 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"እባክዎ በቅንብሮች ውስጥ ፋይሎችን እና ሚዲያን ያንቁ።"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"አንቃ"</string> <string name="open_my_photos" msgid="4107196465713868381">"የእኔ ፎቶዎችን ክፈት"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"ማያ ገፅ ቁልፍ"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"መነሻ ማያ ገፅ"</string> + <string name="reset" msgid="4945445169532850631">"ዳግም አስጀምር"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"እርግጠኛ ነዎት?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ይህ ሁሉንም ያደረጓቸውን ለውጦች ዳግም ያስጀምራቸዋል።"</string> </resources> diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index fa761286..d2b6e1b7 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"يُرجى تفعيل الملفات والوسائط في الإعدادات."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"تفعيل"</string> <string name="open_my_photos" msgid="4107196465713868381">"فتح صوري"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"شاشة القفل"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"الشاشة الرئيسية"</string> + <string name="reset" msgid="4945445169532850631">"إعادة الضبط"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"هل أنت متأكد؟"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"سيؤدي هذا الإجراء إلى إعادة ضبط كل التغييرات التي أجريتها."</string> </resources> diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index 3fe46d03..136eaabd 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"অনুগ্ৰহ কৰি ছেটিঙত ফাইল আৰু মিডিয়া সক্ষম কৰক।"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"সক্ষম কৰক"</string> <string name="open_my_photos" msgid="4107196465713868381">"My Photos খোলক"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"লক স্ক্ৰীন"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"গৃহ স্ক্ৰীন"</string> + <string name="reset" msgid="4945445169532850631">"ৰিছেট কৰক"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"আপুনি নিশ্চিতনে?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"এইটোৱে আপুনি কৰা আটাইবোৰ সালসলনি ৰিছেট কৰিব।"</string> </resources> diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index 6614c859..3ab7f418 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Ayarlarda faylları və medianı aktiv edin."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktiv edin"</string> <string name="open_my_photos" msgid="4107196465713868381">"Fotolarımı açın"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Ekran kilidi"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Əsas ekran"</string> + <string name="reset" msgid="4945445169532850631">"Sıfırlayın"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Əminsiniz?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Bu, etdiyiniz bütün dəyişiklikləri sıfırlayacaq."</string> </resources> diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index 90e006f4..a005273c 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Omogućite fajlove i medije u podešavanjima."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Omogući"</string> <string name="open_my_photos" msgid="4107196465713868381">"Otvori Moje slike"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Zaključani ekran"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Početni ekran"</string> + <string name="reset" msgid="4945445169532850631">"Resetuj"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Jeste li sigurni?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ovim resetujete sve promene koje ste uneli."</string> </resources> diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index 4e216002..77756990 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Уключыце файлы і мультымедыя ў наладах."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Уключыць"</string> <string name="open_my_photos" msgid="4107196465713868381">"Адкрыць \"Мае фота\""</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Экран блакіроўкі"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Галоўны экран"</string> + <string name="reset" msgid="4945445169532850631">"Скінуць"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Вы ўпэўненыя?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Будуць скінуты ўсе ўнесеныя вамі змяненні."</string> </resources> diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index e8313b91..773bd1f9 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Моля, активирайте разрешението за файлове и мултимедия от настройките."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Активиране"</string> <string name="open_my_photos" msgid="4107196465713868381">"Отваряне на „Моите снимки“"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Заключен екран"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Начален екран"</string> + <string name="reset" msgid="4945445169532850631">"Нулиране"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Наистина ли искате това?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Това действие ще нулира всички направени от вас промени."</string> </resources> diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 8524e59f..c954c727 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"সেটিংসে ফাইল এবং মিডিয়ার সুবিধা চালু করুন।"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"চালু করুন"</string> <string name="open_my_photos" msgid="4107196465713868381">"আমার ফটো বিভাগ খুলুন"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"লক স্ক্রিন"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"হোম স্ক্রিন"</string> + <string name="reset" msgid="4945445169532850631">"রিসেট করুন"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"আপনি কি নিশ্চিত?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"এর ফলে আপনার করা সব পরিবর্তন রিসেট হয়ে যাবে।"</string> </resources> diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 8368b467..3432f9af 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Omogućite fajlove i medije u postavkama."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Omogući"</string> <string name="open_my_photos" msgid="4107196465713868381">"Otvori Moje fotografije"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Zaključavanje ekrana"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Početni ekran"</string> + <string name="reset" msgid="4945445169532850631">"Poništi"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Jeste li sigurni?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ovim će se poništiti sve promjene koje ste izvršili."</string> </resources> diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index f8510ffd..802e09bd 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Activa Fitxers i contingut multimèdia a la configuració."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Activa"</string> <string name="open_my_photos" msgid="4107196465713868381">"Obre les meves fotos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Pantalla de bloqueig"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Pantalla d\'inici"</string> + <string name="reset" msgid="4945445169532850631">"Restableix"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Vols continuar?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Es restabliran tots els canvis que hagis fet."</string> </resources> diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index b15bbd97..5635b896 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"V nastavení povolte soubory a média."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Povolit"</string> <string name="open_my_photos" msgid="4107196465713868381">"Otevřít Moje fotky"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Obrazovka uzamčení"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Plocha"</string> + <string name="reset" msgid="4945445169532850631">"Resetovat"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Jste si jisti?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Tímto resetujete všechny provedené změny."</string> </resources> diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index 8db34d68..a98c22ae 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Aktivér adgang til filer og medier i indstillingerne."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktivér"</string> <string name="open_my_photos" msgid="4107196465713868381">"Åbn mine billeder"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Låseskærm"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Startskærm"</string> + <string name="reset" msgid="4945445169532850631">"Nulstil"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Er du sikker?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Dette vil nulstille alle dine ændringer."</string> </resources> diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 97027a95..26059cea 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Bitte aktiviere „Dateien und Medien“ in den Einstellungen."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktivieren"</string> <string name="open_my_photos" msgid="4107196465713868381">"Meine Fotos öffnen"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Sperrbildschirm"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Startbildschirm"</string> + <string name="reset" msgid="4945445169532850631">"Zurücksetzen"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Bist du sicher?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Hierdurch werden alle vorgenommenen Änderungen rückgängig gemacht."</string> </resources> diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 6ae2bea0..931856df 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Ενεργοποιήστε την άδεια Αρχεία και μέσα στις ρυθμίσεις."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Ενεργοποίηση"</string> <string name="open_my_photos" msgid="4107196465713868381">"Άνοιγμα των φωτογραφιών μου"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Οθόνη κλειδώματος"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Αρχική οθόνη"</string> + <string name="reset" msgid="4945445169532850631">"Επαναφορά"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Είστε βέβαιοι;"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Αυτή η ενέργεια θα επαναφέρει όλες τις αλλαγές που κάνατε."</string> </resources> diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index 796f7a9a..42ef3030 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Please enable files and media in settings."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Enable"</string> <string name="open_my_photos" msgid="4107196465713868381">"Open My photos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Lock screen"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Home screen"</string> + <string name="reset" msgid="4945445169532850631">"Reset"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Are you sure?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"This will reset all changes that you made."</string> </resources> diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml index 796f7a9a..799b6e4c 100644 --- a/res/values-en-rCA/strings.xml +++ b/res/values-en-rCA/strings.xml @@ -111,4 +111,14 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Please enable files and media in settings."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Enable"</string> <string name="open_my_photos" msgid="4107196465713868381">"Open My photos"</string> + <!-- no translation found for lock_screen_tab (6672930765010407652) --> + <skip /> + <!-- no translation found for home_screen_tab (1080445697837877526) --> + <skip /> + <!-- no translation found for reset (4945445169532850631) --> + <skip /> + <!-- no translation found for reset_confirmation_dialog_title (3006691785800178536) --> + <skip /> + <!-- no translation found for reset_confirmation_dialog_message (4304013650135221616) --> + <skip /> </resources> diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 796f7a9a..42ef3030 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Please enable files and media in settings."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Enable"</string> <string name="open_my_photos" msgid="4107196465713868381">"Open My photos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Lock screen"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Home screen"</string> + <string name="reset" msgid="4945445169532850631">"Reset"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Are you sure?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"This will reset all changes that you made."</string> </resources> diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 796f7a9a..42ef3030 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Please enable files and media in settings."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Enable"</string> <string name="open_my_photos" msgid="4107196465713868381">"Open My photos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Lock screen"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Home screen"</string> + <string name="reset" msgid="4945445169532850631">"Reset"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Are you sure?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"This will reset all changes that you made."</string> </resources> diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml index cefd8184..7da2e4bd 100644 --- a/res/values-en-rXC/strings.xml +++ b/res/values-en-rXC/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Please enable files and media in settings."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Enable"</string> <string name="open_my_photos" msgid="4107196465713868381">"Open My Photos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Lock screen"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Home screen"</string> + <string name="reset" msgid="4945445169532850631">"Reset"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Are you sure?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"This will reset all changes you made."</string> </resources> diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 8dca0076..1cc7c211 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Habilita los archivos y el contenido multimedia en la configuración."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Habilitar"</string> <string name="open_my_photos" msgid="4107196465713868381">"Abrir Mis fotos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Pantalla de bloqueo"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Pantalla principal"</string> + <string name="reset" msgid="4945445169532850631">"Restablecer"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"¿Confirmas esta acción?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Se restablecerán todos los cambios."</string> </resources> diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index fdab1098..822f3153 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Ve a Ajustes y habilita Archivos y contenido multimedia."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Habilitar"</string> <string name="open_my_photos" msgid="4107196465713868381">"Abrir Mis fotos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Pantalla de bloqueo"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Pantalla de inicio"</string> + <string name="reset" msgid="4945445169532850631">"Restablecer"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"¿Seguro que quieres hacerlo?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Se restablecerán todos los cambios que has hecho."</string> </resources> diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index 28d52f4d..4e60dc5d 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Lubage seadetes failid ja meedia."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Luba"</string> <string name="open_my_photos" msgid="4107196465713868381">"Ava jaotis Minu fotod"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Lukustuskuva"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Avakuva"</string> + <string name="reset" msgid="4945445169532850631">"Lähtesta"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Kas olete kindel?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"See lähtestab kõik tehtud muudatused."</string> </resources> diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index d03813d0..bf5a9f92 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Gaitu fitxategiak eta multimedia-edukia Ezarpenak atalean."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Gaitu"</string> <string name="open_my_photos" msgid="4107196465713868381">"Ireki nire argazkiak"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Pantaila blokeatua"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Hasierako pantaila"</string> + <string name="reset" msgid="4945445169532850631">"Berrezarri"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ziur zaude?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Egindako aldaketa guztiak berrezarriko dira."</string> </resources> diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 37b7f8c9..3efdf406 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"لطفاً فایلها و رسانهها را در تنظیمات فعال کنید."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"فعال کردن"</string> <string name="open_my_photos" msgid="4107196465713868381">"باز کردن «عکسهای من»"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"صفحه قفل"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"صفحه اصلی"</string> + <string name="reset" msgid="4945445169532850631">"بازنشانی"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"مطمئن هستید؟"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"با این کار، همه تغییرات ایجادشده بازنشانی خواهد شد."</string> </resources> diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index b3e23e80..24671374 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Ota tiedostot ja media käyttöön asetuksista."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Ota käyttöön"</string> <string name="open_my_photos" msgid="4107196465713868381">"Avaa Omat kuvat"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Lukitusnäyttö"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Aloitusnäyttö"</string> + <string name="reset" msgid="4945445169532850631">"Nollaa"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Oletko varma?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Tämä nollaa kaikki tekemäsi muutokset."</string> </resources> diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index ab17c8fc..c4dbddf5 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Veuillez activer les fichiers et les contenus multimédia dans les paramètres."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Activer"</string> <string name="open_my_photos" msgid="4107196465713868381">"Ouvrir mes photos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Écran de verrouillage"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Écran d\'accueil"</string> + <string name="reset" msgid="4945445169532850631">"Réinitialiser"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Voulez-vous vraiment continuer?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Cette action réinitialisera toutes les modifications que vous avez apportées."</string> </resources> diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index b7daa829..a3802c20 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Veuillez activer \"Fichiers et contenus multimédias\" dans les paramètres."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Activer"</string> <string name="open_my_photos" msgid="4107196465713868381">"Ouvrir mes photos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Écran de verrouillage"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Écran d\'accueil"</string> + <string name="reset" msgid="4945445169532850631">"Réinitialiser"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Voulez-vous vraiment continuer ?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Cela réinitialisera toutes les modifications que vous avez effectuées."</string> </resources> diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index 3d87796a..fd4f05dd 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Activa na configuración o uso de ficheiros e contido multimedia."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Activar"</string> <string name="open_my_photos" msgid="4107196465713868381">"Abrir As miñas fotos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Pantalla de bloqueo"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Pantalla de inicio"</string> + <string name="reset" msgid="4945445169532850631">"Restablecer"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Seguro que queres continuar?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Restableceranse todos os cambios que fixeses."</string> </resources> diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index b148a895..def244f6 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"કૃપા કરીને સેટિંગમાં ફાઇલો અને મીડિયાનો ઍક્સેસ ચાલુ કરો."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"ચાલુ કરો"</string> <string name="open_my_photos" msgid="4107196465713868381">"મારા ફોટા ખોલો"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"લૉક સ્ક્રીન"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"હોમ સ્ક્રીન"</string> + <string name="reset" msgid="4945445169532850631">"રીસેટ કરો"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ખરેખર?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"તમે કરેલા બધા ફેરફારો, આનાથી રીસેટ થશે."</string> </resources> diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 92a7df4e..59133d11 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"कृपया सेटिंग में जाकर फ़ाइलों और मीडिया को ऐक्सेस करने की सुविधा चालू करें."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"चालू करें"</string> <string name="open_my_photos" msgid="4107196465713868381">"मेरी फ़ोटो खोलो"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"लॉक स्क्रीन"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"होम स्क्रीन"</string> + <string name="reset" msgid="4945445169532850631">"रीसेट करें"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"क्या आपको बदलाव रीसेट करने हैं?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ऐसा करने पर, आपके किए हुए सभी बदलाव रीसेट कर दिए जाएंगे."</string> </resources> diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index b02717af..dd87a58c 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"U postavkama omogućite datoteke i medije."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Omogući"</string> <string name="open_my_photos" msgid="4107196465713868381">"Otvori Moje fotografije"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Zaključan zaslon"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Početni zaslon"</string> + <string name="reset" msgid="4945445169532850631">"Poništi"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Jeste li sigurni?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Time će se poništiti sve promjene koje ste unijeli."</string> </resources> diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 12407f9d..e9422558 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Engedélyezze a fájlokat és a médiatartalmakat a beállításokban."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Engedélyezés"</string> <string name="open_my_photos" msgid="4107196465713868381">"Saját fotóim megnyitása"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Lezárási képernyő"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Kezdőképernyő"</string> + <string name="reset" msgid="4945445169532850631">"Alaphelyzet"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Biztos benne?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ez visszaállítja az Ön által végzett összes módosítást."</string> </resources> diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 26af6e6a..12e2bfed 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Թույլատրեք ֆայլերի և մեդիաֆայլերի օգտագործումը կարգավորումներում։"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Թույլատրել"</string> <string name="open_my_photos" msgid="4107196465713868381">"Բացել «Իմ լուսանկարները»"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Կողպէկրան"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Հիմնական էկրան"</string> + <string name="reset" msgid="4945445169532850631">"Զրոյացնել"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Համոզվա՞ծ եք"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ձեր կատարած բոլոր փոփոխությունները կզրոյացվեն։"</string> </resources> diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 6d2e16cc..3d00b199 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Aktifkan izin file dan media di setelan."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktifkan"</string> <string name="open_my_photos" msgid="4107196465713868381">"Buka Foto Saya"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Layar kunci"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Layar utama"</string> + <string name="reset" msgid="4945445169532850631">"Reset"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Yakin?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Tindakan ini akan mereset semua perubahan yang Anda lakukan."</string> </resources> diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 8b7094b7..bfa6d9a6 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Virkjaðu skrár og margmiðlunarefni í stillingum."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Virkja"</string> <string name="open_my_photos" msgid="4107196465713868381">"Opna myndirnar mínar"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Lásskjár"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Heimaskjár"</string> + <string name="reset" msgid="4945445169532850631">"Endurstilla"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ertu viss?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Þetta mun endurstilla allar breytingarnar sem þú gerðir."</string> </resources> diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 40a9a48a..737d6b0a 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Attiva l\'autorizzazione File e contenuti multimediali nelle impostazioni."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Attiva"</string> <string name="open_my_photos" msgid="4107196465713868381">"Apri Le mie foto"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Schermata di blocco"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Schermata Home"</string> + <string name="reset" msgid="4945445169532850631">"Reimposta"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Confermi?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Verranno reimpostate tutte le modifiche apportate."</string> </resources> diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 88cb7824..7fa7be07 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"עליך להפעיל את ההרשאה \'קבצים ומדיה\' בהגדרות."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"הפעלה"</string> <string name="open_my_photos" msgid="4107196465713868381">"פתיחת \'התמונות שלי\'"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"מסך נעילה"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"מסך הבית"</string> + <string name="reset" msgid="4945445169532850631">"איפוס"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"להמשיך?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"הפעולה הזו תאפס את כל השינויים שביצעת."</string> </resources> diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index bc77269c..fdf14e1c 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"ファイルとメディアへのアクセス権限を設定で有効にしてください。"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"有効にする"</string> <string name="open_my_photos" msgid="4107196465713868381">"マイフォトを開く"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"ロック画面"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"ホーム画面"</string> + <string name="reset" msgid="4945445169532850631">"リセット"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"本当によろしいですか?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"行った変更はすべてリセットされます。"</string> </resources> diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 1c7ab289..dcc0de23 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"ჩართეთ ფაილები და მედია პარამეტრებიდან."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"ჩართვა"</string> <string name="open_my_photos" msgid="4107196465713868381">"ჩემი ფოტოების გახსნა"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"ჩაკეტილი ეკრანი"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"მთავარი ეკრანი"</string> + <string name="reset" msgid="4945445169532850631">"გადაყენება"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"დარწმუნებული ხართ?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ამით გადაყენდება თქვენ მიერ განხორციელებული ყველა ცვლილება."</string> </resources> diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index a5e0e4bd..eb78e7ba 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"\"Параметрлер\" бөлімінде \"Файлдар және мультимедиа\" рұқсатын қосыңыз."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Қосу"</string> <string name="open_my_photos" msgid="4107196465713868381">"Фотоларымды көру"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Экранды құлыптау"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Негізгі экран"</string> + <string name="reset" msgid="4945445169532850631">"Қайта орнату"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Сенімдісіз бе?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Жасалған барлық өзгеріс қайтарылады."</string> </resources> diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index 68c9ccb4..86c8d552 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"សូមបើកឯកសារ និងមេឌៀនៅក្នុងការកំណត់។"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"បើក"</string> <string name="open_my_photos" msgid="4107196465713868381">"បើករូបថតរបស់ខ្ញុំ"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"អេក្រង់ចាក់សោ"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"អេក្រង់ដើម"</string> + <string name="reset" msgid="4945445169532850631">"កំណត់ឡើងវិញ"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"តើអ្នកប្រាកដដែរទេ?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ការធ្វើបែបនេះនឹងកំណត់ការផ្លាស់ប្ដូរទាំងអស់ដែលអ្នកបានធ្វើឡើងវិញ។"</string> </resources> diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index ec59f179..91c92fc9 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಫೈಲ್ಗಳು ಮತ್ತು ಮಾಧ್ಯಮವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> <string name="open_my_photos" msgid="4107196465713868381">"ನನ್ನ ಫೋಟೋಗಳನ್ನು ತೆರೆಯಿರಿ"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಮಾಡಿ"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"ಹೋಮ್ ಸ್ಕ್ರೀನ್"</string> + <string name="reset" msgid="4945445169532850631">"ರೀಸೆಟ್ ಮಾಡಿ"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ರೀಸೆಟ್ ಮಾಡಲು ಬಯಸುತ್ತೀರಾ?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ಇದು ನೀವು ಮಾಡಿದ ಎಲ್ಲಾ ಬದಲಾವಣೆಗಳನ್ನು ರೀಸೆಟ್ ಮಾಡುತ್ತದೆ."</string> </resources> diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 8b2cc38d..6100cf43 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"설정에서 파일 및 미디어를 사용 설정하세요."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"사용"</string> <string name="open_my_photos" msgid="4107196465713868381">"내 사진 열기"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"잠금 화면"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"홈 화면"</string> + <string name="reset" msgid="4945445169532850631">"초기화"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"계속하시겠습니까?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"이렇게 하면 모든 변경사항이 재설정됩니다."</string> </resources> diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index a375d844..e3bb2987 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Жөндөөлөрдөн файлдарды жана медианы иштетиңиз."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Иштетүү"</string> <string name="open_my_photos" msgid="4107196465713868381">"Сүрөттөрүмдү ачуу"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Экранды кулпулоо"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Башкы экран"</string> + <string name="reset" msgid="4945445169532850631">"Баштапкы абалга келтирүү"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Чын элеби?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ушуну менен бардык өзгөртүүлөр баштапкы абалга келтирилет."</string> </resources> diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index d7bd62f9..82d5d5c5 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"ກະລຸນາເປີດການນຳໃຊ້ໄຟລ໌ ແລະ ມີເດຍໃນການຕັ້ງຄ່າ"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"ເປີດການນຳໃຊ້"</string> <string name="open_my_photos" msgid="4107196465713868381">"ເປີດຮູບພາບຂອງຂ້ອຍ"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"ໜ້າຈໍລັອກ"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"ໂຮມສະກຣີນ"</string> + <string name="reset" msgid="4945445169532850631">"ຣີເຊັດ"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ທ່ານແນ່ໃຈບໍ?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ນີ້ຈະຣີເຊັດການປ່ຽນແປງທັງໝົດທີ່ທ່ານເຮັດ."</string> </resources> diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 94c2be03..b4e4bd57 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Įgalinkite failus ir mediją nustatymuose."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Įgalinti"</string> <string name="open_my_photos" msgid="4107196465713868381">"Atidaryti skiltį „Mano nuotraukos“"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Užrakinimo ekranas"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Pagrindinis ekranas"</string> + <string name="reset" msgid="4945445169532850631">"Nust. iš n."</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ar tikrai norite tai padaryti?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Bus iš naujo nustatyti visi atlikti pakeitimai."</string> </resources> diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index dc367756..c39c931a 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Lūdzu, iestatījumos iespējojiet atļauju izmantot failus un multivides saturu."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Iespējot"</string> <string name="open_my_photos" msgid="4107196465713868381">"Atvērt sadaļu “Mani fotoattēli”"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Bloķēšanas ekrāns"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Sākuma ekrāns"</string> + <string name="reset" msgid="4945445169532850631">"Atiestatīt"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Vai esat pārliecināts?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Tādējādi tiks atiestatītas visas jūsu veiktās izmaiņas."</string> </resources> diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index 0c854131..727d1495 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Овозможете „Датотеки и аудиовизуелни содржини“ во поставки."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Овозможи"</string> <string name="open_my_photos" msgid="4107196465713868381">"Отвори „Мои фотографии“"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Заклучување екран"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Почетен екран"</string> + <string name="reset" msgid="4945445169532850631">"Ресетирај"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Дали сте сигурни?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ова ќе ги ресетира сите извршени промени."</string> </resources> diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index 9dd8f116..b69d425b 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"ക്രമീകരണത്തിൽ ഫയലുകളും മീഡിയയും പ്രവർത്തനക്ഷമമാക്കുക."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"പ്രവർത്തനക്ഷമമാക്കുക"</string> <string name="open_my_photos" msgid="4107196465713868381">"എന്റെ ഫോട്ടോകൾ തുറക്കുക"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"സ്ക്രീൻ ലോക്ക് ചെയ്യുക"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"ഹോം സ്ക്രീൻ"</string> + <string name="reset" msgid="4945445169532850631">"റീസെറ്റ് ചെയ്യൂ"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ഉറപ്പാണോ?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"നിങ്ങൾ വരുത്തിയ മാറ്റങ്ങളെല്ലാം ഇത് റീസെറ്റ് ചെയ്യും."</string> </resources> diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 8f702bcf..87590b51 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Тохиргоо хэсгээс файл болон мeдиаг идэвхжүүлнэ үү."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Идэвхжүүлэх"</string> <string name="open_my_photos" msgid="4107196465713868381">"Миний Зургийг нээх"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Түгжигдсэн дэлгэц"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Үндсэн нүүр"</string> + <string name="reset" msgid="4945445169532850631">"Шинэчлэх"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ta итгэлтэй байна уу?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Энэ нь таны хийсэн бүх өөрчлөлтийг шинэчилнэ."</string> </resources> diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index a1853dc3..7f4c6c5a 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"कृपया सेटिंग्जमध्ये फाइल आणि मीडिया वापरण्याची ॲपला परवानगी द्या."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"परवानगी द्या"</string> <string name="open_my_photos" msgid="4107196465713868381">"माझे फोटो उघडा"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"लॉक स्क्रीन"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"मुख्य स्क्रीन"</string> + <string name="reset" msgid="4945445169532850631">"रीसेट करा"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"तुमची खात्री आहे का?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"यामुळे तुम्ही केलेले सर्व बदल रीसेट होतील."</string> </resources> diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index c4982430..1cdccfa8 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Sila dayakan fail dan media dalam tetapan."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Dayakan"</string> <string name="open_my_photos" msgid="4107196465713868381">"Buka Foto Saya"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Skrin kunci"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Skrin utama"</string> + <string name="reset" msgid="4945445169532850631">"Tetapkan semula"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Anda pasti?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Tindakan ini akan menetapkan semula semua perubahan yang telah dibuat."</string> </resources> diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index f716ad81..a3e5f268 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"ဆက်တင်များတွင် Files နှင့် မီဒီယာကို ဖွင့်ပါ။"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"ဖွင့်ရန်"</string> <string name="open_my_photos" msgid="4107196465713868381">"ဓာတ်ပုံများဖွင့်ရန်"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"လော့ခ်မျက်နှာပြင်"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"ပင်မစာမျက်နှာ"</string> + <string name="reset" msgid="4945445169532850631">"ပြင်ဆင်ရန်"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"သေချာပါသလား။"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"၎င်းက သင့်အပြောင်းအလဲအားလုံးကို ပြင်ဆင်သတ်မှတ်မည်။"</string> </resources> diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index f7cd1f24..9da3e428 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -44,7 +44,7 @@ <string name="daily_refresh_tile_title" msgid="3270456074558525091">"Daglig bakgrunn"</string> <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"Trykk for å slå på funksjonen"</string> <string name="start_rotation_dialog_body_live_wallpaper_needed" msgid="5132580257563846082">"Bakgrunnen endres automatisk hver dag. For å fullføre konfigureringen, trykk på <strong>Angi bakgrunn</strong> på den neste skjermen."</string> - <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"Last ned nye bakgrunner bare via Wi-Fi"</string> + <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"Last ned nye bakgrunner bare via Wifi"</string> <string name="start_rotation_dialog_continue" msgid="276678987852274872">"Fortsett"</string> <string name="start_rotation_progress_message" msgid="7872623873682262083">"Laster ned den første bakgrunnen …"</string> <string name="start_rotation_error_message" msgid="3053799836719618972">"Kunne ikke laste ned den første bakgrunnen. Sjekk nettverksinnstillingene, og prøv igjen."</string> @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Slå på filer og medier i innstillingene."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Slå på"</string> <string name="open_my_photos" msgid="4107196465713868381">"Åpne Mine bilder"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Låseskjerm"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Startskjerm"</string> + <string name="reset" msgid="4945445169532850631">"Tilbakestill"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Er du sikker?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Dette tilbakestiller alle endringene du har gjort."</string> </resources> diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index dc7499ff..ced01b65 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"कृपया सेटिङहरूमा गई फाइल र मिडिया प्रयोग गर्न दिनुहोस्।"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"अन गर्नुहोस्"</string> <string name="open_my_photos" msgid="4107196465713868381">"\"मेरा फोटोहरू\" खोल्नुहोस्"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"स्क्रिन लक गर्नुहोस्"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"होम स्क्रिन"</string> + <string name="reset" msgid="4945445169532850631">"रिसेट गर्नुहोस्"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"तपाईंले यसो गर्न खोज्नुभएकै हो?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"तपाईंले यसो गर्नुभयो भने तपाईंले परिवर्तन गरेका सबै कुरा रिसेट हुने छन्।"</string> </resources> diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index ced3ee43..bec12053 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Zet het recht voor bestanden en media aan in de instellingen."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aanzetten"</string> <string name="open_my_photos" msgid="4107196465713868381">"Mijn foto\'s openen"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Vergrendelscherm"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Startscherm"</string> + <string name="reset" msgid="4945445169532850631">"Resetten"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Weet je het zeker?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Hiermee worden al je wijzigingen gereset."</string> </resources> diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml index fbca74a2..ba538b9b 100644 --- a/res/values-or/strings.xml +++ b/res/values-or/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"ଦୟାକରି ସେଟିଂସରେ ଫାଇଲଗୁଡ଼ିକ ଏବଂ ମିଡିଆକୁ ସକ୍ଷମ କରନ୍ତୁ।"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"ସକ୍ଷମ କରନ୍ତୁ"</string> <string name="open_my_photos" msgid="4107196465713868381">"ମୋ ଫଟୋଗୁଡ଼ିକ ଖୋଲନ୍ତୁ"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"ଲକ ସ୍କ୍ରିନ"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"ହୋମ ସ୍କ୍ରିନ"</string> + <string name="reset" msgid="4945445169532850631">"ରିସେଟ କରନ୍ତୁ"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ଆପଣ ନିଶ୍ଚିତ?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ଏହା ଆପଣ କରିଥିବା ସମସ୍ତ ପରିବର୍ତ୍ତନକୁ ରିସେଟ କରିବ।"</string> </resources> diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index 0ec041fa..03c1ff71 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"ਕਿਰਪਾ ਕਰਕੇ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਫ਼ਾਈਲਾਂ ਅਤੇ ਮੀਡੀਆ ਨੂੰ ਚਾਲੂ ਕਰੋ।"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"ਚਾਲੂ ਕਰੋ"</string> <string name="open_my_photos" msgid="4107196465713868381">"ਮੇਰੀਆਂ ਫ਼ੋਟੋਆਂ ਖੋਲ੍ਹੋ"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"ਲਾਕ ਸਕ੍ਰੀਨ"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"ਹੋਮ ਸਕ੍ਰੀਨ"</string> + <string name="reset" msgid="4945445169532850631">"ਰੀਸੈੱਟ ਕਰੋ"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ਪੱਕਾ?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ਇਸ ਨਾਲ ਤੁਹਾਡੇ ਵੱਲੋਂ ਕੀਤੀਆਂ ਸਾਰੀਆਂ ਤਬਦੀਲੀਆਂ ਰੀਸੈੱਟ ਹੋ ਜਾਣਗੀਆਂ।"</string> </resources> diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 02e04302..c4e6f662 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"W ustawieniach włącz dostęp do plików i multimediów."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Włącz"</string> <string name="open_my_photos" msgid="4107196465713868381">"Otwórz Moje zdjęcia"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Ekran blokady"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Ekran główny"</string> + <string name="reset" msgid="4945445169532850631">"Resetuj"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Czy na pewno tego chcesz?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Spowoduje to cofnięcie wszystkich wprowadzonych zmian."</string> </resources> diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 034ac26b..75fa890e 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Ative os ficheiros e multimédia nas definições."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Ativar"</string> <string name="open_my_photos" msgid="4107196465713868381">"Abrir As minhas fotos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Ecrã de bloqueio"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Ecrã principal"</string> + <string name="reset" msgid="4945445169532850631">"Repor"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Tem a certeza?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Esta ação repõe todas as alterações que fez."</string> </resources> diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 17567498..b02248e5 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Ative a permissão de acesso a arquivos e mídia nas configurações."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Ativar"</string> <string name="open_my_photos" msgid="4107196465713868381">"Abrir minhas fotos"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Tela de bloqueio"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Tela inicial"</string> + <string name="reset" msgid="4945445169532850631">"Redefinir"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Tem certeza?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Essa ação redefine todas as mudanças feitas."</string> </resources> diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 1ca9cb59..61d6b06c 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Activează Fișiere și media în setări."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Activează"</string> <string name="open_my_photos" msgid="4107196465713868381">"Deschide Fotografiile mele"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Ecran de blocare"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Ecran de pornire"</string> + <string name="reset" msgid="4945445169532850631">"Resetează"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Confirmi?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Astfel, toate modificările pe care le-ai făcut vor fi resetate."</string> </resources> diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 48908602..010fa89a 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Разрешите доступ к файлам и медиаконтенту в настройках."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Разрешить"</string> <string name="open_my_photos" msgid="4107196465713868381">"Открыть \"Мои фото\""</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Заблокированный экран"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Главный экран"</string> + <string name="reset" msgid="4945445169532850631">"Сбросить"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Вы уверены?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Все внесенные изменения будут сброшены."</string> </resources> diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index 6d4710b4..923df35e 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"කරුණාකර සැකසීම් තුළ ගොනු සහ මාධ්ය සබල කරන්න."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"සබල කරන්න"</string> <string name="open_my_photos" msgid="4107196465713868381">"මගේ ඡායාරූප විවෘත කරන්න"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"අගුළු තිරය"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"මුල් තිරය"</string> + <string name="reset" msgid="4945445169532850631">"යළි සකසන්න"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ඔබට විශ්වාස ද?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"මෙය ඔබ සිදු කළ සියලු වෙනස්කම් යළි සකසනු ඇත."</string> </resources> diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index f644edfe..264e7726 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"V nastaveniach povoľte súbory a médiá."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Povoliť"</string> <string name="open_my_photos" msgid="4107196465713868381">"Otvoriť Moje fotky"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Uzamknutá obrazovka"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Plocha"</string> + <string name="reset" msgid="4945445169532850631">"Resetovať"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Naozaj to chcete urobiť?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Týmto resetujete všetky vykonané zmeny."</string> </resources> diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 7491e1c4..516fd2b8 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"V nastavitvah omogočite datoteke in predstavnost."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Omogoči"</string> <string name="open_my_photos" msgid="4107196465713868381">"Odpri Moje fotografije"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Zaklenjen zaslon"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Začetni zaslon"</string> + <string name="reset" msgid="4945445169532850631">"Ponastavi"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ali ste prepričani?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"S tem boste ponastavili vse izvedene spremembe."</string> </resources> diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 6fc08dfe..78424cb9 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Aktivizo skedarët dhe median te cilësimet."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktivizo"</string> <string name="open_my_photos" msgid="4107196465713868381">"Hap \"Fotografitë e mia\""</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Ekrani i kyçjes"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Ekrani bazë"</string> + <string name="reset" msgid="4945445169532850631">"Rivendos"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"E konfirmon?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Kjo do t\'i rivendosë të gjitha ndryshimet që bëre."</string> </resources> diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index f2a1d96c..c894c797 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Омогућите фајлове и медије у подешавањима."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Омогући"</string> <string name="open_my_photos" msgid="4107196465713868381">"Отвори Моје слике"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Закључани екран"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Почетни екран"</string> + <string name="reset" msgid="4945445169532850631">"Ресетуј"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Јесте ли сигурни?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Овим ресетујете све промене које сте унели."</string> </resources> diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 1114bb6b..6b6403e9 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Aktivera Filer och media i inställningarna."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktivera"</string> <string name="open_my_photos" msgid="4107196465713868381">"Öppna Mina bilder"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Låsskärm"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Startskärm"</string> + <string name="reset" msgid="4945445169532850631">"Återställ"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Är du säker?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Detta återställer alla dina ändringar."</string> </resources> diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 6f26e803..d6be66c7 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Tafadhali washa faili na maudhui katika mipangilio."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Washa"</string> <string name="open_my_photos" msgid="4107196465713868381">"Fungua Picha Zangu"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Skrini iliyofungwa"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Skrini ya kwanza"</string> + <string name="reset" msgid="4945445169532850631">"Weka upya"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Una uhakika?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Hali hii itaweka upya mabadiliko yote uliyofanya."</string> </resources> diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 21119713..199e320d 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"அமைப்புகளில் உள்ள ஃபைல்களையும் மீடியாவையும் இயக்கவும்."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"இயக்கு"</string> <string name="open_my_photos" msgid="4107196465713868381">"எனது படங்களைத் திற"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"பூட்டுத் திரை"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"முகப்புத் திரை"</string> + <string name="reset" msgid="4945445169532850631">"மீட்டமை"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"நிச்சயமாக மீட்டமைக்க வேண்டுமா?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"இது நீங்கள் செய்த அனைத்து மாற்றங்களையும் மீட்டமைக்கும்."</string> </resources> diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index 96b14bed..7fdb8c3d 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -45,7 +45,7 @@ <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"ఆన్ చేయడానికి నొక్కండి"</string> <string name="start_rotation_dialog_body_live_wallpaper_needed" msgid="5132580257563846082">"వాల్పేపర్ ప్రతి రోజు ఆటోమేటిక్గా మారుతుంది. సెటప్ను ముగించడానికి, తదుపరి స్క్రీన్లో <strong>వాల్ పేపర్ను సెట్ చేయి</strong>ని నొక్కండి."</string> <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"భవిష్యత్తులో వాల్పేపర్లను Wi-Fiలో ఉన్నప్పుడు మాత్రమే డౌన్లోడ్ చేయండి"</string> - <string name="start_rotation_dialog_continue" msgid="276678987852274872">"కొనసాగించు"</string> + <string name="start_rotation_dialog_continue" msgid="276678987852274872">"కొనసాగించండి"</string> <string name="start_rotation_progress_message" msgid="7872623873682262083">"మొదటి వాల్పేపర్ను డౌన్లోడ్ చేస్తోంది…"</string> <string name="start_rotation_error_message" msgid="3053799836719618972">"మొదటి వాల్పేపర్ను డౌన్లోడ్ చేయలేకపోయింది. దయచేసి మీ నెట్వర్క్ సెట్టింగ్లను చెక్ చేసి, ఆపై మళ్లీ ప్రయత్నించండి."</string> <string name="start_rotation_dialog_body" msgid="7903554799046364916">"వాల్పేపర్ ప్రతి రోజు ఆటోమేటిక్గా మారుతుంది"</string> @@ -83,7 +83,7 @@ <string name="tab_effects" msgid="3213606157589233901">"ఎఫెక్ట్లు"</string> <string name="my_photos" msgid="8613021349284084982">"నా ఫోటోలు"</string> <string name="configure_wallpaper" msgid="849882179182976621">"సెట్టింగ్లు…"</string> - <string name="delete_live_wallpaper" msgid="589212696102662329">"తొలగించు"</string> + <string name="delete_live_wallpaper" msgid="589212696102662329">"తొలగించండి"</string> <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"మీ ఫోన్ నుండి ఈ వాల్పేపర్ను తొలగించాలా?"</string> <string name="bottom_action_bar_back" msgid="8237013112999946725">"వెనుకకు"</string> <string name="bottom_action_bar_edit" msgid="1214742990893082138">"ఎడిట్"</string> @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"దయచేసి సెట్టింగ్లలో ఫైల్లు, మీడియాను ఎనేబుల్ చేయండి."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"ఎనేబుల్ చేయండి"</string> <string name="open_my_photos" msgid="4107196465713868381">"నా ఫోటోలను తెరవండి"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"లాక్ స్క్రీన్"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"మొదటి స్క్రీన్"</string> + <string name="reset" msgid="4945445169532850631">"రీసెట్ చేయి"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"మీరు రీసెట్ చేయాలనుకుంటున్నారా?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ఇది మీరు చేసిన అన్ని మార్పులను రీసెట్ చేస్తుంది."</string> </resources> diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 8b49f782..b7badedd 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"โปรดเปิดใช้ไฟล์และสื่อในการตั้งค่า"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"เปิดใช้"</string> <string name="open_my_photos" msgid="4107196465713868381">"เปิดรูปภาพของฉัน"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"ล็อกหน้าจอ"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"หน้าจอหลัก"</string> + <string name="reset" msgid="4945445169532850631">"รีเซ็ต"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"คุณแน่ใจใช่ไหม"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"การดำเนินการนี้จะรีเซ็ตการเปลี่ยนแปลงทั้งหมดที่คุณทำ"</string> </resources> diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index f6a19b71..b45da502 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -47,7 +47,7 @@ <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"Mag-download lang ng mga wallpaper sa hinaharap gamit ang Wi-Fi"</string> <string name="start_rotation_dialog_continue" msgid="276678987852274872">"Magpatuloy"</string> <string name="start_rotation_progress_message" msgid="7872623873682262083">"Dina-download ang unang wallpaper…"</string> - <string name="start_rotation_error_message" msgid="3053799836719618972">"Hindi ma-download ang unang wallpaper. Pakisuri ang mga setting ng iyong network at subukang muli."</string> + <string name="start_rotation_error_message" msgid="3053799836719618972">"Hindi ma-download ang unang wallpaper. Pakisuri ang mga setting ng iyong network at subukan ulit."</string> <string name="start_rotation_dialog_body" msgid="7903554799046364916">"Awtomatikong magpapalit ang wallpaper araw-araw"</string> <string name="settings_button_label" msgid="8724734130079207955">"Mga Setting"</string> <string name="explore" msgid="7468719504199497281">"I-explore"</string> @@ -55,7 +55,7 @@ <string name="wallpaper_disabled_message" msgid="7309484130562148185">"Naka-disable sa device na ito ang pagtatakda ng wallpaper"</string> <string name="wallpaper_disabled_by_administrator_message" msgid="1551430406714747884">"Na-disable ng administrator ng iyong device ang pagtatakda ng wallpaper"</string> <string name="wallpaper_set_successfully_message" msgid="2958998799111688578">"Matagumpay na naitakda ang wallpaper"</string> - <string name="wallpapers_unavailable_offline_message" msgid="8136405438621689532">"Kailangan mo ng koneksyon sa Internet para tingnan ang mga wallpaper. Kumonekta at subukang muli."</string> + <string name="wallpapers_unavailable_offline_message" msgid="8136405438621689532">"Kailangan mo ng koneksyon sa Internet para tingnan ang mga wallpaper. Kumonekta at subukan ulit."</string> <string name="currently_set_home_wallpaper_thumbnail" msgid="4022381436821898917">"Thumbnail ng kasalukuyang nakatakdang wallpaper ng home screen"</string> <string name="currently_set_lock_wallpaper_thumbnail" msgid="2094209303934569997">"Thumbnail ng kasalukuyang nakatakdang wallpaper ng lock screen"</string> <string name="currently_set_wallpaper_thumbnail" msgid="8651887838745545107">"Thumbnail ng kasalukuyang nakatakdang wallpaper"</string> @@ -65,7 +65,7 @@ <string name="refresh_daily_wallpaper_home_content_description" msgid="2770445044556164259">"I-refresh ang pang-araw-araw na wallpaper ng home screen"</string> <string name="refresh_daily_wallpaper_content_description" msgid="4362142658237147583">"I-refresh ang pang-araw-araw na wallpaper"</string> <string name="refreshing_daily_wallpaper_dialog_message" msgid="1975910873362855761">"Nire-refresh ang pang-araw-araw na wallpaper…"</string> - <string name="refresh_daily_wallpaper_failed_message" msgid="4749879993812557166">"Hindi na-refresh ang pang-araw-araw na wallpaper. Pakitingnan ang iyong koneksyon sa network at subukang muli."</string> + <string name="refresh_daily_wallpaper_failed_message" msgid="4749879993812557166">"Hindi na-refresh ang pang-araw-araw na wallpaper. Pakitingnan ang iyong koneksyon sa network at subukan ulit."</string> <string name="on_device_wallpapers_category_title" msgid="805819102071369004">"Mga nasa device na wallpaper"</string> <string name="on_device_wallpapers_category_title_desktop" msgid="316919420410065369">"Nasa device"</string> <string name="on_device_wallpaper_title" msgid="5262564748034629524">"Wallpaper ng Android"</string> @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Paki-enable ang mga file at media sa mga setting."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"I-enable"</string> <string name="open_my_photos" msgid="4107196465713868381">"Buksan ang Mga Larawan Ko"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"I-lock ang screen"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Home screen"</string> + <string name="reset" msgid="4945445169532850631">"I-reset"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Sigurado ka ba?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ire-reset nito ang lahat ng pagbabagong ginawa mo."</string> </resources> diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index a5d54825..f4fc63e6 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Lütfen ayarlarda dosyalar ve medya seçeneğini etkinleştirin."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Etkinleştir"</string> <string name="open_my_photos" msgid="4107196465713868381">"Fotoğraflarım\'ı aç"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Kilit ekranı"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Ana ekran"</string> + <string name="reset" msgid="4945445169532850631">"Sıfırla"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Emin misiniz?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Bu işlemle, yaptığınız tüm değişiklikler sıfırlanacak."</string> </resources> diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 628b2444..f8f148c2 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Дозвольте використання файлів і мультимедіа в налаштуваннях."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Дозволити"</string> <string name="open_my_photos" msgid="4107196465713868381">"Відкрити мої фото"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Заблокований екран"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Головний екран"</string> + <string name="reset" msgid="4945445169532850631">"Скинути"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Продовжити?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Усі внесені зміни буде скасовано."</string> </resources> diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index 38450be9..8f212f7d 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"براہ کرم ترتیبات میں فائلز اور میڈیا فعال کریں۔"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"فعال کریں"</string> <string name="open_my_photos" msgid="4107196465713868381">"میری تصاویر کھولیں"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"مقفل اسکرین"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"ہوم اسکرین"</string> + <string name="reset" msgid="4945445169532850631">"ری سیٹ کریں"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"آپ پُر یقین ہیں؟"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"یہ آپ کی کی گئی تمام تبدیلیوں کو ری سیٹ کر دے گا۔"</string> </resources> diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index f9f9f3dd..701b5c7f 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Fayllar va mediani sozlamalar orqali yoqing."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Yoqish"</string> <string name="open_my_photos" msgid="4107196465713868381">"Suratlarimni ochish"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Ekran qulfi"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Bosh ekran"</string> + <string name="reset" msgid="4945445169532850631">"Yangilash"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ishonchingiz komilmi?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Barcha oʻzgarishlarni asliga tiklaydi."</string> </resources> diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index a45f44a6..4899bbf2 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Vui lòng bật quyền truy cập tệp và nội dung nghe nhìn trong phần cài đặt."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Bật"</string> <string name="open_my_photos" msgid="4107196465713868381">"Mở Ảnh của tôi"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Màn hình khoá"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Màn hình chính"</string> + <string name="reset" msgid="4945445169532850631">"Đặt lại"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Bạn có chắc chắn không?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Thao tác này sẽ đặt lại tất cả thay đổi bạn đã thực hiện."</string> </resources> diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 91a9f239..79ae629f 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"请在设置中启用文件和媒体。"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"启用"</string> <string name="open_my_photos" msgid="4107196465713868381">"打开“我的照片”"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"锁定屏幕"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"主屏幕"</string> + <string name="reset" msgid="4945445169532850631">"重置"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"确定吗?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"此操作将重置您做出的所有更改。"</string> </resources> diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 6e1fffd2..41784898 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"請在設定中啟用檔案和媒體。"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"啟用"</string> <string name="open_my_photos" msgid="4107196465713868381">"開啟「我的相片」"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"上鎖畫面"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"主畫面"</string> + <string name="reset" msgid="4945445169532850631">"重設"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"您確定嗎?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"此操作將重設您所作的所有變更。"</string> </resources> diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 2c86c5a4..3015dd90 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"請前往設定啟用「檔案和媒體」權限。"</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"啟用"</string> <string name="open_my_photos" msgid="4107196465713868381">"開啟我的相片"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"螢幕鎖定畫面"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"主畫面"</string> + <string name="reset" msgid="4945445169532850631">"重設"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"確定要這麼做嗎?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"這麼做會重設你進行的所有變更。"</string> </resources> diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index adbdf03a..27554a49 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -111,4 +111,9 @@ <string name="settings_snackbar_description" msgid="890168814524778486">"Sicela unike amafayela nemidiya amandla kumasethingi."</string> <string name="settings_snackbar_enable" msgid="5992112808061426068">"Nika amandla"</string> <string name="open_my_photos" msgid="4107196465713868381">"Vula Izithombe Zami"</string> + <string name="lock_screen_tab" msgid="6672930765010407652">"Khiya isikrini"</string> + <string name="home_screen_tab" msgid="1080445697837877526">"Isikrini sasekhaya"</string> + <string name="reset" msgid="4945445169532850631">"Setha kabusha"</string> + <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Uqinisekile?"</string> + <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Lokhu kuzosetha kabusha zonke izinhlobo ozenzile."</string> </resources> diff --git a/res/values/colors.xml b/res/values/colors.xml index ea3aaee3..c6dc8ba7 100755 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -42,7 +42,15 @@ <color name="toolbar_color">@color/settingslib_colorSurfaceHeader</color> <color name="myphoto_background_color">@color/white</color> <color name="myphoto_background_cloud">@color/google_grey200</color> + <color name="preview_toolbar_text_light">@android:color/system_neutral1_50</color> + <color name="preview_toolbar_text_dark">@android:color/system_neutral1_900</color> + <color name="color_accent_primary">@android:color/system_accent1_100</color> <color name="color_accent_primary_variant">@android:color/system_accent1_600</color> - + <color name="color_accent_secondary">@color/settingslib_accent_secondary_device_default</color> + <color name="color_surface">@color/settingslib_colorSurface</color> + <color name="color_surface_variant">@color/settingslib_colorSurfaceVariant</color> + <color name="text_color_primary">@color/settingslib_text_color_primary</color> + <color name="text_color_secondary">@color/settingslib_text_color_secondary</color> + <color name="text_color_secondary_inverse">@color/settingslib_text_color_secondary_device_default</color> </resources> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 023aba4f..7c27bd94 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -151,7 +151,7 @@ <dimen name="full_preview_page_default_padding_top">24dp</dimen> <dimen name="full_preview_page_default_padding_bottom">32dp</dimen> <dimen name="full_preview_page_default_horizontal_padding">24dp</dimen> - <dimen name="full_preview_page_tabs_horizontal_padding">20dp</dimen> + <dimen name="full_preview_page_tabs_horizontal_padding">24dp</dimen> <!-- Dimensions for the bottom bar. --> <dimen name="bottom_actions_height">84dp</dimen> @@ -238,4 +238,42 @@ <dimen name="wallpaper_effect_failed_container_margin_top">24dp</dimen> <dimen name="wallpaper_effect_failed_button_size">16sp</dimen> + <!-- Clipping of the home screen overlay --> + <dimen name="home_screen_overlay_top_clipping">100dp</dimen> + <dimen name="home_screen_overlay_bottom_clipping">40dp</dimen> + + <!-- Set wallpaper button --> + <dimen name="set_wallpaper_button_horizontal_padding">18dp</dimen> + <dimen name="set_wallpaper_button_vertical_padding">10dp</dimen> + <dimen name="set_wallpaper_button_corner_radius">20dp</dimen> + <dimen name="set_wallpaper_button_margin_end">16dp</dimen> + + <!-- Wallpaper control button group --> + <dimen name="wallpaper_control_button_group_divider_space">16dp</dimen> + <dimen name="wallpaper_control_button_group_margin_top">24dp</dimen> + <dimen name="wallpaper_control_button_group_margin_top_when_no_set_wallpaper_button">16dp + </dimen> + <dimen name="wallpaper_control_button_group_margin_end">16dp</dimen> + <dimen name="wallpaper_control_button_padding">12dp</dimen> + <dimen name="wallpaper_control_button_size">48dp</dimen> + <dimen name="wallpaper_control_icon_size">24dp</dimen> + <item name="wallpaper_control_icon_viewport_size" format="float" type="dimen">24</item> + + <!-- Duo tabs --> + <dimen name="duo_tabs_button_corner_radius">12dp</dimen> + <dimen name="duo_tabs_divider_space">8dp</dimen> + + <!-- Floating Sheet --> + <dimen name="floating_sheet_corner_radius">12dp</dimen> + <dimen name="floating_sheet_margin">24dp</dimen> + + <!-- Dimensions for "Wallpaper category headers" --> + <dimen name="wallpaper_header_text_size">16sp</dimen> + <dimen name="wallpaper_header_line_height">16dp</dimen> + + <!-- + Height of the screen preview. The width will be determined based on the aspect ratio of the + display. + --> + <dimen name="screen_preview_height">380dp</dimen> </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index c2389611..e5b104fd 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -399,4 +399,49 @@ <!-- Label for a button which lets user try to open their photos. [CHAR LIMIT=40] --> <string name="open_my_photos">Open My Photos</string> + + <!-- + Name of the authority corresponding to a ContentProvider in system UI that can provide remote + rendering of a preview of the lock screen. + --> + <string name="lock_screen_preview_provider_authority" translatable="false">com.android.systemui.customization</string> + + <!-- + Label for a navigational tab/button that takes the user to a UI that lets them customize the + lock screen. + + [CHAR LIMIT=24]. + --> + <string name="lock_screen_tab">Lock screen</string> + + <!-- + Label for a navigational tab/button that takes the user to a UI that lets them customize the + home screen. + + [CHAR LIMIT=24]. + --> + <string name="home_screen_tab">Home screen</string> + + <!-- + Label for button that lets the user reset the changes they have made. + + [CHAR LIMIT=12]. + --> + <string name="reset">Reset</string> + + <!-- + Title for dialog confirming with the user that they really wish to reset the customizations they + applied to the home and lock screens. + + [CHAR LIMIT=32]. + --> + <string name="reset_confirmation_dialog_title">Are you sure?</string> + + <!-- + Message for dialog confirming with the user that they really wish to reset the customizations + they applied to the home and lock screens. + + [CHAR LIMIT=128]. + --> + <string name="reset_confirmation_dialog_message">This will reset all changes you made.</string> </resources> diff --git a/res/values/styles.xml b/res/values/styles.xml index 2eb74179..b48fba0e 100755 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -127,6 +127,9 @@ <!-- Preview attribution pane styles --> <style name="WallpaperPicker.BottomPaneStyle" parent="@android:style/Theme.DeviceDefault.Settings"/> + <style name="WallpaperPicker.FloatingPaneStyle" + parent="@android:style/Theme.DeviceDefault.Settings" /> + <!-- Preview customization pane styles --> <style name="WallpaperPicker.CustomizationPaneStyle" parent="@android:style/Theme.DeviceDefault.Settings"> <item name="tabTextAppearance">@style/WallpaperPicker.Preview.TextAppearance.NoAllCaps</item> diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk deleted file mode 100644 index 666c08df..00000000 --- a/robolectric_tests/Android.mk +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (C) 2019 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. - -############################################# -# WallPaperPicker2 Robolectric test target. # -############################################# -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE := WallPaperPicker2RoboTests -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_SDK_VERSION := current -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := \ - androidx.test.runner \ - androidx.test.rules \ - mockito-robolectric-prebuilt \ - truth-prebuilt -LOCAL_JAVA_LIBRARIES := \ - platform-robolectric-3.6.2-prebuilt - -LOCAL_JAVA_RESOURCE_DIRS := config - -LOCAL_INSTRUMENTATION_FOR := WallpaperPicker2 -LOCAL_MODULE_TAGS := optional - -include $(BUILD_STATIC_JAVA_LIBRARY) - -############################################ -# Target to run the previous target. # -############################################ -include $(CLEAR_VARS) - -LOCAL_MODULE := RunWallPaperPicker2RoboTests -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_SDK_VERSION := current -LOCAL_JAVA_LIBRARIES := \ - WallPaperPicker2RoboTests - -LOCAL_TEST_PACKAGE := WallpaperPicker2 - -LOCAL_INSTRUMENT_SOURCE_DIRS := packages/apps/WallpaperPicker2/src \ - -LOCAL_ROBOTEST_TIMEOUT := 36000 - -include prebuilts/misc/common/robolectric/3.6.2/run_robotests.mk diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties deleted file mode 100644 index c222b112..00000000 --- a/robolectric_tests/config/robolectric.properties +++ /dev/null @@ -1,2 +0,0 @@ -manifest=packages/apps/WallpaperPicker2/AndroidManifest.xml -sdk=27 diff --git a/robolectric_tests/robolectric_gradle_config/robolectric.properties b/robolectric_tests/robolectric_gradle_config/robolectric.properties deleted file mode 100644 index 2916ca96..00000000 --- a/robolectric_tests/robolectric_gradle_config/robolectric.properties +++ /dev/null @@ -1,3 +0,0 @@ -# Do not include the manifest definition in this version -# as it is specified directly in the gradle file -sdk=31
\ No newline at end of file diff --git a/src/com/android/wallpaper/asset/Asset.java b/src/com/android/wallpaper/asset/Asset.java index d2beb93c..122c3795 100755 --- a/src/com/android/wallpaper/asset/Asset.java +++ b/src/com/android/wallpaper/asset/Asset.java @@ -288,8 +288,11 @@ public abstract class Asset { * @param imageView ImageView which is the target view of this asset. * @param placeholderColor Color of placeholder set to ImageView while waiting for image to * load. + * @param offsetToStart true to let the preview show from the start of the image, false to + * center-aligned to the image. */ - public void loadPreviewImage(Activity activity, ImageView imageView, int placeholderColor) { + public void loadPreviewImage(Activity activity, ImageView imageView, int placeholderColor, + boolean offsetToStart) { boolean needsTransition = imageView.getDrawable() == null; Drawable placeholderDrawable = new ColorDrawable(placeholderColor); if (needsTransition) { @@ -306,7 +309,9 @@ public abstract class Asset { Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(defaultDisplay); Rect visibleRawWallpaperRect = WallpaperCropUtils.calculateVisibleRect(dimensions, screenSize); - adjustCropRect(activity, dimensions, visibleRawWallpaperRect); + + // TODO(b/264234793): Make offsetToStart general support or for the specific asset. + adjustCropRect(activity, dimensions, visibleRawWallpaperRect, offsetToStart); BitmapCropper bitmapCropper = InjectorProvider.getInjector().getBitmapCropper(); bitmapCropper.cropAndScaleBitmap(this, /* scale= */ 1f, visibleRawWallpaperRect, @@ -378,7 +383,8 @@ public abstract class Asset { void onDrawableLoaded(); } - protected void adjustCropRect(Context context, Point assetDimensions, Rect cropRect) { + protected void adjustCropRect(Context context, Point assetDimensions, Rect cropRect, + boolean offsetToStart) { WallpaperCropUtils.adjustCropRect(context, cropRect, true /* zoomIn */); } diff --git a/src/com/android/wallpaper/asset/BitmapCachingAsset.java b/src/com/android/wallpaper/asset/BitmapCachingAsset.java index bc38a9e4..cb37ee57 100644 --- a/src/com/android/wallpaper/asset/BitmapCachingAsset.java +++ b/src/com/android/wallpaper/asset/BitmapCachingAsset.java @@ -147,8 +147,9 @@ public class BitmapCachingAsset extends Asset { } @Override - public void loadPreviewImage(Activity activity, ImageView imageView, int placeholderColor) { + public void loadPreviewImage(Activity activity, ImageView imageView, int placeholderColor, + boolean offsetToStart) { // Honor the original Asset's preview image loading - mOriginalAsset.loadPreviewImage(activity, imageView, placeholderColor); + mOriginalAsset.loadPreviewImage(activity, imageView, placeholderColor, offsetToStart); } } diff --git a/src/com/android/wallpaper/asset/CurrentWallpaperAssetVN.java b/src/com/android/wallpaper/asset/CurrentWallpaperAssetVN.java index 8f30cae0..45f56eb8 100755 --- a/src/com/android/wallpaper/asset/CurrentWallpaperAssetVN.java +++ b/src/com/android/wallpaper/asset/CurrentWallpaperAssetVN.java @@ -138,8 +138,11 @@ public class CurrentWallpaperAssetVN extends StreamableAsset { } @Override - protected void adjustCropRect(Context context, Point assetDimensions, Rect cropRect) { - cropRect.offsetTo(0, 0); + protected void adjustCropRect(Context context, Point assetDimensions, Rect cropRect, + boolean offsetToStart) { + if (offsetToStart) { + cropRect.offsetTo(0, 0); + } WallpaperCropUtils.adjustCurrentWallpaperCropRect(context, assetDimensions, cropRect); } diff --git a/src/com/android/wallpaper/config/BaseFlags.kt b/src/com/android/wallpaper/config/BaseFlags.kt new file mode 100644 index 00000000..37f3d6e0 --- /dev/null +++ b/src/com/android/wallpaper/config/BaseFlags.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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.wallpaper.config + +import android.content.Context +import android.os.SystemProperties +import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl +import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking + +abstract class BaseFlags { + open fun isStagingBackdropContentEnabled() = false + open fun isEnableWallpaperEffect() = false + fun isMonochromaticFlagEnabled() = + SystemProperties.getBoolean("persist.sysui.monochromatic", false) + open fun isEnableEffectOnMultiplePanel() = false + open fun isFullscreenWallpaperPreview() = false + fun isUseRevampedUi(context: Context): Boolean { + return runBlocking { CustomizationProviderClientImpl(context, Dispatchers.IO).queryFlags() } + .firstOrNull { flag -> + flag.name == Contract.FlagsTable.FLAG_NAME_REVAMPED_WALLPAPER_UI + } + ?.value == true + } +} diff --git a/src/com/android/wallpaper/effects/EffectsController.java b/src/com/android/wallpaper/effects/EffectsController.java index c0d3d567..8c745718 100644 --- a/src/com/android/wallpaper/effects/EffectsController.java +++ b/src/com/android/wallpaper/effects/EffectsController.java @@ -22,11 +22,18 @@ import android.os.Bundle; * Utility class to provide methods to generate effects for the wallpaper. */ public abstract class EffectsController { + public static final int RESULT_ORIGINAL_UNKNOWN = -1; public static final int RESULT_SUCCESS = 0; public static final int RESULT_ERROR_TRY_ANOTHER_PHOTO = 1; public static final int RESULT_ERROR_TRY_AGAIN_LATER = 2; public static final int RESULT_ERROR_CONTINUE = 4; + public static final int RESULT_ERROR_DEFAULT = + RESULT_ERROR_TRY_ANOTHER_PHOTO + RESULT_ERROR_CONTINUE; public static final int RESULT_ERROR_DISCONNECT_NO_BUTTON = 8; + public static final int RESULT_PROBE_SUCCESS = 16; + public static final int RESULT_PROBE_ERROR = 32; + public static final int RESULT_SUCCESS_REUSED = 64; + public static final int RESULT_SUCCESS_WITH_GENERATION_ERROR = 128; /** * Interface of the Effect enum. */ @@ -78,9 +85,10 @@ public abstract class EffectsController { * @param effect The effect that was generated. * @param bundle The data that the Service might have sent to the picker. * @param error The error code. if there's an error, value is greater than zero. + * @param originalStatusCode The original status code used for metrics logging. * @param errorMessage The error message. */ void onEffectFinished(EffectEnumInterface effect, Bundle bundle, int error, - String errorMessage); + int originalStatusCode, String errorMessage); } } diff --git a/src/com/android/wallpaper/model/CustomizationSectionController.java b/src/com/android/wallpaper/model/CustomizationSectionController.java index b01f0982..58ef178b 100644 --- a/src/com/android/wallpaper/model/CustomizationSectionController.java +++ b/src/com/android/wallpaper/model/CustomizationSectionController.java @@ -34,6 +34,9 @@ public interface CustomizationSectionController<T extends SectionView> { interface CustomizationSectionNavigationController { /** Navigates to the given {@code fragment}. */ void navigateTo(Fragment fragment); + + /** Navigates to a {@code fragment} that maps to the given destination ID. */ + void navigateTo(String destinationId); } /** Returns {@code true} if the customization section is available. */ @@ -42,6 +45,16 @@ public interface CustomizationSectionController<T extends SectionView> { /** * Returns a newly created {@link SectionView} for the section. * + * @param context The {@link Context} to inflate view. + * @param isOnLockScreen Whether we are on the lock screen. + */ + default T createView(Context context, boolean isOnLockScreen) { + return createView(context); + } + + /** + * Returns a newly created {@link SectionView} for the section. + * * @param context the {@link Context} to inflate view */ T createView(Context context); @@ -54,4 +67,7 @@ public interface CustomizationSectionController<T extends SectionView> { /** Gets called when the section gets transitioned out. */ default void onTransitionOut() {} + + /** Notifies when the screen was switched. */ + default void onScreenSwitched(boolean isOnLockScreen) {} } diff --git a/src/com/android/wallpaper/model/DefaultWallpaperInfo.java b/src/com/android/wallpaper/model/DefaultWallpaperInfo.java index 6ed0eebf..a3f00f4b 100755 --- a/src/com/android/wallpaper/model/DefaultWallpaperInfo.java +++ b/src/com/android/wallpaper/model/DefaultWallpaperInfo.java @@ -49,7 +49,7 @@ public class DefaultWallpaperInfo extends WallpaperInfo { public DefaultWallpaperInfo() {} - private DefaultWallpaperInfo(Parcel in) { + protected DefaultWallpaperInfo(Parcel in) { super(in); } diff --git a/src/com/android/wallpaper/model/LiveWallpaperInfo.java b/src/com/android/wallpaper/model/LiveWallpaperInfo.java index a952ed57..47bf89af 100755 --- a/src/com/android/wallpaper/model/LiveWallpaperInfo.java +++ b/src/com/android/wallpaper/model/LiveWallpaperInfo.java @@ -141,7 +141,7 @@ public class LiveWallpaperInfo extends WallpaperInfo { protected android.app.WallpaperInfo mInfo; protected LiveWallpaperThumbAsset mThumbAsset; - private boolean mVisibleTitle; + protected boolean mVisibleTitle; @Nullable private final String mCollectionId; /** diff --git a/src/com/android/wallpaper/model/PartnerWallpaperInfo.java b/src/com/android/wallpaper/model/PartnerWallpaperInfo.java index 63f93269..2f71ca4c 100755 --- a/src/com/android/wallpaper/model/PartnerWallpaperInfo.java +++ b/src/com/android/wallpaper/model/PartnerWallpaperInfo.java @@ -34,7 +34,7 @@ import java.util.List; /** * Represents a wallpaper from the "partner customization" APK installed on the system. */ -public class PartnerWallpaperInfo extends WallpaperInfo { +public class PartnerWallpaperInfo extends DefaultWallpaperInfo { public static final Creator<PartnerWallpaperInfo> CREATOR = new Creator<PartnerWallpaperInfo>() { @Override @@ -80,7 +80,7 @@ public class PartnerWallpaperInfo extends WallpaperInfo { return wallpaperInfos; } - final int resId = partnerRes.getIdentifier(PartnerProvider.WALLPAPER_RES_ID, "array", + final int resId = partnerRes.getIdentifier(PartnerProvider.LEGACY_WALLPAPER_RES_ID, "array", packageName); // Certain partner configurations don't have wallpapers provided, so need to check; return // early if they are missing. @@ -146,6 +146,11 @@ public class PartnerWallpaperInfo extends WallpaperInfo { } @Override + public String getWallpaperId() { + return "" + mFullRes; + } + + @Override public void showPreview(Activity srcActivity, InlinePreviewIntentFactory factory, int requestCode) { srcActivity.startActivityForResult(factory.newIntent(srcActivity, this), requestCode); diff --git a/src/com/android/wallpaper/model/WallpaperColorsViewModel.kt b/src/com/android/wallpaper/model/WallpaperColorsViewModel.kt index cecadb11..3d5b7715 100644 --- a/src/com/android/wallpaper/model/WallpaperColorsViewModel.kt +++ b/src/com/android/wallpaper/model/WallpaperColorsViewModel.kt @@ -19,22 +19,16 @@ import android.app.WallpaperColors import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -/** - * ViewModel class to keep track of WallpaperColors for the current wallpaper - */ +/** ViewModel class to keep track of WallpaperColors for the current wallpaper */ class WallpaperColorsViewModel : ViewModel() { - /** - * WallpaperColors for the currently set home wallpaper - */ + /** WallpaperColors for the currently set home wallpaper */ val homeWallpaperColors: MutableLiveData<WallpaperColors> by lazy { MutableLiveData<WallpaperColors>() } - /** - * WallpaperColors for the currently set lock wallpaper - */ + /** WallpaperColors for the currently set lock wallpaper */ val lockWallpaperColors: MutableLiveData<WallpaperColors> by lazy { MutableLiveData<WallpaperColors>() } -}
\ No newline at end of file +} diff --git a/src/com/android/wallpaper/model/WallpaperInfo.java b/src/com/android/wallpaper/model/WallpaperInfo.java index 28c2f432..0fc0d275 100755 --- a/src/com/android/wallpaper/model/WallpaperInfo.java +++ b/src/com/android/wallpaper/model/WallpaperInfo.java @@ -18,6 +18,7 @@ package com.android.wallpaper.model; import android.app.Activity; import android.app.WallpaperColors; import android.content.Context; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.Drawable; @@ -296,6 +297,23 @@ public abstract class WallpaperInfo implements Parcelable { } /** + * Returns a group name under which this Wallpaper should be grouped when displayed in + * a gallery, or an empty String if no grouping is required. + */ + public String getGroupName(Context context) { + return ""; + } + + /** + * Returns the resource id of a drawable to use as a badge when displaying this wallpaper + * in a gallery, or {@link Resources#ID_NULL} if no badge is required. + */ + @DrawableRes + public int getBadgeDrawableRes() { + return Resources.ID_NULL; + } + + /** * Inner class to keep wallpaper colors and placeholder color. */ public static class ColorInfo { diff --git a/src/com/android/wallpaper/model/WallpaperSectionController.java b/src/com/android/wallpaper/model/WallpaperSectionController.java index c8242ee6..cf07e76c 100644 --- a/src/com/android/wallpaper/model/WallpaperSectionController.java +++ b/src/com/android/wallpaper/model/WallpaperSectionController.java @@ -51,6 +51,7 @@ import androidx.lifecycle.OnLifecycleEvent; import com.android.wallpaper.R; import com.android.wallpaper.asset.Asset; import com.android.wallpaper.asset.BitmapCachingAsset; +import com.android.wallpaper.asset.CurrentWallpaperAssetVN; import com.android.wallpaper.model.WallpaperInfo.ColorInfo; import com.android.wallpaper.module.CurrentWallpaperInfoFactory; import com.android.wallpaper.module.InjectorProvider; @@ -59,6 +60,8 @@ import com.android.wallpaper.picker.CategorySelectorFragment; import com.android.wallpaper.picker.MyPhotosStarter; import com.android.wallpaper.picker.WallpaperSectionView; import com.android.wallpaper.picker.WorkspaceSurfaceHolderCallback; +import com.android.wallpaper.util.DisplayUtils; +import com.android.wallpaper.util.PreviewUtils; import com.android.wallpaper.util.ResourceUtils; import com.android.wallpaper.util.WallpaperConnection; import com.android.wallpaper.util.WallpaperSurfaceCallback; @@ -105,13 +108,15 @@ public class WallpaperSectionController implements private final CustomizationSectionNavigationController mSectionNavigationController; private final WallpaperPreviewNavigator mWallpaperPreviewNavigator; private final Bundle mSavedInstanceState; + private final DisplayUtils mDisplayUtils; public WallpaperSectionController(Activity activity, LifecycleOwner lifecycleOwner, PermissionRequester permissionRequester, WallpaperColorsViewModel colorsViewModel, WorkspaceViewModel workspaceViewModel, CustomizationSectionNavigationController sectionNavigationController, WallpaperPreviewNavigator wallpaperPreviewNavigator, - Bundle savedInstanceState) { + Bundle savedInstanceState, + DisplayUtils displayUtils) { mActivity = activity; mLifecycleOwner = lifecycleOwner; mPermissionRequester = permissionRequester; @@ -121,6 +126,7 @@ public class WallpaperSectionController implements mSectionNavigationController = sectionNavigationController; mWallpaperPreviewNavigator = wallpaperPreviewNavigator; mSavedInstanceState = savedInstanceState; + mDisplayUtils = displayUtils; } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @@ -164,7 +170,9 @@ public class WallpaperSectionController implements mWorkspaceSurface = mHomePreviewCard.findViewById(R.id.workspace_surface); mHomePreviewProgress = mHomePreviewCard.findViewById(R.id.wallpaper_preview_spinner); mWorkspaceSurfaceCallback = new WorkspaceSurfaceHolderCallback( - mWorkspaceSurface, mAppContext); + mWorkspaceSurface, + new PreviewUtils( + mAppContext, mAppContext.getString(R.string.grid_control_metadata_name))); mHomeWallpaperSurface = mHomePreviewCard.findViewById(R.id.wallpaper_surface); Future<ColorInfo> colorFuture = CompletableFuture.completedFuture( @@ -174,7 +182,8 @@ public class WallpaperSectionController implements mHomeWallpaperSurfaceCallback = new WallpaperSurfaceCallback(mActivity, mHomePreviewCard, mHomeWallpaperSurface, colorFuture, () -> { if (mHomePreviewWallpaperInfo != null) { - maybeLoadThumbnail(mHomePreviewWallpaperInfo, mHomeWallpaperSurfaceCallback); + maybeLoadThumbnail(mHomePreviewWallpaperInfo, mHomeWallpaperSurfaceCallback, + mDisplayUtils.isOnWallpaperDisplay(mActivity)); } }); @@ -188,7 +197,8 @@ public class WallpaperSectionController implements mLockWallpaperSurfaceCallback = new WallpaperSurfaceCallback(mActivity, mLockscreenPreviewCard, mLockWallpaperSurface, colorFuture, () -> { if (mLockPreviewWallpaperInfo != null) { - maybeLoadThumbnail(mLockPreviewWallpaperInfo, mLockWallpaperSurfaceCallback); + maybeLoadThumbnail(mLockPreviewWallpaperInfo, mLockWallpaperSurfaceCallback, + mDisplayUtils.isOnWallpaperDisplay(mActivity)); } }); mLockPreviewContainer = mLockscreenPreviewCard.findViewById( @@ -331,7 +341,7 @@ public class WallpaperSectionController implements */ private void refreshCurrentWallpapers(boolean forceRefresh) { CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector() - .getCurrentWallpaperFactory(mAppContext); + .getCurrentWallpaperInfoFactory(mAppContext); factory.createCurrentWallpaperInfos( (homeWallpaper, lockWallpaper, presentationMode) -> { @@ -383,7 +393,8 @@ public class WallpaperSectionController implements ? mHomeWallpaperSurfaceCallback : mLockWallpaperSurfaceCallback; // Load thumb regardless of live wallpaper to make sure we have a placeholder while // the live wallpaper initializes in that case. - maybeLoadThumbnail(wallpaperInfo, surfaceCallback); + maybeLoadThumbnail(wallpaperInfo, surfaceCallback, + mDisplayUtils.isOnWallpaperDisplay(mActivity)); if (isHomeWallpaper) { if (mWallpaperConnection != null) { @@ -404,13 +415,16 @@ public class WallpaperSectionController implements @NonNull private Asset maybeLoadThumbnail(WallpaperInfo wallpaperInfo, - WallpaperSurfaceCallback surfaceCallback) { + WallpaperSurfaceCallback surfaceCallback, boolean offsetToStart) { ImageView imageView = surfaceCallback.getHomeImageWallpaper(); - Asset thumbAsset = new BitmapCachingAsset(mAppContext, - wallpaperInfo.getThumbAsset(mAppContext)); + Asset thumbAsset = wallpaperInfo.getThumbAsset(mAppContext); + // Respect offsetToStart only for CurrentWallpaperAssetVN otherwise true. + offsetToStart = !(thumbAsset instanceof CurrentWallpaperAssetVN) || offsetToStart; + thumbAsset = new BitmapCachingAsset(mAppContext, thumbAsset); if (imageView != null && imageView.getDrawable() == null) { thumbAsset.loadPreviewImage(mActivity, imageView, - ResourceUtils.getColorAttr(mActivity, android.R.attr.colorSecondary)); + ResourceUtils.getColorAttr(mActivity, android.R.attr.colorSecondary), + offsetToStart); } return thumbAsset; } diff --git a/src/com/android/wallpaper/model/WorkspaceViewModel.kt b/src/com/android/wallpaper/model/WorkspaceViewModel.kt index bafd6aaa..627406ee 100644 --- a/src/com/android/wallpaper/model/WorkspaceViewModel.kt +++ b/src/com/android/wallpaper/model/WorkspaceViewModel.kt @@ -22,10 +22,8 @@ import androidx.lifecycle.ViewModel class WorkspaceViewModel : ViewModel() { /** - * Triggers workspace updates through flipping the value from {@code false} to {@code true}, - * or from {@code true} to {@code false}. + * Triggers workspace updates through flipping the value from {@code false} to {@code true}, or + * from {@code true} to {@code false}. */ - val updateWorkspace: MutableLiveData<Boolean> by lazy { - MutableLiveData<Boolean>() - } + val updateWorkspace: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() } } diff --git a/src/com/android/wallpaper/module/BaseWallpaperInjector.java b/src/com/android/wallpaper/module/BaseWallpaperInjector.java deleted file mode 100755 index 6e729017..00000000 --- a/src/com/android/wallpaper/module/BaseWallpaperInjector.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.wallpaper.module; - -import android.content.Context; - -import com.android.wallpaper.compat.WallpaperManagerCompat; -import com.android.wallpaper.effects.EffectsController; -import com.android.wallpaper.network.Requester; -import com.android.wallpaper.network.WallpaperRequester; -import com.android.wallpaper.picker.individual.IndividualPickerFragment; -import com.android.wallpaper.util.DisplayUtils; - -/** - * Base implementation of Injector. - */ -public abstract class BaseWallpaperInjector implements Injector { - private BitmapCropper mBitmapCropper; - private PartnerProvider mPartnerProvider; - private WallpaperPersister mWallpaperPersister; - private WallpaperPreferences mPrefs; - private WallpaperRefresher mWallpaperRefresher; - private Requester mRequester; - private WallpaperManagerCompat mWallpaperManagerCompat; - private WallpaperStatusChecker mWallpaperStatusChecker; - private CurrentWallpaperInfoFactory mCurrentWallpaperFactory; - private NetworkStatusNotifier mNetworkStatusNotifier; - private AlarmManagerWrapper mAlarmManagerWrapper; - private ExploreIntentChecker mExploreIntentChecker; - private SystemFeatureChecker mSystemFeatureChecker; - private PackageStatusNotifier mPackageStatusNotifier; - private LiveWallpaperInfoFactory mLiveWallpaperInfoFactory; - private DrawableLayerResolver mDrawableLayerResolver; - private CustomizationSections mCustomizationSections; - private DisplayUtils mDisplayUtils; - - @Override - public synchronized BitmapCropper getBitmapCropper() { - if (mBitmapCropper == null) { - mBitmapCropper = new DefaultBitmapCropper(); - } - return mBitmapCropper; - } - - @Override - public synchronized PartnerProvider getPartnerProvider(Context context) { - if (mPartnerProvider == null) { - mPartnerProvider = new DefaultPartnerProvider(context.getApplicationContext()); - } - return mPartnerProvider; - } - - @Override - public synchronized WallpaperPreferences getPreferences(Context context) { - if (mPrefs == null) { - mPrefs = new DefaultWallpaperPreferences(context.getApplicationContext()); - } - return mPrefs; - } - - @Override - public synchronized WallpaperPersister getWallpaperPersister(Context context) { - if (mWallpaperPersister == null) { - mWallpaperPersister = new DefaultWallpaperPersister(context.getApplicationContext()); - } - return mWallpaperPersister; - } - - @Override - public synchronized WallpaperRefresher getWallpaperRefresher(Context context) { - if (mWallpaperRefresher == null) { - mWallpaperRefresher = new DefaultWallpaperRefresher(context.getApplicationContext()); - } - return mWallpaperRefresher; - } - - @Override - public synchronized Requester getRequester(Context context) { - if (mRequester == null) { - mRequester = new WallpaperRequester(context.getApplicationContext()); - } - return mRequester; - } - - @Override - public synchronized WallpaperManagerCompat getWallpaperManagerCompat(Context context) { - if (mWallpaperManagerCompat == null) { - mWallpaperManagerCompat = WallpaperManagerCompat.getInstance(context); - } - return mWallpaperManagerCompat; - } - - @Override - public WallpaperStatusChecker getWallpaperStatusChecker() { - if (mWallpaperStatusChecker == null) { - mWallpaperStatusChecker = new DefaultWallpaperStatusChecker(); - } - return mWallpaperStatusChecker; - } - - @Override - public synchronized CurrentWallpaperInfoFactory getCurrentWallpaperFactory(Context context) { - if (mCurrentWallpaperFactory == null) { - mCurrentWallpaperFactory = - new DefaultCurrentWallpaperInfoFactory(context.getApplicationContext()); - } - return mCurrentWallpaperFactory; - } - - @Override - public synchronized NetworkStatusNotifier getNetworkStatusNotifier(Context context) { - if (mNetworkStatusNotifier == null) { - mNetworkStatusNotifier = new DefaultNetworkStatusNotifier(context.getApplicationContext()); - } - return mNetworkStatusNotifier; - } - - @Override - public synchronized PackageStatusNotifier getPackageStatusNotifier(Context context) { - if (mPackageStatusNotifier == null) { - mPackageStatusNotifier = new DefaultPackageStatusNotifier( - context.getApplicationContext()); - } - return mPackageStatusNotifier; - } - - @Override - public synchronized AlarmManagerWrapper getAlarmManagerWrapper(Context context) { - if (mAlarmManagerWrapper == null) { - mAlarmManagerWrapper = new DefaultAlarmManagerWrapper(context.getApplicationContext()); - } - return mAlarmManagerWrapper; - } - - @Override - public synchronized ExploreIntentChecker getExploreIntentChecker(Context context) { - if (mExploreIntentChecker == null) { - mExploreIntentChecker = new DefaultExploreIntentChecker(context.getApplicationContext()); - } - return mExploreIntentChecker; - } - - @Override - public synchronized SystemFeatureChecker getSystemFeatureChecker() { - if (mSystemFeatureChecker == null) { - mSystemFeatureChecker = new DefaultSystemFeatureChecker(); - } - return mSystemFeatureChecker; - } - - @Override - public synchronized IndividualPickerFragment getIndividualPickerFragment(String collectionId) { - return IndividualPickerFragment.newInstance(collectionId); - } - - @Override - public LiveWallpaperInfoFactory getLiveWallpaperInfoFactory(Context context) { - if (mLiveWallpaperInfoFactory == null) { - mLiveWallpaperInfoFactory = new DefaultLiveWallpaperInfoFactory(); - } - return mLiveWallpaperInfoFactory; - } - - @Override - public DrawableLayerResolver getDrawableLayerResolver() { - if (mDrawableLayerResolver == null) { - mDrawableLayerResolver = new DefaultDrawableLayerResolver(); - } - return mDrawableLayerResolver; - } - - @Override - public CustomizationSections getCustomizationSections() { - if (mCustomizationSections == null) { - mCustomizationSections = new WallpaperPickerSections(); - } - return mCustomizationSections; - } - - @Override - public DisplayUtils getDisplayUtils(Context context) { - if (mDisplayUtils == null) { - mDisplayUtils = new DisplayUtils(context.getApplicationContext()); - } - return mDisplayUtils; - } - - @Override - public EffectsController createEffectsController(Context context, - EffectsController.EffectsServiceListener listener) { - return null; - } -} diff --git a/src/com/android/wallpaper/module/CustomizationSections.java b/src/com/android/wallpaper/module/CustomizationSections.java index c611d50e..66ae64d1 100644 --- a/src/com/android/wallpaper/module/CustomizationSections.java +++ b/src/com/android/wallpaper/module/CustomizationSections.java @@ -1,9 +1,9 @@ package com.android.wallpaper.module; -import android.app.Activity; import android.os.Bundle; import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.LifecycleOwner; import com.android.wallpaper.model.CustomizationSectionController; @@ -12,12 +12,39 @@ import com.android.wallpaper.model.PermissionRequester; import com.android.wallpaper.model.WallpaperColorsViewModel; import com.android.wallpaper.model.WallpaperPreviewNavigator; import com.android.wallpaper.model.WorkspaceViewModel; +import com.android.wallpaper.util.DisplayUtils; import java.util.List; /** Interface for carry {@link CustomizationSectionController}s. */ public interface CustomizationSections { + /** Enumerates all screens supported by {@code getSectionControllersForScreen}. */ + enum Screen { + LOCK_SCREEN, + HOME_SCREEN, + } + + /** + * Gets a new instance of the section controller list for the given {@link Screen}. + * + * Note that the section views will be displayed by the list ordering. + * + * <p>Don't keep the section controllers as singleton since they contain views. + */ + List<CustomizationSectionController<?>> getSectionControllersForScreen( + Screen screen, + FragmentActivity activity, + LifecycleOwner lifecycleOwner, + WallpaperColorsViewModel wallpaperColorsViewModel, + WorkspaceViewModel workspaceViewModel, + PermissionRequester permissionRequester, + WallpaperPreviewNavigator wallpaperPreviewNavigator, + CustomizationSectionNavigationController sectionNavigationController, + @Nullable Bundle savedInstanceState, + CurrentWallpaperInfoFactory wallpaperInfoFactory, + DisplayUtils displayUtils); + /** * Gets a new instance of the section controller list. * @@ -26,12 +53,13 @@ public interface CustomizationSections { * <p>Don't keep the section controllers as singleton since they contain views. */ List<CustomizationSectionController<?>> getAllSectionControllers( - Activity activity, + FragmentActivity activity, LifecycleOwner lifecycleOwner, WallpaperColorsViewModel wallpaperColorsViewModel, WorkspaceViewModel workspaceViewModel, PermissionRequester permissionRequester, WallpaperPreviewNavigator wallpaperPreviewNavigator, CustomizationSectionNavigationController sectionNavigationController, - @Nullable Bundle savedInstanceState); + @Nullable Bundle savedInstanceState, + DisplayUtils displayUtils); } diff --git a/src/com/android/wallpaper/module/DefaultWallpaperPreviewFragmentManager.kt b/src/com/android/wallpaper/module/DefaultWallpaperPreviewFragmentManager.kt new file mode 100644 index 00000000..10f58341 --- /dev/null +++ b/src/com/android/wallpaper/module/DefaultWallpaperPreviewFragmentManager.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 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.wallpaper.module + +import android.content.Context +import android.os.Bundle +import androidx.fragment.app.Fragment +import com.android.wallpaper.model.LiveWallpaperInfo +import com.android.wallpaper.model.WallpaperInfo +import com.android.wallpaper.picker.ImagePreviewFragment +import com.android.wallpaper.picker.LivePreviewFragment +import com.android.wallpaper.picker.PreviewFragment + +class DefaultWallpaperPreviewFragmentManager : WallpaperPreviewFragmentManager { + override fun getPreviewFragment( + context: Context, + wallpaperInfo: WallpaperInfo, + @PreviewFragment.PreviewMode mode: Int, + viewAsHome: Boolean, + viewFullScreen: Boolean, + testingModeEnabled: Boolean + ): Fragment { + val args = Bundle() + args.putParcelable(PreviewFragment.ARG_WALLPAPER, wallpaperInfo) + args.putInt(PreviewFragment.ARG_PREVIEW_MODE, mode) + args.putBoolean(PreviewFragment.ARG_VIEW_AS_HOME, viewAsHome) + args.putBoolean(PreviewFragment.ARG_FULL_SCREEN, viewFullScreen) + args.putBoolean(PreviewFragment.ARG_TESTING_MODE_ENABLED, testingModeEnabled) + val fragment = + if (wallpaperInfo is LiveWallpaperInfo) LivePreviewFragment() + else ImagePreviewFragment() + fragment.arguments = args + return fragment + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java b/src/com/android/wallpaper/module/FragmentFactory.kt index f4434b10..14ddce23 100644 --- a/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java +++ b/src/com/android/wallpaper/module/FragmentFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2022 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. @@ -12,16 +12,13 @@ * 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.wallpaper.testing; -import com.android.wallpaper.module.LoggingOptInStatusProvider; +package com.android.wallpaper.module -/** Test implementation of {@link LoggingOptInStatusProvider}. */ -public class TestLoggingOptInStatusProvider implements LoggingOptInStatusProvider { +import androidx.fragment.app.Fragment - @Override - public void fetchOptInValue(OptInValueReceiver receiver) { - receiver.onOptInValueReady(true /* optedIn */); - } +interface FragmentFactory { + fun create(id: String): Fragment? } diff --git a/src/com/android/wallpaper/module/Injector.java b/src/com/android/wallpaper/module/Injector.java deleted file mode 100755 index 1fb43435..00000000 --- a/src/com/android/wallpaper/module/Injector.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.wallpaper.module; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; - -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - -import com.android.wallpaper.compat.WallpaperManagerCompat; -import com.android.wallpaper.effects.EffectsController; -import com.android.wallpaper.effects.EffectsController.EffectsServiceListener; -import com.android.wallpaper.model.CategoryProvider; -import com.android.wallpaper.model.WallpaperInfo; -import com.android.wallpaper.monitor.PerformanceMonitor; -import com.android.wallpaper.network.Requester; -import com.android.wallpaper.picker.PreviewFragment.PreviewMode; -import com.android.wallpaper.picker.individual.IndividualPickerFragment; -import com.android.wallpaper.util.DisplayUtils; - -/** - * Interface for a provider of "injected dependencies." (NOTE: The term "injector" is somewhat of a - * misnomer; this is more aptly a service registry as part of a service locator design pattern.) - */ -public interface Injector { - AlarmManagerWrapper getAlarmManagerWrapper(Context context); - - BitmapCropper getBitmapCropper(); - - CategoryProvider getCategoryProvider(Context context); - - CurrentWallpaperInfoFactory getCurrentWallpaperFactory(Context context); - - ExploreIntentChecker getExploreIntentChecker(Context context); - - LoggingOptInStatusProvider getLoggingOptInStatusProvider(Context context); - - NetworkStatusNotifier getNetworkStatusNotifier(Context context); - - PartnerProvider getPartnerProvider(Context context); - - PerformanceMonitor getPerformanceMonitor(); - - Requester getRequester(Context context); - - SystemFeatureChecker getSystemFeatureChecker(); - - UserEventLogger getUserEventLogger(Context context); - - WallpaperManagerCompat getWallpaperManagerCompat(Context context); - - WallpaperStatusChecker getWallpaperStatusChecker(); - - WallpaperPersister getWallpaperPersister(Context context); - - WallpaperPreferences getPreferences(Context context); - - WallpaperRefresher getWallpaperRefresher(Context context); - - WallpaperRotationRefresher getWallpaperRotationRefresher(); - - Fragment getPreviewFragment( - Context context, - WallpaperInfo wallpaperInfo, - @PreviewMode int mode, - boolean viewAsHome, - boolean viewFullScreen, - boolean testingModeEnabled); - - PackageStatusNotifier getPackageStatusNotifier(Context context); - - IndividualPickerFragment getIndividualPickerFragment(String collectionId); - - LiveWallpaperInfoFactory getLiveWallpaperInfoFactory(Context context); - - DrawableLayerResolver getDrawableLayerResolver(); - - Intent getDeepLinkRedirectIntent(Context context, Uri uri); - - String getDownloadableIntentAction(); - - CustomizationSections getCustomizationSections(); - - /** - * @return the singleton instance of {@link DisplayUtils} - */ - DisplayUtils getDisplayUtils(Context context); - - /** - * @return the singleton instance of {@link EffectsController} - */ - @Nullable - EffectsController createEffectsController(Context context, EffectsServiceListener listener); -} diff --git a/src/com/android/wallpaper/module/Injector.kt b/src/com/android/wallpaper/module/Injector.kt new file mode 100755 index 00000000..bcbf0e59 --- /dev/null +++ b/src/com/android/wallpaper/module/Injector.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wallpaper.module + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.fragment.app.Fragment +import com.android.wallpaper.compat.WallpaperManagerCompat +import com.android.wallpaper.config.BaseFlags +import com.android.wallpaper.effects.EffectsController +import com.android.wallpaper.effects.EffectsController.EffectsServiceListener +import com.android.wallpaper.model.CategoryProvider +import com.android.wallpaper.model.WallpaperInfo +import com.android.wallpaper.monitor.PerformanceMonitor +import com.android.wallpaper.network.Requester +import com.android.wallpaper.picker.PreviewFragment +import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer +import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor +import com.android.wallpaper.util.DisplayUtils + +/** + * Interface for a provider of "injected dependencies." (NOTE: The term "injector" is somewhat of a + * misnomer; this is more aptly a service registry as part of a service locator design pattern.) + */ +interface Injector { + fun getAlarmManagerWrapper(context: Context): AlarmManagerWrapper + + fun getBitmapCropper(): BitmapCropper + + fun getCategoryProvider(context: Context): CategoryProvider + + fun getCurrentWallpaperInfoFactory(context: Context): CurrentWallpaperInfoFactory + + fun getCustomizationSections(activity: Activity): CustomizationSections + + fun getDeepLinkRedirectIntent(context: Context, uri: Uri): Intent + + fun getDisplayUtils(context: Context): DisplayUtils + + fun getDownloadableIntentAction(): String? + + fun getDrawableLayerResolver(): DrawableLayerResolver + + fun getEffectsController(context: Context, listener: EffectsServiceListener): EffectsController? + + fun getExploreIntentChecker(context: Context): ExploreIntentChecker + + fun getIndividualPickerFragment(collectionId: String): Fragment + + fun getLiveWallpaperInfoFactory(context: Context): LiveWallpaperInfoFactory + + fun getNetworkStatusNotifier(context: Context): NetworkStatusNotifier + + fun getPackageStatusNotifier(context: Context): PackageStatusNotifier + + fun getPartnerProvider(context: Context): PartnerProvider + + fun getPerformanceMonitor(): PerformanceMonitor? + + // TODO b/242908637 Remove this method when migrating to the new wallpaper preview screen + fun getPreviewFragment( + context: Context, + wallpaperInfo: WallpaperInfo, + @PreviewFragment.PreviewMode mode: Int, + viewAsHome: Boolean, + viewFullScreen: Boolean, + testingModeEnabled: Boolean + ): Fragment + + fun getRequester(context: Context): Requester + + fun getSystemFeatureChecker(): SystemFeatureChecker + + fun getUserEventLogger(context: Context): UserEventLogger + + fun getWallpaperManagerCompat(context: Context): WallpaperManagerCompat + + fun getWallpaperPersister(context: Context): WallpaperPersister + + fun getPreferences(context: Context): WallpaperPreferences + + fun getWallpaperPreviewFragmentManager(): WallpaperPreviewFragmentManager + + fun getWallpaperRefresher(context: Context): WallpaperRefresher + + fun getWallpaperRotationRefresher(): WallpaperRotationRefresher + + fun getWallpaperStatusChecker(): WallpaperStatusChecker + + fun getFragmentFactory(): FragmentFactory? { + return null + } + + fun getFlags(): BaseFlags + + fun getUndoInteractor(context: Context): UndoInteractor + + fun getSnapshotRestorers(context: Context): Map<Int, SnapshotRestorer> { + // Empty because we don't support undoing in WallpaperPicker2. + return HashMap() + } +} diff --git a/src/com/android/wallpaper/module/LargeScreenMultiPanesChecker.kt b/src/com/android/wallpaper/module/LargeScreenMultiPanesChecker.kt index b5b28a67..7e42d2da 100644 --- a/src/com/android/wallpaper/module/LargeScreenMultiPanesChecker.kt +++ b/src/com/android/wallpaper/module/LargeScreenMultiPanesChecker.kt @@ -21,9 +21,7 @@ import android.content.Intent.ACTION_SET_WALLPAPER import android.content.pm.PackageManager.MATCH_DEFAULT_ONLY import android.provider.Settings.* -/** - * Utility class to check the support of multi panes integration (trampoline) - */ +/** Utility class to check the support of multi panes integration (trampoline) */ class LargeScreenMultiPanesChecker : MultiPanesChecker { companion object { private const val TAG = "LargeScreenMultiPanesChecker" @@ -32,8 +30,8 @@ class LargeScreenMultiPanesChecker : MultiPanesChecker { override fun isMultiPanesEnabled(context: Context): Boolean { val pm = context.packageManager - val intent = getMultiPanesIntent( - Intent(ACTION_SET_WALLPAPER).setPackage(context.packageName)) + val intent = + getMultiPanesIntent(Intent(ACTION_SET_WALLPAPER).setPackage(context.packageName)) val resolveInfo = pm.resolveActivity(intent, MATCH_DEFAULT_ONLY)?.activityInfo?.enabled return resolveInfo != null @@ -42,8 +40,10 @@ class LargeScreenMultiPanesChecker : MultiPanesChecker { override fun getMultiPanesIntent(intent: Intent): Intent { return Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY).apply { putExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY, VALUE_HIGHLIGHT_MENU) - putExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI, - intent.toUri(Intent.URI_INTENT_SCHEME)) - }; + putExtra( + EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI, + intent.toUri(Intent.URI_INTENT_SCHEME) + ) + } } } diff --git a/src/com/android/wallpaper/module/MultiPanesChecker.kt b/src/com/android/wallpaper/module/MultiPanesChecker.kt index 8a18b9ca..b13b9fa7 100644 --- a/src/com/android/wallpaper/module/MultiPanesChecker.kt +++ b/src/com/android/wallpaper/module/MultiPanesChecker.kt @@ -18,9 +18,7 @@ package com.android.wallpaper.module import android.content.Context import android.content.Intent -/** - * Checks if the device is supporting multi panes' features - */ +/** Checks if the device is supporting multi panes' features */ interface MultiPanesChecker { /** * Checks if the device is supporting multi-panes landscape diff --git a/src/com/android/wallpaper/module/NoOpUserEventLogger.java b/src/com/android/wallpaper/module/NoOpUserEventLogger.java index 77e2a4b9..070ad271 100755 --- a/src/com/android/wallpaper/module/NoOpUserEventLogger.java +++ b/src/com/android/wallpaper/module/NoOpUserEventLogger.java @@ -148,7 +148,12 @@ public class NoOpUserEventLogger implements UserEventLogger { } @Override - public void logEffectApply(String effect, int status) { + public void logEffectApply(String effect, int status, long timeElapsedMillis, int resultCode) { + + } + + @Override + public void logEffectProbe(String effect, int status) { } } diff --git a/src/com/android/wallpaper/module/PartnerProvider.java b/src/com/android/wallpaper/module/PartnerProvider.java index 0463f77e..30ebee53 100755 --- a/src/com/android/wallpaper/module/PartnerProvider.java +++ b/src/com/android/wallpaper/module/PartnerProvider.java @@ -33,11 +33,16 @@ public interface PartnerProvider { "com.android.launcher3.action.PARTNER_CUSTOMIZATION"; /** - * The resource ID in the partner APK for its list of wallpapers. + * The resource ID in the partner APK for an xml describing collections and wallpapers. */ String WALLPAPER_RES_ID = "wallpapers"; /** + * The resource ID in the partner APK for its list of wallpapers in legacy string-array format. + */ + String LEGACY_WALLPAPER_RES_ID = "partner_wallpapers"; + + /** * Directory for system wallpapers in legacy versions of the partner APK. */ String RES_LEGACY_SYSTEM_WALLPAPER_DIR = "system_wallpaper_directory"; diff --git a/src/com/android/wallpaper/module/UserEventLogger.java b/src/com/android/wallpaper/module/UserEventLogger.java index 09e7b6b5..8a2156da 100755 --- a/src/com/android/wallpaper/module/UserEventLogger.java +++ b/src/com/android/wallpaper/module/UserEventLogger.java @@ -197,7 +197,13 @@ public interface UserEventLogger { /** * Logs the action related to effect. */ - void logEffectApply(String effect, @EffectStatus int status); + void logEffectApply(String effect, @EffectStatus int status, long timeElapsedMillis, + int resultCode); + + /** + * Logs the effect probe result. + */ + void logEffectProbe(String effect, @EffectStatus int status); /** * Possible results of a "set wallpaper" operation. @@ -258,7 +264,7 @@ public interface UserEventLogger { EFFECT_PREFERENCE_UNSPECIFIED, EFFECT_APPLIED_ON_SUCCESS, EFFECT_APPLIED_ON_FAILED, - EFFECT_APPLIED_OFF, + EFFECT_APPLIED_OFF }) @interface EffectStatus { } diff --git a/src/com/android/wallpaper/module/WallpaperPersister.java b/src/com/android/wallpaper/module/WallpaperPersister.java index 5d9992c2..cfb3cab9 100755 --- a/src/com/android/wallpaper/module/WallpaperPersister.java +++ b/src/com/android/wallpaper/module/WallpaperPersister.java @@ -15,6 +15,10 @@ */ package com.android.wallpaper.module; +import static android.app.WallpaperManager.FLAG_LOCK; +import static android.app.WallpaperManager.FLAG_SYSTEM; +import static android.app.WallpaperManager.SetWallpaperFlags; + import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Rect; @@ -194,4 +198,21 @@ public interface WallpaperPersister { WALLPAPER_POSITION_STRETCH}) @interface WallpaperPosition { } + + /** + * Converts a {@link Destination} to the corresponding set of {@link SetWallpaperFlags}. + */ + @SetWallpaperFlags + static int destinationToFlags(@Destination int destination) { + switch (destination) { + case DEST_HOME_SCREEN: + return FLAG_SYSTEM; + case DEST_LOCK_SCREEN: + return FLAG_LOCK; + case DEST_BOTH: + return FLAG_SYSTEM | FLAG_LOCK; + default: + throw new AssertionError("Unknown @Destination"); + } + } } diff --git a/src/com/android/wallpaper/module/WallpaperPicker2Injector.kt b/src/com/android/wallpaper/module/WallpaperPicker2Injector.kt new file mode 100755 index 00000000..d97e4bea --- /dev/null +++ b/src/com/android/wallpaper/module/WallpaperPicker2Injector.kt @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wallpaper.module + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import androidx.fragment.app.Fragment +import com.android.wallpaper.compat.WallpaperManagerCompat +import com.android.wallpaper.config.BaseFlags +import com.android.wallpaper.effects.EffectsController +import com.android.wallpaper.effects.EffectsController.EffectsServiceListener +import com.android.wallpaper.model.CategoryProvider +import com.android.wallpaper.model.LiveWallpaperInfo +import com.android.wallpaper.model.WallpaperInfo +import com.android.wallpaper.monitor.PerformanceMonitor +import com.android.wallpaper.network.Requester +import com.android.wallpaper.network.WallpaperRequester +import com.android.wallpaper.picker.CustomizationPickerActivity +import com.android.wallpaper.picker.ImagePreviewFragment +import com.android.wallpaper.picker.LivePreviewFragment +import com.android.wallpaper.picker.PreviewFragment +import com.android.wallpaper.picker.individual.IndividualPickerFragment +import com.android.wallpaper.picker.undo.data.repository.UndoRepository +import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor +import com.android.wallpaper.util.DisplayUtils +import kotlinx.coroutines.GlobalScope + +open class WallpaperPicker2Injector : Injector { + private var alarmManagerWrapper: AlarmManagerWrapper? = null + private var bitmapCropper: BitmapCropper? = null + private var categoryProvider: CategoryProvider? = null + private var currentWallpaperFactory: CurrentWallpaperInfoFactory? = null + private var customizationSections: CustomizationSections? = null + private var displayUtils: DisplayUtils? = null + private var drawableLayerResolver: DrawableLayerResolver? = null + private var exploreIntentChecker: ExploreIntentChecker? = null + private var liveWallpaperInfoFactory: LiveWallpaperInfoFactory? = null + private var networkStatusNotifier: NetworkStatusNotifier? = null + private var packageStatusNotifier: PackageStatusNotifier? = null + private var partnerProvider: PartnerProvider? = null + private var performanceMonitor: PerformanceMonitor? = null + private var requester: Requester? = null + private var systemFeatureChecker: SystemFeatureChecker? = null + private var userEventLogger: UserEventLogger? = null + private var wallpaperManagerCompat: WallpaperManagerCompat? = null + private var wallpaperPersister: WallpaperPersister? = null + private var prefs: WallpaperPreferences? = null + private var wallpaperPreviewFragmentManager: WallpaperPreviewFragmentManager? = null + private var wallpaperRefresher: WallpaperRefresher? = null + private var wallpaperRotationRefresher: WallpaperRotationRefresher? = null + private var wallpaperStatusChecker: WallpaperStatusChecker? = null + private var flags: BaseFlags? = null + private var undoInteractor: UndoInteractor? = null + + @Synchronized + override fun getAlarmManagerWrapper(context: Context): AlarmManagerWrapper { + return alarmManagerWrapper + ?: DefaultAlarmManagerWrapper(context.applicationContext).also { + alarmManagerWrapper = it + } + } + + @Synchronized + override fun getBitmapCropper(): BitmapCropper { + return bitmapCropper ?: DefaultBitmapCropper().also { bitmapCropper = it } + } + + override fun getCategoryProvider(context: Context): CategoryProvider { + return categoryProvider + ?: DefaultCategoryProvider(context.applicationContext).also { categoryProvider = it } + } + + @Synchronized + override fun getCurrentWallpaperInfoFactory(context: Context): CurrentWallpaperInfoFactory { + return currentWallpaperFactory + ?: DefaultCurrentWallpaperInfoFactory(context.applicationContext).also { + currentWallpaperFactory = it + } + } + + override fun getCustomizationSections(activity: Activity): CustomizationSections { + + return customizationSections + ?: WallpaperPickerSections().also { customizationSections = it } + } + + override fun getDeepLinkRedirectIntent(context: Context, uri: Uri): Intent { + val intent = Intent() + intent.setClass(context, CustomizationPickerActivity::class.java) + intent.data = uri + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + return intent + } + + override fun getDisplayUtils(context: Context): DisplayUtils { + return displayUtils ?: DisplayUtils(context.applicationContext).also { displayUtils = it } + } + + override fun getDownloadableIntentAction(): String? { + return null + } + + override fun getDrawableLayerResolver(): DrawableLayerResolver { + return drawableLayerResolver + ?: DefaultDrawableLayerResolver().also { drawableLayerResolver = it } + } + + override fun getEffectsController( + context: Context, + listener: EffectsServiceListener + ): EffectsController? { + return null + } + + @Synchronized + override fun getExploreIntentChecker(context: Context): ExploreIntentChecker { + return exploreIntentChecker + ?: DefaultExploreIntentChecker(context.applicationContext).also { + exploreIntentChecker = it + } + } + + @Synchronized + override fun getIndividualPickerFragment(collectionId: String): Fragment { + return IndividualPickerFragment.newInstance(collectionId) + } + + override fun getLiveWallpaperInfoFactory(context: Context): LiveWallpaperInfoFactory { + return liveWallpaperInfoFactory + ?: DefaultLiveWallpaperInfoFactory().also { liveWallpaperInfoFactory = it } + } + + @Synchronized + override fun getNetworkStatusNotifier(context: Context): NetworkStatusNotifier { + return networkStatusNotifier + ?: DefaultNetworkStatusNotifier(context.applicationContext).also { + networkStatusNotifier = it + } + } + + @Synchronized + override fun getPackageStatusNotifier(context: Context): PackageStatusNotifier { + return packageStatusNotifier + ?: DefaultPackageStatusNotifier(context.applicationContext).also { + packageStatusNotifier = it + } + } + + @Synchronized + override fun getPartnerProvider(context: Context): PartnerProvider { + return partnerProvider + ?: DefaultPartnerProvider(context.applicationContext).also { partnerProvider = it } + } + + @Synchronized + override fun getPerformanceMonitor(): PerformanceMonitor? { + + return performanceMonitor + ?: PerformanceMonitor { + /** No Op */ + } + .also { performanceMonitor = it } + } + + override fun getPreviewFragment( + context: Context, + wallpaperInfo: WallpaperInfo, + mode: Int, + viewAsHome: Boolean, + viewFullScreen: Boolean, + testingModeEnabled: Boolean + ): Fragment { + val args = Bundle() + args.putParcelable(PreviewFragment.ARG_WALLPAPER, wallpaperInfo) + args.putInt(PreviewFragment.ARG_PREVIEW_MODE, mode) + args.putBoolean(PreviewFragment.ARG_VIEW_AS_HOME, viewAsHome) + args.putBoolean(PreviewFragment.ARG_FULL_SCREEN, viewFullScreen) + args.putBoolean(PreviewFragment.ARG_TESTING_MODE_ENABLED, testingModeEnabled) + val fragment = + if (wallpaperInfo is LiveWallpaperInfo) LivePreviewFragment() + else ImagePreviewFragment() + fragment.arguments = args + return fragment + } + + @Synchronized + override fun getRequester(context: Context): Requester { + return requester ?: WallpaperRequester(context.applicationContext).also { requester = it } + } + + @Synchronized + override fun getSystemFeatureChecker(): SystemFeatureChecker { + return systemFeatureChecker + ?: DefaultSystemFeatureChecker().also { systemFeatureChecker = it } + } + + override fun getUserEventLogger(context: Context): UserEventLogger { + return userEventLogger ?: NoOpUserEventLogger().also { userEventLogger = it } + } + + @Synchronized + override fun getWallpaperManagerCompat(context: Context): WallpaperManagerCompat { + return wallpaperManagerCompat + ?: WallpaperManagerCompat.getInstance(context).also { wallpaperManagerCompat = it } + } + + @Synchronized + override fun getWallpaperPersister(context: Context): WallpaperPersister { + return wallpaperPersister + ?: DefaultWallpaperPersister(context.applicationContext).also { + wallpaperPersister = it + } + } + + @Synchronized + override fun getPreferences(context: Context): WallpaperPreferences { + return prefs ?: DefaultWallpaperPreferences(context.applicationContext).also { prefs = it } + } + + @Synchronized + override fun getWallpaperPreviewFragmentManager(): WallpaperPreviewFragmentManager { + return wallpaperPreviewFragmentManager + ?: DefaultWallpaperPreviewFragmentManager().also { + wallpaperPreviewFragmentManager = it + } + } + + @Synchronized + override fun getWallpaperRefresher(context: Context): WallpaperRefresher { + return wallpaperRefresher + ?: DefaultWallpaperRefresher(context.applicationContext).also { + wallpaperRefresher = it + } + } + + @Synchronized + override fun getWallpaperRotationRefresher(): WallpaperRotationRefresher { + return wallpaperRotationRefresher + ?: WallpaperRotationRefresher { _, listener -> + // Not implemented + listener.onError() + } + .also { wallpaperRotationRefresher = it } + } + + override fun getWallpaperStatusChecker(): WallpaperStatusChecker { + return wallpaperStatusChecker + ?: DefaultWallpaperStatusChecker().also { wallpaperStatusChecker = it } + } + + override fun getFlags(): BaseFlags { + return flags ?: object : BaseFlags() {}.also { flags = it } + } + + override fun getUndoInteractor(context: Context): UndoInteractor { + return undoInteractor + ?: UndoInteractor(GlobalScope, UndoRepository(), getSnapshotRestorers(context)).also { + undoInteractor = it + } + } + + companion object { + /** + * When this injector is overridden, this is the minimal value that should be used by + * restorers returns in [getSnapshotRestorers]. + */ + @JvmStatic protected val MIN_SNAPSHOT_RESTORER_KEY = 0 + } +} diff --git a/src/com/android/wallpaper/module/WallpaperPickerSections.java b/src/com/android/wallpaper/module/WallpaperPickerSections.java index b5e8d175..16782ce3 100644 --- a/src/com/android/wallpaper/module/WallpaperPickerSections.java +++ b/src/com/android/wallpaper/module/WallpaperPickerSections.java @@ -1,9 +1,9 @@ package com.android.wallpaper.module; -import android.app.Activity; import android.os.Bundle; import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.LifecycleOwner; import com.android.wallpaper.model.CustomizationSectionController; @@ -13,6 +13,8 @@ import com.android.wallpaper.model.WallpaperColorsViewModel; import com.android.wallpaper.model.WallpaperPreviewNavigator; import com.android.wallpaper.model.WallpaperSectionController; import com.android.wallpaper.model.WorkspaceViewModel; +import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewSectionController; +import com.android.wallpaper.util.DisplayUtils; import java.util.ArrayList; import java.util.List; @@ -21,18 +23,49 @@ import java.util.List; public final class WallpaperPickerSections implements CustomizationSections { @Override - public List<CustomizationSectionController<?>> getAllSectionControllers(Activity activity, - LifecycleOwner lifecycleOwner, WallpaperColorsViewModel wallpaperColorsViewModel, - WorkspaceViewModel workspaceViewModel, PermissionRequester permissionRequester, + public List<CustomizationSectionController<?>> getSectionControllersForScreen( + Screen screen, + FragmentActivity activity, + LifecycleOwner lifecycleOwner, + WallpaperColorsViewModel wallpaperColorsViewModel, + WorkspaceViewModel workspaceViewModel, + PermissionRequester permissionRequester, WallpaperPreviewNavigator wallpaperPreviewNavigator, CustomizationSectionNavigationController sectionNavigationController, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState, + CurrentWallpaperInfoFactory wallpaperInfoFactory, + DisplayUtils displayUtils) { + List<CustomizationSectionController<?>> sectionControllers = new ArrayList<>(); + + sectionControllers.add( + new ScreenPreviewSectionController( + activity, + lifecycleOwner, + screen, + wallpaperInfoFactory, + wallpaperColorsViewModel, + displayUtils)); + + return sectionControllers; + } + + @Override + public List<CustomizationSectionController<?>> getAllSectionControllers( + FragmentActivity activity, + LifecycleOwner lifecycleOwner, + WallpaperColorsViewModel wallpaperColorsViewModel, + WorkspaceViewModel workspaceViewModel, + PermissionRequester permissionRequester, + WallpaperPreviewNavigator wallpaperPreviewNavigator, + CustomizationSectionNavigationController sectionNavigationController, + @Nullable Bundle savedInstanceState, + DisplayUtils displayUtils) { List<CustomizationSectionController<?>> sectionControllers = new ArrayList<>(); sectionControllers.add(new WallpaperSectionController( activity, lifecycleOwner, permissionRequester, wallpaperColorsViewModel, workspaceViewModel, sectionNavigationController, wallpaperPreviewNavigator, - savedInstanceState)); + savedInstanceState, displayUtils)); return sectionControllers; } diff --git a/src/com/android/wallpaper/module/LoggingOptInStatusProvider.java b/src/com/android/wallpaper/module/WallpaperPreviewFragmentManager.kt index ed6ea3ec..fc6b8d41 100755..100644 --- a/src/com/android/wallpaper/module/LoggingOptInStatusProvider.java +++ b/src/com/android/wallpaper/module/WallpaperPreviewFragmentManager.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2022 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. @@ -13,22 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.wallpaper.module; +package com.android.wallpaper.module -/** - * Provides the status of whether it is OK to opt in this user to logging. - */ -public interface LoggingOptInStatusProvider { - - /** - * Fetches the current opt-in state of logging and supplies it to the given receiver. - */ - void fetchOptInValue(OptInValueReceiver receiver); +import android.content.Context +import androidx.fragment.app.Fragment +import com.android.wallpaper.model.WallpaperInfo - /** - * Interface for receivers of the usage & diagnostics opt-in value. - */ - interface OptInValueReceiver { - void onOptInValueReady(boolean optedIn); - } +/** A manager that provides the wallpaper preview fragment. */ +interface WallpaperPreviewFragmentManager { + fun getPreviewFragment( + context: Context, + wallpaperInfo: WallpaperInfo, + mode: Int, + viewAsHome: Boolean, + viewFullScreen: Boolean, + testingModeEnabled: Boolean + ): Fragment } diff --git a/src/com/android/wallpaper/module/WallpaperSetter.java b/src/com/android/wallpaper/module/WallpaperSetter.java index a8db6de0..6924a078 100644 --- a/src/com/android/wallpaper/module/WallpaperSetter.java +++ b/src/com/android/wallpaper/module/WallpaperSetter.java @@ -6,6 +6,7 @@ import android.app.Activity; import android.app.ProgressDialog; import android.app.WallpaperColors; import android.app.WallpaperManager; +import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.Point; @@ -39,6 +40,7 @@ import com.android.wallpaper.util.WallpaperCropUtils; import com.bumptech.glide.Glide; import java.io.IOException; +import java.lang.reflect.Method; import java.util.Optional; /** @@ -200,15 +202,11 @@ public class WallpaperSetter { "Live wallpaper cannot be applied on lock screen only"); } WallpaperManager wallpaperManager = WallpaperManager.getInstance(activity); - wallpaperManager.setWallpaperComponent( - wallpaper.getWallpaperComponent().getComponent()); + setWallpaperComponent(wallpaperManager, wallpaper, destination); wallpaperManager.setWallpaperOffsetSteps(0.5f /* xStep */, 0.0f /* yStep */); wallpaperManager.setWallpaperOffsets( activity.getWindow().getDecorView().getRootView().getWindowToken(), 0.5f /* xOffset */, 0.0f /* yOffset */); - if (destination == WallpaperPersister.DEST_BOTH) { - wallpaperManager.clear(FLAG_LOCK); - } mPreferences.storeLatestHomeWallpaper(wallpaper.getWallpaperId(), wallpaper, colors); onWallpaperApplied(wallpaper, activity); if (callback != null) { @@ -222,6 +220,24 @@ public class WallpaperSetter { } } + private void setWallpaperComponent(WallpaperManager wallpaperManager, + LiveWallpaperInfo wallpaper, int destination) throws IOException { + try { + Method m = wallpaperManager.getClass().getMethod("setWallpaperComponentWithFlags", + ComponentName.class, int.class); + wallpaperManager.setWallpaperComponentWithFlags( + wallpaper.getWallpaperComponent().getComponent(), + WallpaperPersister.destinationToFlags(destination)); + } catch (NoSuchMethodException e) { + Log.d(TAG, "setWallpaperComponentWithFlags not available, using setWallpaperComponent"); + wallpaperManager.setWallpaperComponent( + wallpaper.getWallpaperComponent().getComponent()); + } + if (destination == WallpaperPersister.DEST_BOTH) { + wallpaperManager.clear(FLAG_LOCK); + } + } + /** * Sets current live wallpaper to the device (restore case) * @@ -240,11 +256,7 @@ public class WallpaperSetter { "Live wallpaper cannot be applied on lock screen only"); } WallpaperManager wallpaperManager = WallpaperManager.getInstance(context); - wallpaperManager.setWallpaperComponent( - wallpaper.getWallpaperComponent().getComponent()); - if (destination == WallpaperPersister.DEST_BOTH) { - wallpaperManager.clear(FLAG_LOCK); - } + setWallpaperComponent(wallpaperManager, wallpaper, destination); mPreferences.storeLatestHomeWallpaper(wallpaper.getWallpaperId(), wallpaper, colors != null ? colors : WallpaperColors.fromBitmap(wallpaper.getThumbAsset(context) diff --git a/src/com/android/wallpaper/picker/CategorySelectorFragment.java b/src/com/android/wallpaper/picker/CategorySelectorFragment.java index f0ca4227..2b6c71dc 100644 --- a/src/com/android/wallpaper/picker/CategorySelectorFragment.java +++ b/src/com/android/wallpaper/picker/CategorySelectorFragment.java @@ -20,7 +20,6 @@ import static com.android.wallpaper.picker.WallpaperPickerDelegate.PREVIEW_WALLP import android.app.Activity; import android.app.AlertDialog; -import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; @@ -119,23 +118,19 @@ public class CategorySelectorFragment extends AppbarFragment { void cleanUp(); } - private final CategoryProvider mCategoryProvider; - private RecyclerView mImageGrid; private CategoryAdapter mAdapter; + private CategoryProvider mCategoryProvider; private ArrayList<Category> mCategories = new ArrayList<>(); private Point mTileSizePx; private boolean mAwaitingCategories; private boolean mIsFeaturedCollectionAvailable; - public CategorySelectorFragment() { - mAdapter = new CategoryAdapter(mCategories); - mCategoryProvider = InjectorProvider.getInjector().getCategoryProvider(getContext()); - } - - public CategorySelectorFragment(Context context) { + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); mAdapter = new CategoryAdapter(mCategories); - mCategoryProvider = InjectorProvider.getInjector().getCategoryProvider(context); + mCategoryProvider = InjectorProvider.getInjector().getCategoryProvider(requireContext()); } @Nullable diff --git a/src/com/android/wallpaper/picker/CustomizationPickerActivity.java b/src/com/android/wallpaper/picker/CustomizationPickerActivity.java index f76c09b7..2351c72e 100644 --- a/src/com/android/wallpaper/picker/CustomizationPickerActivity.java +++ b/src/com/android/wallpaper/picker/CustomizationPickerActivity.java @@ -35,6 +35,7 @@ import androidx.fragment.app.FragmentManager; import com.android.wallpaper.R; import com.android.wallpaper.model.Category; +import com.android.wallpaper.model.CustomizationSectionController.CustomizationSectionNavigationController; import com.android.wallpaper.model.PermissionRequester; import com.android.wallpaper.model.WallpaperCategory; import com.android.wallpaper.model.WallpaperInfo; @@ -51,6 +52,7 @@ import com.android.wallpaper.picker.AppbarFragment.AppbarFragmentHost; import com.android.wallpaper.picker.CategorySelectorFragment.CategorySelectorFragmentHost; import com.android.wallpaper.picker.MyPhotosStarter.PermissionChangedListener; import com.android.wallpaper.picker.individual.IndividualPickerFragment.IndividualPickerFragmentHost; +import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor; import com.android.wallpaper.util.ActivityUtils; import com.android.wallpaper.util.DeepLinkUtils; import com.android.wallpaper.util.LaunchUtils; @@ -67,6 +69,7 @@ public class CustomizationPickerActivity extends FragmentActivity implements App WallpaperPreviewNavigator { private static final String TAG = "CustomizationPickerActivity"; + private static final String EXTRA_DESTINATION = "destination"; private WallpaperPickerDelegate mDelegate; private UserEventLogger mUserEventLogger; @@ -76,6 +79,7 @@ public class CustomizationPickerActivity extends FragmentActivity implements App private BottomActionBar mBottomActionBar; private boolean mIsSafeToCommitFragmentTransaction; + @Nullable private UndoInteractor mUndoInteractor; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -105,6 +109,8 @@ public class CustomizationPickerActivity extends FragmentActivity implements App // See go/pdr-edge-to-edge-guide. WindowCompat.setDecorFitsSystemWindows(getWindow(), isSUWMode(this)); + final boolean isUseRevampedUi = injector.getFlags().isUseRevampedUi(this); + Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container); if (fragment == null) { // App launch specific logic: log the "app launch source" event. @@ -117,13 +123,28 @@ public class CustomizationPickerActivity extends FragmentActivity implements App // Switch to the target fragment. switchFragment(isWallpaperOnlyMode(getIntent()) ? new WallpaperOnlyFragment() - : new CustomizationPickerFragment()); + : CustomizationPickerFragment.newInstance(isUseRevampedUi)); + } + + if (isUseRevampedUi) { + mUndoInteractor = injector.getUndoInteractor(this); + mUndoInteractor.startSession(); } - // Deep link case - Intent intent = getIntent(); - String deepLinkCollectionId = DeepLinkUtils.getCollectionId(intent); - if (!TextUtils.isEmpty(deepLinkCollectionId)) { + final Intent intent = getIntent(); + final String navigationDestination = intent.getStringExtra(EXTRA_DESTINATION); + final String deepLinkCollectionId = DeepLinkUtils.getCollectionId(intent); + + if (!TextUtils.isEmpty(navigationDestination)) { + // Navigation deep link case + fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container); + if (fragment instanceof CustomizationSectionNavigationController) { + final CustomizationSectionNavigationController navController = + (CustomizationSectionNavigationController) fragment; + navController.navigateTo(navigationDestination); + } + } else if (!TextUtils.isEmpty(deepLinkCollectionId)) { + // Wallpaper Collection deep link case switchFragmentWithBackStack(new CategorySelectorFragment()); switchFragmentWithBackStack(InjectorProvider.getInjector().getIndividualPickerFragment( deepLinkCollectionId)); diff --git a/src/com/android/wallpaper/picker/CustomizationPickerFragment.java b/src/com/android/wallpaper/picker/CustomizationPickerFragment.java index dd0719f0..380fe13c 100644 --- a/src/com/android/wallpaper/picker/CustomizationPickerFragment.java +++ b/src/com/android/wallpaper/picker/CustomizationPickerFragment.java @@ -37,7 +37,11 @@ import com.android.wallpaper.model.WallpaperColorsViewModel; import com.android.wallpaper.model.WallpaperPreviewNavigator; import com.android.wallpaper.model.WorkspaceViewModel; import com.android.wallpaper.module.CustomizationSections; +import com.android.wallpaper.module.FragmentFactory; +import com.android.wallpaper.module.Injector; import com.android.wallpaper.module.InjectorProvider; +import com.android.wallpaper.picker.customization.ui.binder.CustomizationPickerBinder; +import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationPickerViewModel; import com.android.wallpaper.util.ActivityUtils; import java.util.ArrayList; @@ -50,18 +54,34 @@ public class CustomizationPickerFragment extends AppbarFragment implements private static final String TAG = "CustomizationPickerFragment"; private static final String SCROLL_POSITION_Y = "SCROLL_POSITION_Y"; + private static final String KEY_IS_USE_REVAMPED_UI = "is_use_revamped_ui"; + + /** Returns a new instance of {@link CustomizationPickerFragment}. */ + public static CustomizationPickerFragment newInstance(boolean isUseRevampedUi) { + final CustomizationPickerFragment fragment = new CustomizationPickerFragment(); + final Bundle args = new Bundle(); + args.putBoolean(KEY_IS_USE_REVAMPED_UI, isUseRevampedUi); + fragment.setArguments(args); + return fragment; + } // Note that the section views will be displayed by the list ordering. private final List<CustomizationSectionController<?>> mSectionControllers = new ArrayList<>(); private NestedScrollView mNestedScrollView; @Nullable private Bundle mBackStackSavedInstanceState; + private final FragmentFactory mFragmentFactory; + @Nullable private CustomizationPickerViewModel mViewModel; + + public CustomizationPickerFragment() { + mFragmentFactory = InjectorProvider.getInjector().getFragmentFactory(); + } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @Nullable Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.collapsing_toolbar_container_layout, container, /* attachToRoot= */ false); - setContentView(view, R.layout.fragment_customization_picker); + if (ActivityUtils.isLaunchedFromSettingsRelated(getActivity().getIntent())) { setUpToolbar(view, !ActivityEmbeddingUtils.shouldHideNavigateUpButton( getActivity(), /* isSecondLayerPage= */ true)); @@ -69,39 +89,78 @@ public class CustomizationPickerFragment extends AppbarFragment implements setUpToolbar(view, /* upArrow= */ false); } - ViewGroup sectionContainer = view.findViewById(R.id.section_container); - sectionContainer.setOnApplyWindowInsetsListener((v, windowInsets) -> { - v.setPadding( - v.getPaddingLeft(), - v.getPaddingTop(), - v.getPaddingRight(), - windowInsets.getSystemWindowInsetBottom()); - return windowInsets.consumeSystemWindowInsets(); - }); - mNestedScrollView = view.findViewById(R.id.scroll_container); + final Injector injector = InjectorProvider.getInjector(); + final Bundle args = getArguments(); + final boolean isUseRevampedUi; + if (args != null && args.containsKey(KEY_IS_USE_REVAMPED_UI)) { + isUseRevampedUi = args.getBoolean(KEY_IS_USE_REVAMPED_UI); + } else { + throw new IllegalStateException( + "Must contain KEY_IS_USE_REVAMPED_UI argument, did you instantiate directly" + + " instead of using the newInstance function?"); + } + if (isUseRevampedUi) { + setContentView(view, R.layout.fragment_tabbed_customization_picker); + mViewModel = new ViewModelProvider( + this, + CustomizationPickerViewModel.newFactory( + this, + savedInstanceState, + injector.getUndoInteractor(requireContext())) + ).get(CustomizationPickerViewModel.class); + + setUpToolbarMenu(R.menu.undoable_customization_menu); + final Bundle finalSavedInstanceState = savedInstanceState; + CustomizationPickerBinder.bind( + view, + getToolbarId(), + mViewModel, + this, + isOnLockScreen -> getSectionControllers( + isOnLockScreen + ? CustomizationSections.Screen.LOCK_SCREEN + : CustomizationSections.Screen.HOME_SCREEN, + finalSavedInstanceState)); + } else { + setContentView(view, R.layout.fragment_customization_picker); + } if (mBackStackSavedInstanceState != null) { savedInstanceState = mBackStackSavedInstanceState; mBackStackSavedInstanceState = null; } - initSections(savedInstanceState); - mSectionControllers.forEach(controller -> - mNestedScrollView.post(() -> { - final Context context = getContext(); - if (context == null) { - Log.w(TAG, "Adding section views with null context"); - return; + mNestedScrollView = view.findViewById(R.id.scroll_container); + + if (!isUseRevampedUi) { + ViewGroup sectionContainer = view.findViewById(R.id.section_container); + sectionContainer.setOnApplyWindowInsetsListener((v, windowInsets) -> { + v.setPadding( + v.getPaddingLeft(), + v.getPaddingTop(), + v.getPaddingRight(), + windowInsets.getSystemWindowInsetBottom()); + return windowInsets.consumeSystemWindowInsets(); + }); + + initSections(savedInstanceState); + mSectionControllers.forEach(controller -> + mNestedScrollView.post(() -> { + final Context context = getContext(); + if (context == null) { + Log.w(TAG, "Adding section views with null context"); + return; + } + sectionContainer.addView(controller.createView(context)); } - sectionContainer.addView(controller.createView(context)); - } - ) - ); - final Bundle savedInstanceStateRef = savedInstanceState; - // Post it to the end of adding views to ensure restoring view state the last task. - mNestedScrollView.post(() -> - restoreViewState(savedInstanceStateRef) - ); + ) + ); + + final Bundle savedInstanceStateRef = savedInstanceState; + // Post it to the end of adding views to ensure restoring view state the last task. + view.post(() -> restoreViewState(savedInstanceStateRef)); + } + return view; } @@ -175,6 +234,15 @@ public class CustomizationPickerFragment extends AppbarFragment implements fragmentManager.executePendingTransactions(); } + @Override + public void navigateTo(String destinationId) { + final Fragment fragment = mFragmentFactory.create(destinationId); + + if (fragment != null) { + navigateTo(fragment); + } + } + /** Saves state of the fragment. */ private void onSaveInstanceStateInternal(Bundle savedInstanceState) { if (mNestedScrollView != null) { @@ -188,18 +256,53 @@ public class CustomizationPickerFragment extends AppbarFragment implements mSectionControllers.forEach(CustomizationSectionController::release); mSectionControllers.clear(); + mSectionControllers.addAll( + getAvailableSections(getAvailableSectionControllers(savedInstanceState))); + } + + private List<CustomizationSectionController<?>> getAvailableSectionControllers( + @Nullable Bundle savedInstanceState) { + return getSectionControllers( + null, + savedInstanceState); + } + + private List<CustomizationSectionController<?>> getSectionControllers( + @Nullable CustomizationSections.Screen screen, + @Nullable Bundle savedInstanceState) { + final Injector injector = InjectorProvider.getInjector(); + WallpaperColorsViewModel wcViewModel = new ViewModelProvider(getActivity()) .get(WallpaperColorsViewModel.class); WorkspaceViewModel workspaceViewModel = new ViewModelProvider(getActivity()) .get(WorkspaceViewModel.class); - CustomizationSections sections = InjectorProvider.getInjector().getCustomizationSections(); - List<CustomizationSectionController<?>> allSectionControllers = - sections.getAllSectionControllers(getActivity(), getViewLifecycleOwner(), - wcViewModel, workspaceViewModel, getPermissionRequester(), - getWallpaperPreviewNavigator(), this, savedInstanceState); - - mSectionControllers.addAll(getAvailableSections(allSectionControllers)); + CustomizationSections sections = injector.getCustomizationSections(getActivity()); + if (screen == null) { + return sections.getAllSectionControllers( + getActivity(), + getViewLifecycleOwner(), + wcViewModel, + workspaceViewModel, + getPermissionRequester(), + getWallpaperPreviewNavigator(), + this, + savedInstanceState, + injector.getDisplayUtils(getActivity())); + } else { + return sections.getSectionControllersForScreen( + screen, + getActivity(), + getViewLifecycleOwner(), + wcViewModel, + workspaceViewModel, + getPermissionRequester(), + getWallpaperPreviewNavigator(), + this, + savedInstanceState, + injector.getCurrentWallpaperInfoFactory(requireContext()), + injector.getDisplayUtils(getActivity())); + } } protected List<CustomizationSectionController<?>> getAvailableSections( diff --git a/src/com/android/wallpaper/picker/DisplayAspectRatioFrameLayout.kt b/src/com/android/wallpaper/picker/DisplayAspectRatioFrameLayout.kt new file mode 100644 index 00000000..a2f1c678 --- /dev/null +++ b/src/com/android/wallpaper/picker/DisplayAspectRatioFrameLayout.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker + +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout +import androidx.core.view.children +import com.android.wallpaper.util.ScreenSizeCalculator + +/** + * [FrameLayout] that sizes its children using a fixed aspect ratio that is the same as that of the + * display. + */ +class DisplayAspectRatioFrameLayout( + context: Context, + attrs: AttributeSet?, +) : FrameLayout(context, attrs) { + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + val screenAspectRatio = ScreenSizeCalculator.getInstance().getScreenAspectRatio(context) + // We're always forcing the width based on the height. This will only work if the + // DisplayAspectRatioFrameLayout is allowed to stretch to fill its parent (for example if + // the parent is a vertical LinearLayout and the DisplayAspectRatioFrameLayout has a height + // if 0 and a weight of 1. + // + // If you need to use this class to force the height dimension based on the width instead, + // you will need to flip the logic below. + children.forEach { child -> + child.measure( + MeasureSpec.makeMeasureSpec( + (child.measuredHeight / screenAspectRatio).toInt(), + MeasureSpec.EXACTLY + ), + MeasureSpec.makeMeasureSpec( + child.measuredHeight, + MeasureSpec.EXACTLY, + ), + ) + } + } +} diff --git a/src/com/android/wallpaper/picker/FadeAnimationSurfaceView.kt b/src/com/android/wallpaper/picker/FadeAnimationSurfaceView.kt new file mode 100644 index 00000000..c4b6695e --- /dev/null +++ b/src/com/android/wallpaper/picker/FadeAnimationSurfaceView.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker + +import android.content.Context +import android.util.AttributeSet +import android.view.SurfaceControl +import android.view.SurfaceView + +/** A `SurfaceView` which allows the surface alpha to be adjusted by setting view alpha. */ +class FadeAnimationSurfaceView(context: Context, attrs: AttributeSet) : + SurfaceView(context, attrs) { + + override fun onSetAlpha(alpha: Int): Boolean { + requestUpdateSurfacePositionAndScale() + return super.onSetAlpha(alpha) + } + + override fun onSetSurfacePositionAndScale( + transaction: SurfaceControl.Transaction, + surface: SurfaceControl, + positionLeft: Int, + positionTop: Int, + postScaleX: Float, + postScaleY: Float + ) { + super.onSetSurfacePositionAndScale( + transaction, + surface, + positionLeft, + positionTop, + postScaleX, + postScaleY + ) + transaction.setAlpha(surface, alpha) + } +} diff --git a/src/com/android/wallpaper/picker/ImagePreviewFragment.java b/src/com/android/wallpaper/picker/ImagePreviewFragment.java index e2524ab2..6d75c87b 100755 --- a/src/com/android/wallpaper/picker/ImagePreviewFragment.java +++ b/src/com/android/wallpaper/picker/ImagePreviewFragment.java @@ -20,6 +20,7 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; +import static com.android.wallpaper.util.WallpaperSurfaceCallback.LOW_RES_BITMAP_BLUR_RADIUS; import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY; import static com.android.wallpaper.widget.BottomActionBar.BottomAction.EDIT; import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION; @@ -37,6 +38,8 @@ import android.graphics.ColorSpace; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.RenderEffect; +import android.graphics.Shader; import android.os.Bundle; import android.os.Handler; import android.util.Log; @@ -69,6 +72,7 @@ import com.android.wallpaper.module.InjectorProvider; import com.android.wallpaper.module.LargeScreenMultiPanesChecker; import com.android.wallpaper.module.WallpaperPersister.Destination; import com.android.wallpaper.module.WallpaperPreferences; +import com.android.wallpaper.util.DisplayUtils; import com.android.wallpaper.util.FullScreenAnimation; import com.android.wallpaper.util.ResourceUtils; import com.android.wallpaper.util.ScreenSizeCalculator; @@ -133,6 +137,7 @@ public class ImagePreviewFragment extends PreviewFragment { protected SubsamplingScaleImageView mFullResImageView; protected Asset mWallpaperAsset; private Future<ColorInfo> mColorFuture; + private DisplayUtils mDisplayUtils; @Override public void onCreate(Bundle savedInstanceState) { @@ -153,12 +158,13 @@ public class ImagePreviewFragment extends PreviewFragment { View view = super.onCreateView(inflater, container, savedInstanceState); Activity activity = requireActivity(); + mDisplayUtils = mInjector.getDisplayUtils(activity); ScreenSizeCalculator screenSizeCalculator = ScreenSizeCalculator.getInstance(); mScreenSize = screenSizeCalculator.getScreenSize( activity.getWindowManager().getDefaultDisplay()); // "Wallpaper screen" size will be the size of the largest screen available mWallpaperScreenSize = screenSizeCalculator.getScreenSize( - mInjector.getDisplayUtils(activity).getWallpaperDisplay()); + mDisplayUtils.getWallpaperDisplay()); mContainer = view.findViewById(R.id.container); mTouchForwardingLayout = mContainer.findViewById(R.id.touch_forwarding_layout); @@ -512,7 +518,7 @@ public class ImagePreviewFragment extends PreviewFragment { Point crop = new Point(cropWidth, cropHeight); Rect visibleRawWallpaperRect = WallpaperCropUtils.calculateVisibleRect(mRawWallpaperSize, crop); - if (offsetToStart) { + if (offsetToStart && mDisplayUtils.isOnWallpaperDisplay(requireActivity())) { if (WallpaperCropUtils.isRtl(requireContext())) { visibleRawWallpaperRect.offsetTo(mRawWallpaperSize.x - visibleRawWallpaperRect.width(), visibleRawWallpaperRect.top); @@ -601,6 +607,9 @@ public class ImagePreviewFragment extends PreviewFragment { R.layout.fullscreen_wallpaper_preview, null); mFullResImageView = wallpaperPreviewContainer.findViewById(R.id.full_res_image); mLowResImageView = wallpaperPreviewContainer.findViewById(R.id.low_res_image); + mLowResImageView.setRenderEffect( + RenderEffect.createBlurEffect(LOW_RES_BITMAP_BLUR_RADIUS, + LOW_RES_BITMAP_BLUR_RADIUS, Shader.TileMode.CLAMP)); mWallpaperAsset.decodeRawDimensions(getActivity(), dimensions -> { // Don't continue loading the wallpaper if the Fragment is detached. if (getActivity() == null) { @@ -633,11 +642,14 @@ public class ImagePreviewFragment extends PreviewFragment { int origWidth = mWallpaperSurface.getWidth(); int origHeight = mWallpaperSurface.getHeight(); - if (!mScreenSize.equals(mWallpaperScreenSize)) { + int scaledOrigWidth = origWidth; + if (!mDisplayUtils.isOnWallpaperDisplay(requireActivity())) { + // Scale the width of the mWallpaperSurface if the current screen is not the + // largest screen (wallpaper screen). float previewToScreenScale = (float) origWidth / mScreenSize.x; - origWidth = (int) (mWallpaperScreenSize.x * previewToScreenScale); + scaledOrigWidth = (int) (mWallpaperScreenSize.x * previewToScreenScale); } - int width = (int) (origWidth * scale); + int width = (int) (scaledOrigWidth * scale); int height = (int) (origHeight * scale); int left = (origWidth - width) / 2; int top = (origHeight - height) / 2; diff --git a/src/com/android/wallpaper/picker/LivePreviewFragment.java b/src/com/android/wallpaper/picker/LivePreviewFragment.java index ccc17405..62e15b9d 100644 --- a/src/com/android/wallpaper/picker/LivePreviewFragment.java +++ b/src/com/android/wallpaper/picker/LivePreviewFragment.java @@ -304,6 +304,7 @@ public class LivePreviewFragment extends PreviewFragment implements return; } if (mWallpaperSurfaceCallback.getHomeImageWallpaper() != null) { + mWallpaperSurfaceCallback.setHomeImageWallpaperBlur(true); ColorInfo colorInfo = getColorInfo(); Integer placeholderColor = colorInfo.getPlaceholderColor(); mWallpaper.getThumbAsset(activity.getApplicationContext()) diff --git a/src/com/android/wallpaper/picker/PreviewFragment.java b/src/com/android/wallpaper/picker/PreviewFragment.java index 0b26ca79..e9e78c8a 100755 --- a/src/com/android/wallpaper/picker/PreviewFragment.java +++ b/src/com/android/wallpaper/picker/PreviewFragment.java @@ -58,6 +58,7 @@ import com.android.wallpaper.module.WallpaperPersister.Destination; import com.android.wallpaper.module.WallpaperPreferences; import com.android.wallpaper.module.WallpaperSetter; import com.android.wallpaper.util.FullScreenAnimation; +import com.android.wallpaper.util.PreviewUtils; import com.android.wallpaper.util.ResourceUtils; import com.android.wallpaper.widget.BottomActionBar; import com.android.wallpaper.widget.BottomActionBar.BottomSheetContent; @@ -105,25 +106,6 @@ public abstract class PreviewFragment extends AppbarFragment implements public static final String ARG_FULL_SCREEN = "view_full_screen"; public static final String ARG_TESTING_MODE_ENABLED = "testing_mode_enabled"; - /** - * Creates and returns new instance of {@link ImagePreviewFragment} with the provided wallpaper - * set as an argument. - */ - public static PreviewFragment newInstance(WallpaperInfo wallpaperInfo, @PreviewMode int mode, - boolean viewAsHome, boolean viewFullScreen, boolean testingModeEnabled) { - Bundle args = new Bundle(); - args.putParcelable(ARG_WALLPAPER, wallpaperInfo); - args.putInt(ARG_PREVIEW_MODE, mode); - args.putBoolean(ARG_VIEW_AS_HOME, viewAsHome); - args.putBoolean(ARG_FULL_SCREEN, viewFullScreen); - args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled); - - PreviewFragment fragment = wallpaperInfo instanceof LiveWallpaperInfo - ? new LivePreviewFragment() : new ImagePreviewFragment(); - fragment.setArguments(args); - return fragment; - } - private static final String TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT = "load_wallpaper_error_dialog"; private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT = @@ -273,7 +255,7 @@ public abstract class PreviewFragment extends AppbarFragment implements return; } startActivity(FullPreviewActivity.newIntent(getActivity(), wallpaperInfo, - /* viewAsHome= */ mLastSelectedTabPositionOptional.orElse(0) == 0), + /* viewAsHome= */ mLastSelectedTabPositionOptional.orElse(0) == 0), ActivityOptions.makeSceneTransitionAnimation(getActivity()).toBundle()); } @@ -336,7 +318,11 @@ public abstract class PreviewFragment extends AppbarFragment implements protected WorkspaceSurfaceHolderCallback createWorkspaceSurfaceCallback( SurfaceView workspaceSurface) { - return new WorkspaceSurfaceHolderCallback(workspaceSurface, getContext()); + return new WorkspaceSurfaceHolderCallback( + workspaceSurface, + new PreviewUtils( + getContext(), + getContext().getString(R.string.grid_control_metadata_name))); } @Override diff --git a/src/com/android/wallpaper/picker/WallpaperPreviewBitmapTransformation.java b/src/com/android/wallpaper/picker/WallpaperPreviewBitmapTransformation.java index fa5dea59..064192f8 100755 --- a/src/com/android/wallpaper/picker/WallpaperPreviewBitmapTransformation.java +++ b/src/com/android/wallpaper/picker/WallpaperPreviewBitmapTransformation.java @@ -77,7 +77,7 @@ public class WallpaperPreviewBitmapTransformation extends BitmapTransformation { cropSize.width(), cropSize.height()); } - return BitmapProcessor.blur(mContext, cropped, cropped.getWidth(), cropped.getHeight()); + return BitmapProcessor.createLowResBitmap(cropped, cropped.getWidth(), cropped.getHeight()); } @Override diff --git a/src/com/android/wallpaper/picker/WallpapersApplication.java b/src/com/android/wallpaper/picker/WallpapersApplication.java index 2b423f4d..fe299ce2 100755 --- a/src/com/android/wallpaper/picker/WallpapersApplication.java +++ b/src/com/android/wallpaper/picker/WallpapersApplication.java @@ -18,7 +18,7 @@ package com.android.wallpaper.picker; import android.app.Application; import com.android.wallpaper.module.InjectorProvider; -import com.android.wallpaper.module.WallpapersInjector; +import com.android.wallpaper.module.WallpaperPicker2Injector; /** * Application subclass that initializes the injector. @@ -30,6 +30,6 @@ public class WallpapersApplication extends Application { super.onCreate(); // Initialize the injector. - InjectorProvider.setInjector(new WallpapersInjector()); + InjectorProvider.setInjector(new WallpaperPicker2Injector()); } } diff --git a/src/com/android/wallpaper/picker/WorkspaceSurfaceHolderCallback.java b/src/com/android/wallpaper/picker/WorkspaceSurfaceHolderCallback.java index 26619235..bb4c0d36 100644 --- a/src/com/android/wallpaper/picker/WorkspaceSurfaceHolderCallback.java +++ b/src/com/android/wallpaper/picker/WorkspaceSurfaceHolderCallback.java @@ -16,7 +16,6 @@ package com.android.wallpaper.picker; import android.app.WallpaperColors; -import android.content.Context; import android.os.Bundle; import android.os.Message; import android.os.RemoteException; @@ -27,7 +26,6 @@ import android.view.SurfaceView; import androidx.annotation.Nullable; -import com.android.wallpaper.R; import com.android.wallpaper.util.PreviewUtils; import com.android.wallpaper.util.SurfaceViewUtils; @@ -60,9 +58,12 @@ public class WorkspaceSurfaceHolderCallback implements SurfaceHolder.Callback { private WorkspaceRenderListener mListener; private boolean mNeedsToCleanUp; + @Nullable private final Bundle mExtras; - public WorkspaceSurfaceHolderCallback(SurfaceView workspaceSurface, Context context) { - this(workspaceSurface, context, false); + public WorkspaceSurfaceHolderCallback( + SurfaceView workspaceSurface, + PreviewUtils previewUtils) { + this(workspaceSurface, previewUtils, false, null); } /** @@ -73,12 +74,33 @@ public class WorkspaceSurfaceHolderCallback implements SurfaceHolder.Callback { * the surface is created and wallpaper colors are set via * {@link #setWallpaperColors(WallpaperColors)} */ - public WorkspaceSurfaceHolderCallback(SurfaceView workspaceSurface, Context context, + public WorkspaceSurfaceHolderCallback( + SurfaceView workspaceSurface, + PreviewUtils previewUtils, boolean shouldUseWallpaperColors) { + this( + workspaceSurface, + previewUtils, + shouldUseWallpaperColors, + null); + } + + public WorkspaceSurfaceHolderCallback( + SurfaceView workspaceSurface, + PreviewUtils previewUtils, + @Nullable Bundle extras) { + this(workspaceSurface, previewUtils, false, extras); + } + + private WorkspaceSurfaceHolderCallback( + SurfaceView workspaceSurface, + PreviewUtils previewUtils, + boolean shouldUseWallpaperColors, + @Nullable Bundle extras) { mWorkspaceSurface = workspaceSurface; - mPreviewUtils = new PreviewUtils(context, - context.getString(R.string.grid_control_metadata_name)); + mPreviewUtils = previewUtils; mShouldUseWallpaperColors = shouldUseWallpaperColors; + mExtras = extras; } @Override @@ -96,7 +118,7 @@ public class WorkspaceSurfaceHolderCallback implements SurfaceHolder.Callback { * * @param colors WallpaperColors extracted from the current wallpaper preview, or {@code null} * if none are available. - * @see #WorkspaceSurfaceHolderCallback(SurfaceView, Context, boolean) + * @see #WorkspaceSurfaceHolderCallback(SurfaceView, PreviewUtils, boolean) */ public void setWallpaperColors(@Nullable WallpaperColors colors) { if (!mShouldUseWallpaperColors) { @@ -141,6 +163,27 @@ public class WorkspaceSurfaceHolderCallback implements SurfaceHolder.Callback { public void surfaceDestroyed(SurfaceHolder holder) { } + /** + * Sends a message to the remote renderer. + * + * @param what An ID for the message (the remote side can pick this up through + * {@link Message#what}. + * @param bundle The data of the message (the remote side can pick this up through + * {@link Message#getData()}. + */ + public void send(final int what, @Nullable Bundle bundle) { + if (mCallback != null) { + try { + final Message message = new Message(); + message.what = what; + message.setData(bundle); + mCallback.replyTo.send(message); + } catch (RemoteException e) { + Log.w(TAG, "Couldn't send message to workspace preview", e); + } + } + } + public void cleanUp() { if (mCallback != null) { try { @@ -170,7 +213,7 @@ public class WorkspaceSurfaceHolderCallback implements SurfaceHolder.Callback { + "crash"); return; } - Bundle request = SurfaceViewUtils.createSurfaceViewRequest(workspaceSurface); + Bundle request = SurfaceViewUtils.createSurfaceViewRequest(workspaceSurface, mExtras); if (mWallpaperColors != null) { request.putParcelable(KEY_WALLPAPER_COLORS, mWallpaperColors); } diff --git a/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerBinder.kt b/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerBinder.kt new file mode 100644 index 00000000..53c8375b --- /dev/null +++ b/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerBinder.kt @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.customization.ui.binder + +import android.view.View +import android.view.ViewGroup +import android.view.WindowInsets +import android.widget.FrameLayout +import androidx.annotation.IdRes +import androidx.core.view.children +import androidx.core.view.updateLayoutParams +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.android.wallpaper.R +import com.android.wallpaper.model.CustomizationSectionController +import com.android.wallpaper.model.WallpaperSectionController +import com.android.wallpaper.picker.SectionView +import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewSectionController +import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationPickerViewModel +import com.android.wallpaper.picker.undo.ui.binder.RevertToolbarButtonBinder +import kotlinx.coroutines.launch + +typealias SectionController = CustomizationSectionController<*> + +/** Binds view to view-model for the customization picker. */ +object CustomizationPickerBinder { + @JvmStatic + fun bind( + view: View, + @IdRes toolbarViewId: Int, + viewModel: CustomizationPickerViewModel, + lifecycleOwner: LifecycleOwner, + sectionControllerProvider: (isOnLockScreen: Boolean) -> List<SectionController>, + ) { + RevertToolbarButtonBinder.bind( + view = view.requireViewById(toolbarViewId), + viewModel = viewModel.undo, + lifecycleOwner = lifecycleOwner, + ) + + CustomizationPickerTabsBinder.bind( + view = view, + viewModel = viewModel, + lifecycleOwner = lifecycleOwner, + ) + + val sectionContainer = view.findViewById<ViewGroup>(R.id.section_container) + sectionContainer.setOnApplyWindowInsetsListener { v: View, windowInsets: WindowInsets -> + v.setPadding( + v.paddingLeft, + v.paddingTop, + v.paddingRight, + windowInsets.systemWindowInsetBottom + ) + windowInsets.consumeSystemWindowInsets() + } + sectionContainer.updateLayoutParams<FrameLayout.LayoutParams> { + // We don't want the top margin from the XML because our tabs have that as padding so + // they can be collapsed into the toolbar with spacing from the actual title text. + topMargin = 0 + } + + lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + viewModel.isOnLockScreen.collect { isOnLockScreen -> + // These are the available section controllers we should use now. + val newSectionControllers = + sectionControllerProvider.invoke(isOnLockScreen).filter { + it.isAvailable(view.context) + } + + check( + newSectionControllers[0] is WallpaperSectionController || + newSectionControllers[0] is ScreenPreviewSectionController + ) { + "The first section must always be the preview or the assumption below" + + " must be updated." + } + + val firstTime = sectionContainer.childCount == 0 + if (!firstTime) { + // Remove all views, except the very first one, which we assume is for + // the wallpaper preview section. + sectionContainer.removeViews(1, sectionContainer.childCount - 1) + + // The old controllers for the removed views should be released, except + // for the very first one, which is for the wallpaper preview section; + // that one we keep but just tell it that we switched screens. + sectionContainer.children + .mapNotNull { it.tag as? SectionController } + .forEachIndexed { index, oldController -> + if (index == 0) { + // We assume that index 0 is the wallpaper preview section. + // We keep it because it's an expensive section (as it needs + // to maintain a wallpaper connection that seems to be + // making assumptions about its SurfaceView always remaining + // attached to the window). + oldController.onScreenSwitched(isOnLockScreen) + } else { + // All other old controllers will be thrown out so let's + // release them. + oldController.release() + } + } + } + + // Let's add the new controllers and views. + newSectionControllers.forEachIndexed { index, controller -> + if (firstTime || index > 0) { + val addedView = controller.createView(view.context, isOnLockScreen) + addedView.tag = controller + sectionContainer.addView(addedView) + } + } + } + } + } + + // This happens when the lifecycle is stopped. + sectionContainer.children + .mapNotNull { it.tag as? CustomizationSectionController<out SectionView> } + .forEach { controller -> controller.release() } + sectionContainer.removeAllViews() + } + } +} diff --git a/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerTabsBinder.kt b/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerTabsBinder.kt new file mode 100644 index 00000000..5c06f038 --- /dev/null +++ b/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerTabsBinder.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.customization.ui.binder + +import android.view.View +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.android.wallpaper.R +import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationPickerViewModel +import com.android.wallpaper.widget.DuoTabs +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.launch + +/** Binds view to view-model for the customization picker tabs. */ +object CustomizationPickerTabsBinder { + @JvmStatic + fun bind( + view: View, + viewModel: CustomizationPickerViewModel, + lifecycleOwner: LifecycleOwner, + ) { + val tabs: DuoTabs = view.requireViewById(R.id.duo_tabs) + tabs.setTabText( + view.context.getString(R.string.lock_screen_tab), + view.context.getString(R.string.home_screen_tab), + ) + + lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + combine(viewModel.lockScreenTab, viewModel.homeScreenTab) { + lockScreenTabViewModel, + homeScreenTabViewModel -> + lockScreenTabViewModel to homeScreenTabViewModel + } + .collect { (lockScreenTabViewModel, homeScreenTabViewModel) -> + tabs.setOnTabSelectedListener(null) + tabs.selectTab( + when { + lockScreenTabViewModel.isSelected -> DuoTabs.TAB_PRIMARY + else -> DuoTabs.TAB_SECONDARY + }, + ) + tabs.setOnTabSelectedListener { tabId -> + when (tabId) { + DuoTabs.TAB_PRIMARY -> + lockScreenTabViewModel.onClicked?.invoke() + DuoTabs.TAB_SECONDARY -> + homeScreenTabViewModel.onClicked?.invoke() + } + } + } + } + } + } + } +} diff --git a/src/com/android/wallpaper/picker/customization/ui/binder/ScreenPreviewBinder.kt b/src/com/android/wallpaper/picker/customization/ui/binder/ScreenPreviewBinder.kt new file mode 100644 index 00000000..9df609eb --- /dev/null +++ b/src/com/android/wallpaper/picker/customization/ui/binder/ScreenPreviewBinder.kt @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.customization.ui.binder + +import android.app.Activity +import android.app.WallpaperColors +import android.content.Intent +import android.os.Bundle +import android.service.wallpaper.WallpaperService +import android.view.SurfaceView +import androidx.cardview.widget.CardView +import androidx.core.view.isVisible +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import com.android.wallpaper.R +import com.android.wallpaper.asset.Asset +import com.android.wallpaper.asset.BitmapCachingAsset +import com.android.wallpaper.asset.CurrentWallpaperAssetVN +import com.android.wallpaper.model.LiveWallpaperInfo +import com.android.wallpaper.model.WallpaperInfo +import com.android.wallpaper.picker.WorkspaceSurfaceHolderCallback +import com.android.wallpaper.picker.customization.ui.viewmodel.ScreenPreviewViewModel +import com.android.wallpaper.util.ResourceUtils +import com.android.wallpaper.util.WallpaperConnection +import com.android.wallpaper.util.WallpaperSurfaceCallback +import java.util.concurrent.CompletableFuture +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch + +/** + * Binds between view and view-model for rendering the preview of the home screen or the lock + * screen. + */ +@OptIn(ExperimentalCoroutinesApi::class) +object ScreenPreviewBinder { + interface Binding { + fun show() + fun hide() + fun sendMessage( + id: Int, + args: Bundle = Bundle.EMPTY, + ) + } + + @JvmStatic + fun bind( + activity: Activity, + previewView: CardView, + viewModel: ScreenPreviewViewModel, + lifecycleOwner: LifecycleOwner, + offsetToStart: Boolean, + ): Binding { + val workspaceSurface: SurfaceView = previewView.requireViewById(R.id.workspace_surface) + val wallpaperSurface: SurfaceView = previewView.requireViewById(R.id.wallpaper_surface) + + previewView.radius = + previewView.resources.getDimension(R.dimen.wallpaper_picker_entry_card_corner_radius) + + var previewSurfaceCallback: WorkspaceSurfaceHolderCallback? = null + var wallpaperSurfaceCallback: WallpaperSurfaceCallback? = null + var wallpaperConnection: WallpaperConnection? = null + var wallpaperInfo: WallpaperInfo? = null + + lifecycleOwner.lifecycle.addObserver( + LifecycleEventObserver { _, event -> + when (event) { + Lifecycle.Event.ON_CREATE -> { + previewSurfaceCallback = + WorkspaceSurfaceHolderCallback( + workspaceSurface, + viewModel.previewUtils, + viewModel.getInitialExtras(), + ) + workspaceSurface.holder.addCallback(previewSurfaceCallback) + workspaceSurface.setZOrderMediaOverlay(true) + + wallpaperSurfaceCallback = + WallpaperSurfaceCallback( + previewView.context, + previewView, + wallpaperSurface, + CompletableFuture.completedFuture( + WallpaperInfo.ColorInfo( + /* wallpaperColors= */ null, + ResourceUtils.getColorAttr( + previewView.context, + android.R.attr.colorSecondary, + ) + ) + ), + ) { + maybeLoadThumbnail( + activity = activity, + wallpaperInfo = wallpaperInfo, + surfaceCallback = wallpaperSurfaceCallback, + offsetToStart = offsetToStart, + ) + } + wallpaperSurface.holder.addCallback(wallpaperSurfaceCallback) + wallpaperSurface.setZOrderMediaOverlay(true) + } + Lifecycle.Event.ON_DESTROY -> { + workspaceSurface.holder.removeCallback(previewSurfaceCallback) + previewSurfaceCallback?.cleanUp() + wallpaperSurface.holder.removeCallback(wallpaperSurfaceCallback) + wallpaperSurfaceCallback?.cleanUp() + } + Lifecycle.Event.ON_RESUME -> { + lifecycleOwner.lifecycleScope.launch { + wallpaperInfo = viewModel.getWallpaperInfo() + (wallpaperInfo as? LiveWallpaperInfo)?.let { liveWallpaperInfo -> + if (WallpaperConnection.isPreviewAvailable()) { + wallpaperConnection = + WallpaperConnection( + Intent(WallpaperService.SERVICE_INTERFACE).apply { + setClassName( + liveWallpaperInfo.wallpaperComponent + .packageName, + liveWallpaperInfo.wallpaperComponent.serviceName + ) + }, + previewView.context, + object : + WallpaperConnection.WallpaperConnectionListener { + override fun onWallpaperColorsChanged( + colors: WallpaperColors?, + displayId: Int + ) { + viewModel.onWallpaperColorsChanged(colors) + } + }, + wallpaperSurface, + null, + ) + + wallpaperConnection?.connect() + wallpaperConnection?.setVisibility(true) + } + } + maybeLoadThumbnail( + activity = activity, + wallpaperInfo = wallpaperInfo, + surfaceCallback = wallpaperSurfaceCallback, + offsetToStart = offsetToStart, + ) + } + } + Lifecycle.Event.ON_PAUSE -> { + wallpaperConnection?.setVisibility(false) + } + Lifecycle.Event.ON_STOP -> { + wallpaperConnection?.disconnect() + } + else -> Unit + } + } + ) + + return object : Binding { + override fun show() { + previewView.isVisible = true + wallpaperSurface.isVisible = true + workspaceSurface.isVisible = true + } + + override fun hide() { + previewView.isVisible = false + wallpaperSurface.isVisible = false + workspaceSurface.isVisible = false + } + + override fun sendMessage(id: Int, args: Bundle) { + previewSurfaceCallback?.send(id, args) + } + } + } + + private fun maybeLoadThumbnail( + activity: Activity, + wallpaperInfo: WallpaperInfo?, + surfaceCallback: WallpaperSurfaceCallback?, + offsetToStart: Boolean, + ) { + if (wallpaperInfo == null || surfaceCallback == null) { + return + } + + val imageView = surfaceCallback.homeImageWallpaper + val thumbAsset: Asset = wallpaperInfo.getThumbAsset(activity) + if (imageView != null && imageView.drawable == null) { + // Respect offsetToStart only for CurrentWallpaperAssetVN otherwise true. + BitmapCachingAsset(activity, thumbAsset) + .loadPreviewImage( + activity, + imageView, + ResourceUtils.getColorAttr(activity, android.R.attr.colorSecondary), + /* offsetToStart= */ thumbAsset !is CurrentWallpaperAssetVN || offsetToStart + ) + } + } +} diff --git a/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewSectionController.kt b/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewSectionController.kt new file mode 100644 index 00000000..6cb7d068 --- /dev/null +++ b/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewSectionController.kt @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.customization.ui.section + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.WallpaperColors +import android.content.Context +import android.view.LayoutInflater +import androidx.cardview.widget.CardView +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.lifecycleScope +import com.android.wallpaper.R +import com.android.wallpaper.model.CustomizationSectionController +import com.android.wallpaper.model.WallpaperColorsViewModel +import com.android.wallpaper.model.WallpaperInfo +import com.android.wallpaper.module.CurrentWallpaperInfoFactory +import com.android.wallpaper.module.CustomizationSections +import com.android.wallpaper.picker.customization.ui.binder.ScreenPreviewBinder +import com.android.wallpaper.picker.customization.ui.viewmodel.ScreenPreviewViewModel +import com.android.wallpaper.util.DisplayUtils +import com.android.wallpaper.util.PreviewUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext + +/** Controls the screen preview section. */ +@OptIn(ExperimentalCoroutinesApi::class) +class ScreenPreviewSectionController( + private val activity: Activity, + private val lifecycleOwner: LifecycleOwner, + private val initialScreen: CustomizationSections.Screen, + private val wallpaperInfoFactory: CurrentWallpaperInfoFactory, + private val colorViewModel: WallpaperColorsViewModel, + private val displayUtils: DisplayUtils, +) : CustomizationSectionController<ScreenPreviewView> { + + private lateinit var lockScreenBinding: ScreenPreviewBinder.Binding + private lateinit var homeScreenBinding: ScreenPreviewBinder.Binding + + override fun isAvailable(context: Context?): Boolean { + // Assumption is that, if this section controller is included, we are using the revamped UI + // so it should always be shown. + return true + } + + @SuppressLint("InflateParams") + override fun createView(context: Context): ScreenPreviewView { + val view = + LayoutInflater.from(context) + .inflate( + R.layout.screen_preview_section, + /* parent= */ null, + ) as ScreenPreviewView + val lockScreenView: CardView = view.requireViewById(R.id.lock_preview) + val homeScreenView: CardView = view.requireViewById(R.id.home_preview) + + lockScreenBinding = + ScreenPreviewBinder.bind( + activity = activity, + previewView = lockScreenView, + viewModel = + ScreenPreviewViewModel( + previewUtils = + PreviewUtils( + context = context, + authority = + context.getString( + R.string.lock_screen_preview_provider_authority, + ), + ), + wallpaperInfoProvider = { + suspendCancellableCoroutine { continuation -> + wallpaperInfoFactory.createCurrentWallpaperInfos( + { homeWallpaper, lockWallpaper, _ -> + val wallpaper = lockWallpaper ?: homeWallpaper + loadInitialColors( + context = context, + wallpaper = wallpaper, + liveData = colorViewModel.lockWallpaperColors, + ) + continuation.resume(wallpaper, null) + }, + /* forceRefresh= */ true, + ) + } + }, + onWallpaperColorChanged = { colors -> + colorViewModel.lockWallpaperColors.value = colors + }, + ), + lifecycleOwner = lifecycleOwner, + offsetToStart = displayUtils.isOnWallpaperDisplay(activity), + ) + homeScreenBinding = + ScreenPreviewBinder.bind( + activity = activity, + previewView = homeScreenView, + viewModel = + ScreenPreviewViewModel( + previewUtils = + PreviewUtils( + context = context, + authorityMetadataKey = + context.getString( + R.string.grid_control_metadata_name, + ), + ), + wallpaperInfoProvider = { + suspendCancellableCoroutine { continuation -> + wallpaperInfoFactory.createCurrentWallpaperInfos( + { homeWallpaper, lockWallpaper, _ -> + val wallpaper = homeWallpaper ?: lockWallpaper + loadInitialColors( + context = context, + wallpaper = wallpaper, + liveData = colorViewModel.homeWallpaperColors, + ) + continuation.resume(wallpaper, null) + }, + /* forceRefresh= */ true, + ) + } + }, + onWallpaperColorChanged = { colors -> + colorViewModel.lockWallpaperColors.value = colors + }, + ), + lifecycleOwner = lifecycleOwner, + offsetToStart = displayUtils.isOnWallpaperDisplay(activity), + ) + + onScreenSwitched(isOnLockScreen = initialScreen == CustomizationSections.Screen.LOCK_SCREEN) + + return view + } + + override fun onScreenSwitched(isOnLockScreen: Boolean) { + if (isOnLockScreen) { + lockScreenBinding.show() + homeScreenBinding.hide() + } else { + lockScreenBinding.hide() + homeScreenBinding.show() + } + } + + private fun loadInitialColors( + context: Context, + wallpaper: WallpaperInfo?, + liveData: MutableLiveData<WallpaperColors>, + ) { + lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { + val colors = wallpaper?.computeColorInfo(context)?.get()?.wallpaperColors + withContext(Dispatchers.Main) { liveData.value = colors } + } + } +} diff --git a/src/com/android/wallpaper/config/BaseFlags.java b/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewView.kt index 156fe358..ee55de41 100644 --- a/src/com/android/wallpaper/config/BaseFlags.java +++ b/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2022 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. @@ -12,14 +12,20 @@ * 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.wallpaper.config; -abstract class BaseFlags { - public static boolean skipDailyWallpaperButtonEnabled = true; - public static boolean desktopUiEnabled = false; - public static boolean dynamicStartRotationTileEnabled = true; - public static boolean stagingBackdropContentEnabled = false; - public static boolean performanceMonitoringEnabled = true; - public static boolean enableWallpaperEffect = false; -} +package com.android.wallpaper.picker.customization.ui.section + +import android.content.Context +import android.util.AttributeSet +import com.android.wallpaper.picker.SectionView + +class ScreenPreviewView( + context: Context, + attrs: AttributeSet?, +) : + SectionView( + context, + attrs, + ) diff --git a/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerTabViewModel.kt b/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerTabViewModel.kt new file mode 100644 index 00000000..67263f2a --- /dev/null +++ b/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerTabViewModel.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.customization.ui.viewmodel + +/** Models UI state for a single tab. */ +data class CustomizationPickerTabViewModel( + val isSelected: Boolean, + val onClicked: (() -> Unit)?, +) diff --git a/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModel.kt b/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModel.kt new file mode 100644 index 00000000..7fd622cb --- /dev/null +++ b/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModel.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.customization.ui.viewmodel + +import android.os.Bundle +import androidx.annotation.VisibleForTesting +import androidx.lifecycle.AbstractSavedStateViewModelFactory +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.savedstate.SavedStateRegistryOwner +import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor +import com.android.wallpaper.picker.undo.ui.viewmodel.UndoViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.map + +/** Models UI state for the customization picker. */ +class CustomizationPickerViewModel +@VisibleForTesting +constructor( + undoInteractor: UndoInteractor, + private val savedStateHandle: SavedStateHandle, +) : ViewModel() { + + val undo: UndoViewModel = + UndoViewModel( + interactor = undoInteractor, + ) + + private val _isOnLockScreen = MutableStateFlow(true) + /** Whether we are on the lock screen. If `false`, we are on the home screen. */ + val isOnLockScreen: Flow<Boolean> = _isOnLockScreen.asStateFlow() + + /** A view-model for the "lock screen" tab. */ + val lockScreenTab: Flow<CustomizationPickerTabViewModel> = + isOnLockScreen.map { onLockScreen -> + CustomizationPickerTabViewModel( + isSelected = onLockScreen, + onClicked = + if (!onLockScreen) { + { + _isOnLockScreen.value = true + savedStateHandle[KEY_SAVED_STATE_IS_ON_LOCK_SCREEN] = true + } + } else { + null + } + ) + } + /** A view-model for the "home screen" tab. */ + val homeScreenTab: Flow<CustomizationPickerTabViewModel> = + isOnLockScreen.map { onLockScreen -> + CustomizationPickerTabViewModel( + isSelected = !onLockScreen, + onClicked = + if (onLockScreen) { + { + _isOnLockScreen.value = false + savedStateHandle[KEY_SAVED_STATE_IS_ON_LOCK_SCREEN] = false + } + } else { + null + } + ) + } + + init { + _isOnLockScreen.value = savedStateHandle[KEY_SAVED_STATE_IS_ON_LOCK_SCREEN] ?: true + } + + companion object { + @JvmStatic + fun newFactory( + owner: SavedStateRegistryOwner, + defaultArgs: Bundle? = null, + undoInteractor: UndoInteractor, + ): AbstractSavedStateViewModelFactory = + object : AbstractSavedStateViewModelFactory(owner, defaultArgs) { + @Suppress("UNCHECKED_CAST") + override fun <T : ViewModel> create( + key: String, + modelClass: Class<T>, + handle: SavedStateHandle, + ): T { + return CustomizationPickerViewModel( + undoInteractor = undoInteractor, + savedStateHandle = handle, + ) + as T + } + } + + private const val KEY_SAVED_STATE_IS_ON_LOCK_SCREEN = "is_on_lock_screen" + } +} diff --git a/src/com/android/wallpaper/picker/customization/ui/viewmodel/ScreenPreviewViewModel.kt b/src/com/android/wallpaper/picker/customization/ui/viewmodel/ScreenPreviewViewModel.kt new file mode 100644 index 00000000..79e18a38 --- /dev/null +++ b/src/com/android/wallpaper/picker/customization/ui/viewmodel/ScreenPreviewViewModel.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.customization.ui.viewmodel + +import android.app.WallpaperColors +import android.os.Bundle +import com.android.wallpaper.model.WallpaperInfo +import com.android.wallpaper.util.PreviewUtils + +/** Models the UI state for a preview of the home screen or lock screen. */ +class ScreenPreviewViewModel( + val previewUtils: PreviewUtils, + private val initialExtrasProvider: () -> Bundle? = { null }, + private val wallpaperInfoProvider: suspend () -> WallpaperInfo?, + private val onWallpaperColorChanged: (WallpaperColors?) -> Unit = {}, +) { + fun getInitialExtras(): Bundle? { + return initialExtrasProvider.invoke() + } + + suspend fun getWallpaperInfo(): WallpaperInfo? { + return wallpaperInfoProvider.invoke() + } + + fun onWallpaperColorsChanged(colors: WallpaperColors?) { + onWallpaperColorChanged.invoke(colors) + } +} diff --git a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment2.kt b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment2.kt new file mode 100644 index 00000000..4a21dbc2 --- /dev/null +++ b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment2.kt @@ -0,0 +1,787 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.individual + +import android.app.Activity +import android.app.ProgressDialog +import android.app.WallpaperManager +import android.content.DialogInterface +import android.content.res.Configuration +import android.content.res.Resources +import android.content.res.Resources.ID_NULL +import android.graphics.Point +import android.os.Build +import android.os.Build.VERSION_CODES +import android.os.Bundle +import android.service.wallpaper.WallpaperService +import android.text.TextUtils +import android.util.ArraySet +import android.util.Log +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.view.WindowInsets +import android.widget.ImageView +import android.widget.RelativeLayout +import android.widget.TextView +import android.widget.Toast +import androidx.annotation.DrawableRes +import androidx.cardview.widget.CardView +import androidx.core.widget.ContentLoadingProgressBar +import androidx.fragment.app.DialogFragment +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.android.wallpaper.R +import com.android.wallpaper.model.Category +import com.android.wallpaper.model.CategoryProvider +import com.android.wallpaper.model.CategoryReceiver +import com.android.wallpaper.model.WallpaperCategory +import com.android.wallpaper.model.WallpaperInfo +import com.android.wallpaper.model.WallpaperRotationInitializer +import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference +import com.android.wallpaper.module.InjectorProvider +import com.android.wallpaper.module.PackageStatusNotifier +import com.android.wallpaper.picker.AppbarFragment +import com.android.wallpaper.picker.FragmentTransactionChecker +import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider +import com.android.wallpaper.picker.RotationStarter +import com.android.wallpaper.picker.StartRotationDialogFragment +import com.android.wallpaper.picker.StartRotationErrorDialogFragment +import com.android.wallpaper.util.ActivityUtils +import com.android.wallpaper.util.DiskBasedLogger +import com.android.wallpaper.util.LaunchUtils +import com.android.wallpaper.util.SizeCalculator +import com.android.wallpaper.widget.GridPaddingDecoration +import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate +import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost +import com.bumptech.glide.Glide +import com.bumptech.glide.MemoryCategory +import java.util.Date + +/** Displays the Main UI for picking an individual wallpaper image. */ +class IndividualPickerFragment2 : + AppbarFragment(), + RotationStarter, + StartRotationErrorDialogFragment.Listener, + StartRotationDialogFragment.Listener { + + companion object { + private const val TAG = "IndividualPickerFrag2" + + /** + * Position of a special tile that doesn't belong to an individual wallpaper of the + * category, such as "my photos" or "daily rotation". + */ + private const val SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0 + + private const val ARG_CATEGORY_COLLECTION_ID = "category_collection_id" + + private const val UNUSED_REQUEST_CODE = 1 + private const val TAG_START_ROTATION_DIALOG = "start_rotation_dialog" + private const val TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog" + private const val PROGRESS_DIALOG_INDETERMINATE = true + private const val KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE" + private const val MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT = 8 + private val PROGRESS_DIALOG_NO_TITLE = null + + fun newInstance(collectionId: String?): IndividualPickerFragment2 { + val args = Bundle() + args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId) + val fragment = IndividualPickerFragment2() + fragment.arguments = args + return fragment + } + } + + private lateinit var imageGrid: RecyclerView + private var adapter: IndividualAdapter? = null + private lateinit var category: WallpaperCategory + private var wallpaperRotationInitializer: WallpaperRotationInitializer? = null + private lateinit var items: MutableList<PickerItem> + private var packageStatusNotifier: PackageStatusNotifier? = null + + private var isWallpapersReceived = false + private var appStatusListener: PackageStatusNotifier.Listener? = null + + private var progressDialog: ProgressDialog? = null + private var testingMode = false + private var loading: ContentLoadingProgressBar? = null + private lateinit var categoryProvider: CategoryProvider + + /** + * Staged error dialog fragments that were unable to be shown when the activity didn't allow + * committing fragment transactions. + */ + private var stagedStartRotationErrorDialogFragment: StartRotationErrorDialogFragment? = null + + private var wallpaperManager: WallpaperManager? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val injector = InjectorProvider.getInjector() + val appContext = requireContext().applicationContext + wallpaperManager = WallpaperManager.getInstance(appContext) + packageStatusNotifier = injector.getPackageStatusNotifier(appContext) + items = ArrayList() + + // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded + if ( + savedInstanceState != null && + (savedInstanceState.getInt(KEY_NIGHT_MODE) != + resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) + ) { + Glide.get(requireContext()).clearMemory() + } + categoryProvider = injector.getCategoryProvider(appContext) + categoryProvider.fetchCategories( + object : CategoryReceiver { + override fun onCategoryReceived(category: Category) { + // Do nothing. + } + + override fun doneFetchingCategories() { + val fetchedCategory = + categoryProvider.getCategory( + arguments?.getString(ARG_CATEGORY_COLLECTION_ID) + ) + if (fetchedCategory != null && fetchedCategory !is WallpaperCategory) { + return + } + + if (fetchedCategory == null) { + DiskBasedLogger.e(TAG, "Failed to find the category.", context) + + // The absence of this category in the CategoryProvider indicates a broken + // state, see b/38030129. Hence, finish the activity and return. + getIndividualPickerFragmentHost().moveToPreviousFragment() + Toast.makeText( + context, + R.string.collection_not_exist_msg, + Toast.LENGTH_SHORT + ) + .show() + return + } + category = fetchedCategory as WallpaperCategory + onCategoryLoaded() + } + }, + false + ) + } + + fun onCategoryLoaded() { + val fragmentHost = getIndividualPickerFragmentHost() + if (fragmentHost.isHostToolbarShown) { + fragmentHost.setToolbarTitle(category.title) + } else { + setTitle(category.title) + } + wallpaperRotationInitializer = category.wallpaperRotationInitializer + if (mToolbar != null && isRotationEnabled()) { + setUpToolbarMenu(R.menu.individual_picker_menu) + } + fetchWallpapers(false) + if (category.supportsThirdParty()) { + appStatusListener = + PackageStatusNotifier.Listener { pkgName: String?, status: Int -> + if ( + status != PackageStatusNotifier.PackageStatus.REMOVED || + category.containsThirdParty(pkgName) + ) { + fetchWallpapers(true) + } + } + packageStatusNotifier?.addListener( + appStatusListener, + WallpaperService.SERVICE_INTERFACE + ) + } + } + + private fun fetchWallpapers(forceReload: Boolean) { + items.clear() + isWallpapersReceived = false + updateLoading() + val context = requireContext() + category.fetchWallpapers( + context.applicationContext, + { fetchedWallpapers -> + isWallpapersReceived = true + updateLoading() + val byGroup = fetchedWallpapers.groupBy { it.getGroupName(context) } + val appliedWallpaperIds = getAppliedWallpaperIds() + byGroup.forEach { (groupName, wallpapers) -> + if (!TextUtils.isEmpty(groupName)) { + items.add( + if (items.isEmpty()) { + PickerItem.FirstHeaderItem(groupName) + } else { + PickerItem.HeaderItem(groupName) + } + ) + } + items.addAll( + wallpapers.map { + PickerItem.WallpaperItem( + it, + appliedWallpaperIds.contains(it.wallpaperId) + ) + } + ) + } + maybeSetUpImageGrid() + + // Wallpapers may load after the adapter is initialized, in which case we have + // to explicitly notify that the data set has changed. + adapter?.notifyDataSetChanged() + if (fetchedWallpapers.isEmpty()) { + // If there are no more wallpapers and we're on phone, just finish the + // Activity. + val activity: Activity? = activity + activity?.finish() + } + }, + forceReload + ) + } + + private fun updateLoading() { + if (isWallpapersReceived) { + loading?.hide() + } else { + loading?.show() + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putInt( + KEY_NIGHT_MODE, + resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + ) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val view: View = inflater.inflate(R.layout.fragment_individual_picker, container, false) + if (getIndividualPickerFragmentHost().isHostToolbarShown) { + view.findViewById<View>(R.id.header_bar).visibility = View.GONE + setUpArrowEnabled(/* upArrow= */ true) + if (isRotationEnabled()) { + getIndividualPickerFragmentHost().setToolbarMenu(R.menu.individual_picker_menu) + } + } else { + setUpToolbar(view) + if (isRotationEnabled()) { + setUpToolbarMenu(R.menu.individual_picker_menu) + } + setTitle(category.title) + } + imageGrid = view.findViewById<View>(R.id.wallpaper_grid) as RecyclerView + loading = view.findViewById(R.id.loading_indicator) + updateLoading() + maybeSetUpImageGrid() + // For nav bar edge-to-edge effect. + imageGrid.setOnApplyWindowInsetsListener { v: View, windowInsets: WindowInsets -> + v.setPadding( + v.paddingLeft, + v.paddingTop, + v.paddingRight, + windowInsets.systemWindowInsetBottom + ) + windowInsets.consumeSystemWindowInsets() + } + return view + } + + private fun getIndividualPickerFragmentHost(): + IndividualPickerFragment.IndividualPickerFragmentHost { + val parentFragment = parentFragment + return if (parentFragment != null) { + parentFragment as IndividualPickerFragment.IndividualPickerFragmentHost + } else { + activity as IndividualPickerFragment.IndividualPickerFragmentHost + } + } + + private fun maybeSetUpImageGrid() { + // Skip if mImageGrid been initialized yet + if (!this::imageGrid.isInitialized) { + return + } + // Skip if category hasn't loaded yet + if (!this::category.isInitialized) { + return + } + if (context == null) { + return + } + + // Wallpaper count could change, so we may need to change the layout(2 or 3 columns layout) + val gridLayoutManager = imageGrid.layoutManager as GridLayoutManager? + val needUpdateLayout = gridLayoutManager?.spanCount != getNumColumns() + + // Skip if the adapter was already created and don't need to change the layout + if (adapter != null && !needUpdateLayout) { + return + } + + // Clear the old decoration + val decorationCount = imageGrid.itemDecorationCount + for (i in 0 until decorationCount) { + imageGrid.removeItemDecorationAt(i) + } + imageGrid.addItemDecoration( + GridPaddingDecoration(getGridItemPaddingHorizontal(), getGridItemPaddingBottom()) + ) + val edgePadding = getEdgePadding() + imageGrid.setPadding( + edgePadding, + imageGrid.paddingTop, + edgePadding, + imageGrid.paddingBottom + ) + val tileSizePx = + if (isFewerColumnLayout()) { + SizeCalculator.getFeaturedIndividualTileSize(activity!!) + } else { + SizeCalculator.getIndividualTileSize(activity!!) + } + setUpImageGrid(tileSizePx) + imageGrid.setAccessibilityDelegateCompat( + WallpaperPickerRecyclerViewAccessibilityDelegate( + imageGrid, + parentFragment as BottomSheetHost?, + getNumColumns() + ) + ) + } + + private fun isFewerColumnLayout(): Boolean = + items.count { it is PickerItem.WallpaperItem } <= MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT + + private fun getGridItemPaddingHorizontal(): Int { + return if (isFewerColumnLayout()) { + resources.getDimensionPixelSize( + R.dimen.grid_item_featured_individual_padding_horizontal + ) + } else { + resources.getDimensionPixelSize(R.dimen.grid_item_individual_padding_horizontal) + } + } + + private fun getGridItemPaddingBottom(): Int { + return if (isFewerColumnLayout()) { + resources.getDimensionPixelSize(R.dimen.grid_item_featured_individual_padding_bottom) + } else { + resources.getDimensionPixelSize(R.dimen.grid_item_individual_padding_bottom) + } + } + + private fun getEdgePadding(): Int { + return if (isFewerColumnLayout()) { + resources.getDimensionPixelSize(R.dimen.featured_wallpaper_grid_edge_space) + } else { + resources.getDimensionPixelSize(R.dimen.wallpaper_grid_edge_space) + } + } + + /** + * Create the adapter and assign it to mImageGrid. Both mImageGrid and mCategory are guaranteed + * to not be null when this method is called. + */ + private fun setUpImageGrid(tileSizePx: Point) { + adapter = + IndividualAdapter( + items, + category, + requireActivity(), + tileSizePx, + isRotationEnabled(), + isFewerColumnLayout() + ) + imageGrid.adapter = adapter + val gridLayoutManager = GridLayoutManager(activity, getNumColumns()) + gridLayoutManager.spanSizeLookup = + object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return when (items[position]) { + is PickerItem.FirstHeaderItem, + is PickerItem.HeaderItem -> { + gridLayoutManager.spanCount + } + else -> 1 + } + } + } + imageGrid.layoutManager = gridLayoutManager + } + + override fun onResume() { + super.onResume() + val preferences = InjectorProvider.getInjector().getPreferences(requireActivity()) + preferences.lastAppActiveTimestamp = Date().time + + // Reset Glide memory settings to a "normal" level of usage since it may have been lowered + // in PreviewFragment. + Glide.get(requireContext()).setMemoryCategory(MemoryCategory.NORMAL) + + // Show the staged 'start rotation' error dialog fragment if there is one that was unable to + // be shown earlier when this fragment's hosting activity didn't allow committing fragment + // transactions. + if (isAdded) { + stagedStartRotationErrorDialogFragment?.show( + parentFragmentManager, + TAG_START_ROTATION_ERROR_DIALOG + ) + } + stagedStartRotationErrorDialogFragment = null + } + + override fun onDestroyView() { + super.onDestroyView() + getIndividualPickerFragmentHost().removeToolbarMenu() + } + + override fun onDestroy() { + super.onDestroy() + progressDialog?.dismiss() + if (appStatusListener != null) { + packageStatusNotifier?.removeListener(appStatusListener) + } + } + + override fun onStartRotationDialogDismiss(dialog: DialogInterface) { + // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change. + // This is to handle config change with StartRotationDialog popup, the StartRotationDialog + // still holds a reference to the destroyed Fragment and is calling + // onStartRotationDialogDismissed on that destroyed Fragment. + } + + override fun retryStartRotation(@NetworkPreference networkPreference: Int) { + startRotation(networkPreference) + } + + /** + * Enable a test mode of operation -- in which certain UI features are disabled to allow for UI + * tests to run correctly. Works around issue in ProgressDialog currently where the dialog + * constantly keeps the UI thread alive and blocks a test forever. + * + * @param testingMode + */ + fun setTestingMode(testingMode: Boolean) { + this.testingMode = testingMode + } + + override fun startRotation(@NetworkPreference networkPreference: Int) { + if (!isRotationEnabled()) { + Log.e(TAG, "Rotation is not enabled for this category " + category.title) + return + } + + // ProgressDialog endlessly updates the UI thread, keeping it from going idle which + // therefore causes Espresso to hang once the dialog is shown. + if (!testingMode) { + val themeResId = + if (Build.VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) { + R.style.ProgressDialogThemePreL + } else { + R.style.LightDialogTheme + } + val progressDialog = ProgressDialog(activity, themeResId) + progressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE) + progressDialog.setMessage(resources.getString(R.string.start_rotation_progress_message)) + progressDialog.isIndeterminate = PROGRESS_DIALOG_INDETERMINATE + progressDialog.show() + this.progressDialog = progressDialog + } + val appContext = activity!!.applicationContext + wallpaperRotationInitializer?.setFirstWallpaperInRotation( + appContext, + networkPreference, + object : WallpaperRotationInitializer.Listener { + override fun onFirstWallpaperInRotationSet() { + progressDialog?.dismiss() + + // The fragment may be detached from its containing activity if the user exits + // the app before the first wallpaper image in rotation finishes downloading. + val activity: Activity? = activity + if (wallpaperRotationInitializer!!.startRotation(appContext)) { + if (activity != null) { + try { + Toast.makeText( + activity, + R.string.wallpaper_set_successfully_message, + Toast.LENGTH_SHORT + ) + .show() + } catch (e: Resources.NotFoundException) { + Log.e(TAG, "Could not show toast $e") + } + activity.setResult(Activity.RESULT_OK) + activity.finish() + if (!ActivityUtils.isSUWMode(appContext)) { + // Go back to launcher home. + LaunchUtils.launchHome(appContext) + } + } + } else { // Failed to start rotation. + showStartRotationErrorDialog(networkPreference) + } + } + + override fun onError() { + progressDialog?.dismiss() + showStartRotationErrorDialog(networkPreference) + } + } + ) + } + + private fun showStartRotationErrorDialog(@NetworkPreference networkPreference: Int) { + val activity = activity as FragmentTransactionChecker? + if (activity != null) { + val startRotationErrorDialogFragment = + StartRotationErrorDialogFragment.newInstance(networkPreference) + startRotationErrorDialogFragment.setTargetFragment( + this@IndividualPickerFragment2, + UNUSED_REQUEST_CODE + ) + if (activity.isSafeToCommitFragmentTransaction) { + startRotationErrorDialogFragment.show( + parentFragmentManager, + TAG_START_ROTATION_ERROR_DIALOG + ) + } else { + stagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment + } + } + } + + private fun getNumColumns(): Int { + val activity = this.activity ?: return 1 + return if (isFewerColumnLayout()) { + SizeCalculator.getNumFeaturedIndividualColumns(activity) + } else { + SizeCalculator.getNumIndividualColumns(activity) + } + } + + /** Returns whether rotation is enabled for this category. */ + private fun isRotationEnabled() = wallpaperRotationInitializer != null + + override fun onMenuItemClick(item: MenuItem): Boolean { + if (item.itemId == R.id.daily_rotation) { + showRotationDialog() + return true + } + return super.onMenuItemClick(item) + } + + /** Popups a daily rotation dialog for the uses to confirm. */ + private fun showRotationDialog() { + val startRotationDialogFragment: DialogFragment = StartRotationDialogFragment() + startRotationDialogFragment.setTargetFragment( + this@IndividualPickerFragment2, + UNUSED_REQUEST_CODE + ) + startRotationDialogFragment.show(parentFragmentManager, TAG_START_ROTATION_DIALOG) + } + + private fun getAppliedWallpaperIds(): Set<String> { + val prefs = InjectorProvider.getInjector().getPreferences(requireContext()) + val wallpaperInfo = wallpaperManager?.wallpaperInfo + val appliedWallpaperIds: MutableSet<String> = ArraySet() + val homeWallpaperId = + if (wallpaperInfo != null) { + wallpaperInfo.serviceName + } else { + prefs.homeWallpaperRemoteId + } + if (!TextUtils.isEmpty(homeWallpaperId)) { + appliedWallpaperIds.add(homeWallpaperId) + } + val isLockWallpaperApplied = + wallpaperManager!!.getWallpaperId(WallpaperManager.FLAG_LOCK) >= 0 + val lockWallpaperId = prefs.lockWallpaperRemoteId + if (isLockWallpaperApplied && !TextUtils.isEmpty(lockWallpaperId)) { + appliedWallpaperIds.add(lockWallpaperId) + } + return appliedWallpaperIds + } + + sealed class PickerItem(val title: CharSequence = "") { + class WallpaperItem(val wallpaperInfo: WallpaperInfo, val isApplied: Boolean) : + PickerItem() + + class HeaderItem(title: CharSequence) : PickerItem(title) + + class FirstHeaderItem(title: CharSequence) : PickerItem(title) + } + + /** RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView. */ + class IndividualAdapter( + private val items: List<PickerItem>, + private val category: Category, + private val activity: Activity, + private val tileSizePx: Point, + private val isRotationEnabled: Boolean, + private val isFewerColumnLayout: Boolean + ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { + companion object { + const val ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2 + const val ITEM_VIEW_TYPE_MY_PHOTOS = 3 + const val ITEM_VIEW_TYPE_HEADER = 4 + const val ITEM_VIEW_TYPE_HEADER_TOP = 5 + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER -> createIndividualHolder(parent) + ITEM_VIEW_TYPE_MY_PHOTOS -> createMyPhotosHolder(parent) + ITEM_VIEW_TYPE_HEADER -> createTitleHolder(parent, /* removePaddingTop= */ false) + ITEM_VIEW_TYPE_HEADER_TOP -> createTitleHolder(parent, /* removePaddingTop= */ true) + else -> { + throw RuntimeException("Unsupported viewType $viewType in IndividualAdapter") + } + } + } + + override fun getItemViewType(position: Int): Int { + // A category cannot have both a "start rotation" tile and a "my photos" tile. + return if ( + category.supportsCustomPhotos() && + !isRotationEnabled && + position == SPECIAL_FIXED_TILE_ADAPTER_POSITION + ) { + ITEM_VIEW_TYPE_MY_PHOTOS + } else { + when (items[position]) { + is PickerItem.WallpaperItem -> ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER + is PickerItem.HeaderItem -> ITEM_VIEW_TYPE_HEADER + is PickerItem.FirstHeaderItem -> ITEM_VIEW_TYPE_HEADER_TOP + } + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (val viewType = getItemViewType(position)) { + ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER -> bindIndividualHolder(holder, position) + ITEM_VIEW_TYPE_MY_PHOTOS -> (holder as MyPhotosViewHolder?)!!.bind() + ITEM_VIEW_TYPE_HEADER, + ITEM_VIEW_TYPE_HEADER_TOP -> { + val textView = holder.itemView as TextView + val item = items[position] + textView.text = item.title + textView.contentDescription = item.title + } + else -> Log.e(TAG, "Unexpected viewType $viewType in IndividualAdapter") + } + } + + override fun getItemCount(): Int { + return if (category.supportsCustomPhotos()) { + items.size + 1 + } else { + items.size + } + } + + private fun createIndividualHolder(parent: ViewGroup): RecyclerView.ViewHolder { + val layoutInflater = LayoutInflater.from(activity) + val view: View = layoutInflater.inflate(R.layout.grid_item_image, parent, false) + return PreviewIndividualHolder(activity, tileSizePx.y, view) + } + + private fun createMyPhotosHolder(parent: ViewGroup): RecyclerView.ViewHolder { + val layoutInflater = LayoutInflater.from(activity) + val view: View = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false) + return MyPhotosViewHolder( + activity, + (activity as MyPhotosStarterProvider).myPhotosStarter, + tileSizePx.y, + view + ) + } + + private fun createTitleHolder( + parent: ViewGroup, + removePaddingTop: Boolean + ): RecyclerView.ViewHolder { + val layoutInflater = LayoutInflater.from(activity) + val view = + layoutInflater.inflate(R.layout.grid_item_header, parent, /* attachToRoot= */ false) + if (removePaddingTop) { + view.setPadding( + view.paddingStart, + /* top= */ 0, + view.paddingEnd, + view.paddingBottom + ) + } + return object : RecyclerView.ViewHolder(view) {} + } + + private fun bindIndividualHolder(holder: RecyclerView.ViewHolder, position: Int) { + val wallpaperIndex = if (category.supportsCustomPhotos()) position - 1 else position + val item = items[wallpaperIndex] as PickerItem.WallpaperItem + val wallpaper = item.wallpaperInfo + wallpaper.computeColorInfo(holder.itemView.context) + (holder as IndividualHolder).bindWallpaper(wallpaper) + val container = holder.itemView.findViewById<CardView>(R.id.wallpaper_container) + val radiusId: Int = + if (isFewerColumnLayout) { + R.dimen.grid_item_all_radius + } else { + R.dimen.grid_item_all_radius_small + } + container.radius = activity.resources.getDimension(radiusId) + showBadge(holder, R.drawable.wallpaper_check_circle_24dp, item.isApplied) + if (!item.isApplied) { + showBadge(holder, wallpaper.badgeDrawableRes, wallpaper.badgeDrawableRes != ID_NULL) + } + } + + private fun showBadge( + holder: RecyclerView.ViewHolder, + @DrawableRes icon: Int, + show: Boolean + ) { + val badge = holder.itemView.findViewById<ImageView>(R.id.indicator_icon) + if (show) { + val margin = + if (isFewerColumnLayout) { + activity.resources.getDimension(R.dimen.grid_item_badge_margin) + } else { + activity.resources.getDimension(R.dimen.grid_item_badge_margin_small) + } + .toInt() + val layoutParams = badge.layoutParams as RelativeLayout.LayoutParams + layoutParams.setMargins(margin, margin, margin, margin) + badge.layoutParams = layoutParams + badge.setBackgroundResource(icon) + badge.visibility = View.VISIBLE + } else { + badge.visibility = View.GONE + } + } + } +} diff --git a/src/com/android/wallpaper/picker/undo/data/repository/UndoRepository.kt b/src/com/android/wallpaper/picker/undo/data/repository/UndoRepository.kt new file mode 100644 index 00000000..be9c035b --- /dev/null +++ b/src/com/android/wallpaper/picker/undo/data/repository/UndoRepository.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.undo.data.repository + +import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +/** Encapsulates application state for the undo system. */ +class UndoRepository { + private val snapshotByOwnerId = mutableMapOf<Int, RestorableSnapshot>() + private val dirtyOwnerIds = MutableStateFlow(emptySet<Int>()) + + /** Whether any area is "dirty" right now (meaning, it could be undone). */ + val isAnythingDirty: Flow<Boolean> = + dirtyOwnerIds.map { it.isNotEmpty() }.distinctUntilChanged() + + /** Associates the given snapshot with the area with the given owner ID. */ + fun putSnapshot(ownerId: Int, snapshot: RestorableSnapshot) { + snapshotByOwnerId[ownerId] = snapshot + } + + /** Returns the latest snapshot for the area with the given owner ID. */ + fun getSnapshot(ownerId: Int): RestorableSnapshot? { + return snapshotByOwnerId[ownerId] + } + + /** + * Marks the area with the owner of the given ID as dirty or not dirty. + * + * A "dirty" area is one that contains changes that can be undone. An area that is not "dirty" + * does not currently have pending changes that can be undone. + */ + fun putDirty(ownerId: Int, isDirty: Boolean) { + if (isDirty) { + dirtyOwnerIds.value = dirtyOwnerIds.value + setOf(ownerId) + } else { + dirtyOwnerIds.value = dirtyOwnerIds.value - setOf(ownerId) + } + } + + /** + * Returns the set of IDs for owners of all areas that are currently marked as dirty (meaning + * all areas that can currently be undone). + */ + fun getAllDirty(): Collection<Int> { + return dirtyOwnerIds.value.toSet() + } + + /** Marks all areas as not dirty (meaning they can't be undone). */ + fun clearAllDirty() { + dirtyOwnerIds.value = emptySet() + } +} diff --git a/src/com/android/wallpaper/picker/undo/domain/interactor/SnapshotRestorer.kt b/src/com/android/wallpaper/picker/undo/domain/interactor/SnapshotRestorer.kt new file mode 100644 index 00000000..30478341 --- /dev/null +++ b/src/com/android/wallpaper/picker/undo/domain/interactor/SnapshotRestorer.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.undo.domain.interactor + +import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot + +/** Defines interface for classes that can handle state restoration. */ +interface SnapshotRestorer { + + /** + * Sets up the restorer. + * + * @param updater An updater the can be used when a new snapshot should be stored; invoke this + * in response to state changes that you wish could be restored when the user asks to reset the + * changes. + * @return A snapshot of the initial state as it was at the moment that this method was invoked. + */ + suspend fun setUpSnapshotRestorer(updater: (RestorableSnapshot) -> Unit): RestorableSnapshot + + /** Restores the state to what is described in the given snapshot. */ + suspend fun restoreToSnapshot(snapshot: RestorableSnapshot) +} diff --git a/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractor.kt b/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractor.kt new file mode 100644 index 00000000..3aa80ae1 --- /dev/null +++ b/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractor.kt @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.undo.domain.interactor + +import com.android.wallpaper.picker.undo.data.repository.UndoRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch + +/** + * Encapsulates the "undo" business logic. + * + * ## Usage + * 1. Instantiate, injecting the supported [SnapshotRestorer] into it, one for each feature that + * ``` + * should support undo functionality. + * ``` + * 2. Call [startSession] which will bootstrap all passed-in [SnapshotRestorer] instances and + * ``` + * hydrate our model with the latest snapshots from each one. + * ``` + * 3. Observe [isUndoable] to know whether the UI for triggering an "undo" action should be made + * ``` + * visible to the user. + * ``` + * 4. Call [revertAll] when the user wishes to revert everything. + */ +class UndoInteractor( + private val scope: CoroutineScope, + private val repository: UndoRepository, + private val restorerByOwnerId: Map<Int, SnapshotRestorer>, +) { + + /** Whether the current state is undoable. */ + val isUndoable: Flow<Boolean> = repository.isAnythingDirty + + /** Bootstraps the undo system, querying each undo-supporting area for the initial snapshot. */ + fun startSession() { + // TODO(b/262924056): take in a saved instance state and reuse it instead. + repository.clearAllDirty() + restorerByOwnerId.forEach { (ownerId, restorer) -> + scope.launch { + val initialSnapshot = + restorer.setUpSnapshotRestorer { subsequentSnapshot -> + val initialSnapshot = repository.getSnapshot(ownerId) + repository.putDirty( + ownerId = ownerId, + isDirty = initialSnapshot != subsequentSnapshot + ) + } + + repository.putSnapshot( + ownerId = ownerId, + snapshot = initialSnapshot, + ) + } + } + } + + /** Triggers a revert for all areas. */ + fun revertAll() { + repository.getAllDirty().forEach { ownerId -> + val restorer = restorerByOwnerId[ownerId] + val snapshot = repository.getSnapshot(ownerId) + if (restorer != null && snapshot != null) { + scope.launch { restorer.restoreToSnapshot(snapshot) } + } + } + + repository.clearAllDirty() + } +} diff --git a/src/com/android/wallpaper/picker/undo/shared/model/RestorableSnapshot.kt b/src/com/android/wallpaper/picker/undo/shared/model/RestorableSnapshot.kt new file mode 100644 index 00000000..aac0e22d --- /dev/null +++ b/src/com/android/wallpaper/picker/undo/shared/model/RestorableSnapshot.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.undo.shared.model + +/** Models a snapshot of the state of an undo-supporting feature at a given time. */ +data class RestorableSnapshot( + val args: Map<String, String>, +) diff --git a/src/com/android/wallpaper/picker/undo/ui/binder/RevertToolbarButtonBinder.kt b/src/com/android/wallpaper/picker/undo/ui/binder/RevertToolbarButtonBinder.kt new file mode 100644 index 00000000..a1eb0185 --- /dev/null +++ b/src/com/android/wallpaper/picker/undo/ui/binder/RevertToolbarButtonBinder.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.undo.ui.binder + +import android.app.AlertDialog +import android.app.Dialog +import android.widget.Toolbar +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.android.wallpaper.R +import com.android.wallpaper.picker.undo.ui.viewmodel.UndoDialogViewModel +import com.android.wallpaper.picker.undo.ui.viewmodel.UndoViewModel +import kotlinx.coroutines.launch + +object RevertToolbarButtonBinder { + /** Binds the given view to the given view-model. */ + fun bind( + view: Toolbar, + viewModel: UndoViewModel, + lifecycleOwner: LifecycleOwner, + ) { + val menuItem = view.menu.findItem(R.id.revert) + menuItem.setOnMenuItemClickListener { + viewModel.onRevertButtonClicked() + true + } + + var dialog: Dialog? = null + + fun showDialog(viewModel: UndoDialogViewModel) { + dialog = + AlertDialog.Builder(view.context, R.style.LightDialogTheme) + .setTitle(R.string.reset_confirmation_dialog_title) + .setMessage(R.string.reset_confirmation_dialog_message) + .setPositiveButton(R.string.reset) { _, _ -> viewModel.onConfirmed() } + .setNegativeButton(R.string.cancel, null) + .setOnDismissListener { + dialog = null + viewModel.onDismissed() + } + .create() + dialog?.show() + } + + fun dismissDialog() { + dialog?.setOnDismissListener(null) + dialog?.dismiss() + dialog = null + } + + lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { viewModel.isRevertButtonVisible.collect { menuItem.isVisible = it } } + + launch { + viewModel.dialog.collect { dialogViewModel -> + if (dialogViewModel != null) { + dismissDialog() + showDialog(dialogViewModel) + } else { + dismissDialog() + } + } + } + } + } + } +} diff --git a/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoDialogViewModel.kt b/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoDialogViewModel.kt new file mode 100644 index 00000000..b7f754b5 --- /dev/null +++ b/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoDialogViewModel.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.undo.ui.viewmodel + +/** Models the UI state for an undo confirmation dialog. */ +data class UndoDialogViewModel( + val onConfirmed: () -> Unit, + val onDismissed: () -> Unit, +) diff --git a/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModel.kt b/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModel.kt new file mode 100644 index 00000000..5e0701bd --- /dev/null +++ b/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModel.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.undo.ui.viewmodel + +import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** Models the UI state of the undo system. */ +class UndoViewModel( + private val interactor: UndoInteractor, +) { + /** Whether the "revert" button should be visible. */ + val isRevertButtonVisible: Flow<Boolean> = interactor.isUndoable + private val _dialog = MutableStateFlow<UndoDialogViewModel?>(null) + /** + * A view-model of the undo confirmation dialog that should be shown, or `null` when no dialog + * should be shown. + */ + val dialog: Flow<UndoDialogViewModel?> = _dialog.asStateFlow() + + /** Notifies that the "revert" button has been clicked by the user. */ + fun onRevertButtonClicked() { + _dialog.value = + UndoDialogViewModel( + onConfirmed = { + interactor.revertAll() + _dialog.value = null + }, + onDismissed = { _dialog.value = null }, + ) + } +} diff --git a/src/com/android/wallpaper/util/BitmapProcessor.java b/src/com/android/wallpaper/util/BitmapProcessor.java index 590164d0..04873461 100644 --- a/src/com/android/wallpaper/util/BitmapProcessor.java +++ b/src/com/android/wallpaper/util/BitmapProcessor.java @@ -15,61 +15,38 @@ */ package com.android.wallpaper.util; -import android.content.Context; import android.graphics.Bitmap; import android.graphics.Rect; import android.util.Log; -import com.google.android.renderscript.Toolkit; - /** * Class with different bitmap processors to apply to bitmaps */ public final class BitmapProcessor { private static final String TAG = "BitmapProcessor"; - private static final float BLUR_RADIUS = 20f; private static final int DOWNSAMPLE = 5; private BitmapProcessor() { } /** - * Function that blurs the content of a bitmap. + * Function that transforms a bitmap into a lower resolution. * - * @param context the Application Context * @param bitmap the bitmap we want to blur. * @param outWidth the end width of the blurred bitmap. * @param outHeight the end height of the blurred bitmap. * @return the blurred bitmap. */ - public static Bitmap blur(Context context, Bitmap bitmap, int outWidth, - int outHeight) { - Bitmap inBitmap = null; - + public static Bitmap createLowResBitmap(Bitmap bitmap, int outWidth, int outHeight) { try { Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); WallpaperCropUtils.fitToSize(rect, outWidth / DOWNSAMPLE, outHeight / DOWNSAMPLE); - inBitmap = Bitmap.createScaledBitmap(bitmap, rect.width(), rect.height(), + return Bitmap.createScaledBitmap(bitmap, rect.width(), rect.height(), true /* filter */); - - // Render script blurs only support ARGB_8888, we need a conversion if we got a - // different bitmap config. - if (inBitmap.getConfig() != Bitmap.Config.ARGB_8888) { - Bitmap oldIn = inBitmap; - inBitmap = oldIn.copy(Bitmap.Config.ARGB_8888, false /* isMutable */); - oldIn.recycle(); - } - - return Toolkit.INSTANCE.blur(inBitmap, (int) BLUR_RADIUS); - } catch (IllegalArgumentException ex) { Log.e(TAG, "error while blurring bitmap", ex); - } finally { - if (inBitmap != null) { - inBitmap.recycle(); - } } return null; diff --git a/src/com/android/wallpaper/util/DisplayUtils.kt b/src/com/android/wallpaper/util/DisplayUtils.kt index 9f7c6440..ce6980ed 100644 --- a/src/com/android/wallpaper/util/DisplayUtils.kt +++ b/src/com/android/wallpaper/util/DisplayUtils.kt @@ -15,6 +15,7 @@ */ package com.android.wallpaper.util +import android.app.Activity import android.content.Context import android.graphics.Point import android.hardware.display.DisplayManager @@ -22,8 +23,8 @@ import android.util.Log import android.view.Display /** - * Utility class to provide methods to find and obtain information about displays via - * {@link DisplayManager} + * Utility class to provide methods to find and obtain information about displays via {@link + * DisplayManager} */ class DisplayUtils(context: Context) { companion object { @@ -35,7 +36,8 @@ class DisplayUtils(context: Context) { init { val appContext = context.applicationContext val dm = appContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager - val allDisplays: Array<out Display> = dm.displays + val allDisplays: Array<out Display> = + dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) if (allDisplays.isEmpty()) { Log.e(TAG, "No displays found on context $appContext") throw RuntimeException("No displays found!") @@ -43,12 +45,20 @@ class DisplayUtils(context: Context) { internalDisplays = allDisplays.filter { it.type == Display.TYPE_INTERNAL } } - /** - * Returns the {@link Display} to be used to calculate wallpaper size and cropping. - */ + /** Returns the {@link Display} to be used to calculate wallpaper size and cropping. */ fun getWallpaperDisplay(): Display { return internalDisplays.maxWithOrNull { a, b -> getRealSize(a) - getRealSize(b) } - ?: internalDisplays[0] + ?: internalDisplays[0] + } + + /** + * Returns `true` if the current display is the wallpaper display on a multi-display device. + * + * On a multi-display device the wallpaper display is the largest display while on a single + * display device the only display is both the wallpaper display and the current display. + */ + fun isOnWallpaperDisplay(activity: Activity): Boolean { + return activity.display.displayId == getWallpaperDisplay().displayId } private fun getRealSize(display: Display): Int { diff --git a/src/com/android/wallpaper/util/PreviewUtils.java b/src/com/android/wallpaper/util/PreviewUtils.java deleted file mode 100644 index 0b8977a1..00000000 --- a/src/com/android/wallpaper/util/PreviewUtils.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2020 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.wallpaper.util; - -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.text.TextUtils; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** Util class for wallpaper preview. */ -public class PreviewUtils { - - private static final String PREVIEW = "preview"; - private static final String METHOD_GET_PREVIEW = "get_preview"; - private static final ExecutorService sExecutorService = Executors.newSingleThreadExecutor(); - - private final Context mContext; - private final String mProviderAuthority; - private ProviderInfo mProviderInfo; - - public PreviewUtils(Context context, String authorityMetadataKey) { - mContext = context; - Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME); - - ResolveInfo info = context.getPackageManager().resolveActivity(homeIntent, - PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA); - if (info != null && info.activityInfo != null && info.activityInfo.metaData != null) { - mProviderAuthority = info.activityInfo.metaData.getString(authorityMetadataKey); - } else { - mProviderAuthority = null; - } - - mProviderInfo = TextUtils.isEmpty(mProviderAuthority) ? null - : mContext.getPackageManager().resolveContentProvider(mProviderAuthority, 0); - if (mProviderInfo != null && !TextUtils.isEmpty(mProviderInfo.readPermission)) { - if (context.checkSelfPermission(mProviderInfo.readPermission) - != PackageManager.PERMISSION_GRANTED) { - mProviderInfo = null; - } - } - } - - /** - * Render preview under the current grid option. - * @param bundle request options to pass on the call - * @param callback to receive the results, it will be called on the main thread. - */ - public void renderPreview(Bundle bundle, WorkspacePreviewCallback callback) { - sExecutorService.submit(() -> { - Bundle result = mContext.getContentResolver().call(getUri(PREVIEW), - METHOD_GET_PREVIEW, null, bundle); - new Handler(Looper.getMainLooper()).post(() -> callback.onPreviewRendered(result)); - }); - } - - /** Easy way to generate a Uri with the provider info from this class. */ - public Uri getUri(String path) { - return new Uri.Builder() - .scheme(ContentResolver.SCHEME_CONTENT) - .authority(mProviderInfo.authority) - .appendPath(path) - .build(); - } - - /** Return whether preview is supported. */ - public boolean supportsPreview() { - return mProviderInfo != null; - } - - /** - * Callback for a call to the provider to render preview - */ - public interface WorkspacePreviewCallback { - /** - * Called with the result from the provider. - */ - void onPreviewRendered(Bundle resultBundle); - } -} diff --git a/src/com/android/wallpaper/util/PreviewUtils.kt b/src/com/android/wallpaper/util/PreviewUtils.kt new file mode 100644 index 00000000..a2e0275a --- /dev/null +++ b/src/com/android/wallpaper/util/PreviewUtils.kt @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 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.wallpaper.util + +import android.content.ContentResolver +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.pm.ProviderInfo +import android.net.Uri +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.text.TextUtils +import java.util.concurrent.Executors + +/** Util class for wallpaper preview. */ +class PreviewUtils( + private val context: Context, + authorityMetadataKey: String? = null, + authority: String? = null, +) { + /** Callback for a call to the provider to render preview */ + interface WorkspacePreviewCallback { + /** Called with the result from the provider. */ + fun onPreviewRendered(resultBundle: Bundle?) + } + + private var providerInfo: ProviderInfo? + + constructor( + context: Context, + authorityMetadataKey: String, + ) : this( + context = context, + authorityMetadataKey = authorityMetadataKey, + authority = null, + ) + + init { + val providerAuthority = + authority ?: homeAuthority(context, checkNotNull(authorityMetadataKey)) + + providerInfo = + if (!TextUtils.isEmpty(providerAuthority)) { + context.packageManager.resolveContentProvider( + providerAuthority, + 0, + ) + } else { + null + } + + providerInfo?.let { + if (!TextUtils.isEmpty(it.readPermission)) { + if ( + context.checkSelfPermission(it.readPermission) != + PackageManager.PERMISSION_GRANTED + ) { + providerInfo = null + } + } + } + } + + /** + * Render preview under the current grid option. + * + * @param bundle request options to pass on the call. + * @param callback to receive the results, it will be called on the main thread. + */ + fun renderPreview(bundle: Bundle?, callback: WorkspacePreviewCallback) { + EXECUTOR_SERVICE.submit { + val result = + context.contentResolver.call( + getUri(PREVIEW), + METHOD_GET_PREVIEW, + null, + bundle, + ) + Handler(Looper.getMainLooper()).post { callback.onPreviewRendered(result) } + } + } + + /** Easy way to generate a Uri with the provider info from this class. */ + fun getUri(path: String?): Uri { + return Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(checkNotNull(providerInfo).authority) + .appendPath(path) + .build() + } + + /** Return whether preview is supported. */ + fun supportsPreview(): Boolean { + return providerInfo != null + } + + companion object { + private const val PREVIEW = "preview" + private const val METHOD_GET_PREVIEW = "get_preview" + private val EXECUTOR_SERVICE = Executors.newSingleThreadExecutor() + + private fun homeAuthority(context: Context, authorityMetadataKey: String): String? { + val homeIntent = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME) + val info = + context.packageManager.resolveActivity( + homeIntent, + PackageManager.MATCH_DEFAULT_ONLY or PackageManager.GET_META_DATA, + ) + + return info?.activityInfo?.metaData?.getString(authorityMetadataKey) + } + } +} diff --git a/src/com/android/wallpaper/util/SurfaceViewUtils.java b/src/com/android/wallpaper/util/SurfaceViewUtils.java index 1656b1b5..1b74f777 100644 --- a/src/com/android/wallpaper/util/SurfaceViewUtils.java +++ b/src/com/android/wallpaper/util/SurfaceViewUtils.java @@ -20,6 +20,8 @@ import android.os.Message; import android.view.SurfaceControlViewHost; import android.view.SurfaceView; +import androidx.annotation.Nullable; + /** Util class to generate surface view requests and parse responses */ public class SurfaceViewUtils { @@ -32,11 +34,21 @@ public class SurfaceViewUtils { /** Create a surface view request. */ public static Bundle createSurfaceViewRequest(SurfaceView surfaceView) { + return createSurfaceViewRequest(surfaceView, null); + } + + /** Create a surface view request. */ + public static Bundle createSurfaceViewRequest( + SurfaceView surfaceView, + @Nullable Bundle extras) { Bundle bundle = new Bundle(); bundle.putBinder(KEY_HOST_TOKEN, surfaceView.getHostToken()); bundle.putInt(KEY_DISPLAY_ID, surfaceView.getDisplay().getDisplayId()); bundle.putInt(KEY_VIEW_WIDTH, surfaceView.getWidth()); bundle.putInt(KEY_VIEW_HEIGHT, surfaceView.getHeight()); + if (extras != null) { + bundle.putAll(extras); + } return bundle; } diff --git a/src/com/android/wallpaper/util/SystemColors.kt b/src/com/android/wallpaper/util/SystemColors.kt new file mode 100644 index 00000000..ec1738c4 --- /dev/null +++ b/src/com/android/wallpaper/util/SystemColors.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 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.wallpaper.util + +import android.annotation.AttrRes +import android.annotation.ColorInt +import android.content.Context +import android.util.TypedValue +import androidx.core.content.ContextCompat + +object SystemColors { + + /** + * Returns the color by fetching the resId from the theme. Throws an exception when resource Id + * is not available in the theme. + */ + @JvmStatic + @ColorInt + fun getColor(context: Context, @AttrRes resId: Int): Int { + val colorValue = TypedValue() + val theme = context.theme + if (theme.resolveAttribute(resId, colorValue, /* resolveRefs= */ true)) { + if ( + TypedValue.TYPE_FIRST_COLOR_INT <= colorValue.type && + colorValue.type <= TypedValue.TYPE_LAST_COLOR_INT + ) { + return colorValue.data + } + if (colorValue.type == TypedValue.TYPE_STRING) { + return ContextCompat.getColor(context, colorValue.resourceId) + } + } + throw IllegalArgumentException( + "Theme is missing expected color ${context.resources.getResourceName(resId)} " + + "($resId) references a missing resource." + ) + } +} diff --git a/src/com/android/wallpaper/util/WallpaperConnection.java b/src/com/android/wallpaper/util/WallpaperConnection.java index 06b4619a..b8409805 100644 --- a/src/com/android/wallpaper/util/WallpaperConnection.java +++ b/src/com/android/wallpaper/util/WallpaperConnection.java @@ -21,11 +21,13 @@ import static android.graphics.Matrix.MSKEW_X; import static android.graphics.Matrix.MSKEW_Y; import android.app.WallpaperColors; +import android.app.WallpaperManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.os.IBinder; @@ -34,8 +36,8 @@ import android.os.RemoteException; import android.service.wallpaper.IWallpaperConnection; import android.service.wallpaper.IWallpaperEngine; import android.service.wallpaper.IWallpaperService; -import android.util.DisplayMetrics; import android.util.Log; +import android.view.Display; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; @@ -44,6 +46,9 @@ import android.view.WindowManager.LayoutParams; import androidx.annotation.Nullable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + /** * Implementation of {@link IWallpaperConnection} that handles communication with a * {@link android.service.wallpaper.WallpaperService} @@ -69,6 +74,7 @@ public class WallpaperConnection extends IWallpaperConnection.Stub implements Se private final SurfaceView mSecondContainerView; private IWallpaperService mService; @Nullable private IWallpaperEngine mEngine; + @Nullable private Point mDisplayMetrics; private boolean mConnected; private boolean mIsVisible; private boolean mIsEngineVisible; @@ -160,11 +166,22 @@ public class WallpaperConnection extends IWallpaperConnection.Stub implements Se mService = IWallpaperService.Stub.asInterface(service); try { int displayId = mContainerView.getDisplay().getDisplayId(); - - mService.attach(this, mContainerView.getWindowToken(), - LayoutParams.TYPE_APPLICATION_MEDIA, - true, mContainerView.getWidth(), mContainerView.getHeight(), - new Rect(0, 0, 0, 0), displayId); + try { + Method preUMethod = mService.getClass().getMethod("attach", + IWallpaperConnection.class, IBinder.class, int.class, boolean.class, + int.class, int.class, Rect.class, int.class); + preUMethod.invoke(mService, this, mContainerView.getWindowToken(), + LayoutParams.TYPE_APPLICATION_MEDIA, true, mContainerView.getWidth(), + mContainerView.getHeight(), new Rect(0, 0, 0, 0), displayId); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + Log.d(TAG, "IWallpaperService#attach method without which argument not available, " + + "will use newer version"); + // Let's try the new attach method that takes "which" argument + mService.attach(this, mContainerView.getWindowToken(), + LayoutParams.TYPE_APPLICATION_MEDIA, true, mContainerView.getWidth(), + mContainerView.getHeight(), new Rect(0, 0, 0, 0), displayId, + WallpaperManager.FLAG_SYSTEM); + } } catch (RemoteException e) { Log.w(TAG, "Failed attaching wallpaper; clearing", e); } @@ -196,12 +213,22 @@ public class WallpaperConnection extends IWallpaperConnection.Stub implements Se setEngineVisibility(true); } - // Some wallpapers don't trigger #onWallpaperColorsChanged from remote. Requesting - // wallpaper color here to ensure the #onWallpaperColorsChanged would get called. try { + Point displayMetrics = getDisplayMetrics(); + // Reset the live wallpaper preview with the correct screen dimensions. It is + // a known issue that the wallpaper service maybe get the Activity window size + // which may differ from the actual physical device screen size, e.g. when in + // 2-pane mode. + // TODO b/262750854 Fix wallpaper service to get the actual physical device + // screen size instead of the window size that might be smaller when in + // 2-pane mode. + mEngine.resizePreview(new Rect(0, 0, displayMetrics.x, displayMetrics.y)); + // Some wallpapers don't trigger #onWallpaperColorsChanged from remote. + // Requesting wallpaper color here to ensure the #onWallpaperColorsChanged + // would get called. mEngine.requestWallpaperColors(); - } catch (RemoteException e) { - Log.w(TAG, "Failed requesting wallpaper colors", e); + } catch (RemoteException | NullPointerException e) { + Log.w(TAG, "Failed calling WallpaperEngine APIs", e); } } else { try { @@ -238,7 +265,7 @@ public class WallpaperConnection extends IWallpaperConnection.Stub implements Se } @Override - public void engineShown(IWallpaperEngine engine) { + public void engineShown(IWallpaperEngine engine) { mEngineReady = true; if (mContainerView != null) { mContainerView.post(() -> reparentWallpaperSurface(mContainerView)); @@ -327,7 +354,7 @@ public class WallpaperConnection extends IWallpaperConnection.Stub implements Se t.reparent(wallpaperMirrorSC, parentSC); t.show(wallpaperMirrorSC); t.apply(); - } catch (RemoteException e) { + } catch (RemoteException | NullPointerException e) { Log.e(TAG, "Couldn't reparent wallpaper surface", e); } } @@ -336,15 +363,31 @@ public class WallpaperConnection extends IWallpaperConnection.Stub implements Se Matrix m = new Matrix(); float[] values = new float[9]; Rect surfacePosition = parentSurface.getHolder().getSurfaceFrame(); - DisplayMetrics metrics = DisplayMetricsRetriever.getInstance().getDisplayMetrics( - mContainerView.getResources(), mContainerView.getDisplay()); - m.postScale(((float) surfacePosition.width()) / metrics.widthPixels, - ((float) surfacePosition.height()) / metrics.heightPixels); + Point displayMetrics = getDisplayMetrics(); + m.postScale(((float) surfacePosition.width()) / displayMetrics.x, + ((float) surfacePosition.height()) / displayMetrics.y); m.getValues(values); return values; } /** + * Get display metrics. Only call this when the display is attached to the window. + */ + private Point getDisplayMetrics() { + if (mDisplayMetrics != null) { + return mDisplayMetrics; + } + ScreenSizeCalculator screenSizeCalculator = ScreenSizeCalculator.getInstance(); + Display display = mContainerView.getDisplay(); + if (display == null) { + throw new NullPointerException( + "Display is null due to the view not currently attached to a window."); + } + mDisplayMetrics = screenSizeCalculator.getScreenSize(display); + return mDisplayMetrics; + } + + /** * Interface to be notified of connect/disconnect events from {@link WallpaperConnection} */ public interface WallpaperConnectionListener { diff --git a/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java b/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java index 7a88ab15..8d736216 100644 --- a/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java +++ b/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java @@ -19,6 +19,8 @@ import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; import android.content.Context; +import android.graphics.RenderEffect; +import android.graphics.Shader; import android.service.wallpaper.WallpaperService; import android.util.Log; import android.view.Surface; @@ -44,6 +46,8 @@ import java.util.concurrent.Future; */ public class WallpaperSurfaceCallback implements SurfaceHolder.Callback { + public static final float LOW_RES_BITMAP_BLUR_RADIUS = 150f; + /** * Listener used to be notified when this surface is created */ @@ -189,4 +193,20 @@ public class WallpaperSurfaceCallback implements SurfaceHolder.Callback { public ImageView getHomeImageWallpaper() { return mHomeImageWallpaper; } + + /** + * @param blur whether to blur the home image wallpaper + */ + public void setHomeImageWallpaperBlur(boolean blur) { + if (mHomeImageWallpaper == null) { + return; + } + if (blur) { + mHomeImageWallpaper.setRenderEffect( + RenderEffect.createBlurEffect(LOW_RES_BITMAP_BLUR_RADIUS, + LOW_RES_BITMAP_BLUR_RADIUS, Shader.TileMode.CLAMP)); + } else { + mHomeImageWallpaper.setRenderEffect(null); + } + } } diff --git a/src/com/android/wallpaper/widget/DuoTabs.java b/src/com/android/wallpaper/widget/DuoTabs.java new file mode 100644 index 00000000..a8f30240 --- /dev/null +++ b/src/com/android/wallpaper/widget/DuoTabs.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2022 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.wallpaper.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.Button; +import android.widget.FrameLayout; + +import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.wallpaper.R; +import com.android.wallpaper.util.SystemColors; + +/** + * Custom layout for duo tabs. + */ +public final class DuoTabs extends FrameLayout { + + public static final int TAB_PRIMARY = 0; + public static final int TAB_SECONDARY = 1; + + /** + * Overlay tab + */ + @IntDef({TAB_PRIMARY, TAB_SECONDARY}) + public @interface Tab { + } + + /** + * Overlay tab selected listener + */ + public interface OnTabSelectedListener { + + /** + * On tab selected + */ + void onTabSelected(@Tab int tab); + } + + OnTabSelectedListener mOnTabSelectedListener; + Button mPrimaryTab; + Button mSecondaryTab; + @Tab int mCurrentOverlayTab; + + /** + * Constructor + */ + public DuoTabs(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + LayoutInflater.from(context).inflate(R.layout.duo_tabs, this, true); + mPrimaryTab = findViewById(R.id.tab_primary); + mSecondaryTab = findViewById(R.id.tab_secondary); + mPrimaryTab.setOnClickListener(v -> selectTab(TAB_PRIMARY)); + mSecondaryTab.setOnClickListener(v -> selectTab(TAB_SECONDARY)); + } + + /** + * Set tab text + */ + public void setTabText(String primaryTabText, String secondaryTabText) { + mPrimaryTab.setText(primaryTabText); + mSecondaryTab.setText(secondaryTabText); + } + + /** + * Select a tab + */ + public void selectTab(@Tab int tab) { + updateTabIndicator(tab); + if (mOnTabSelectedListener != null) { + mOnTabSelectedListener.onTabSelected(tab); + } + mCurrentOverlayTab = tab; + } + + /** + * Set listener + */ + public void setOnTabSelectedListener( + OnTabSelectedListener onTabSelectedListener) { + mOnTabSelectedListener = onTabSelectedListener; + } + + /** + * Update the background color in case the context theme has changed. + */ + public void updateBackgroundColor() { + mPrimaryTab.setBackground(null); + mSecondaryTab.setBackground(null); + updateTabIndicator(mCurrentOverlayTab); + } + + private void updateTabIndicator(@Tab int tab) { + Context c = getContext(); + mPrimaryTab.setBackgroundResource( + tab == TAB_PRIMARY + ? R.drawable.duo_tabs_button_indicator_background + : R.drawable.duo_tabs_button_background); + mPrimaryTab.setTextColor( + tab == TAB_PRIMARY + ? getResources().getColor(R.color.text_color_on_accent) + : SystemColors.getColor(c, android.R.attr.textColorPrimary)); + mSecondaryTab.setBackgroundResource( + tab == TAB_SECONDARY + ? R.drawable.duo_tabs_button_indicator_background + : R.drawable.duo_tabs_button_background); + mSecondaryTab.setTextColor( + tab == TAB_SECONDARY + ? getResources().getColor(R.color.text_color_on_accent) + : SystemColors.getColor(c, android.R.attr.textColorPrimary)); + } + + public @Tab int getSelectedTab() { + return mCurrentOverlayTab; + } +} diff --git a/src/com/android/wallpaper/widget/FloatingSheet.kt b/src/com/android/wallpaper/widget/FloatingSheet.kt new file mode 100644 index 00000000..7c8bbd76 --- /dev/null +++ b/src/com/android/wallpaper/widget/FloatingSheet.kt @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2022 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.wallpaper.widget + +import android.annotation.IntDef +import android.content.Context +import android.transition.AutoTransition +import android.transition.TransitionManager +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.appcompat.content.res.AppCompatResources +import com.android.wallpaper.R +import com.android.wallpaper.util.SizeCalculator +import com.android.wallpaper.widget.floatingsheetcontent.FloatingSheetContent +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback +import java.util.function.Consumer + +/** A `ViewGroup` which provides the specific actions for the user to interact with. */ +class FloatingSheet(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) { + + companion object { + + @Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE) + @IntDef(CUSTOMIZE, INFORMATION, EFFECTS) + @Retention(AnnotationRetention.SOURCE) + annotation class FloatingSheetContentType + + const val CUSTOMIZE = 0 + const val INFORMATION = 1 + const val EFFECTS = 2 + } + + private val floatingSheetView: ViewGroup + private val floatingSheetContainer: ViewGroup + private val floatingSheetBehavior: BottomSheetBehavior<ViewGroup> + private val contentViewMap: + MutableMap<@FloatingSheetContentType Int, FloatingSheetContent<*>?> = + HashMap() + + // The system "short" animation time duration, in milliseconds. This + // duration is ideal for subtle animations or animations that occur + // very frequently. + private val shortAnimTimeMillis: Long + + init { + LayoutInflater.from(context).inflate(R.layout.floating_sheet, this, true) + floatingSheetView = requireViewById(R.id.floating_sheet_content) + SizeCalculator.adjustBackgroundCornerRadius(floatingSheetView) + setColor(context) + floatingSheetContainer = requireViewById(R.id.floating_sheet_container) + floatingSheetBehavior = BottomSheetBehavior.from(floatingSheetContainer) + floatingSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + shortAnimTimeMillis = resources.getInteger(android.R.integer.config_shortAnimTime).toLong() + } + + /** + * Binds the `floatingSheetContent` with an id that can be used identify and switch between + * floating sheet content + * + * @param floatingSheetContent the content object with view being added to the floating sheet + */ + fun putFloatingSheetContent( + @FloatingSheetContentType type: Int, + floatingSheetContent: FloatingSheetContent<*> + ) { + floatingSheetContent.initView() + contentViewMap[type] = floatingSheetContent + floatingSheetView.addView(floatingSheetContent.contentView) + } + + /** Dynamic update color with `Context`. */ + fun setColor(context: Context) { + // Set floating sheet background. + floatingSheetView.background = + AppCompatResources.getDrawable(context, R.drawable.floating_sheet_background) + if (floatingSheetView.childCount > 0) { + // Update the bottom sheet content view if any. + floatingSheetView.removeAllViews() + contentViewMap.values.forEach( + Consumer { floatingSheetContent: FloatingSheetContent<*>? -> + floatingSheetContent?.let { + it.recreateView() + floatingSheetView.addView(it.contentView) + } + } + ) + } + } + + /** Returns `true` if the state of bottom sheet is collapsed. */ + val isFloatingSheetCollapsed: Boolean + get() = floatingSheetBehavior.state == BottomSheetBehavior.STATE_HIDDEN + + /** Expands [FloatingSheet]. */ + fun expand() { + floatingSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED + } + + /** Collapses [FloatingSheet]. */ + fun collapse() { + floatingSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + endContentViewAnimation() + } + + /** + * Updates content view of [FloatingSheet] with transition animation + * + * @param type the integer or enum used to identify the content view + */ + fun updateContentViewWithAnimation(@FloatingSheetContentType type: Int) { + val transition = AutoTransition() + transition.duration = shortAnimTimeMillis + /** + * This line records changes you make to its views and applies a transition that animates + * the changes when the system redraws the user interface + */ + TransitionManager.beginDelayedTransition(floatingSheetContainer, transition) + + updateContentView(type) + } + + fun endContentViewAnimation() { + TransitionManager.endTransitions(floatingSheetContainer) + } + + /** + * Updates content view of [FloatingSheet] + * + * @param type the integer or enum used to identify the content view + */ + fun updateContentView(@FloatingSheetContentType type: Int) { + contentViewMap.forEach { (i: Int, content: FloatingSheetContent<*>?) -> + content?.setVisibility(i == type) + } + } + + /** + * Adds Floating Sheet Callback to connected [BottomSheetBehavior]. + * + * @param callback the callback for floating sheet state changes, has to be in the type of + * [BottomSheetBehavior.BottomSheetCallback] since the floating sheet behavior is currently + * based on [BottomSheetBehavior] + */ + fun addFloatingSheetCallback(callback: BottomSheetCallback?) { + floatingSheetBehavior.addBottomSheetCallback(callback!!) + } +} diff --git a/src/com/android/wallpaper/widget/GridRowSpacerDecoration.kt b/src/com/android/wallpaper/widget/GridRowSpacerDecoration.kt index 8b1fcdb5..c3b7afea 100644 --- a/src/com/android/wallpaper/widget/GridRowSpacerDecoration.kt +++ b/src/com/android/wallpaper/widget/GridRowSpacerDecoration.kt @@ -20,15 +20,14 @@ import android.view.View import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView -/** - * RecyclerView ItemDecorator that adds a vertical space between grid rows. - */ +/** RecyclerView ItemDecorator that adds a vertical space between grid rows. */ class GridRowSpacerDecoration(private val padding: Int) : RecyclerView.ItemDecoration() { override fun getItemOffsets( - outRect: Rect, - view: View, - parent: RecyclerView, - state: RecyclerView.State) { + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { val layoutManager = parent.layoutManager if (layoutManager is GridLayoutManager) { val position = parent.getChildAdapterPosition(view) diff --git a/src/com/android/wallpaper/widget/WallpaperControlButtonGroup.java b/src/com/android/wallpaper/widget/WallpaperControlButtonGroup.java new file mode 100644 index 00000000..00a681ae --- /dev/null +++ b/src/com/android/wallpaper/widget/WallpaperControlButtonGroup.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2022 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.wallpaper.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.CompoundButton; +import android.widget.FrameLayout; +import android.widget.ToggleButton; + +import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.content.res.AppCompatResources; + +import com.android.wallpaper.R; + +/** + * Custom layout for a group of wallpaper control buttons. + */ +public final class WallpaperControlButtonGroup extends FrameLayout { + + public static final int DELETE = 0; + public static final int CUSTOMIZE = 1; + public static final int EFFECTS = 2; + public static final int INFORMATION = 3; + + /** + * Overlay tab + */ + @IntDef({DELETE, CUSTOMIZE, EFFECTS, INFORMATION}) + public @interface WallpaperControlType { + } + + final int[] mFloatingSheetControlButtonTypes = { CUSTOMIZE, EFFECTS, INFORMATION }; + + ToggleButton mDeleteButton; + ToggleButton mCustomizeButton; + ToggleButton mEffectsButton; + ToggleButton mInformationButton; + + /** + * Constructor + */ + public WallpaperControlButtonGroup(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + LayoutInflater.from(context).inflate(R.layout.wallpaper_control_button_group, this, true); + mDeleteButton = findViewById(R.id.delete_button); + mCustomizeButton = findViewById(R.id.customize_button); + mEffectsButton = findViewById(R.id.effects_button); + mInformationButton = findViewById(R.id.information_button); + } + + /** + * Show a button by giving a correspondent listener + */ + public void showButton(@WallpaperControlType int type, + CompoundButton.OnCheckedChangeListener listener) { + ToggleButton button = getActionButton(type); + if (button != null) { + button.setVisibility(VISIBLE); + button.setOnCheckedChangeListener(listener); + } + } + + private ToggleButton getActionButton(@WallpaperControlType int type) { + switch (type) { + case DELETE: + return mDeleteButton; + case CUSTOMIZE: + return mCustomizeButton; + case EFFECTS: + return mEffectsButton; + case INFORMATION: + return mInformationButton; + default: + return null; + } + } + + /** + * Hide a button + */ + public void hideButton(@WallpaperControlType int type) { + getActionButton(type).setVisibility(GONE); + } + + /** + * Set checked for a button + */ + public void setChecked(@WallpaperControlType int type, boolean checked) { + getActionButton(type).setChecked(checked); + } + + /** + * Update the background color in case the context theme has changed. + */ + public void updateBackgroundColor() { + Context context = getContext(); + if (context == null) { + return; + } + mDeleteButton.setForeground(null); + mCustomizeButton.setForeground(null); + mEffectsButton.setForeground(null); + mInformationButton.setForeground(null); + mDeleteButton.setForeground(AppCompatResources.getDrawable(context, + R.drawable.wallpaper_control_button_delete)); + mCustomizeButton.setForeground(AppCompatResources.getDrawable(context, + R.drawable.wallpaper_control_button_customize)); + mEffectsButton.setForeground(AppCompatResources.getDrawable(context, + R.drawable.wallpaper_control_button_effect)); + mInformationButton.setForeground( + AppCompatResources.getDrawable(context, R.drawable.wallpaper_control_button_info)); + } + + /** + * Ensures only one toggle button with a floating sheet is selected at a time + */ + public void deselectOtherFloatingSheetControlButtons(@WallpaperControlType int selectedType) { + for (int type : mFloatingSheetControlButtonTypes) { + if (type != selectedType) { + getActionButton(type).setChecked(false); + } + } + } + + /** + * Returns true if there is a floating sheet button selected, and false if not + */ + public boolean isFloatingSheetControlButtonSelected() { + for (int type : mFloatingSheetControlButtonTypes) { + if (getActionButton(type).isChecked()) { + return true; + } + } + return false; + } + + /** + * Deselects all floating sheet toggle buttons in the Wallpaper Control Button Group + */ + public void deselectAllFloatingSheetControlButtons() { + for (int type : mFloatingSheetControlButtonTypes) { + getActionButton(type).setChecked(false); + } + } +} diff --git a/src/com/android/wallpaper/widget/WallpaperDownloadButton.java b/src/com/android/wallpaper/widget/WallpaperDownloadButton.java new file mode 100644 index 00000000..c1047eb6 --- /dev/null +++ b/src/com/android/wallpaper/widget/WallpaperDownloadButton.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 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.wallpaper.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.FrameLayout; +import android.widget.ToggleButton; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.content.res.AppCompatResources; + +import com.android.wallpaper.R; + +/** + * Custom layout for the download button. + */ +public final class WallpaperDownloadButton extends FrameLayout { + + ToggleButton mDownloadButton; + View mDownloadActionProgressBar; + + /** + * Constructor + */ + public WallpaperDownloadButton(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + LayoutInflater.from(context).inflate(R.layout.button_download_wallpaper, this, true); + mDownloadButton = findViewById(R.id.download_button); + mDownloadActionProgressBar = findViewById(R.id.action_download_progress); + } + + /** + * Set {@link CompoundButton.OnCheckedChangeListener } + */ + public void setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener listener) { + mDownloadButton.setOnCheckedChangeListener(listener); + } + + /** + * Show the progress bar for the download button + */ + public void showDownloadActionProgress() { + mDownloadButton.setVisibility(GONE); + mDownloadActionProgressBar.setVisibility(VISIBLE); + } + + /** + * Hide the progress bar for the download button + */ + public void hideDownloadActionProgress() { + mDownloadButton.setVisibility(VISIBLE); + mDownloadActionProgressBar.setVisibility(GONE); + } + + /** + * Update the color in case the context theme has changed. + */ + public void updateColor() { + Context context = getContext(); + if (context == null) { + return; + } + mDownloadButton.setForeground(null); + mDownloadButton.setForeground(AppCompatResources.getDrawable(context, + R.drawable.wallpaper_control_button_download)); + } +} diff --git a/src/com/android/wallpaper/widget/floatingsheetcontent/FloatingSheetContent.kt b/src/com/android/wallpaper/widget/floatingsheetcontent/FloatingSheetContent.kt new file mode 100644 index 00000000..4bdda26e --- /dev/null +++ b/src/com/android/wallpaper/widget/floatingsheetcontent/FloatingSheetContent.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 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.wallpaper.widget.floatingsheetcontent + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import androidx.annotation.LayoutRes + +/** + * Object to host content view for floating sheet to display. + * + * The view would be created in the constructor. + * + * @param <T> the floating sheet content type </T> + * + * TODO: refactoring FloatingSheetContent b/258468645 + */ +abstract class FloatingSheetContent<T : View>(private val context: Context) { + + lateinit var contentView: T + private var isVisible = false + + /** Gets the view id to inflate. */ + @get:LayoutRes abstract val viewId: Int + + /** Gets called when the content view is created. */ + abstract fun onViewCreated(view: T) + + /** Gets called when the current content view is going to recreate. */ + open fun onRecreateView(oldView: T) {} + + fun initView() { + contentView = createView() + setVisibility(true) + } + + fun recreateView() { + // Inform that the view is going to recreate. + onRecreateView(contentView) + // Create a new view with the given context. + contentView = createView() + setVisibility(isVisible) + } + + private fun createView(): T { + @Suppress("UNCHECKED_CAST") + val contentView = LayoutInflater.from(context).inflate(viewId, null) as T + onViewCreated(contentView) + contentView.isFocusable = true + return contentView + } + + open fun setVisibility(isVisible: Boolean) { + this.isVisible = isVisible + contentView.visibility = if (this.isVisible) FrameLayout.VISIBLE else FrameLayout.GONE + } +} diff --git a/src/com/android/wallpaper/widget/floatingsheetcontent/PreviewCustomizeSettingsContent.kt b/src/com/android/wallpaper/widget/floatingsheetcontent/PreviewCustomizeSettingsContent.kt new file mode 100644 index 00000000..c836c680 --- /dev/null +++ b/src/com/android/wallpaper/widget/floatingsheetcontent/PreviewCustomizeSettingsContent.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 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.wallpaper.widget.floatingsheetcontent + +import android.content.Context +import android.net.Uri +import android.view.View +import androidx.lifecycle.LiveData +import androidx.slice.Slice +import androidx.slice.widget.SliceLiveData +import androidx.slice.widget.SliceView +import com.android.wallpaper.R + +class PreviewCustomizeSettingsContent( + private val context: Context, + private val uriSettingsSlice: Uri? +) : FloatingSheetContent<View>(context) { + + private lateinit var settingsSliceView: SliceView + private var settingsLiveData: LiveData<Slice>? = null + override val viewId: Int + get() = R.layout.preview_customize_settings + + override fun onViewCreated(previewPage: View) { + settingsSliceView = previewPage.findViewById(R.id.settings_slice) + settingsSliceView.mode = SliceView.MODE_LARGE + settingsSliceView.isScrollable = false + if (uriSettingsSlice != null) { + settingsLiveData = SliceLiveData.fromUri(context, uriSettingsSlice) + } + settingsLiveData?.observeForever(settingsSliceView) + } + + override fun onRecreateView(oldPreviewPage: View) { + if (settingsLiveData != null && settingsLiveData!!.hasObservers()) { + settingsLiveData!!.removeObserver(settingsSliceView) + } + } +} diff --git a/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoContent.kt b/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoContent.kt new file mode 100644 index 00000000..2bc75a7a --- /dev/null +++ b/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoContent.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 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.wallpaper.widget.floatingsheetcontent + +import android.content.Context +import android.content.Intent +import com.android.wallpaper.R +import com.android.wallpaper.model.WallpaperInfo +import com.android.wallpaper.module.InjectorProvider +import com.android.wallpaper.picker.WallpaperInfoHelper + +/** Floating Sheet Content for displaying wallpaper info */ +class WallpaperInfoContent(private var context: Context, private val wallpaper: WallpaperInfo?) : + FloatingSheetContent<WallpaperInfoView>(context) { + + private var exploreIntent: Intent? = null + private var actionLabel: CharSequence? = null + private var wallpaperInfoView: WallpaperInfoView? = null + override val viewId: Int + get() = R.layout.floating_sheet_wallpaper_info_view + + /** Gets called when the content view is created or recreated by [FloatingSheetContent] */ + override fun onViewCreated(view: WallpaperInfoView) { + wallpaperInfoView = view + context = view.context + initializeWallpaperContent() + } + + private fun initializeWallpaperContent() { + if (wallpaper == null) { + return + } + if (actionLabel == null) { + setUpExploreIntentAndLabel { populateWallpaperInfo(wallpaperInfoView) } + } else { + populateWallpaperInfo(wallpaperInfoView) + } + } + + private fun setUpExploreIntentAndLabel(callback: Runnable?) { + WallpaperInfoHelper.loadExploreIntent(context, wallpaper!!) { + actionLabel: CharSequence?, + exploreIntent: Intent? -> + this.actionLabel = actionLabel + this.exploreIntent = exploreIntent + callback?.run() + } + } + + private fun onExploreClicked() { + val injector = InjectorProvider.getInjector() + val userEventLogger = injector.getUserEventLogger(context!!.applicationContext) + userEventLogger.logActionClicked( + wallpaper!!.getCollectionId(context), + wallpaper.getActionLabelRes(context) + ) + context.startActivity(exploreIntent) + } + + private fun populateWallpaperInfo(view: WallpaperInfoView?) { + view!!.populateWallpaperInfo( + wallpaper!!, + actionLabel, + WallpaperInfoHelper.shouldShowExploreButton(context, exploreIntent) + ) { onExploreClicked() } + } +} diff --git a/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoView.kt b/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoView.kt new file mode 100644 index 00000000..fe88ea4e --- /dev/null +++ b/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoView.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2022 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.wallpaper.widget.floatingsheetcontent + +import android.content.Context +import android.os.Handler +import android.os.Looper +import android.util.AttributeSet +import android.widget.Button +import android.widget.LinearLayout +import android.widget.TextView +import com.android.wallpaper.R +import com.android.wallpaper.model.WallpaperInfo +import java.util.concurrent.Executors + +/** A view for displaying wallpaper info. */ +class WallpaperInfoView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) { + + private val executorService = Executors.newCachedThreadPool() + private var title: TextView? = null + private var subtitle1: TextView? = null + private var subtitle2: TextView? = null + private var exploreButton: Button? = null + + override fun onFinishInflate() { + super.onFinishInflate() + title = findViewById(R.id.wallpaper_info_title) + subtitle1 = findViewById(R.id.wallpaper_info_subtitle1) + subtitle2 = findViewById(R.id.wallpaper_info_subtitle2) + exploreButton = findViewById(R.id.wallpaper_info_explore_button) + } + + /** Populates wallpaper info. */ + fun populateWallpaperInfo( + wallpaperInfo: WallpaperInfo, + actionLabel: CharSequence?, + shouldShowExploreButton: Boolean, + exploreButtonClickListener: OnClickListener? + ) { + executorService.execute { + val attributions = wallpaperInfo.getAttributions(context) + Handler(Looper.getMainLooper()).post { + + // Reset wallpaper information UI + title?.text = "" + subtitle1?.text = "" + subtitle1?.visibility = GONE + subtitle2?.text = "" + subtitle2?.visibility = GONE + exploreButton?.text = "" + exploreButton?.setOnClickListener(null) + exploreButton?.visibility = GONE + if (attributions.size > 0 && attributions[0] != null) { + title?.text = attributions[0] + } + if (shouldShowMetadata(wallpaperInfo)) { + if (attributions.size > 1 && attributions[1] != null) { + subtitle1?.visibility = VISIBLE + subtitle1?.text = attributions[1] + } + if (attributions.size > 2 && attributions[2] != null) { + subtitle2?.visibility = VISIBLE + subtitle2?.text = attributions[2] + } + if (shouldShowExploreButton) { + exploreButton?.visibility = VISIBLE + exploreButton?.text = actionLabel + exploreButton?.setOnClickListener(exploreButtonClickListener) + } + } + } + } + } + + private fun shouldShowMetadata(wallpaperInfo: WallpaperInfo): Boolean { + val wallpaperComponent = wallpaperInfo.wallpaperComponent + return wallpaperComponent == null || wallpaperComponent.showMetadataInPreview + } +} diff --git a/src_override/com/android/wallpaper/module/WallpapersInjector.java b/src_override/com/android/wallpaper/module/WallpapersInjector.java deleted file mode 100755 index 54a2533d..00000000 --- a/src_override/com/android/wallpaper/module/WallpapersInjector.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.wallpaper.module; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; - -import androidx.fragment.app.Fragment; - -import com.android.wallpaper.model.CategoryProvider; -import com.android.wallpaper.model.WallpaperInfo; -import com.android.wallpaper.monitor.PerformanceMonitor; -import com.android.wallpaper.picker.CustomizationPickerActivity; -import com.android.wallpaper.picker.ImagePreviewFragment; - -/** - * A concrete, real implementation of the dependency provider. - */ -public class WallpapersInjector extends BaseWallpaperInjector { - private CategoryProvider mCategoryProvider; - private UserEventLogger mUserEventLogger; - private WallpaperRotationRefresher mWallpaperRotationRefresher; - private PerformanceMonitor mPerformanceMonitor; - - @Override - public synchronized CategoryProvider getCategoryProvider(Context context) { - if (mCategoryProvider == null) { - mCategoryProvider = new DefaultCategoryProvider(context.getApplicationContext()); - } - return mCategoryProvider; - } - - @Override - public synchronized UserEventLogger getUserEventLogger(Context context) { - if (mUserEventLogger == null) { - mUserEventLogger = new NoOpUserEventLogger(); - } - return mUserEventLogger; - } - - @Override - public synchronized WallpaperRotationRefresher getWallpaperRotationRefresher() { - if (mWallpaperRotationRefresher == null) { - mWallpaperRotationRefresher = new WallpaperRotationRefresher() { - @Override - public void refreshWallpaper(Context context, Listener listener) { - // Not implemented - listener.onError(); - } - }; - } - return mWallpaperRotationRefresher; - } - - @Override - public Fragment getPreviewFragment( - Context context, - WallpaperInfo wallpaperInfo, - int mode, - boolean viewAsHome, - boolean viewFullScreen, - boolean testingModeEnabled) { - return ImagePreviewFragment.newInstance(wallpaperInfo, mode, viewAsHome, viewFullScreen, - testingModeEnabled); - } - - @Override - public Intent getDeepLinkRedirectIntent(Context context, Uri uri) { - Intent intent = new Intent(); - intent.setClass(context, CustomizationPickerActivity.class); - intent.setData(uri); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - return intent; - } - - @Override - public synchronized PerformanceMonitor getPerformanceMonitor() { - if (mPerformanceMonitor == null) { - mPerformanceMonitor = new PerformanceMonitor() { - @Override - public void recordFullResPreviewLoadedMemorySnapshot() { - // No Op - } - }; - } - return mPerformanceMonitor; - } - - @Override - public synchronized LoggingOptInStatusProvider getLoggingOptInStatusProvider(Context context) { - return null; - } - - @Override - public String getDownloadableIntentAction() { - return null; - } -} diff --git a/tests/Android.bp b/tests/Android.bp index 54a3842e..c21b5266 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -20,6 +20,23 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } +android_library { + name: "WallpaperPicker2TestLib", + + defaults: ["WallpaperPicker2_defaults"], + srcs: [ + "src/com/android/wallpaper/testing/**/*.java", + "src/com/android/wallpaper/testing/**/*.kt", + ], + static_libs: [ + "androidx.annotation_annotation", + "kotlinx_coroutines_test", + "truth-prebuilt", + ], + + platform_apis: true, +} + android_test { name: "WallpaperPicker2Tests", @@ -28,8 +45,12 @@ android_test { "src/**/*.java", "src/**/*.kt", ], + exclude_srcs: [ + "src/com/android/wallpaper/testing/**/*.java", + "src/com/android/wallpaper/testing/**/*.kt", + ], static_libs: [ - "androidx.annotation_annotation", + "WallpaperPicker2TestLib", "androidx.test.espresso.core", "androidx.test.espresso.contrib", "androidx.test.espresso.intents", @@ -39,6 +60,8 @@ android_test { "mockito-target-minus-junit4", "ub-uiautomator", "junit", + "kotlinx_coroutines_test", + "truth-prebuilt", ], libs: [ "android.test.runner", diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp new file mode 100644 index 00000000..37fb21a9 --- /dev/null +++ b/tests/robotests/Android.bp @@ -0,0 +1,22 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_robolectric_test { + enabled: true, + + name: "WallpaperPicker2RoboTests", + + srcs: [ + "src/**/*.java", + ], + + java_resource_dirs: ["config"], + + libs: [ + "androidx.test.core", + "androidx.test.runner", + ], + + instrumentation_for: "WallpaperPicker2", +} diff --git a/tests/robotests/AndroidManifest.xml b/tests/robotests/AndroidManifest.xml new file mode 100644 index 00000000..09095932 --- /dev/null +++ b/tests/robotests/AndroidManifest.xml @@ -0,0 +1,6 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wallpaper"> + + <application/> + +</manifest> diff --git a/tests/robotests/config/robolectric.properties b/tests/robotests/config/robolectric.properties new file mode 100644 index 00000000..fab7251d --- /dev/null +++ b/tests/robotests/config/robolectric.properties @@ -0,0 +1 @@ +sdk=NEWEST_SDK diff --git a/robolectric_tests/src/com/android/wallpaper/picker/BaseActivityTest.java b/tests/robotests/src/com/android/wallpaper/picker/BaseActivityTest.java index 9ded630f..9ded630f 100644 --- a/robolectric_tests/src/com/android/wallpaper/picker/BaseActivityTest.java +++ b/tests/robotests/src/com/android/wallpaper/picker/BaseActivityTest.java diff --git a/tests/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModelTest.kt b/tests/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModelTest.kt new file mode 100644 index 00000000..f9c06405 --- /dev/null +++ b/tests/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModelTest.kt @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.customization.ui.viewmodel + +import androidx.lifecycle.SavedStateHandle +import androidx.test.filters.SmallTest +import com.android.wallpaper.picker.undo.data.repository.UndoRepository +import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor +import com.android.wallpaper.testing.FAKE_RESTORERS +import com.android.wallpaper.testing.collectLastValue +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class CustomizationPickerViewModelTest { + + private lateinit var underTest: CustomizationPickerViewModel + + private lateinit var savedStateHandle: SavedStateHandle + private lateinit var testScope: TestScope + private lateinit var undoInteractor: UndoInteractor + + @Before + fun setUp() { + val testDispatcher = StandardTestDispatcher() + testScope = TestScope(testDispatcher) + savedStateHandle = SavedStateHandle() + undoInteractor = + UndoInteractor( + scope = testScope.backgroundScope, + repository = UndoRepository(), + restorerByOwnerId = FAKE_RESTORERS, + ) + + underTest = + CustomizationPickerViewModel( + undoInteractor = undoInteractor, + savedStateHandle = savedStateHandle, + ) + } + + @Test + fun `initial tab is lock screen`() = + testScope.runTest { + val homeScreenTab = collectLastValue(underTest.homeScreenTab) + val lockScreenTab = collectLastValue(underTest.lockScreenTab) + val isOnLockScreen = collectLastValue(underTest.isOnLockScreen) + + assertThat(homeScreenTab()?.isSelected).isFalse() + assertThat(lockScreenTab()?.isSelected).isTrue() + assertThat(isOnLockScreen()).isTrue() + } + + @Test + fun `switching to the home screen`() = + testScope.runTest { + val homeScreenTab = collectLastValue(underTest.homeScreenTab) + val lockScreenTab = collectLastValue(underTest.lockScreenTab) + val isOnLockScreen = collectLastValue(underTest.isOnLockScreen) + + homeScreenTab()?.onClicked?.invoke() + + assertThat(homeScreenTab()?.isSelected).isTrue() + assertThat(lockScreenTab()?.isSelected).isFalse() + assertThat(isOnLockScreen()).isFalse() + } + + @Test + fun `switching to the home screen and back to the lock screen`() = + testScope.runTest { + val homeScreenTab = collectLastValue(underTest.homeScreenTab) + val lockScreenTab = collectLastValue(underTest.lockScreenTab) + val isOnLockScreen = collectLastValue(underTest.isOnLockScreen) + + homeScreenTab()?.onClicked?.invoke() + lockScreenTab()?.onClicked?.invoke() + + assertThat(homeScreenTab()?.isSelected).isFalse() + assertThat(lockScreenTab()?.isSelected).isTrue() + assertThat(isOnLockScreen()).isTrue() + } + + @Test + fun `restores saved state`() = + testScope.runTest { + val oldHomeScreenTab = collectLastValue(underTest.homeScreenTab) + + // Switch to the home screen, which is **not** the default. + oldHomeScreenTab()?.onClicked?.invoke() + + // Instantiate a new view-model with the same saved state + val newUnderTest = + CustomizationPickerViewModel( + undoInteractor = undoInteractor, + savedStateHandle = savedStateHandle, + ) + val newHomeScreenTab = collectLastValue(newUnderTest.homeScreenTab) + + assertThat(newHomeScreenTab()?.isSelected).isTrue() + } +} diff --git a/tests/src/com/android/wallpaper/picker/undo/data/repository/UndoRepositoryTest.kt b/tests/src/com/android/wallpaper/picker/undo/data/repository/UndoRepositoryTest.kt new file mode 100644 index 00000000..d9f85626 --- /dev/null +++ b/tests/src/com/android/wallpaper/picker/undo/data/repository/UndoRepositoryTest.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.undo.data.repository + +import androidx.test.filters.SmallTest +import com.android.wallpaper.testing.collectLastValue +import com.android.wallpaper.testing.snapshot +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class UndoRepositoryTest { + + private lateinit var underTest: UndoRepository + + @Before + fun setUp() { + underTest = UndoRepository() + } + + @Test + fun `put and get initial snapshot`() { + val ownerId1 = 1 + val ownerId2 = 2 + + underTest.putSnapshot(ownerId1, snapshot(ownerId1, 1)) + underTest.putSnapshot(ownerId2, snapshot(ownerId2, 1)) + + assertThat(underTest.getSnapshot(ownerId1)).isEqualTo(snapshot(ownerId1, 1)) + assertThat(underTest.getSnapshot(ownerId2)).isEqualTo(snapshot(ownerId2, 1)) + } + + @Test + fun dirty() = runTest { + val ownerId1 = 1 + val ownerId2 = 2 + val isUndoable = collectLastValue(underTest.isAnythingDirty) + + assertThat(isUndoable()).isFalse() + assertThat(underTest.getAllDirty()).isEmpty() + + underTest.putDirty(ownerId1, true) + assertThat(isUndoable()).isTrue() + assertThat(underTest.getAllDirty()).isEqualTo(setOf(ownerId1)) + + underTest.putDirty(ownerId2, true) + assertThat(isUndoable()).isTrue() + assertThat(underTest.getAllDirty()).isEqualTo(setOf(ownerId1, ownerId2)) + + underTest.putDirty(ownerId1, false) + assertThat(isUndoable()).isTrue() + assertThat(underTest.getAllDirty()).isEqualTo(setOf(ownerId2)) + + underTest.putDirty(ownerId2, false) + assertThat(isUndoable()).isFalse() + assertThat(underTest.getAllDirty()).isEmpty() + } +} diff --git a/tests/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractorTest.kt b/tests/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractorTest.kt new file mode 100644 index 00000000..5cbf67a2 --- /dev/null +++ b/tests/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractorTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.undo.domain.interactor + +import androidx.test.filters.SmallTest +import com.android.wallpaper.picker.undo.data.repository.UndoRepository +import com.android.wallpaper.testing.FAKE_RESTORERS +import com.android.wallpaper.testing.collectLastValue +import com.android.wallpaper.testing.snapshot +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class UndoInteractorTest { + + private lateinit var underTest: UndoInteractor + + private lateinit var testScope: TestScope + + @Before + fun setUp() { + val testDispatcher = StandardTestDispatcher() + testScope = TestScope(testDispatcher) + + underTest = + UndoInteractor( + scope = testScope.backgroundScope, + repository = UndoRepository(), + restorerByOwnerId = FAKE_RESTORERS, + ) + } + + @Test + fun `start session - update - and undo all`() = + testScope.runTest { + val isUndoable = collectLastValue(underTest.isUndoable) + assertThat(isUndoable()).isFalse() + + underTest.startSession() + assertThat(isUndoable()).isFalse() + + FAKE_RESTORERS[1]?.update(2) + assertThat(isUndoable()).isTrue() + + FAKE_RESTORERS[1]?.update(0) // This resets back to the initial snapshot + assertThat(isUndoable()).isFalse() + + FAKE_RESTORERS[1]?.update(1) + FAKE_RESTORERS[2]?.update(2) + assertThat(isUndoable()).isTrue() + + underTest.revertAll() + assertThat(isUndoable()).isFalse() + assertThat(FAKE_RESTORERS[1]?.restored).isEqualTo(snapshot(1, 0)) + assertThat(FAKE_RESTORERS[2]?.restored).isEqualTo(snapshot(2, 0)) + assertThat(FAKE_RESTORERS[3]?.restored).isNull() + } +} diff --git a/tests/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModelTest.kt b/tests/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModelTest.kt new file mode 100644 index 00000000..0def5f29 --- /dev/null +++ b/tests/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModelTest.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2022 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.wallpaper.picker.undo.ui.viewmodel + +import androidx.test.filters.SmallTest +import com.android.wallpaper.picker.undo.data.repository.UndoRepository +import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor +import com.android.wallpaper.testing.FAKE_RESTORERS +import com.android.wallpaper.testing.collectLastValue +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class UndoViewModelTest { + + private lateinit var underTest: UndoViewModel + + private lateinit var testScope: TestScope + private lateinit var interactor: UndoInteractor + + @Before + fun setUp() { + val testDispatcher = StandardTestDispatcher() + testScope = TestScope(testDispatcher) + interactor = + UndoInteractor( + scope = testScope.backgroundScope, + repository = UndoRepository(), + restorerByOwnerId = FAKE_RESTORERS, + ) + + underTest = + UndoViewModel( + interactor = interactor, + ) + } + + @Test + fun revert() = + testScope.runTest { + val isRevertButtonVisible = collectLastValue(underTest.isRevertButtonVisible) + val dialog = collectLastValue(underTest.dialog) + assertThat(isRevertButtonVisible()).isFalse() + assertThat(dialog()).isNull() + + // Start the session without anything to revert. + interactor.startSession() + assertThat(isRevertButtonVisible()).isFalse() + assertThat(dialog()).isNull() + + // Record a change that can be reverted. + FAKE_RESTORERS[1]?.update(2) + assertThat(isRevertButtonVisible()).isTrue() + assertThat(dialog()).isNull() + + // Click the revert button. + underTest.onRevertButtonClicked() + assertThat(isRevertButtonVisible()).isTrue() + assertThat(dialog()).isNotNull() + + // Cancel the revert. + dialog()?.onDismissed?.invoke() + assertThat(isRevertButtonVisible()).isTrue() + assertThat(dialog()).isNull() + + // Click the revert button again. + underTest.onRevertButtonClicked() + assertThat(isRevertButtonVisible()).isTrue() + assertThat(dialog()).isNotNull() + + // Confirm the revert. + dialog()?.onConfirmed?.invoke() + assertThat(isRevertButtonVisible()).isFalse() + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestCustomizationSections.kt b/tests/src/com/android/wallpaper/testing/TestCustomizationSections.kt new file mode 100644 index 00000000..0c0ac2ee --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestCustomizationSections.kt @@ -0,0 +1,48 @@ +package com.android.wallpaper.testing + +import android.os.Bundle +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.LifecycleOwner +import com.android.wallpaper.model.CustomizationSectionController +import com.android.wallpaper.model.PermissionRequester +import com.android.wallpaper.model.WallpaperColorsViewModel +import com.android.wallpaper.model.WallpaperPreviewNavigator +import com.android.wallpaper.model.WorkspaceViewModel +import com.android.wallpaper.module.CurrentWallpaperInfoFactory +import com.android.wallpaper.module.CustomizationSections +import com.android.wallpaper.util.DisplayUtils + +/** Test implementation of [CustomizationSections] */ +class TestCustomizationSections : CustomizationSections { + override fun getSectionControllersForScreen( + screen: CustomizationSections.Screen?, + activity: FragmentActivity?, + lifecycleOwner: LifecycleOwner?, + wallpaperColorsViewModel: WallpaperColorsViewModel?, + workspaceViewModel: WorkspaceViewModel?, + permissionRequester: PermissionRequester?, + wallpaperPreviewNavigator: WallpaperPreviewNavigator?, + sectionNavigationController: + CustomizationSectionController.CustomizationSectionNavigationController?, + savedInstanceState: Bundle?, + wallpaperInfoFactory: CurrentWallpaperInfoFactory?, + displayUtils: DisplayUtils? + ): MutableList<CustomizationSectionController<*>> { + return arrayListOf() + } + + override fun getAllSectionControllers( + activity: FragmentActivity?, + lifecycleOwner: LifecycleOwner?, + wallpaperColorsViewModel: WallpaperColorsViewModel?, + workspaceViewModel: WorkspaceViewModel?, + permissionRequester: PermissionRequester?, + wallpaperPreviewNavigator: WallpaperPreviewNavigator?, + sectionNavigationController: + CustomizationSectionController.CustomizationSectionNavigationController?, + savedInstanceState: Bundle?, + displayUtils: DisplayUtils? + ): MutableList<CustomizationSectionController<*>> { + return arrayListOf() + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestDrawableLayerResolver.kt b/tests/src/com/android/wallpaper/testing/TestDrawableLayerResolver.kt new file mode 100644 index 00000000..43b3ad47 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestDrawableLayerResolver.kt @@ -0,0 +1,12 @@ +package com.android.wallpaper.testing + +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import com.android.wallpaper.module.DrawableLayerResolver + +/** Test implementation of [DrawableLayerResolver] */ +class TestDrawableLayerResolver : DrawableLayerResolver { + override fun resolveLayer(layerDrawable: LayerDrawable?): Drawable { + return layerDrawable!!.getDrawable(0) + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestInjector.java b/tests/src/com/android/wallpaper/testing/TestInjector.java deleted file mode 100644 index ec8fb16f..00000000 --- a/tests/src/com/android/wallpaper/testing/TestInjector.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2019 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.wallpaper.testing; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; - -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - -import com.android.wallpaper.compat.WallpaperManagerCompat; -import com.android.wallpaper.effects.EffectsController; -import com.android.wallpaper.model.CategoryProvider; -import com.android.wallpaper.model.WallpaperInfo; -import com.android.wallpaper.module.AlarmManagerWrapper; -import com.android.wallpaper.module.BitmapCropper; -import com.android.wallpaper.module.CurrentWallpaperInfoFactory; -import com.android.wallpaper.module.CustomizationSections; -import com.android.wallpaper.module.DefaultLiveWallpaperInfoFactory; -import com.android.wallpaper.module.DrawableLayerResolver; -import com.android.wallpaper.module.ExploreIntentChecker; -import com.android.wallpaper.module.Injector; -import com.android.wallpaper.module.LiveWallpaperInfoFactory; -import com.android.wallpaper.module.LoggingOptInStatusProvider; -import com.android.wallpaper.module.NetworkStatusNotifier; -import com.android.wallpaper.module.PackageStatusNotifier; -import com.android.wallpaper.module.PartnerProvider; -import com.android.wallpaper.module.SystemFeatureChecker; -import com.android.wallpaper.module.UserEventLogger; -import com.android.wallpaper.module.WallpaperPersister; -import com.android.wallpaper.module.WallpaperPreferences; -import com.android.wallpaper.module.WallpaperRefresher; -import com.android.wallpaper.module.WallpaperRotationRefresher; -import com.android.wallpaper.module.WallpaperStatusChecker; -import com.android.wallpaper.monitor.PerformanceMonitor; -import com.android.wallpaper.network.Requester; -import com.android.wallpaper.picker.ImagePreviewFragment; -import com.android.wallpaper.picker.individual.IndividualPickerFragment; -import com.android.wallpaper.util.DisplayUtils; - -/** - * Test implementation of the dependency injector. - */ -public class TestInjector implements Injector { - - private BitmapCropper mBitmapCropper; - private CategoryProvider mCategoryProvider; - private PartnerProvider mPartnerProvider; - private WallpaperPreferences mPrefs; - private WallpaperPersister mWallpaperPersister; - private WallpaperRefresher mWallpaperRefresher; - private Requester mRequester; - private WallpaperManagerCompat mWallpaperManagerCompat; - private CurrentWallpaperInfoFactory mCurrentWallpaperInfoFactory; - private NetworkStatusNotifier mNetworkStatusNotifier; - private AlarmManagerWrapper mAlarmManagerWrapper; - private UserEventLogger mUserEventLogger; - private ExploreIntentChecker mExploreIntentChecker; - private SystemFeatureChecker mSystemFeatureChecker; - private WallpaperRotationRefresher mWallpaperRotationRefresher; - private PerformanceMonitor mPerformanceMonitor; - private LoggingOptInStatusProvider mLoggingOptInStatusProvider; - - @Override - public BitmapCropper getBitmapCropper() { - if (mBitmapCropper == null) { - mBitmapCropper = new com.android.wallpaper.testing.TestBitmapCropper(); - } - return mBitmapCropper; - } - - @Override - public CategoryProvider getCategoryProvider(Context context) { - if (mCategoryProvider == null) { - mCategoryProvider = new TestCategoryProvider(); - } - return mCategoryProvider; - } - - @Override - public PartnerProvider getPartnerProvider(Context context) { - if (mPartnerProvider == null) { - mPartnerProvider = new TestPartnerProvider(); - } - return mPartnerProvider; - } - - @Override - public WallpaperPreferences getPreferences(Context context) { - if (mPrefs == null) { - mPrefs = new TestWallpaperPreferences(); - } - return mPrefs; - } - - @Override - public WallpaperPersister getWallpaperPersister(Context context) { - if (mWallpaperPersister == null) { - mWallpaperPersister = new TestWallpaperPersister(context.getApplicationContext()); - } - return mWallpaperPersister; - } - - @Override - public WallpaperRefresher getWallpaperRefresher(Context context) { - if (mWallpaperRefresher == null) { - mWallpaperRefresher = new TestWallpaperRefresher(context.getApplicationContext()); - } - return mWallpaperRefresher; - } - - @Override - public Requester getRequester(Context unused) { - return null; - } - - @Override - public WallpaperManagerCompat getWallpaperManagerCompat(Context context) { - if (mWallpaperManagerCompat == null) { - mWallpaperManagerCompat = new com.android.wallpaper.testing.TestWallpaperManagerCompat( - context.getApplicationContext()); - } - return mWallpaperManagerCompat; - } - - @Override - public WallpaperStatusChecker getWallpaperStatusChecker() { - return new WallpaperStatusChecker() { - @Override - public boolean isHomeStaticWallpaperSet(Context context) { - return true; - } - - @Override - public boolean isLockWallpaperSet(Context context) { - return true; - } - }; - } - - @Override - public CurrentWallpaperInfoFactory getCurrentWallpaperFactory(Context context) { - if (mCurrentWallpaperInfoFactory == null) { - mCurrentWallpaperInfoFactory = - new TestCurrentWallpaperInfoFactory(context.getApplicationContext()); - } - return mCurrentWallpaperInfoFactory; - } - - @Override - public LoggingOptInStatusProvider getLoggingOptInStatusProvider(Context context) { - if (mLoggingOptInStatusProvider == null) { - mLoggingOptInStatusProvider = new TestLoggingOptInStatusProvider(); - } - return mLoggingOptInStatusProvider; - } - - @Override - public NetworkStatusNotifier getNetworkStatusNotifier(Context context) { - if (mNetworkStatusNotifier == null) { - mNetworkStatusNotifier = new TestNetworkStatusNotifier(); - } - return mNetworkStatusNotifier; - } - - @Override - public AlarmManagerWrapper getAlarmManagerWrapper(Context unused) { - if (mAlarmManagerWrapper == null) { - mAlarmManagerWrapper = new TestAlarmManagerWrapper(); - } - return mAlarmManagerWrapper; - } - - @Override - public UserEventLogger getUserEventLogger(Context unused) { - if (mUserEventLogger == null) { - mUserEventLogger = new com.android.wallpaper.testing.TestUserEventLogger(); - } - return mUserEventLogger; - } - - @Override - public ExploreIntentChecker getExploreIntentChecker(Context unused) { - if (mExploreIntentChecker == null) { - mExploreIntentChecker = new TestExploreIntentChecker(); - } - return mExploreIntentChecker; - } - - @Override - public SystemFeatureChecker getSystemFeatureChecker() { - if (mSystemFeatureChecker == null) { - mSystemFeatureChecker = new com.android.wallpaper.testing.TestSystemFeatureChecker(); - } - return mSystemFeatureChecker; - } - - @Override - public WallpaperRotationRefresher getWallpaperRotationRefresher() { - if (mWallpaperRotationRefresher == null) { - mWallpaperRotationRefresher = (context, listener) -> { - // Not implemented - listener.onError(); - }; - } - return mWallpaperRotationRefresher; - } - - @Override - public Fragment getPreviewFragment(Context context, WallpaperInfo wallpaperInfo, int mode, - boolean viewAsHome, boolean viewFullScreen, boolean testingModeEnabled) { - return ImagePreviewFragment.newInstance(wallpaperInfo, mode, viewAsHome, - viewFullScreen, testingModeEnabled); - } - - @Override - public PackageStatusNotifier getPackageStatusNotifier(Context context) { - return null; - } - - @Override - public IndividualPickerFragment getIndividualPickerFragment(String collectionId) { - return IndividualPickerFragment.newInstance(collectionId); - } - - @Override - public LiveWallpaperInfoFactory getLiveWallpaperInfoFactory(Context context) { - return new DefaultLiveWallpaperInfoFactory(); - } - - @Override - public DrawableLayerResolver getDrawableLayerResolver() { - return null; - } - - @Override - public Intent getDeepLinkRedirectIntent(Context context, Uri uri) { - return null; - } - - @Override - public String getDownloadableIntentAction() { - return null; - } - - @Override - public PerformanceMonitor getPerformanceMonitor() { - if (mPerformanceMonitor == null) { - mPerformanceMonitor = new TestPerformanceMonitor(); - } - return mPerformanceMonitor; - } - - @Override - public CustomizationSections getCustomizationSections() { - return null; - } - - @Override - public DisplayUtils getDisplayUtils(Context context) { - return new DisplayUtils(context); - } - - @Nullable - @Override - public EffectsController createEffectsController(Context context, - EffectsController.EffectsServiceListener listener) { - return null; - } -} diff --git a/tests/src/com/android/wallpaper/testing/TestInjector.kt b/tests/src/com/android/wallpaper/testing/TestInjector.kt new file mode 100644 index 00000000..7c15540e --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestInjector.kt @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2019 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.wallpaper.testing + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import androidx.fragment.app.Fragment +import com.android.wallpaper.compat.WallpaperManagerCompat +import com.android.wallpaper.config.BaseFlags +import com.android.wallpaper.effects.EffectsController +import com.android.wallpaper.effects.EffectsController.EffectsServiceListener +import com.android.wallpaper.model.CategoryProvider +import com.android.wallpaper.model.WallpaperInfo +import com.android.wallpaper.module.AlarmManagerWrapper +import com.android.wallpaper.module.BitmapCropper +import com.android.wallpaper.module.CurrentWallpaperInfoFactory +import com.android.wallpaper.module.CustomizationSections +import com.android.wallpaper.module.DefaultLiveWallpaperInfoFactory +import com.android.wallpaper.module.DrawableLayerResolver +import com.android.wallpaper.module.ExploreIntentChecker +import com.android.wallpaper.module.Injector +import com.android.wallpaper.module.LiveWallpaperInfoFactory +import com.android.wallpaper.module.NetworkStatusNotifier +import com.android.wallpaper.module.PackageStatusNotifier +import com.android.wallpaper.module.PartnerProvider +import com.android.wallpaper.module.SystemFeatureChecker +import com.android.wallpaper.module.UserEventLogger +import com.android.wallpaper.module.WallpaperPersister +import com.android.wallpaper.module.WallpaperPreferences +import com.android.wallpaper.module.WallpaperPreviewFragmentManager +import com.android.wallpaper.module.WallpaperRefresher +import com.android.wallpaper.module.WallpaperRotationRefresher +import com.android.wallpaper.module.WallpaperStatusChecker +import com.android.wallpaper.monitor.PerformanceMonitor +import com.android.wallpaper.network.Requester +import com.android.wallpaper.picker.ImagePreviewFragment +import com.android.wallpaper.picker.PreviewFragment +import com.android.wallpaper.picker.individual.IndividualPickerFragment +import com.android.wallpaper.picker.undo.data.repository.UndoRepository +import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor +import com.android.wallpaper.util.DisplayUtils +import kotlinx.coroutines.GlobalScope + +/** Test implementation of [Injector] */ +open class TestInjector : Injector { + private var alarmManagerWrapper: AlarmManagerWrapper? = null + private var bitmapCropper: BitmapCropper? = null + private var categoryProvider: CategoryProvider? = null + private var currentWallpaperInfoFactory: CurrentWallpaperInfoFactory? = null + private var customizationSections: CustomizationSections? = null + private var drawableLayerResolver: DrawableLayerResolver? = null + private var exploreIntentChecker: ExploreIntentChecker? = null + private var networkStatusNotifier: NetworkStatusNotifier? = null + private var packageStatusNotifier: PackageStatusNotifier? = null + private var partnerProvider: PartnerProvider? = null + private var performanceMonitor: PerformanceMonitor? = null + private var requester: Requester? = null + private var systemFeatureChecker: SystemFeatureChecker? = null + private var userEventLogger: UserEventLogger? = null + private var wallpaperManagerCompat: WallpaperManagerCompat? = null + private var wallpaperPersister: WallpaperPersister? = null + private var prefs: WallpaperPreferences? = null + private var wallpaperPreviewFragmentManager: WallpaperPreviewFragmentManager? = null + private var wallpaperRefresher: WallpaperRefresher? = null + private var wallpaperRotationRefresher: WallpaperRotationRefresher? = null + private var flags: BaseFlags? = null + private var undoInteractor: UndoInteractor? = null + + override fun getAlarmManagerWrapper(context: Context): AlarmManagerWrapper { + return alarmManagerWrapper ?: TestAlarmManagerWrapper().also { alarmManagerWrapper = it } + } + + override fun getBitmapCropper(): BitmapCropper { + return bitmapCropper ?: TestBitmapCropper().also { bitmapCropper = it } + } + + override fun getCategoryProvider(context: Context): CategoryProvider { + return categoryProvider ?: TestCategoryProvider().also { categoryProvider = it } + } + + override fun getCurrentWallpaperInfoFactory(context: Context): CurrentWallpaperInfoFactory { + return currentWallpaperInfoFactory + ?: TestCurrentWallpaperInfoFactory(context.applicationContext).also { + currentWallpaperInfoFactory = it + } + } + + override fun getCustomizationSections(activity: Activity): CustomizationSections { + return customizationSections + ?: TestCustomizationSections().also { customizationSections = it } + } + + override fun getDeepLinkRedirectIntent(context: Context, uri: Uri): Intent { + return Intent() + } + + override fun getDisplayUtils(context: Context): DisplayUtils { + return DisplayUtils(context) + } + + override fun getDownloadableIntentAction(): String? { + return null + } + + override fun getDrawableLayerResolver(): DrawableLayerResolver { + return drawableLayerResolver + ?: TestDrawableLayerResolver().also { drawableLayerResolver = it } + } + + override fun getEffectsController( + context: Context, + listener: EffectsServiceListener + ): EffectsController? { + return null + } + + override fun getExploreIntentChecker(context: Context): ExploreIntentChecker { + return exploreIntentChecker ?: TestExploreIntentChecker().also { exploreIntentChecker = it } + } + + override fun getIndividualPickerFragment(collectionId: String): IndividualPickerFragment { + return IndividualPickerFragment.newInstance(collectionId) + } + + override fun getLiveWallpaperInfoFactory(context: Context): LiveWallpaperInfoFactory { + return DefaultLiveWallpaperInfoFactory() + } + + override fun getNetworkStatusNotifier(context: Context): NetworkStatusNotifier { + return networkStatusNotifier + ?: TestNetworkStatusNotifier().also { networkStatusNotifier = it } + } + + override fun getPackageStatusNotifier(context: Context): PackageStatusNotifier { + return packageStatusNotifier + ?: TestPackageStatusNotifier().also { packageStatusNotifier = it } + } + + override fun getPartnerProvider(context: Context): PartnerProvider { + return partnerProvider ?: TestPartnerProvider().also { partnerProvider = it } + } + + override fun getPerformanceMonitor(): PerformanceMonitor? { + return performanceMonitor ?: TestPerformanceMonitor().also { performanceMonitor = it } + } + + override fun getPreviewFragment( + context: Context, + wallpaperInfo: WallpaperInfo, + mode: Int, + viewAsHome: Boolean, + viewFullScreen: Boolean, + testingModeEnabled: Boolean + ): Fragment { + val args = Bundle() + args.putParcelable(PreviewFragment.ARG_WALLPAPER, wallpaperInfo) + args.putInt(PreviewFragment.ARG_PREVIEW_MODE, mode) + args.putBoolean(PreviewFragment.ARG_VIEW_AS_HOME, viewAsHome) + args.putBoolean(PreviewFragment.ARG_FULL_SCREEN, viewFullScreen) + args.putBoolean(PreviewFragment.ARG_TESTING_MODE_ENABLED, testingModeEnabled) + val fragment = ImagePreviewFragment() + fragment.arguments = args + return fragment + } + + override fun getRequester(unused: Context): Requester { + return requester ?: TestRequester().also { requester = it } + } + + override fun getSystemFeatureChecker(): SystemFeatureChecker { + return systemFeatureChecker ?: TestSystemFeatureChecker().also { systemFeatureChecker = it } + } + + override fun getUserEventLogger(context: Context): UserEventLogger { + return userEventLogger ?: TestUserEventLogger().also { userEventLogger = it } + } + + override fun getWallpaperManagerCompat(context: Context): WallpaperManagerCompat { + return wallpaperManagerCompat + ?: TestWallpaperManagerCompat(context.applicationContext).also { + wallpaperManagerCompat = it + } + } + + override fun getWallpaperPersister(context: Context): WallpaperPersister { + return wallpaperPersister + ?: TestWallpaperPersister(context.applicationContext).also { wallpaperPersister = it } + } + + override fun getPreferences(context: Context): WallpaperPreferences { + return prefs ?: TestWallpaperPreferences().also { prefs = it } + } + + override fun getWallpaperPreviewFragmentManager(): WallpaperPreviewFragmentManager { + return wallpaperPreviewFragmentManager + ?: TestWallpaperPreviewFragmentManager().also { wallpaperPreviewFragmentManager = it } + } + + override fun getWallpaperRefresher(context: Context): WallpaperRefresher { + return wallpaperRefresher + ?: TestWallpaperRefresher(context.applicationContext).also { wallpaperRefresher = it } + } + + override fun getWallpaperRotationRefresher(): WallpaperRotationRefresher { + return wallpaperRotationRefresher + ?: WallpaperRotationRefresher { + context: Context?, + listener: WallpaperRotationRefresher.Listener -> + // Not implemented + listener.onError() + } + .also { wallpaperRotationRefresher = it } + } + + override fun getWallpaperStatusChecker(): WallpaperStatusChecker { + return object : WallpaperStatusChecker { + override fun isHomeStaticWallpaperSet(context: Context): Boolean { + return true + } + + override fun isLockWallpaperSet(context: Context): Boolean { + return true + } + } + } + + override fun getFlags(): BaseFlags { + return flags ?: object : BaseFlags() {}.also { flags = it } + } + + override fun getUndoInteractor(context: Context): UndoInteractor { + return undoInteractor + ?: UndoInteractor( + GlobalScope, + UndoRepository(), + HashMap() + ) // Empty because we don't support undoing in WallpaperPicker2..also{} + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestPackageStatusNotifier.kt b/tests/src/com/android/wallpaper/testing/TestPackageStatusNotifier.kt new file mode 100644 index 00000000..fad86524 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestPackageStatusNotifier.kt @@ -0,0 +1,14 @@ +package com.android.wallpaper.testing + +import com.android.wallpaper.module.PackageStatusNotifier + +/** Test implementation of [PackageStatusNotifier] */ +class TestPackageStatusNotifier : PackageStatusNotifier { + override fun addListener(listener: PackageStatusNotifier.Listener?, action: String?) { + // Do nothing intended + } + + override fun removeListener(listener: PackageStatusNotifier.Listener?) { + // Do nothing intended + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestRequester.kt b/tests/src/com/android/wallpaper/testing/TestRequester.kt new file mode 100644 index 00000000..7f2f007c --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestRequester.kt @@ -0,0 +1,32 @@ +package com.android.wallpaper.testing + +import android.app.Activity +import android.graphics.Bitmap +import android.net.Uri +import com.android.volley.Request +import com.android.wallpaper.network.Requester +import com.bumptech.glide.request.target.Target +import java.io.File + +/** Test implementation of [Requester] */ +class TestRequester : Requester { + override fun <T : Any?> addToRequestQueue(request: Request<T>?) { + // Do nothing intended + } + + override fun loadImageFile(imageUrl: Uri?): File { + return File("test_file.txt") + } + + override fun loadImageFileWithActivity( + activity: Activity?, + imageUrl: Uri?, + target: Target<File>? + ) { + // Do nothing intended + } + + override fun loadImageBitmap(imageUrl: Uri?, target: Target<Bitmap>?) { + // Do nothing intended + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestScopeExt.kt b/tests/src/com/android/wallpaper/testing/TestScopeExt.kt new file mode 100644 index 00000000..b5d45b02 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestScopeExt.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 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. + * + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.wallpaper.testing + +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent + +/** Collect [flow] in a new [Job] and return a getter for the last collected value. */ +fun <T> TestScope.collectLastValue( + flow: Flow<T>, + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, +): () -> T? { + var lastValue: T? = null + backgroundScope.launch(context, start) { flow.collect { lastValue = it } } + return { + runCurrent() + lastValue + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java b/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java index f439678a..d99bf4b0 100644 --- a/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java +++ b/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java @@ -52,6 +52,10 @@ public class TestUserEventLogger implements UserEventLogger { private String mWallpaperSetEffects; private String mWallpaperApplyEffect; private int mWallpaperApplyEffectStatus; + private long mTimeElapsedMillis; + private int mResultCode; + private String mWallpaperProbeEffect; + private int mWallpaperProbeStatus; public TestUserEventLogger() { mLastDailyRotationHour = -1; @@ -240,9 +244,17 @@ public class TestUserEventLogger implements UserEventLogger { } @Override - public void logEffectApply(String effect, int status) { + public void logEffectApply(String effect, int status, long timeElapsedMillis, int resultCode) { mWallpaperApplyEffect = effect; mWallpaperApplyEffectStatus = status; + mTimeElapsedMillis = timeElapsedMillis; + mResultCode = resultCode; + } + + @Override + public void logEffectProbe(String effect, @EffectStatus int status) { + mWallpaperProbeEffect = effect; + mWallpaperProbeStatus = status; } public int getNumWallpaperSetEvents() { diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperPreviewFragmentManager.kt b/tests/src/com/android/wallpaper/testing/TestWallpaperPreviewFragmentManager.kt new file mode 100644 index 00000000..a144ed3a --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestWallpaperPreviewFragmentManager.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 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.wallpaper.testing + +import android.content.Context +import android.os.Bundle +import androidx.fragment.app.Fragment +import com.android.wallpaper.model.WallpaperInfo +import com.android.wallpaper.module.WallpaperPreviewFragmentManager +import com.android.wallpaper.picker.ImagePreviewFragment +import com.android.wallpaper.picker.PreviewFragment + +class TestWallpaperPreviewFragmentManager : WallpaperPreviewFragmentManager { + override fun getPreviewFragment( + context: Context, + wallpaperInfo: WallpaperInfo, + mode: Int, + viewAsHome: Boolean, + viewFullScreen: Boolean, + testingModeEnabled: Boolean + ): Fragment { + val args = Bundle() + args.putParcelable(PreviewFragment.ARG_WALLPAPER, wallpaperInfo) + args.putInt(PreviewFragment.ARG_PREVIEW_MODE, mode) + args.putBoolean(PreviewFragment.ARG_VIEW_AS_HOME, viewAsHome) + args.putBoolean(PreviewFragment.ARG_FULL_SCREEN, viewFullScreen) + args.putBoolean(PreviewFragment.ARG_TESTING_MODE_ENABLED, testingModeEnabled) + val fragment = ImagePreviewFragment() + fragment.arguments = args + return fragment + } +} diff --git a/tests/src/com/android/wallpaper/testing/UndoTestUtil.kt b/tests/src/com/android/wallpaper/testing/UndoTestUtil.kt new file mode 100644 index 00000000..0f30db2a --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/UndoTestUtil.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 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.wallpaper.testing + +import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer +import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot + +val FAKE_RESTORERS = + mapOf( + 1 to FakeSnapshotRestorer(1), + 2 to FakeSnapshotRestorer(2), + 3 to FakeSnapshotRestorer(3), + ) + +fun snapshot(ownerId: Int, version: Int): RestorableSnapshot { + return RestorableSnapshot( + mapOf( + KEY_OWNER_ID to "$ownerId", + KEY_VERSION to "$version", + ) + ) +} + +class FakeSnapshotRestorer( + private val ownerId: Int, +) : SnapshotRestorer { + private lateinit var updater: (RestorableSnapshot) -> Unit + var restored: RestorableSnapshot? = null + private set + + fun update(version: Int) { + updater(snapshot(ownerId, version)) + } + + override suspend fun setUpSnapshotRestorer( + updater: (RestorableSnapshot) -> Unit, + ): RestorableSnapshot { + this.updater = updater + return snapshot( + ownerId = ownerId, + version = 0, + ) + } + + override suspend fun restoreToSnapshot(snapshot: RestorableSnapshot) { + restored = snapshot + } +} + +private const val KEY_OWNER_ID = "ownerId" +private const val KEY_VERSION = "version" |