diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2019-11-21 02:10:26 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2019-11-21 02:10:26 +0000 |
commit | 846cf730fceaefd927a1d2fbbe0af18bfb739ee8 (patch) | |
tree | f1225c36792ebf211f4f90937fa4092dfd0fd3db | |
parent | d6b718de5f6bd24410087482a7687bb1fd09536b (diff) | |
parent | 9734f7414149c6fc9f0a704576ad17e4c09ec2d8 (diff) | |
download | android_packages_apps_WallpaperPicker2-846cf730fceaefd927a1d2fbbe0af18bfb739ee8.tar.gz android_packages_apps_WallpaperPicker2-846cf730fceaefd927a1d2fbbe0af18bfb739ee8.tar.bz2 android_packages_apps_WallpaperPicker2-846cf730fceaefd927a1d2fbbe0af18bfb739ee8.zip |
Snap for 6021960 from 9734f7414149c6fc9f0a704576ad17e4c09ec2d8 to qt-qpr2-release
Change-Id: I4e7faba13af7127727057361073631ba16328b73
70 files changed, 6012 insertions, 2736 deletions
@@ -16,24 +16,13 @@ LOCAL_PATH := $(call my-dir) -# -# Prebuilt Java Libraries -# -include $(CLEAR_VARS) -LOCAL_MODULE := libStyleProtos -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := JAVA_LIBRARIES -LOCAL_SRC_FILES := libs/style_protos.jar -LOCAL_UNINSTALLABLE_MODULE := true -LOCAL_SDK_VERSION := current -include $(BUILD_PREBUILT) - include $(CLEAR_VARS) LOCAL_MODULE_CLASS := JAVA_LIBRARIES LOCAL_MODULE := wallpaper2-glide-target LOCAL_SDK_VERSION := current LOCAL_SRC_FILES := ../../../prebuilts/maven_repo/bumptech/com/github/bumptech/glide/glide/SNAPSHOT/glide-SNAPSHOT$(COMMON_JAVA_PACKAGE_SUFFIX) LOCAL_UNINSTALLABLE_MODULE := true +LOCAL_JETIFIER_ENABLED := true include $(BUILD_PREBUILT) include $(CLEAR_VARS) @@ -64,7 +53,7 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../../../external/subsampling-scale-image-vi LOCAL_PROGUARD_ENABLED := disabled LOCAL_SDK_VERSION := current -LOCAL_MIN_SDK_VERSION := 21 +LOCAL_MIN_SDK_VERSION := 26 LOCAL_MODULE := wallpaper-subsampling-scale-image-view LOCAL_MANIFEST_FILE := ../../../external/subsampling-scale-image-view/library/src/main/AndroidManifest.xml @@ -82,6 +71,7 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ androidx.appcompat_appcompat \ androidx.cardview_cardview \ androidx.recyclerview_recyclerview \ + androidx.slice_slice-view \ androidx-constraintlayout_constraintlayout \ com.google.android.material_material \ androidx.exifinterface_exifinterface \ @@ -99,14 +89,7 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_PROGUARD_ENABLED := disabled LOCAL_MANIFEST_FILE := AndroidManifest.xml -ifneq (,$(wildcard frameworks/base)) - LOCAL_STATIC_JAVA_LIBRARIES += SystemUISharedLib styleprotosnano - LOCAL_PRIVATE_PLATFORM_APIS := true -else - LOCAL_STATIC_JAVA_LIBRARIES += libSharedSystemUI libStyleProtos - LOCAL_SDK_VERSION := current -endif - +LOCAL_SDK_VERSION := current LOCAL_MODULE := WallpaperPicker2CommonDepsLib LOCAL_PRIVILEGED_MODULE := true @@ -131,12 +114,18 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_PROGUARD_FLAG_FILES := proguard.flags LOCAL_PROGUARD_ENABLED := disabled +LOCAL_PRIVILEGED_MODULE := true + ifneq (,$(wildcard frameworks/base)) LOCAL_PRIVATE_PLATFORM_APIS := true else LOCAL_SDK_VERSION := system_current + LOCAL_STATIC_JAVA_LIBRARIES += libSharedWallpaper endif + LOCAL_PACKAGE_NAME := WallpaperPicker2 LOCAL_JETIFIER_ENABLED := true include $(BUILD_PACKAGE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 0192398..6096d10 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -20,7 +20,7 @@ android:name="com.android.wallpaper.NOTIFY_ROTATING_WALLPAPER_CHANGED" /> <application - tools:replace="android:name" + tools:replace="android:icon,android:name" android:allowBackup="true" android:icon="@mipmap/product_logo_wallpapers_launcher_color_48" android:label="@string/app_name" @@ -37,7 +37,7 @@ <activity android:name="com.android.wallpaper.picker.TopLevelPickerActivity" android:label="@string/app_name" android:theme="@style/WallpaperTheme" - android:resizeableActivity="true"> + android:resizeableActivity="false"> <intent-filter> <action android:name="android.intent.action.SET_WALLPAPER"/> <category android:name="android.intent.category.DEFAULT"/> @@ -50,14 +50,13 @@ android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity-alias> <activity android:name="com.android.wallpaper.picker.individual.IndividualPickerActivity" android:label="@string/app_name" android:theme="@style/WallpaperTheme" - android:resizeableActivity="true" + android:resizeableActivity="false" android:parentActivityName="com.android.wallpaper.picker.TopLevelPickerActivity"> </activity> diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg new file mode 100644 index 0000000..f3db20e --- /dev/null +++ b/PREUPLOAD.cfg @@ -0,0 +1,2 @@ +[Hook Scripts] +checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} diff --git a/libs/style_protos.jar b/libs/style_protos.jar Binary files differdeleted file mode 100644 index 8708555..0000000 --- a/libs/style_protos.jar +++ /dev/null diff --git a/res/drawable/gradient_background.xml b/res/drawable/gradient_background.xml new file mode 100644 index 0000000..47d864a --- /dev/null +++ b/res/drawable/gradient_background.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + + 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <gradient android:angle="270" + android:centerY=".1" + android:startColor="@color/translucent_black" + android:centerColor="@android:color/transparent" + android:endColor="@android:color/transparent" + /> +</shape>
\ No newline at end of file diff --git a/res/drawable/ic_delete_24px.xml b/res/drawable/ic_delete_24px.xml new file mode 100644 index 0000000..bc83f36 --- /dev/null +++ b/res/drawable/ic_delete_24px.xml @@ -0,0 +1,30 @@ +<!-- + 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="@android: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_settings.xml b/res/drawable/ic_settings.xml new file mode 100644 index 0000000..f24a0a8 --- /dev/null +++ b/res/drawable/ic_settings.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + + 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24.0dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0" + android:width="24.0dp" > + + <path + android:fillColor="#FFFFFFFF" + android:pathData="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z" /> + +</vector>
\ No newline at end of file diff --git a/res/layout/fragment_preview.xml b/res/layout/fragment_image_preview.xml index 82c481f..6c3e324 100755 --- a/res/layout/fragment_preview.xml +++ b/res/layout/fragment_image_preview.xml @@ -1,4 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" @@ -37,13 +52,19 @@ android:layout_height="wrap_content" android:layout_gravity="bottom"> - <include - layout="@layout/preview_page_info" + <FrameLayout android:id="@+id/bottom_sheet" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@drawable/preview_bottom_sheet_background" + android:theme="@style/WallpaperPicker.BottomPaneStyle" app:behavior_peekHeight="@dimen/preview_attribution_pane_collapsed_height" - app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"/> + app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> + <include + layout="@layout/preview_page_info" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + </FrameLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/res/layout/fragment_live_preview.xml b/res/layout/fragment_live_preview.xml new file mode 100755 index 0000000..418a289 --- /dev/null +++ b/res/layout/fragment_live_preview.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<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" + android:fitsSystemWindows="false"> + + <View + android:layout_width="match_parent" + android:layout_height="@dimen/preview_gradient_background_height" + android:layout_gravity="top" + android:background="@drawable/gradient_background"/> + + <FrameLayout + android:id="@+id/loading" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/black" + android:forceHasOverlappingRendering="false"> + + <ImageView + android:id="@+id/loading_indicator" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:visibility="visible" + android:fitsSystemWindows="false"/> + + </FrameLayout> + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true"> + + <androidx.coordinatorlayout.widget.CoordinatorLayout + android:id="@+id/coordinator_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom"> + + <LinearLayout + android:id="@+id/bottom_sheet" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:background="@drawable/preview_bottom_sheet_background" + android:theme="@style/WallpaperPicker.BottomPaneStyle" + app:behavior_peekHeight="@dimen/preview_attribution_pane_collapsed_height" + app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> + + <com.google.android.material.tabs.TabLayout + android:id="@+id/tablayout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:tabTextAppearance="@style/WallpaperPicker.Preview.TextAppearance.NoAllCaps" + app:tabIndicatorColor="?android:attr/textColorPrimary" + android:visibility="gone"/> + + <com.android.wallpaper.widget.ConstraintViewPager + android:id="@+id/viewpager" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </LinearLayout> + + </androidx.coordinatorlayout.widget.CoordinatorLayout> + + <androidx.appcompat.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="top" + style="@style/TranslucentToolbarStyle"/> + + </FrameLayout> + +</FrameLayout> diff --git a/res/layout/preview_page_info.xml b/res/layout/preview_page_info.xml index 9299426..2489561 100644 --- a/res/layout/preview_page_info.xml +++ b/res/layout/preview_page_info.xml @@ -21,8 +21,7 @@ android:layout_width="match_parent" android:orientation="vertical" android:paddingHorizontal="@dimen/preview_attribution_pane_horizontal_padding" - android:background="@drawable/preview_bottom_sheet_background" - android:theme="@android:style/Theme.DeviceDefault.Settings"> + android:theme="@style/WallpaperPicker.BottomPaneStyle"> <Space android:id="@+id/preview_attribution_pane_title_spacer" diff --git a/res/layout/preview_page_settings.xml b/res/layout/preview_page_settings.xml new file mode 100644 index 0000000..72e83a5 --- /dev/null +++ b/res/layout/preview_page_settings.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- 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. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:orientation="vertical" + android:paddingHorizontal="@dimen/preview_attribution_pane_horizontal_padding" + android:theme="@style/WallpaperPicker.BottomPaneStyle"> + + <androidx.slice.widget.SliceView + android:id="@+id/settings_slice" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + <Space + android:id="@+id/preview_attribution_pane_spacer" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1"/> + + <Button + style="@style/ButtonStyle" + android:id="@+id/preview_attribution_pane_set_wallpaper_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/set_wallpaper_button_text"/> + +</LinearLayout> diff --git a/res/menu/preview_menu.xml b/res/menu/preview_menu.xml index 7c9d610..f83560e 100755 --- a/res/menu/preview_menu.xml +++ b/res/menu/preview_menu.xml @@ -20,4 +20,18 @@ app:actionLayout="@layout/preview_action" app:showAsAction="always|withText" android:title="@string/preview"/> + + <item + android:id="@+id/configure" + android:icon="@drawable/ic_settings" + app:showAsAction="ifRoom" + android:title="@string/configure_wallpaper" + android:visible="false"/> + + <item + android:id="@+id/delete_wallpaper" + android:icon="@drawable/ic_delete_24px" + app:showAsAction="ifRoom" + android:title="@string/delete_live_wallpaper" + android:visible="false"/> </menu> diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 2a6c183..b35d76f 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -30,11 +30,11 @@ <string name="rotating_wallpaper_presentation_mode_message" msgid="3361676041605733288">"خلفية يومية"</string> <string name="wallpaper_destination_both" msgid="1124197176741944063">"الشاشة الرئيسية وشاشة التأمين"</string> <string name="home_screen_message" msgid="106444102822522813">"الشاشة الرئيسية"</string> - <string name="lock_screen_message" msgid="1534506081955058013">"شاشة التأمين"</string> + <string name="lock_screen_message" msgid="1534506081955058013">"شاشة القفل"</string> <string name="home_and_lock_short_label" msgid="2937922943541927983">"الشاشة الرئيسية وشاشة التأمين"</string> <string name="set_wallpaper_dialog_message" msgid="6114951028768599417">"تعيين كخلفية"</string> <string name="set_wallpaper_home_screen_destination" msgid="7315594722013109354">"الشاشة الرئيسية"</string> - <string name="set_wallpaper_lock_screen_destination" msgid="6224685559375417945">"شاشة التأمين"</string> + <string name="set_wallpaper_lock_screen_destination" msgid="6224685559375417945">"شاشة القفل"</string> <string name="set_wallpaper_both_destination" msgid="6967226064958263939">"الشاشة الرئيسية وشاشة التأمين"</string> <string name="no_backup_image_wallpaper_label" msgid="6316627676107284851">"خلفية الصور التي يتم عرضها بالتناوب"</string> <string name="permission_needed_explanation" msgid="139166837541426823">"لعرض الخلفية الحالية هنا، يحتاج تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> إلى الوصول إلى مساحة تخزين الجهاز."</string> diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml new file mode 100644 index 0000000..158ce10 --- /dev/null +++ b/res/values-en-rCA/strings.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="wallpaper_app_name" msgid="1719889291772891695">"Wallpapers"</string> + <!-- no translation found for app_name (8773648973927541493) --> + <skip /> + <string name="select_wallpaper_label" msgid="6989581259339646085">"Wallpaper categories"</string> + <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set Wallpaper"</string> + <string name="set_wallpaper_progress_message" msgid="7986528287618716715">"Setting wallpaper…"</string> + <string name="try_again" msgid="8278874823700921234">"Try again"</string> + <string name="set_wallpaper_error_message" msgid="6819986999041085130">"Unable to set wallpaper."</string> + <string name="load_wallpaper_error_message" msgid="7913278480467707374">"Unable to load wallpaper. The image is either corrupted or unavailable."</string> + <string name="static_wallpaper_presentation_mode_message" msgid="417940227049360906">"Currently set"</string> + <string name="rotating_wallpaper_presentation_mode_message" msgid="3361676041605733288">"Daily wallpaper"</string> + <string name="wallpaper_destination_both" msgid="1124197176741944063">"Home & Lock screen"</string> + <string name="home_screen_message" msgid="106444102822522813">"Home screen"</string> + <string name="lock_screen_message" msgid="1534506081955058013">"Lock screen"</string> + <string name="home_and_lock_short_label" msgid="2937922943541927983">"Home & Lock"</string> + <string name="set_wallpaper_dialog_message" msgid="6114951028768599417">"Set wallpaper"</string> + <string name="set_wallpaper_home_screen_destination" msgid="7315594722013109354">"Home screen"</string> + <string name="set_wallpaper_lock_screen_destination" msgid="6224685559375417945">"Lock screen"</string> + <string name="set_wallpaper_both_destination" msgid="6967226064958263939">"Home screen and lock screen"</string> + <string name="no_backup_image_wallpaper_label" msgid="6316627676107284851">"Rotating Image Wallpaper"</string> + <string name="permission_needed_explanation" msgid="139166837541426823">"To display the current wallpaper here, <xliff:g id="APP_NAME">%1$s</xliff:g> needs access to your device\'s storage."</string> + <string name="permission_needed_explanation_go_to_settings" msgid="3923551582092599609">"To display the current wallpaper here, Wallpapers needs access to your device’s storage.\n\nTo change this setting, go to the Permissions area of the Wallpapers’ app info."</string> + <string name="permission_needed_allow_access_button_label" msgid="1943133660612924306">"Allow access"</string> + <string name="no_backup_image_wallpaper_description" msgid="8303268619408738057">"Live wallpaper service for rotating wallpapers"</string> + <string name="daily_refresh_tile_title" msgid="3270456074558525091">"Daily wallpaper"</string> + <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"Tap to turn on"</string> + <string name="start_rotation_dialog_body_live_wallpaper_needed" msgid="5132580257563846082">"Wallpaper will change automatically every day. To finish setup, tap <strong>Set wallpaper</strong> on the next screen."</string> + <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"Download future wallpapers on Wi-Fi only"</string> + <string name="start_rotation_dialog_continue" msgid="276678987852274872">"Continue"</string> + <string name="start_rotation_progress_message" msgid="7872623873682262083">"Downloading first wallpaper…"</string> + <string name="start_rotation_error_message" msgid="3053799836719618972">"Unable to download first wallpaper. Please check your network settings and try again."</string> + <string name="start_rotation_dialog_body" msgid="7903554799046364916">"Wallpaper will change automatically every day"</string> + <string name="settings_button_label" msgid="8724734130079207955">"Settings"</string> + <string name="explore" msgid="7468719504199497281">"Explore"</string> + <string name="next_wallpaper" msgid="3911873152952596232">"Next wallpaper"</string> + <string name="wallpaper_disabled_message" msgid="7309484130562148185">"Setting a wallpaper is disabled on this device"</string> + <string name="wallpaper_disabled_by_administrator_message" msgid="1551430406714747884">"Setting a wallpaper is disabled by your device administrator"</string> + <string name="wallpaper_set_successfully_message" msgid="2958998799111688578">"Wallpaper set successfully"</string> + <string name="wallpapers_unavailable_offline_message" msgid="8136405438621689532">"You need an Internet connection to view wallpapers. Please connect and try again."</string> + <string name="currently_set_home_wallpaper_thumbnail" msgid="4022381436821898917">"Currently set home screen wallpaper thumbnail"</string> + <string name="currently_set_lock_wallpaper_thumbnail" msgid="2094209303934569997">"Currently set lock screen wallpaper thumbnail"</string> + <string name="currently_set_wallpaper_thumbnail" msgid="8651887838745545107">"Currently set wallpaper thumbnail"</string> + <string name="wallpaper_thumbnail" msgid="569931475923605974">"Wallpaper thumbnail"</string> + <string name="explore_home_screen" msgid="8756346794535765482">"Explore home screen wallpaper"</string> + <string name="explore_lock_screen" msgid="268938342103703665">"Explore lock screen wallpaper"</string> + <string name="refresh_daily_wallpaper_home_content_description" msgid="2770445044556164259">"Refresh daily home screen wallpaper"</string> + <string name="refresh_daily_wallpaper_content_description" msgid="4362142658237147583">"Refresh daily wallpaper"</string> + <string name="refreshing_daily_wallpaper_dialog_message" msgid="1975910873362855761">"Refreshing daily wallpaper…"</string> + <string name="refresh_daily_wallpaper_failed_message" msgid="4749879993812557166">"Failed to refresh daily wallpaper. Please check your network connection and try again."</string> + <string name="collapse_attribution_panel" msgid="4367971404848122275">"Collapse wallpaper info panel"</string> + <string name="expand_attribution_panel" msgid="6975094181456095915">"Expand wallpaper info panel"</string> + <string name="on_device_wallpapers_category_title" msgid="805819102071369004">"On-device wallpapers"</string> + <string name="on_device_wallpapers_category_title_desktop" msgid="316919420410065369">"On device"</string> + <string name="on_device_wallpaper_title" msgid="5262564748034629524">"Android wallpaper"</string> + <string name="live_wallpapers_category_title" msgid="1814374812192366349">"Live wallpapers"</string> + <string name="my_photos_category_title" msgid="4294567122144565273">"My photos"</string> + <string name="my_photos_generic_wallpaper_title" msgid="7002867526154631172">"My photo"</string> + <string name="fallback_wallpaper_title" msgid="6154655421012506001">"Wallpaper"</string> + <string name="app_not_found" msgid="4431461707854088231">"App isn\'t installed."</string> + <string name="center_wallpaper_position" msgid="4166894762352288883">"Centre"</string> + <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Centre crop"</string> + <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Stretch"</string> + <string name="preview" msgid="1774602101743861071">"Preview"</string> +</resources> diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml new file mode 100644 index 0000000..b06a217 --- /dev/null +++ b/res/values-en-rXC/strings.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="wallpaper_app_name" msgid="1719889291772891695">"Wallpapers"</string> + <!-- no translation found for app_name (8773648973927541493) --> + <skip /> + <string name="select_wallpaper_label" msgid="6989581259339646085">"Wallpaper categories"</string> + <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set Wallpaper"</string> + <string name="set_wallpaper_progress_message" msgid="7986528287618716715">"Setting wallpaper…"</string> + <string name="try_again" msgid="8278874823700921234">"Try again"</string> + <string name="set_wallpaper_error_message" msgid="6819986999041085130">"Unable to set wallpaper."</string> + <string name="load_wallpaper_error_message" msgid="7913278480467707374">"Unable to load wallpaper. The image is either corrupted or unavailable."</string> + <string name="static_wallpaper_presentation_mode_message" msgid="417940227049360906">"Currently set"</string> + <string name="rotating_wallpaper_presentation_mode_message" msgid="3361676041605733288">"Daily wallpaper"</string> + <string name="wallpaper_destination_both" msgid="1124197176741944063">"Home & Lock screen"</string> + <string name="home_screen_message" msgid="106444102822522813">"Home screen"</string> + <string name="lock_screen_message" msgid="1534506081955058013">"Lock screen"</string> + <string name="home_and_lock_short_label" msgid="2937922943541927983">"Home & Lock"</string> + <string name="set_wallpaper_dialog_message" msgid="6114951028768599417">"Set wallpaper"</string> + <string name="set_wallpaper_home_screen_destination" msgid="7315594722013109354">"Home screen"</string> + <string name="set_wallpaper_lock_screen_destination" msgid="6224685559375417945">"Lock screen"</string> + <string name="set_wallpaper_both_destination" msgid="6967226064958263939">"Home screen and lock screen"</string> + <string name="no_backup_image_wallpaper_label" msgid="6316627676107284851">"Rotating Image Wallpaper"</string> + <string name="permission_needed_explanation" msgid="139166837541426823">"To display the current wallpaper here, <xliff:g id="APP_NAME">%1$s</xliff:g> needs access to your device\'s storage."</string> + <string name="permission_needed_explanation_go_to_settings" msgid="3923551582092599609">"To display the current wallpaper here, Wallpapers needs access to your device’s storage.\n\nTo change this setting, go to the Permissions area of Wallpapers’ app info."</string> + <string name="permission_needed_allow_access_button_label" msgid="1943133660612924306">"Allow access"</string> + <string name="no_backup_image_wallpaper_description" msgid="8303268619408738057">"Live wallpaper service for rotating wallpapers"</string> + <string name="daily_refresh_tile_title" msgid="3270456074558525091">"Daily wallpaper"</string> + <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"Tap to turn on"</string> + <string name="start_rotation_dialog_body_live_wallpaper_needed" msgid="5132580257563846082">"Wallpaper will change automatically every day. To finish setup, tap <strong>Set wallpaper</strong> on the next screen."</string> + <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"Download future wallpapers on Wi-Fi only"</string> + <string name="start_rotation_dialog_continue" msgid="276678987852274872">"Continue"</string> + <string name="start_rotation_progress_message" msgid="7872623873682262083">"Downloading first wallpaper…"</string> + <string name="start_rotation_error_message" msgid="3053799836719618972">"Unable to download first wallpaper. Please check your network settings and try again."</string> + <string name="start_rotation_dialog_body" msgid="7903554799046364916">"Wallpaper will change automatically every day"</string> + <string name="settings_button_label" msgid="8724734130079207955">"Settings"</string> + <string name="explore" msgid="7468719504199497281">"Explore"</string> + <string name="next_wallpaper" msgid="3911873152952596232">"Next wallpaper"</string> + <string name="wallpaper_disabled_message" msgid="7309484130562148185">"Setting a wallpaper is disabled on this device"</string> + <string name="wallpaper_disabled_by_administrator_message" msgid="1551430406714747884">"Setting a wallpaper is disabled by your device administrator"</string> + <string name="wallpaper_set_successfully_message" msgid="2958998799111688578">"Wallpaper set successfully"</string> + <string name="wallpapers_unavailable_offline_message" msgid="8136405438621689532">"You need an Internet connection to view wallpapers. Please connect and try again."</string> + <string name="currently_set_home_wallpaper_thumbnail" msgid="4022381436821898917">"Currently set home screen wallpaper thumbnail"</string> + <string name="currently_set_lock_wallpaper_thumbnail" msgid="2094209303934569997">"Currently set lock screen wallpaper thumbnail"</string> + <string name="currently_set_wallpaper_thumbnail" msgid="8651887838745545107">"Currently set wallpaper thumbnail"</string> + <string name="wallpaper_thumbnail" msgid="569931475923605974">"Wallpaper thumbnail"</string> + <string name="explore_home_screen" msgid="8756346794535765482">"Explore home screen wallpaper"</string> + <string name="explore_lock_screen" msgid="268938342103703665">"Explore lock screen wallpaper"</string> + <string name="refresh_daily_wallpaper_home_content_description" msgid="2770445044556164259">"Refresh daily home screen wallpaper"</string> + <string name="refresh_daily_wallpaper_content_description" msgid="4362142658237147583">"Refresh daily wallpaper"</string> + <string name="refreshing_daily_wallpaper_dialog_message" msgid="1975910873362855761">"Refreshing daily wallpaper…"</string> + <string name="refresh_daily_wallpaper_failed_message" msgid="4749879993812557166">"Failed to refresh daily wallpaper. Please check your network connection and try again."</string> + <string name="collapse_attribution_panel" msgid="4367971404848122275">"Collapse wallpaper info panel"</string> + <string name="expand_attribution_panel" msgid="6975094181456095915">"Expand wallpaper info panel"</string> + <string name="on_device_wallpapers_category_title" msgid="805819102071369004">"On-device wallpapers"</string> + <string name="on_device_wallpapers_category_title_desktop" msgid="316919420410065369">"On device"</string> + <string name="on_device_wallpaper_title" msgid="5262564748034629524">"Android wallpaper"</string> + <string name="live_wallpapers_category_title" msgid="1814374812192366349">"Live wallpapers"</string> + <string name="my_photos_category_title" msgid="4294567122144565273">"My photos"</string> + <string name="my_photos_generic_wallpaper_title" msgid="7002867526154631172">"My photo"</string> + <string name="fallback_wallpaper_title" msgid="6154655421012506001">"Wallpaper"</string> + <string name="app_not_found" msgid="4431461707854088231">"App isn\'t installed."</string> + <string name="center_wallpaper_position" msgid="4166894762352288883">"Center"</string> + <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Center crop"</string> + <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Stretch"</string> + <string name="preview" msgid="1774602101743861071">"Preview"</string> +</resources> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 53c95c0..843ff79 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -126,6 +126,7 @@ <dimen name="preview_attribution_pane_description_height">34dp</dimen> <dimen name="preview_attribution_pane_button_bottom_margin">8dp</dimen> + <dimen name="preview_gradient_background_height">256dp</dimen> <!-- Dimensions for the "start rotation" dialog. --> <dimen name="start_rotation_dialog_subhead_margin_top">19dp</dimen> diff --git a/res/values/strings.xml b/res/values/strings.xml index fe99050..0201f96 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -300,4 +300,19 @@ <!-- Label for a checkbox which lets user preview the displayed image as wallpaper. [CHAR LIMIT=30] --> <string name="preview">Preview</string> + + <!-- Label for the 'Info' tab of view pager in wallpaper preview activity. [CHAR_LIMIT=25] --> + <string name="tab_info">Info</string> + <!-- Label for the 'Customize' tab of view pager in wallpaper preview activity. + [CHAR_LIMIT=25] --> + <string name="tab_customize">Customize</string> + + <!-- List item for configuring the current wallpaper [CHAR LIMIT=30] --> + <string name="configure_wallpaper">Settings…</string> + + <!-- List item for deleting the current wallpaper [CHAR LIMIT=30] --> + <string name="delete_live_wallpaper">Delete</string> + + <!-- Confirmation dialog. Shown after user selects to delete one wallpaper. [CHAR LIMIT=NONE] --> + <string name="delete_wallpaper_confirmation">Delete this wallpaper from your phone?</string> </resources> diff --git a/res/values/styles.xml b/res/values/styles.xml index 6259281..1796b1d 100755 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -111,6 +111,15 @@ </style> <!-- Preview attribution pane styles --> + <style name="WallpaperPicker.BottomPaneStyle" parent="@android:style/Theme.DeviceDefault.Settings"> + <item name="android:textColorPrimary">@color/material_white_100</item> + <item name="android:textColorSecondary">@color/white_70_alpha</item> + <item name="tabTextAppearance">@style/WallpaperPicker.Preview.TextAppearance.NoAllCaps</item> + <item name="tabIndicatorColor">?android:attr/textColorPrimary</item> + <item name="tabGravity">fill</item> + <item name="tabMaxWidth">0dp</item> + </style> + <style name="preview_attribution_pane_title"> <item name="android:textColor">@color/material_white_text</item> <item name="android:textSize">@dimen/abc_text_size_subhead_material</item> @@ -121,6 +130,11 @@ <item name="android:textSize">@dimen/abc_text_size_body_2_material</item> </style> + <style name="WallpaperPicker.Preview.TextAppearance.NoAllCaps" + parent="@android:style/TextAppearance.DeviceDefault.Widget.TabWidget"> + <item name="android:textAllCaps">false</item> + </style> + <!-- Set wallpaper destination item --> <style name="set_wallpaper_destination_item"> <item name="android:minHeight">@dimen/set_wallpaper_dialog_item_min_height</item> diff --git a/src/com/android/wallpaper/model/CurrentWallpaperInfoV16.java b/src/com/android/wallpaper/model/CurrentWallpaperInfoV16.java deleted file mode 100755 index 3db46e7..0000000 --- a/src/com/android/wallpaper/model/CurrentWallpaperInfoV16.java +++ /dev/null @@ -1,139 +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.model; - -import android.app.Activity; -import android.content.Context; -import android.os.Parcel; - -import com.android.wallpaper.asset.Asset; -import com.android.wallpaper.asset.CurrentWallpaperAssetV16; -import com.android.wallpaper.asset.FileAsset; -import com.android.wallpaper.module.InjectorProvider; -import com.android.wallpaper.module.NoBackupImageWallpaper; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.DrawableRes; -import androidx.annotation.StringRes; - -/** - * Represents the wallpaper currently set to the device for API 16 through 23. Should not be used - * to set a new wallpaper. - */ -public class CurrentWallpaperInfoV16 extends WallpaperInfo { - - public static final Creator<CurrentWallpaperInfoV16> CREATOR = - new Creator<CurrentWallpaperInfoV16>() { - @Override - public CurrentWallpaperInfoV16 createFromParcel(Parcel source) { - return new CurrentWallpaperInfoV16(source); - } - - @Override - public CurrentWallpaperInfoV16[] newArray(int size) { - return new CurrentWallpaperInfoV16[size]; - } - }; - private List<String> mAttributions; - private Asset mAsset; - private String mActionUrl; - @StringRes - private int mActionLabelRes; - @DrawableRes - private int mActionIconRes; - private String mCollectionId; - - public CurrentWallpaperInfoV16(List<String> attributions, String actionUrl, - @StringRes int actionLabelRes, @DrawableRes int actionIconRes, - String collectionId) { - mAttributions = attributions; - mActionUrl = actionUrl; - mActionLabelRes = actionLabelRes; - mActionIconRes = actionIconRes; - mCollectionId = collectionId; - } - - private CurrentWallpaperInfoV16(Parcel in) { - mAttributions = new ArrayList<>(); - in.readStringList(mAttributions); - mActionUrl = in.readString(); - mCollectionId = in.readString(); - mActionLabelRes = in.readInt(); - mActionIconRes = in.readInt(); - } - - @Override - public List<String> getAttributions(Context context) { - return mAttributions; - } - - @Override - public Asset getAsset(Context context) { - if (mAsset == null) { - boolean isNoBackupImageWallpaperSet = InjectorProvider.getInjector() - .getLiveWallpaperStatusChecker(context).isNoBackupImageWallpaperSet(); - - mAsset = isNoBackupImageWallpaperSet - ? new FileAsset(new File(context.getApplicationContext().getFilesDir(), - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH)) - : new CurrentWallpaperAssetV16(context); - } - return mAsset; - } - - @Override - public Asset getThumbAsset(Context context) { - return getAsset(context); - } - - @Override - public String getActionUrl(Context unused) { - return mActionUrl; - } - - @Override - public int getActionIconRes(Context unused) { - return mActionIconRes != 0 ? mActionIconRes : WallpaperInfo.getDefaultActionIcon(); - } - - @Override - public int getActionLabelRes(Context unused) { - return mActionLabelRes != 0 ? mActionLabelRes : WallpaperInfo.getDefaultActionLabel(); - } - - @Override - public String getCollectionId(Context unused) { - return mCollectionId; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeStringList(mAttributions); - parcel.writeString(mActionUrl); - parcel.writeString(mCollectionId); - parcel.writeInt(mActionLabelRes); - parcel.writeInt(mActionIconRes); - } - - @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/CurrentWallpaperInfoVN.java b/src/com/android/wallpaper/model/CurrentWallpaperInfoVN.java index e44e717..bf54bfd 100755 --- a/src/com/android/wallpaper/model/CurrentWallpaperInfoVN.java +++ b/src/com/android/wallpaper/model/CurrentWallpaperInfoVN.java @@ -27,13 +27,10 @@ import androidx.annotation.StringRes; import com.android.wallpaper.asset.Asset; import com.android.wallpaper.asset.BuiltInWallpaperAsset; import com.android.wallpaper.asset.CurrentWallpaperAssetVN; -import com.android.wallpaper.asset.FileAsset; import com.android.wallpaper.compat.WallpaperManagerCompat; import com.android.wallpaper.compat.WallpaperManagerCompat.WallpaperLocation; import com.android.wallpaper.module.InjectorProvider; -import com.android.wallpaper.module.NoBackupImageWallpaper; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -142,17 +139,6 @@ public class CurrentWallpaperInfoVN extends WallpaperInfo { * Constructs and returns an Asset instance representing the currently-set wallpaper asset. */ private Asset createCurrentWallpaperAssetVN(Context context) { - // If the live wallpaper for rotating wallpapers is set, then provide a file asset - // representing that wallpaper. - boolean isNoBackupImageWallpaperSet = InjectorProvider.getInjector() - .getLiveWallpaperStatusChecker(context).isNoBackupImageWallpaperSet(); - if (mWallpaperManagerFlag == WallpaperManagerCompat.FLAG_SYSTEM - && isNoBackupImageWallpaperSet) { - Context deviceProtectedContext = context.createDeviceProtectedStorageContext(); - return new FileAsset(new File(deviceProtectedContext.getFilesDir(), - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH)); - } - WallpaperManagerCompat wallpaperManagerCompat = InjectorProvider.getInjector() .getWallpaperManagerCompat(context); diff --git a/src/com/android/wallpaper/model/InlinePreviewIntentFactory.java b/src/com/android/wallpaper/model/InlinePreviewIntentFactory.java index ce29761..28062d8 100755 --- a/src/com/android/wallpaper/model/InlinePreviewIntentFactory.java +++ b/src/com/android/wallpaper/model/InlinePreviewIntentFactory.java @@ -23,6 +23,16 @@ import android.content.Intent; * wallpaper, if appropriate for that wallpaper. */ public interface InlinePreviewIntentFactory { + + /** + * @return whether it's possible to use the internal live picker, or {@code false} to use the + * the Framework Activity. + */ + default boolean shouldUseInternalLivePicker(Context context) { + return false; // Disable always for now + // ContextCompat.checkSelfPermission(context, BIND_WALLPAPER) == PERMISSION_GRANTED; + } + /** * Gets an intent to show the preview activity for the given wallpaper. * @@ -30,5 +40,5 @@ public interface InlinePreviewIntentFactory { * @param wallpaper * @return Intent to show the inline preview activity. */ - public Intent newIntent(Context ctx, WallpaperInfo wallpaper); + Intent newIntent(Context ctx, WallpaperInfo wallpaper); } diff --git a/src/com/android/wallpaper/model/LiveWallpaperInfo.java b/src/com/android/wallpaper/model/LiveWallpaperInfo.java index a408aed..4337230 100755 --- a/src/com/android/wallpaper/model/LiveWallpaperInfo.java +++ b/src/com/android/wallpaper/model/LiveWallpaperInfo.java @@ -245,7 +245,7 @@ public class LiveWallpaperInfo extends WallpaperInfo { return wallpaperInfos; } - static boolean isSystemApp(ApplicationInfo appInfo) { + private static boolean isSystemApp(ApplicationInfo appInfo) { return (appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0; } @@ -262,11 +262,12 @@ public class LiveWallpaperInfo extends WallpaperInfo { @Override public List<String> getAttributions(Context context) { List<String> attributions = new ArrayList<>(); - CharSequence labelCharSeq = mInfo.loadLabel(context.getPackageManager()); + PackageManager packageManager = context.getPackageManager(); + CharSequence labelCharSeq = mInfo.loadLabel(packageManager); attributions.add(labelCharSeq == null ? null : labelCharSeq.toString()); try { - CharSequence authorCharSeq = mInfo.loadAuthor(context.getPackageManager()); + CharSequence authorCharSeq = mInfo.loadAuthor(packageManager); if (authorCharSeq != null) { String author = authorCharSeq.toString(); attributions.add(author); @@ -275,6 +276,16 @@ public class LiveWallpaperInfo extends WallpaperInfo { // No author specified, so no other attribution to add. } + try { + CharSequence descCharSeq = mInfo.loadDescription(packageManager); + if (descCharSeq != null) { + String desc = descCharSeq.toString(); + attributions.add(desc); + } + } catch (Resources.NotFoundException e) { + // No description specified, so no other attribution to add. + } + return attributions; } @@ -294,6 +305,18 @@ public class LiveWallpaperInfo extends WallpaperInfo { return null; } + /** + * Get an optional description for the action button if provided by this LiveWallpaper. + */ + @Nullable + public CharSequence getActionDescription(Context context) { + try { + return mInfo.loadContextDescription(context.getPackageManager()); + } catch (Resources.NotFoundException e) { + return null; + } + } + @Override public Asset getAsset(Context context) { return null; @@ -310,9 +333,14 @@ public class LiveWallpaperInfo extends WallpaperInfo { @Override public void showPreview(Activity srcActivity, InlinePreviewIntentFactory factory, int requestCode) { - Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER); - preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, mInfo.getComponent()); - ActivityUtils.startActivityForResultSafely(srcActivity, preview, requestCode); + //Only use internal live picker if available, otherwise, default to the Framework one + if (factory.shouldUseInternalLivePicker(srcActivity)) { + srcActivity.startActivityForResult(factory.newIntent(srcActivity, this), requestCode); + } else { + Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER); + preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, mInfo.getComponent()); + ActivityUtils.startActivityForResultSafely(srcActivity, preview, requestCode); + } } @Override diff --git a/src/com/android/wallpaper/model/WallpaperRotationInitializer.java b/src/com/android/wallpaper/model/WallpaperRotationInitializer.java index ad9eef9..5797650 100755 --- a/src/com/android/wallpaper/model/WallpaperRotationInitializer.java +++ b/src/com/android/wallpaper/model/WallpaperRotationInitializer.java @@ -58,13 +58,6 @@ public interface WallpaperRotationInitializer extends Parcelable { Listener listener); /** - * Returns whether the live wallpaper needs to be set to the device in order to be able to start - * rotation or is already set but on home-only on N-MR2 or later, which means the user has the - * option to pick a new destination preference. - */ - boolean isNoBackupImageWallpaperPreviewNeeded(Context appContext); - - /** * Gets the current state of the possible wallpaper rotation represented by this object. */ void fetchRotationInitializationState(Context context, RotationStateListener listener); diff --git a/src/com/android/wallpaper/module/BaseWallpaperInjector.java b/src/com/android/wallpaper/module/BaseWallpaperInjector.java index f8191fc..de9ddb1 100755 --- a/src/com/android/wallpaper/module/BaseWallpaperInjector.java +++ b/src/com/android/wallpaper/module/BaseWallpaperInjector.java @@ -34,12 +34,10 @@ public abstract class BaseWallpaperInjector implements Injector { private Requester mRequester; private WallpaperManagerCompat mWallpaperManagerCompat; private CurrentWallpaperInfoFactory mCurrentWallpaperFactory; - private LiveWallpaperStatusChecker mLiveWallpaperStatusChecker; private NetworkStatusNotifier mNetworkStatusNotifier; private AlarmManagerWrapper mAlarmManagerWrapper; private ExploreIntentChecker mExploreIntentChecker; private SystemFeatureChecker mSystemFeatureChecker; - private RotatingWallpaperComponentChecker mRotatingWallpaperComponentChecker; private FormFactorChecker mFormFactorChecker; private PackageStatusNotifier mPackageStatusNotifier; private LiveWallpaperInfoFactory mLiveWallpaperInfoFactory; @@ -111,15 +109,6 @@ public abstract class BaseWallpaperInjector implements Injector { } @Override - public synchronized LiveWallpaperStatusChecker getLiveWallpaperStatusChecker(Context context) { - if (mLiveWallpaperStatusChecker == null) { - mLiveWallpaperStatusChecker = - new DefaultLiveWallpaperStatusChecker(context.getApplicationContext()); - } - return mLiveWallpaperStatusChecker; - } - - @Override public synchronized NetworkStatusNotifier getNetworkStatusNotifier(Context context) { if (mNetworkStatusNotifier == null) { mNetworkStatusNotifier = new DefaultNetworkStatusNotifier(context.getApplicationContext()); @@ -161,14 +150,6 @@ public abstract class BaseWallpaperInjector implements Injector { } @Override - public synchronized RotatingWallpaperComponentChecker getRotatingWallpaperComponentChecker() { - if (mRotatingWallpaperComponentChecker == null) { - mRotatingWallpaperComponentChecker = new DefaultRotatingWallpaperComponentChecker(); - } - return mRotatingWallpaperComponentChecker; - } - - @Override public synchronized FormFactorChecker getFormFactorChecker(Context context) { if (mFormFactorChecker == null) { mFormFactorChecker = new DefaultFormFactorChecker(context.getApplicationContext()); diff --git a/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java b/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java index c74f9b2..dbe3722 100755 --- a/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java +++ b/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java @@ -17,16 +17,13 @@ package com.android.wallpaper.module; import android.content.Context; -import com.android.wallpaper.compat.BuildCompat; +import androidx.annotation.Nullable; + import com.android.wallpaper.compat.WallpaperManagerCompat; -import com.android.wallpaper.model.CurrentWallpaperInfoV16; import com.android.wallpaper.model.CurrentWallpaperInfoVN; -import com.android.wallpaper.model.LiveWallpaperInfo; import com.android.wallpaper.model.WallpaperInfo; import com.android.wallpaper.module.WallpaperPreferences.PresentationMode; -import androidx.annotation.Nullable; - /** * Default implementation of {@link CurrentWallpaperInfoFactory} which actually constructs * {@link WallpaperInfo} instances representing the wallpapers currently set to the device. @@ -35,7 +32,6 @@ public class DefaultCurrentWallpaperInfoFactory implements CurrentWallpaperInfoF private final Context mAppContext; private final WallpaperRefresher mWallpaperRefresher; - private final LiveWallpaperStatusChecker mLiveWallpaperStatusChecker; private final LiveWallpaperInfoFactory mLiveWallpaperInfoFactory; // Cached copies of the currently-set WallpaperInfo(s) and presentation mode. @@ -49,8 +45,6 @@ public class DefaultCurrentWallpaperInfoFactory implements CurrentWallpaperInfoF mAppContext = context.getApplicationContext(); Injector injector = InjectorProvider.getInjector(); mWallpaperRefresher = injector.getWallpaperRefresher(mAppContext); - mLiveWallpaperStatusChecker = - injector.getLiveWallpaperStatusChecker(mAppContext); mLiveWallpaperInfoFactory = injector.getLiveWallpaperInfoFactory(mAppContext); } @@ -72,29 +66,16 @@ public class DefaultCurrentWallpaperInfoFactory implements CurrentWallpaperInfoF mWallpaperRefresher.refresh( (homeWallpaperMetadata, lockWallpaperMetadata, presentationMode) -> { - WallpaperInfo homeWallpaper; - - if (homeWallpaperMetadata.getWallpaperComponent() == null - || mLiveWallpaperStatusChecker.isNoBackupImageWallpaperSet()) { - // Image wallpaper - if (BuildCompat.isAtLeastN()) { - homeWallpaper = new CurrentWallpaperInfoVN( - homeWallpaperMetadata.getAttributions(), - homeWallpaperMetadata.getActionUrl(), - homeWallpaperMetadata.getActionLabelRes(), - homeWallpaperMetadata.getActionIconRes(), - homeWallpaperMetadata.getCollectionId(), - WallpaperManagerCompat.FLAG_SYSTEM); - } else { - homeWallpaper = new CurrentWallpaperInfoV16( - homeWallpaperMetadata.getAttributions(), - homeWallpaperMetadata.getActionUrl(), - homeWallpaperMetadata.getActionLabelRes(), - homeWallpaperMetadata.getActionIconRes(), - homeWallpaperMetadata.getCollectionId()); - } - } else { // Live wallpaper + if (homeWallpaperMetadata.getWallpaperComponent() == null) { + homeWallpaper = new CurrentWallpaperInfoVN( + homeWallpaperMetadata.getAttributions(), + homeWallpaperMetadata.getActionUrl(), + homeWallpaperMetadata.getActionLabelRes(), + homeWallpaperMetadata.getActionIconRes(), + homeWallpaperMetadata.getCollectionId(), + WallpaperManagerCompat.FLAG_SYSTEM); + } else { homeWallpaper = mLiveWallpaperInfoFactory.getLiveWallpaperInfo( homeWallpaperMetadata.getWallpaperComponent()); } diff --git a/src/com/android/wallpaper/module/DefaultLiveWallpaperStatusChecker.java b/src/com/android/wallpaper/module/DefaultLiveWallpaperStatusChecker.java deleted file mode 100755 index 003a06b..0000000 --- a/src/com/android/wallpaper/module/DefaultLiveWallpaperStatusChecker.java +++ /dev/null @@ -1,42 +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.annotation.SuppressLint; -import android.app.WallpaperManager; -import android.content.Context; - -/** - * Default implementation of {@link LiveWallpaperStatusChecker}. - */ -@SuppressLint("ServiceCast") -public class DefaultLiveWallpaperStatusChecker implements LiveWallpaperStatusChecker { - - private WallpaperManager mWallpaperManager; - - public DefaultLiveWallpaperStatusChecker(Context context) { - // Retrieve WallpaperManager using Context#getSystemService instead of - // WallpaperManager#getInstance so it can be mocked out in test. - mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE); - } - - @Override - public boolean isNoBackupImageWallpaperSet() { - android.app.WallpaperInfo liveWallpaper = mWallpaperManager.getWallpaperInfo(); - return liveWallpaper != null - && liveWallpaper.getServiceName().equals(NoBackupImageWallpaper.class.getName()); - } -} diff --git a/src/com/android/wallpaper/module/DefaultRotatingWallpaperComponentChecker.java b/src/com/android/wallpaper/module/DefaultRotatingWallpaperComponentChecker.java deleted file mode 100755 index eddb6bf..0000000 --- a/src/com/android/wallpaper/module/DefaultRotatingWallpaperComponentChecker.java +++ /dev/null @@ -1,77 +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.pm.PackageManager; - -/** - * Default implementation of {@link RotatingWallpaperComponentChecker}. - */ -public class DefaultRotatingWallpaperComponentChecker implements RotatingWallpaperComponentChecker { - - private static boolean isLiveWallpaperSupported(Context context) { - return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LIVE_WALLPAPER); - } - - @Override - @RotatingWallpaperComponent - public int getCurrentRotatingWallpaperComponent(Context context) { - if (!isLiveWallpaperSupported(context)) { - return ROTATING_WALLPAPER_COMPONENT_STATIC; - } - - // If presentation mode is ROTATING but the live wallpaper is not set, then "legacy" rotation - // from older APKs is in effect and the current rotating wallpaper component is a static WP. - Injector injector = InjectorProvider.getInjector(); - WallpaperPreferences preferences = injector.getPreferences(context); - LiveWallpaperStatusChecker liveWallpaperStatusChecker = injector - .getLiveWallpaperStatusChecker(context); - if (preferences.getWallpaperPresentationMode() - == WallpaperPreferences.PRESENTATION_MODE_ROTATING - && !liveWallpaperStatusChecker.isNoBackupImageWallpaperSet()) { - return ROTATING_WALLPAPER_COMPONENT_STATIC; - } - - return ROTATING_WALLPAPER_COMPONENT_LIVE; - } - - @Override - @RotatingWallpaperComponent - public int getNextRotatingWallpaperComponent(Context context) { - if (!isLiveWallpaperSupported(context)) { - return ROTATING_WALLPAPER_COMPONENT_STATIC; - } - - return ROTATING_WALLPAPER_COMPONENT_LIVE; - } - - @Override - @RotatingWallpaperSupport - public int getRotatingWallpaperSupport(Context context) { - FormFactorChecker formFactorChecker = - InjectorProvider.getInjector().getFormFactorChecker(context); - - if (formFactorChecker.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) { - return ROTATING_WALLPAPER_SUPPORT_SUPPORTED; - } - - // While static daily rotation is supported on desktops, it isn't (yet?) supported on phones. - // For phones which don't support live wallpapers thus we disallow daily rotation altogether. - return isLiveWallpaperSupported(context) ? ROTATING_WALLPAPER_SUPPORT_SUPPORTED - : ROTATING_WALLPAPER_SUPPORT_NOT_SUPPORTED; - } -} diff --git a/src/com/android/wallpaper/module/DefaultWallpaperPersister.java b/src/com/android/wallpaper/module/DefaultWallpaperPersister.java index e2c968c..27e4491 100755 --- a/src/com/android/wallpaper/module/DefaultWallpaperPersister.java +++ b/src/com/android/wallpaper/module/DefaultWallpaperPersister.java @@ -19,7 +19,6 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.WallpaperManager; import android.content.Context; -import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; @@ -32,6 +31,8 @@ import android.util.Log; import android.view.Display; import android.view.WindowManager; +import androidx.annotation.Nullable; + import com.android.wallpaper.asset.Asset; import com.android.wallpaper.asset.Asset.BitmapReceiver; import com.android.wallpaper.asset.Asset.DimensionsReceiver; @@ -42,24 +43,16 @@ import com.android.wallpaper.compat.BuildCompat; import com.android.wallpaper.compat.WallpaperManagerCompat; import com.android.wallpaper.model.WallpaperInfo; import com.android.wallpaper.module.BitmapCropper.Callback; -import com.android.wallpaper.module.RotatingWallpaperComponentChecker.RotatingWallpaperComponent; import com.android.wallpaper.util.BitmapTransformer; -import com.android.wallpaper.util.DiskBasedLogger; -import com.android.wallpaper.util.FileMover; import com.android.wallpaper.util.ScreenSizeCalculator; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; -import androidx.annotation.Nullable; - /** * Concrete implementation of WallpaperPersister which actually sets wallpapers to the system via * the WallpaperManager. @@ -71,11 +64,9 @@ public class DefaultWallpaperPersister implements WallpaperPersister { private final Context mAppContext; // The application's context. // Context that accesses files in device protected storage - private final Context mDeviceProtectedContext; private final WallpaperManager mWallpaperManager; private final WallpaperManagerCompat mWallpaperManagerCompat; private final WallpaperPreferences mWallpaperPreferences; - private final RotatingWallpaperComponentChecker mRotatingWallpaperComponentChecker; private final WallpaperChangedNotifier mWallpaperChangedNotifier; private WallpaperInfo mWallpaperInfoInPreview; @@ -83,21 +74,19 @@ public class DefaultWallpaperPersister implements WallpaperPersister { @SuppressLint("ServiceCast") public DefaultWallpaperPersister(Context context) { mAppContext = context.getApplicationContext(); - mDeviceProtectedContext = mAppContext.createDeviceProtectedStorageContext(); // Retrieve WallpaperManager using Context#getSystemService instead of // WallpaperManager#getInstance so it can be mocked out in test. Injector injector = InjectorProvider.getInjector(); mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE); mWallpaperManagerCompat = injector.getWallpaperManagerCompat(context); mWallpaperPreferences = injector.getPreferences(context); - mRotatingWallpaperComponentChecker = injector.getRotatingWallpaperComponentChecker(); mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance(); } @Override public void setIndividualWallpaper(final WallpaperInfo wallpaper, Asset asset, - @Nullable Rect cropRect, float scale, @Destination final int destination, - final SetWallpaperCallback callback) { + @Nullable Rect cropRect, float scale, @Destination final int destination, + final SetWallpaperCallback callback) { // Set wallpaper without downscaling directly from an input stream if there's no crop rect // specified by the caller and the asset is streamable. if (cropRect == null && asset instanceof StreamableAsset) { @@ -149,7 +138,7 @@ public class DefaultWallpaperPersister implements WallpaperPersister { @Override public void setIndividualWallpaperWithPosition(Activity activity, WallpaperInfo wallpaper, - @WallpaperPosition int wallpaperPosition, SetWallpaperCallback callback) { + @WallpaperPosition int wallpaperPosition, SetWallpaperCallback callback) { Display display = ((WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display); @@ -164,9 +153,10 @@ public class DefaultWallpaperPersister implements WallpaperPersister { } switch (wallpaperPosition) { - // Crop out screen-sized center portion of the source image if it's larger than the screen - // in both dimensions. Otherwise, decode the entire bitmap and fill the space around it to - // fill a new screen-sized bitmap with plain black pixels. + // Crop out screen-sized center portion of the source image if it's larger + // than the screen + // in both dimensions. Otherwise, decode the entire bitmap and fill the space + // around it to fill a new screen-sized bitmap with plain black pixels. case WALLPAPER_POSITION_CENTER: setIndividualWallpaperWithCenterPosition( wallpaper, asset, dimensions, screenSize, callback); @@ -178,19 +168,22 @@ public class DefaultWallpaperPersister implements WallpaperPersister { wallpaper, asset, dimensions, screenSize, callback); break; - // Decode full bitmap sized for screen and stretch it to fill the screen dimensions. + // Decode full bitmap sized for screen and stretch it to fill the screen + // dimensions. case WALLPAPER_POSITION_STRETCH: asset.decodeBitmap(screenSize.x, screenSize.y, new BitmapReceiver() { @Override public void onBitmapDecoded(@Nullable Bitmap bitmap) { - setIndividualWallpaperStretch(wallpaper, bitmap, screenSize /* stretchSize */, + setIndividualWallpaperStretch(wallpaper, bitmap, + screenSize /* stretchSize */, WallpaperPersister.DEST_BOTH, callback); } }); break; default: - Log.e(TAG, "Unsupported wallpaper position option specified: " + wallpaperPosition); + Log.e(TAG, "Unsupported wallpaper position option specified: " + + wallpaperPosition); callback.onError(null); } } @@ -208,7 +201,7 @@ public class DefaultWallpaperPersister implements WallpaperPersister { * @param callback Callback used to notify original caller of wallpaper set operation result. */ private void setIndividualWallpaperWithCenterPosition(WallpaperInfo wallpaper, Asset asset, - Point dimensions, Point screenSize, SetWallpaperCallback callback) { + Point dimensions, Point screenSize, SetWallpaperCallback callback) { if (dimensions.x >= screenSize.x && dimensions.y >= screenSize.y) { Rect cropRect = new Rect( (dimensions.x - screenSize.x) / 2, @@ -218,7 +211,8 @@ public class DefaultWallpaperPersister implements WallpaperPersister { asset.decodeBitmapRegion(cropRect, screenSize.x, screenSize.y, new BitmapReceiver() { @Override public void onBitmapDecoded(@Nullable Bitmap bitmap) { - setIndividualWallpaper(wallpaper, bitmap, WallpaperPersister.DEST_BOTH, callback); + setIndividualWallpaper(wallpaper, bitmap, WallpaperPersister.DEST_BOTH, + callback); } }); } else { @@ -249,7 +243,7 @@ public class DefaultWallpaperPersister implements WallpaperPersister { * @param callback Callback used to notify original caller of wallpaper set operation result. */ private void setIndividualWallpaperWithCenterCropPosition(WallpaperInfo wallpaper, Asset asset, - Point dimensions, Point screenSize, SetWallpaperCallback callback) { + Point dimensions, Point screenSize, SetWallpaperCallback callback) { float scale = Math.max((float) screenSize.x / dimensions.x, (float) screenSize.y / dimensions.y); @@ -276,7 +270,7 @@ public class DefaultWallpaperPersister implements WallpaperPersister { * @param callback Called once the wallpaper was set or if an error occurred. */ private void setIndividualWallpaper(WallpaperInfo wallpaper, Bitmap croppedBitmap, - @Destination int destination, SetWallpaperCallback callback) { + @Destination int destination, SetWallpaperCallback callback) { SetWallpaperTask setWallpaperTask = new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback); setWallpaperTask.execute(); @@ -287,15 +281,16 @@ public class DefaultWallpaperPersister implements WallpaperPersister { * * @param wallpaper Wallpaper model object. * @param croppedBitmap Bitmap representing the individual wallpaper image. - * @param fillSize Specifies the final bitmap size that should be set to WallpaperManager. This - * final bitmap will show the visible area of the provided bitmap after applying a mask with - * black background the source bitmap and centering. There may be black borders around the - * original bitmap if it's smaller than the fillSize in one or both dimensions. + * @param fillSize Specifies the final bitmap size that should be set to WallpaperManager. + * This final bitmap will show the visible area of the provided bitmap + * after applying a mask with black background the source bitmap and + * centering. There may be black borders around the original bitmap if + * it's smaller than the fillSize in one or both dimensions. * @param destination The destination - where to set the wallpaper to. * @param callback Called once the wallpaper was set or if an error occurred. */ private void setIndividualWallpaperFill(WallpaperInfo wallpaper, Bitmap croppedBitmap, - Point fillSize, @Destination int destination, SetWallpaperCallback callback) { + Point fillSize, @Destination int destination, SetWallpaperCallback callback) { SetWallpaperTask setWallpaperTask = new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback); setWallpaperTask.setFillSize(fillSize); @@ -308,13 +303,14 @@ public class DefaultWallpaperPersister implements WallpaperPersister { * * @param wallpaper Wallpaper model object. * @param croppedBitmap Bitmap representing the individual wallpaper image. - * @param stretchSize Specifies the final size to which the the bitmap should be stretched prior + * @param stretchSize Specifies the final size to which the bitmap should be stretched + * prior * to being set to the device. * @param destination The destination - where to set the wallpaper to. * @param callback Called once the wallpaper was set or if an error occurred. */ private void setIndividualWallpaperStretch(WallpaperInfo wallpaper, Bitmap croppedBitmap, - Point stretchSize, @Destination int destination, SetWallpaperCallback callback) { + Point stretchSize, @Destination int destination, SetWallpaperCallback callback) { SetWallpaperTask setWallpaperTask = new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback); setWallpaperTask.setStretchSize(stretchSize); @@ -330,7 +326,7 @@ public class DefaultWallpaperPersister implements WallpaperPersister { * @param callback Called once the wallpaper was set or if an error occurred. */ private void setIndividualWallpaper(WallpaperInfo wallpaper, InputStream inputStream, - @Destination int destination, SetWallpaperCallback callback) { + @Destination int destination, SetWallpaperCallback callback) { SetWallpaperTask setWallpaperTask = new SetWallpaperTask(wallpaper, inputStream, destination, callback); setWallpaperTask.execute(); @@ -338,58 +334,31 @@ public class DefaultWallpaperPersister implements WallpaperPersister { @Override public boolean setWallpaperInRotation(Bitmap wallpaperBitmap, List<String> attributions, - int actionLabelRes, int actionIconRes, - String actionUrl, String collectionId) { - @RotatingWallpaperComponent int rotatingWallpaperComponent = mRotatingWallpaperComponentChecker - .getCurrentRotatingWallpaperComponent(mAppContext); - - switch (rotatingWallpaperComponent) { - case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC: - return setWallpaperInRotationStatic(wallpaperBitmap, attributions, actionUrl, - actionLabelRes, actionIconRes, collectionId); - case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_LIVE: - return setWallpaperInRotationLive(wallpaperBitmap, attributions, actionUrl, - actionLabelRes, actionIconRes, collectionId); - default: - Log.e(TAG, "Unknown rotating wallpaper component: " + rotatingWallpaperComponent); - return false; - } + int actionLabelRes, int actionIconRes, String actionUrl, String collectionId) { + + return setWallpaperInRotationStatic(wallpaperBitmap, attributions, actionUrl, + actionLabelRes, actionIconRes, collectionId); } @Override public int setWallpaperBitmapInNextRotation(Bitmap wallpaperBitmap) { - @RotatingWallpaperComponent int rotatingWallpaperComponent = mRotatingWallpaperComponentChecker - .getNextRotatingWallpaperComponent(mAppContext); - - switch (rotatingWallpaperComponent) { - case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC: - return setWallpaperBitmapInRotationStatic(wallpaperBitmap); - case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_LIVE: - boolean isSuccess = setWallpaperBitmapInRotationLive(wallpaperBitmap, true /* isPreview */); - return isSuccess ? 1 : 0; - default: - Log.e(TAG, "Unknown rotating wallpaper component: " + rotatingWallpaperComponent); - return 0; - } + return setWallpaperBitmapInRotationStatic(wallpaperBitmap); } @Override public boolean finalizeWallpaperForNextRotation(List<String> attributions, String actionUrl, - int actionLabelRes, int actionIconRes, - String collectionId, int wallpaperId) { - @RotatingWallpaperComponent int rotatingWallpaperComponent = - mRotatingWallpaperComponentChecker.getNextRotatingWallpaperComponent(mAppContext); + int actionLabelRes, int actionIconRes, String collectionId, int wallpaperId) { return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes, - actionIconRes, collectionId, wallpaperId, rotatingWallpaperComponent); + actionIconRes, collectionId, wallpaperId); } /** - * Sets wallpaper image and attributions when a static wallpaper is responsible for presenting the + * Sets wallpaper image and attributions when a static wallpaper is responsible for presenting + * the * current "daily wallpaper". */ private boolean setWallpaperInRotationStatic(Bitmap wallpaperBitmap, List<String> attributions, - String actionUrl, int actionLabelRes, - int actionIconRes, String collectionId) { + String actionUrl, int actionLabelRes, int actionIconRes, String collectionId) { final int wallpaperId = setWallpaperBitmapInRotationStatic(wallpaperBitmap); if (wallpaperId == 0) { @@ -397,8 +366,7 @@ public class DefaultWallpaperPersister implements WallpaperPersister { } return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes, - actionIconRes, collectionId, wallpaperId, - RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC); + actionIconRes, collectionId, wallpaperId); } /** @@ -413,83 +381,21 @@ public class DefaultWallpaperPersister implements WallpaperPersister { int actionLabelRes, int actionIconRes, String collectionId, - int wallpaperId, - @RotatingWallpaperComponent int rotatingWallpaperComponent) { + int wallpaperId) { mWallpaperPreferences.clearHomeWallpaperMetadata(); boolean isLockWallpaperSet = isSeparateLockScreenWallpaperSet(); - // Persist wallpaper IDs if the rotating wallpaper component is static and this device is - // running Android N or later. - if (rotatingWallpaperComponent - == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC) { - if (BuildCompat.isAtLeastN()) { - mWallpaperPreferences.setHomeWallpaperManagerId(wallpaperId); + // Persist wallpaper IDs if the rotating wallpaper component + mWallpaperPreferences.setHomeWallpaperManagerId(wallpaperId); - // Only copy over wallpaper ID to lock wallpaper if no explicit lock wallpaper is set (so - // metadata isn't lost if a user explicitly sets a home-only wallpaper). - if (!isLockWallpaperSet) { - mWallpaperPreferences.setLockWallpaperId(wallpaperId); - } - } else { // Pre-N but using static component - // Compute bitmap hash code after setting the wallpaper because JPEG compression has likely - // changed many pixels' color values. Forget the previously loaded wallpaper bitmap so that - // WallpaperManager doesn't return the old wallpaper drawable. - mWallpaperManager.forgetLoadedWallpaper(); - Bitmap bitmap = ((BitmapDrawable) mWallpaperManagerCompat.getDrawable()).getBitmap(); - long bitmapHash = BitmapUtils.generateHashCode(bitmap); - - mWallpaperPreferences.setHomeWallpaperHashCode(bitmapHash); - } - } else { // Live wallpaper rotating component. - - // Copy "preview" JPEG to "rotating" JPEG if the preview file exists. - File rotatingWallpaper; - try { - rotatingWallpaper = moveToDeviceProtectedStorage( - NoBackupImageWallpaper.PREVIEW_WALLPAPER_FILE_PATH, - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH); - } catch (Exception e) { - DiskBasedLogger.e( - TAG, - "Unable to move preview to final file for rotating wallpaper " + - "file (exception)" + e.toString(), - mAppContext); - return false; - } - if (rotatingWallpaper == null) { - rotatingWallpaper = mDeviceProtectedContext.getFileStreamPath( - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH); - } - try { - FileInputStream fis = new FileInputStream(rotatingWallpaper.getAbsolutePath()); - Bitmap bitmap = BitmapFactory.decodeStream(fis); - fis.close(); - - if (bitmap != null) { - long bitmapHash = BitmapUtils.generateHashCode(bitmap); - mWallpaperPreferences.setHomeWallpaperHashCode(bitmapHash); - } else { - Log.e(TAG, "Unable to decode rotating wallpaper file"); - return false; - } - } catch (FileNotFoundException e) { - Log.e(TAG, "Rotating wallpaper file not found at path: " - + rotatingWallpaper.getAbsolutePath()); - e.printStackTrace(); - return false; - } catch (IOException e) { - Log.e(TAG, "IOException when closing FileInputStream " + e); - return false; - } - - mWallpaperChangedNotifier.notifyWallpaperChanged(); - - // Send a broadcast to {@link RotatingWallpaperChangedReceiver} in the :live_wallpaper - // process so the currently displayed wallpaper updates. - notifyLiveWallpaperBitmapChanged(); + // Only copy over wallpaper ID to lock wallpaper if no explicit lock wallpaper is set + // (so metadata isn't lost if a user explicitly sets a home-only wallpaper). + if (!isLockWallpaperSet) { + mWallpaperPreferences.setLockWallpaperId(wallpaperId); } + mWallpaperPreferences.setHomeWallpaperAttributions(attributions); mWallpaperPreferences.setHomeWallpaperActionUrl(actionUrl); mWallpaperPreferences.setHomeWallpaperActionLabelRes(actionLabelRes); @@ -498,11 +404,9 @@ public class DefaultWallpaperPersister implements WallpaperPersister { mWallpaperPreferences.setHomeWallpaperBaseImageUrl(null); mWallpaperPreferences.setHomeWallpaperCollectionId(collectionId); - // Set metadata to lock screen also when the rotating wallpaper is a static one so if user sets - // a home screen-only wallpaper later, these attributions will still be available. - if (rotatingWallpaperComponent - == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC - && !isLockWallpaperSet) { + // Set metadata to lock screen also when the rotating wallpaper so if user sets a home + // screen-only wallpaper later, these attributions will still be available. + if (!isLockWallpaperSet) { mWallpaperPreferences.setLockWallpaperAttributions(attributions); mWallpaperPreferences.setLockWallpaperActionUrl(actionUrl); mWallpaperPreferences.setLockWallpaperActionLabelRes(actionLabelRes); @@ -514,26 +418,6 @@ public class DefaultWallpaperPersister implements WallpaperPersister { } /** - * Sets wallpaper image and attributions when a live wallpaper is responsible for presenting the - * current "daily wallpaper". - */ - private boolean setWallpaperInRotationLive(Bitmap wallpaperBitmap, List<String> attributions, - String actionUrl, int actionLabelRes, - int actionIconRes, String collectionId) { - - synchronized (RotatingWallpaperLockProvider.getInstance()) { - if (!setWallpaperBitmapInRotationLive(wallpaperBitmap, false /* isPreview */)) { - return false; - } - - return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes, - actionIconRes, collectionId, - 0 /* wallpaperId */, - RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_LIVE); - } - } - - /** * Sets a wallpaper in rotation as a static wallpaper to the {@link WallpaperManager} with the * option allowBackup=false to save user data. * @@ -553,84 +437,6 @@ public class DefaultWallpaperPersister implements WallpaperPersister { } /** - * Sets a wallpaper in rotation as a live wallpaper. Writes wallpaper bitmap to a file in internal - * storage and sends a broadcast to the live wallpaper notifying it that rotating wallpaper image - * data changed. - * - * @return whether the set wallpaper operation was successful. - */ - private boolean setWallpaperBitmapInRotationLive(Bitmap wallpaperBitmap, boolean isPreview) { - File pendingFile; - try { - pendingFile = File.createTempFile("rotating_pending", ".jpg", mAppContext.getFilesDir()); - } catch (IOException e) { - Log.e(TAG, "Unable to create temp file for rotating wallpaper"); - return false; - } - - FileOutputStream fos; - try { - fos = mAppContext.openFileOutput(pendingFile.getName(), Context.MODE_PRIVATE); - } catch (FileNotFoundException e) { - Log.e(TAG, "Unable to open file output stream for pending rotating wallpaper file"); - return false; - } - - boolean compressedSuccessfully = - wallpaperBitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, fos); - - // Close the file stream. - try { - fos.flush(); - fos.close(); - } catch (IOException e) { - Log.e(TAG, "Unable to close FileOutputStream for pending rotating wallpaper file" - + " (compress succeeded"); - return false; - } - - if (compressedSuccessfully) { - // Compressing/writing to disk succeeded, so move the pending file to the final location. - try { - if (isPreview) { - if (!pendingFile.renameTo(mAppContext.getFileStreamPath( - NoBackupImageWallpaper.PREVIEW_WALLPAPER_FILE_PATH))) { - return false; - } - } else { - moveToDeviceProtectedStorage(pendingFile.getName(), - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH); - } - } catch (Exception e) { - Log.e(TAG, "Unable to rename pending to final file for rotating wallpaper file" - + " (exception)" + e.toString()); - return false; - } - } else { - Log.e(TAG, "Unable to compress the wallpaper bitmap"); - - // Delete the pending file since compressing/writing the image to disk failed. - try { - pendingFile.delete(); - } catch (SecurityException e) { - Log.e(TAG, "Unable to delete pending rotating wallpaper file"); - return false; - } - - return false; - } - - mWallpaperChangedNotifier.notifyWallpaperChanged(); - - // Send a broadcast to {@link RotatingWallpaperChangedReceiver} in the :live_wallpaper - // process so the currently displayed wallpaper updates if the live wallpaper is set to the - // device. - notifyLiveWallpaperBitmapChanged(); - - return true; - } - - /** * Sets a wallpaper bitmap to the {@link WallpaperManagerCompat}. * * @return an integer wallpaper ID. This is an actual wallpaper ID on N and later versions of @@ -638,7 +444,7 @@ public class DefaultWallpaperPersister implements WallpaperPersister { * operation was successful and zero if the operation encountered an error. */ private int setBitmapToWallpaperManagerCompat(Bitmap wallpaperBitmap, boolean allowBackup, - int whichWallpaper) { + int whichWallpaper) { ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(); if (wallpaperBitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) { try { @@ -668,9 +474,10 @@ public class DefaultWallpaperPersister implements WallpaperPersister { } private int setStreamToWallpaperManagerCompat(InputStream inputStream, boolean allowBackup, - int whichWallpaper) { + int whichWallpaper) { try { - return mWallpaperManagerCompat.setStream(inputStream, null, allowBackup, whichWallpaper); + return mWallpaperManagerCompat.setStream(inputStream, null, allowBackup, + whichWallpaper); } catch (IOException e) { return 0; } @@ -688,7 +495,8 @@ public class DefaultWallpaperPersister implements WallpaperPersister { mWallpaperInfoInPreview.getWallpaperComponent(); // If there is no live wallpaper set on the WallpaperManager or it doesn't match the - // WallpaperInfo which was last previewed, then do nothing and nullify last previewed wallpaper. + // WallpaperInfo which was last previewed, then do nothing and nullify last previewed + // wallpaper. if (currentWallpaperComponent == null || previewedWallpaperComponent == null || !currentWallpaperComponent.getPackageName() .equals(previewedWallpaperComponent.getPackageName())) { @@ -729,9 +537,9 @@ public class DefaultWallpaperPersister implements WallpaperPersister { mWallpaperInfoInPreview.getWallpaperComponent(); mWallpaperPreferences.clearHomeWallpaperMetadata(); - // NOTE: We explicitly do not also clear the lock wallpaper metadata. Since the user may have - // set the live wallpaper on the home screen only, we leave the lock wallpaper metadata intact. - // If the user has set the live wallpaper for both home and lock screens, then the + // NOTE: We explicitly do not also clear the lock wallpaper metadata. Since the user may + // have set the live wallpaper on the home screen only, we leave the lock wallpaper metadata + // intact. If the user has set the live wallpaper for both home and lock screens, then the // WallpaperRefresher will pick up on that and update the preferences later. mWallpaperPreferences .setHomeWallpaperAttributions(mWallpaperInfoInPreview.getAttributions(mAppContext)); @@ -744,36 +552,6 @@ public class DefaultWallpaperPersister implements WallpaperPersister { mWallpaperPreferences.clearDailyRotations(); } - /** - * Notifies the :live_wallpaper process that the contents of the rotating live wallpaper bitmap - * changed. - */ - private void notifyLiveWallpaperBitmapChanged() { - Intent intent = new Intent(mAppContext.getPackageName() - + NoBackupImageWallpaper.ACTION_ROTATING_WALLPAPER_CHANGED); - // Handled by a runtime-registered receiver in NoBackupImageWallpaper. - intent.setPackage(mAppContext.getPackageName()); - mAppContext.sendBroadcast(intent); - } - - /** - * Moves a file from the app's files directory to the device content protected storage - * directory. - * @param srcFileName Name of the source file (just the name, no path). It's expected to be - * located in {@link Context#getFilesDir()} for {@link #mAppContext} - * @param dstFileName Name of the destination file (just the name, no path), which will be - * located in {@link Context#getFilesDir()} - * for {@link #mDeviceProtectedContext} - * @return a {@link File} corresponding to the moved file in its new location, or null if - * nothing was moved (because srcFileName didn't exist). - */ - @Nullable - private File moveToDeviceProtectedStorage(String srcFileName, String dstFileName) - throws IOException { - return FileMover.moveFileBetweenContexts(mAppContext, srcFileName, mDeviceProtectedContext, - dstFileName); - } - private class SetWallpaperTask extends AsyncTask<Void, Void, Boolean> { private final WallpaperInfo mWallpaper; @@ -793,7 +571,7 @@ public class DefaultWallpaperPersister implements WallpaperPersister { private Point mStretchSize; SetWallpaperTask(WallpaperInfo wallpaper, Bitmap bitmap, @Destination int destination, - WallpaperPersister.SetWallpaperCallback callback) { + WallpaperPersister.SetWallpaperCallback callback) { super(); mWallpaper = wallpaper; mBitmap = bitmap; @@ -806,7 +584,7 @@ public class DefaultWallpaperPersister implements WallpaperPersister { * will close the InputStream once it is done with it. */ SetWallpaperTask(WallpaperInfo wallpaper, InputStream stream, - @Destination int destination, WallpaperPersister.SetWallpaperCallback callback) { + @Destination int destination, WallpaperPersister.SetWallpaperCallback callback) { mWallpaper = wallpaper; mInputStream = stream; mDestination = destination; @@ -815,16 +593,18 @@ public class DefaultWallpaperPersister implements WallpaperPersister { void setFillSize(Point fillSize) { if (mStretchSize != null) { - throw new IllegalArgumentException("Can't pass a fill size option if a stretch size is " - + "already set."); + throw new IllegalArgumentException( + "Can't pass a fill size option if a stretch size is " + + "already set."); } mFillSize = fillSize; } void setStretchSize(Point stretchSize) { if (mFillSize != null) { - throw new IllegalArgumentException("Can't pass a stretch size option if a fill size is " - + "already set."); + throw new IllegalArgumentException( + "Can't pass a stretch size option if a fill size is " + + "already set."); } mStretchSize = stretchSize; } @@ -841,16 +621,9 @@ public class DefaultWallpaperPersister implements WallpaperPersister { | WallpaperManagerCompat.FLAG_LOCK; } - // NOTE: The rotating wallpaper component must be determined here, _before_ actually setting - // the bitmap/stream on WallpaperManagerCompat, to ensure that the - // RotatingWallpaperComponentChecker is doing its check while rotation is still enabled. - // E.g., if "live wallpaper" is the component, then it needs to check while live wallpaper is - // still set as the active wallpaper on the device. Otherwise, the checker would see a static - // wallpaper is currently set and it would return the wrong value. - @RotatingWallpaperComponent int currentRotatingWallpaperComponent = - mRotatingWallpaperComponentChecker.getCurrentRotatingWallpaperComponent(mAppContext); - boolean wasLockWallpaperSet = LockWallpaperStatusChecker.isLockWallpaperSet(mAppContext); + boolean wasLockWallpaperSet = LockWallpaperStatusChecker.isLockWallpaperSet( + mAppContext); boolean allowBackup = mWallpaper.getBackupPermission() == WallpaperInfo.BACKUP_ALLOWED; final int wallpaperId; @@ -860,15 +633,20 @@ public class DefaultWallpaperPersister implements WallpaperPersister { mBitmap = BitmapTransformer.applyFillTransformation(mBitmap, mFillSize); } if (mStretchSize != null) { - mBitmap = Bitmap.createScaledBitmap(mBitmap, mStretchSize.x, mStretchSize.y, true); + mBitmap = Bitmap.createScaledBitmap(mBitmap, mStretchSize.x, mStretchSize.y, + true); } - wallpaperId = setBitmapToWallpaperManagerCompat(mBitmap, allowBackup, whichWallpaper); + wallpaperId = setBitmapToWallpaperManagerCompat(mBitmap, allowBackup, + whichWallpaper); } else if (mInputStream != null) { - wallpaperId = setStreamToWallpaperManagerCompat(mInputStream, allowBackup, whichWallpaper); + wallpaperId = setStreamToWallpaperManagerCompat(mInputStream, allowBackup, + whichWallpaper); } else { - Log.e(TAG, "Both the wallpaper bitmap and input stream are null so we're unable to set any " - + "kind of wallpaper here."); + Log.e(TAG, + "Both the wallpaper bitmap and input stream are null so we're unable to " + + "set any " + + "kind of wallpaper here."); wallpaperId = 0; } @@ -878,7 +656,7 @@ public class DefaultWallpaperPersister implements WallpaperPersister { == WallpaperPreferences.PRESENTATION_MODE_ROTATING && !wasLockWallpaperSet && BuildCompat.isAtLeastN()) { - copyRotatingWallpaperToLock(currentRotatingWallpaperComponent); + copyRotatingWallpaperToLock(); } setImageWallpaperMetadata(mDestination, wallpaperId); return true; @@ -914,12 +692,8 @@ public class DefaultWallpaperPersister implements WallpaperPersister { * Used to accommodate the case where a user had gone from a home+lock daily rotation to * selecting a static wallpaper on home-only. The image and metadata that was previously * rotating is now copied to the lock screen. - * - * @param currentRotatingWallpaperComponent The component in which rotating wallpapers were - * presented. */ - private void copyRotatingWallpaperToLock( - @RotatingWallpaperComponent int currentRotatingWallpaperComponent) { + private void copyRotatingWallpaperToLock() { mWallpaperPreferences.setLockWallpaperAttributions( mWallpaperPreferences.getHomeWallpaperAttributions()); @@ -935,41 +709,28 @@ public class DefaultWallpaperPersister implements WallpaperPersister { // Set the lock wallpaper ID to what Android set it to, following its having // copied the system wallpaper over to the lock screen when we changed from // "both" to distinct system and lock screen wallpapers. - if (currentRotatingWallpaperComponent - == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC) { - mWallpaperPreferences.setLockWallpaperId( - mWallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_LOCK)); - } else { - try { - FileInputStream fileInputStream = mDeviceProtectedContext.openFileInput( - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH); - int lockWallpaperId = setStreamToWallpaperManagerCompat( - fileInputStream, false /* allowBackup */, WallpaperManagerCompat.FLAG_LOCK); - fileInputStream.close(); - mWallpaperPreferences.setLockWallpaperId(lockWallpaperId); - } catch (FileNotFoundException e) { - Log.e(TAG, "Couldn't copy over previously rotating wallpaper to lock screen."); - } catch (IOException e) { - Log.e(TAG, "IOException when closing the file input stream " + e); - } - } + mWallpaperPreferences.setLockWallpaperId( + mWallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_LOCK)); + } /** - * Sets the image wallpaper's metadata on SharedPreferences. This method is called after the set - * wallpaper operation is successful. + * Sets the image wallpaper's metadata on SharedPreferences. This method is called after the + * set wallpaper operation is successful. * - * @param destination Which destination of wallpaper the metadata corresponds to (home screen, - * lock screen, or both). - * @param wallpaperId The ID of the static wallpaper returned by WallpaperManager, which on N - * and later versions of Android uniquely identifies a wallpaper image. + * @param destination Which destination of wallpaper the metadata corresponds to (home + * screen, lock screen, or both). + * @param wallpaperId The ID of the static wallpaper returned by WallpaperManager, which + * on N and later versions of Android uniquely identifies a wallpaper + * image. */ private void setImageWallpaperMetadata(@Destination int destination, int wallpaperId) { if (destination == DEST_HOME_SCREEN || destination == DEST_BOTH) { mWallpaperPreferences.clearHomeWallpaperMetadata(); setImageWallpaperHomeMetadata(wallpaperId); - // Reset presentation mode to STATIC if an individual wallpaper is set to the home screen + // Reset presentation mode to STATIC if an individual wallpaper is set to the + // home screen // because rotation always affects at least the home screen. mWallpaperPreferences.setWallpaperPresentationMode( WallpaperPreferences.PRESENTATION_MODE_STATIC); @@ -988,10 +749,11 @@ public class DefaultWallpaperPersister implements WallpaperPersister { mWallpaperPreferences.setHomeWallpaperManagerId(homeWallpaperId); } - // Compute bitmap hash code after setting the wallpaper because JPEG compression has likely - // changed many pixels' color values. Forget the previously loaded wallpaper bitmap so that - // WallpaperManager doesn't return the old wallpaper drawable. Do this on N+ devices in - // addition to saving the wallpaper ID for the purpose of backup & restore. + // Compute bitmap hash code after setting the wallpaper because JPEG compression has + // likely changed many pixels' color values. Forget the previously loaded wallpaper + // bitmap so that WallpaperManager doesn't return the old wallpaper drawable. Do this + // on N+ devices in addition to saving the wallpaper ID for the purpose of backup & + // restore. mWallpaperManager.forgetLoadedWallpaper(); mBitmap = ((BitmapDrawable) mWallpaperManagerCompat.getDrawable()).getBitmap(); long bitmapHash = BitmapUtils.generateHashCode(mBitmap); @@ -1023,9 +785,10 @@ public class DefaultWallpaperPersister implements WallpaperPersister { mWallpaperPreferences.setLockWallpaperCollectionId( mWallpaper.getCollectionId(mAppContext)); - // Save the lock wallpaper image's hash code as well for the sake of backup & restore because - // WallpaperManager-generated IDs are specific to a physical device and cannot be used to - // identify a wallpaper image on another device after restore is complete. + // Save the lock wallpaper image's hash code as well for the sake of backup & restore + // because WallpaperManager-generated IDs are specific to a physical device and + // cannot be used to identify a wallpaper image on another device after restore is + // complete. saveLockWallpaperHashCode(); } @@ -1051,7 +814,9 @@ public class DefaultWallpaperPersister implements WallpaperPersister { try { fileStream.close(); } catch (IOException e) { - Log.e(TAG, "IO exception when closing the input stream for the lock screen WP."); + Log.e(TAG, + "IO exception when closing the input stream for the lock screen " + + "WP."); } } } diff --git a/src/com/android/wallpaper/module/DefaultWallpaperPreferences.java b/src/com/android/wallpaper/module/DefaultWallpaperPreferences.java index 319276b..b040ebb 100755 --- a/src/com/android/wallpaper/module/DefaultWallpaperPreferences.java +++ b/src/com/android/wallpaper/module/DefaultWallpaperPreferences.java @@ -24,6 +24,8 @@ import android.util.Log; import androidx.annotation.Nullable; +import com.android.wallpaper.module.WallpaperPreferenceKeys.NoBackupKeys; + import org.json.JSONArray; import org.json.JSONException; @@ -39,10 +41,12 @@ import java.util.List; */ public class DefaultWallpaperPreferences implements WallpaperPreferences { public static final String PREFS_NAME = "wallpaper"; + public static final String NO_BACKUP_PREFS_NAME = "wallpaper-nobackup"; private static final String TAG = "DefaultWPPreferences"; protected SharedPreferences mSharedPrefs; + protected SharedPreferences mNoBackupPrefs; protected Context mContext; // Keep a strong reference to this OnSharedPreferenceChangeListener to prevent the listener from @@ -51,19 +55,106 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { public DefaultWallpaperPreferences(Context context) { mSharedPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); + mNoBackupPrefs = context.getSharedPreferences(NO_BACKUP_PREFS_NAME, Context.MODE_PRIVATE); + if (mNoBackupPrefs.getAll().isEmpty() && !mSharedPrefs.getAll().isEmpty()) { + upgradePrefs(); + } mContext = context.getApplicationContext(); // Register a prefs changed listener so that all prefs changes trigger a backup event. final BackupManager backupManager = new BackupManager(context); - mSharedPrefsChangedListener = new OnSharedPreferenceChangeListener() { - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - backupManager.dataChanged(); - } - }; + mSharedPrefsChangedListener = (sharedPreferences, key) -> backupManager.dataChanged(); mSharedPrefs.registerOnSharedPreferenceChangeListener(mSharedPrefsChangedListener); } + /** + * Move {@link NoBackupKeys} preferences that might have been in mSharedPrefs from previous + * versions of the app into mNoBackupPrefs. + */ + private void upgradePrefs() { + SharedPreferences.Editor editor = mNoBackupPrefs.edit(); + if (mSharedPrefs.contains( + NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL)) { + editor.putString(NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, + mSharedPrefs.getString(NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, null)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID)) { + editor.putInt(NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID, + mSharedPrefs.getInt(NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID, 0)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID)) { + editor.putString(NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID, + mSharedPrefs.getString(NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID, null)); + } + if (mSharedPrefs.contains( + NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE)) { + editor.putString(NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE, + mSharedPrefs.getString(NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE, null)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID)) { + editor.putInt(NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, + mSharedPrefs.getInt(NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, 0)); + } + if (mSharedPrefs.contains( + NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE)) { + editor.putString(NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE, + mSharedPrefs.getString(NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE, null)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS)) { + editor.putString(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, + mSharedPrefs.getString(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, null)); + } + if (mSharedPrefs.contains( + NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP)) { + editor.putLong(NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP, + mSharedPrefs.getLong(NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP, -1)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_LAST_DAILY_LOG_TIMESTAMP)) { + editor.putLong(NoBackupKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, + mSharedPrefs.getLong(NoBackupKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, 0)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP)) { + editor.putLong(NoBackupKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, + mSharedPrefs.getLong(NoBackupKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, 0)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_LAST_ROTATION_STATUS)) { + editor.putInt(NoBackupKeys.KEY_LAST_ROTATION_STATUS, + mSharedPrefs.getInt(NoBackupKeys.KEY_LAST_ROTATION_STATUS, -1)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP)) { + editor.putLong(NoBackupKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP, + mSharedPrefs.getLong(NoBackupKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP, 0)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_LAST_SYNC_TIMESTAMP)) { + editor.putLong(NoBackupKeys.KEY_LAST_SYNC_TIMESTAMP, + mSharedPrefs.getLong(NoBackupKeys.KEY_LAST_SYNC_TIMESTAMP, 0)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS)) { + editor.putInt(NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS, + mSharedPrefs.getInt(NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS, + WALLPAPER_SET_NOT_PENDING)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS)) { + editor.putInt(NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS, + mSharedPrefs.getInt(NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS, + DAILY_WALLPAPER_UPDATE_NOT_PENDING)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED)) { + editor.putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, + mSharedPrefs.getInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, 0)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED)) { + editor.putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, + mSharedPrefs.getInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, 0)); + } + if (mSharedPrefs.contains(NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME)) { + editor.putString(NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, + mSharedPrefs.getString(NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, null)); + } + + editor.apply(); + } + private int getResIdPersistedByName(String key, String type) { String resName = mSharedPrefs.getString(key, null); if (resName == null) { @@ -106,13 +197,16 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { public void setHomeWallpaperAttributions(List<String> attributions) { SharedPreferences.Editor editor = mSharedPrefs.edit(); if (attributions.size() > 0) { - editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_1, attributions.get(0)); + editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_1, + attributions.get(0)); } if (attributions.size() > 1) { - editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_2, attributions.get(1)); + editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_2, + attributions.get(1)); } if (attributions.size() > 2) { - editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_3, attributions.get(2)); + editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_3, + attributions.get(2)); } editor.apply(); } @@ -155,19 +249,22 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { @Override public String getHomeWallpaperBaseImageUrl() { - return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, null); + return mNoBackupPrefs.getString( + NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, null); } @Override public void setHomeWallpaperBaseImageUrl(String baseImageUrl) { - mSharedPrefs.edit().putString( - WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, baseImageUrl).apply(); + mNoBackupPrefs.edit().putString( + NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, baseImageUrl) + .apply(); } @Override @Nullable public String getHomeWallpaperCollectionId() { - return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_COLLECTION_ID, null); + return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_COLLECTION_ID, + null); } @Override @@ -179,14 +276,14 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { @Override @Nullable public String getHomeWallpaperBackingFileName() { - return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BACKING_FILE, - null); + return mNoBackupPrefs.getString( + NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE, null); } @Override public void setHomeWallpaperBackingFileName(String fileName) { - mSharedPrefs.edit().putString( - WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BACKING_FILE, fileName).apply(); + mNoBackupPrefs.edit().putString( + NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE, fileName).apply(); } @Override @@ -213,47 +310,56 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ACTION_URL) .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ACTION_LABEL_RES) .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ACTION_ICON_RES) - .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL) .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_HASH_CODE) - .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_MANAGER_ID) - .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME) - .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_REMOTE_ID) - .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BACKING_FILE) + .apply(); + + mNoBackupPrefs.edit() + .remove(NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME) + .remove(NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID) + .remove(NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID) + .remove(NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL) + .remove(NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE) .apply(); } @Override public String getHomeWallpaperPackageName() { - return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, null); + return mNoBackupPrefs.getString( + NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, null); } @Override public void setHomeWallpaperPackageName(String packageName) { - mSharedPrefs.edit().putString( - WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, packageName).apply(); + mNoBackupPrefs.edit().putString( + NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, packageName) + .apply(); } @Override public int getHomeWallpaperManagerId() { - return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_MANAGER_ID, 0); + return mNoBackupPrefs.getInt( + NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID, 0); } @Override public void setHomeWallpaperManagerId(int homeWallpaperId) { - mSharedPrefs.edit().putInt( - WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_MANAGER_ID, homeWallpaperId).apply(); + mNoBackupPrefs.edit().putInt( + NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID, homeWallpaperId) + .apply(); } @Nullable @Override public String getHomeWallpaperRemoteId() { - return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_REMOTE_ID, null); + return mNoBackupPrefs.getString( + NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID, null); } @Override public void setHomeWallpaperRemoteId(@Nullable String wallpaperRemoteId) { - mSharedPrefs.edit().putString( - WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_REMOTE_ID, wallpaperRemoteId).apply(); + mNoBackupPrefs.edit().putString( + NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID, wallpaperRemoteId) + .apply(); } @Override @@ -269,13 +375,16 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { public void setLockWallpaperAttributions(List<String> attributions) { SharedPreferences.Editor editor = mSharedPrefs.edit(); if (attributions.size() > 0) { - editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_1, attributions.get(0)); + editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_1, + attributions.get(0)); } if (attributions.size() > 1) { - editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_2, attributions.get(1)); + editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_2, + attributions.get(1)); } if (attributions.size() > 2) { - editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_3, attributions.get(2)); + editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_3, + attributions.get(2)); } editor.apply(); } @@ -319,7 +428,8 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { @Override @Nullable public String getLockWallpaperCollectionId() { - return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_COLLECTION_ID, null); + return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_COLLECTION_ID, + null); } @Override @@ -331,25 +441,27 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { @Override @Nullable public String getLockWallpaperBackingFileName() { - return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_BACKING_FILE, - null); + return mNoBackupPrefs.getString( + NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE, null); } @Override public void setLockWallpaperBackingFileName(String fileName) { - mSharedPrefs.edit().putString( - WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_BACKING_FILE, fileName).apply(); + mNoBackupPrefs.edit().putString( + NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE, fileName).apply(); } @Override public int getLockWallpaperId() { - return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, 0); + return mNoBackupPrefs.getInt( + NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, 0); } @Override public void setLockWallpaperId(int lockWallpaperId) { - mSharedPrefs.edit().putInt( - WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, lockWallpaperId).apply(); + mNoBackupPrefs.edit().putInt( + NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, lockWallpaperId) + .apply(); } @Override @@ -378,21 +490,25 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { .remove(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ACTION_LABEL_RES) .remove(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ACTION_ICON_RES) .remove(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_HASH_CODE) - .remove(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_MANAGER_ID) - .remove(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_BACKING_FILE) + .apply(); + + mNoBackupPrefs.edit() + .remove(NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID) + .remove(NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE) .apply(); } @Override public void addDailyRotation(long timestamp) { - String jsonString = mSharedPrefs.getString( - WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]"); + String jsonString = mNoBackupPrefs.getString( + NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]"); try { JSONArray jsonArray = new JSONArray(jsonString); jsonArray.put(timestamp); - mSharedPrefs.edit() - .putString(WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, jsonArray.toString()) + mNoBackupPrefs.edit() + .putString(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, + jsonArray.toString()) .apply(); } catch (JSONException e) { Log.e(TAG, "Failed to add a daily rotation timestamp due to a JSON parse exception"); @@ -401,8 +517,8 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { @Override public long getLastDailyRotationTimestamp() { - String jsonString = mSharedPrefs.getString( - WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]"); + String jsonString = mNoBackupPrefs.getString( + NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]"); try { JSONArray jsonArray = new JSONArray(jsonString); @@ -428,15 +544,15 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { oneWeekAgo.add(Calendar.WEEK_OF_YEAR, -1); long oneWeekAgoTimestamp = oneWeekAgo.getTimeInMillis(); - // Return null if daily rotation wasn't enabled (timestamp value of -1) or was enabled earlier - // than one week ago. + // Return null if daily rotation wasn't enabled (timestamp value of -1) or was enabled + // less than one week ago. if (enabledTimestamp == -1 || enabledTimestamp > oneWeekAgoTimestamp) { return null; } List<Long> timestamps = new ArrayList<>(); - String jsonString = mSharedPrefs.getString( - WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]"); + String jsonString = mNoBackupPrefs.getString( + NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]"); try { JSONArray jsonArray = new JSONArray(jsonString); @@ -451,8 +567,9 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { } jsonArray = new JSONArray(timestamps); - mSharedPrefs.edit() - .putString(WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, jsonArray.toString()) + mNoBackupPrefs.edit() + .putString(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, + jsonArray.toString()) .apply(); } catch (JSONException e) { Log.e(TAG, "Failed to get daily rotation timestamps due to a JSON parse exception"); @@ -479,21 +596,21 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { midnightToday.set(Calendar.MINUTE, 0); long midnightTodayTimestamp = midnightToday.getTimeInMillis(); - // Return null if daily rotation wasn't enabled (timestamp value of -1) or was enabled earlier - // than midnight yesterday. + // Return null if daily rotation wasn't enabled (timestamp value of -1) or was enabled + // less than midnight yesterday. if (enabledTimestamp == -1 || enabledTimestamp > midnightYesterdayTimestamp) { return null; } List<Long> timestamps = new ArrayList<>(); - String jsonString = mSharedPrefs.getString( - WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]"); + String jsonString = mNoBackupPrefs.getString( + NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]"); try { JSONArray jsonArray = new JSONArray(jsonString); - // Filter the timestamps (which cover up to one week of data) to only include those between - // midnight yesterday and midnight today. + // Filter the timestamps (which cover up to one week of data) to only include those + // between midnight yesterday and midnight today. for (int i = 0; i < jsonArray.length(); i++) { long timestamp = jsonArray.getLong(i); if (timestamp >= midnightYesterdayTimestamp && timestamp < midnightTodayTimestamp) { @@ -510,160 +627,175 @@ public class DefaultWallpaperPreferences implements WallpaperPreferences { @Override public long getDailyWallpaperEnabledTimestamp() { - return mSharedPrefs.getLong(WallpaperPreferenceKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP, -1); + return mNoBackupPrefs.getLong( + NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP, -1); } @Override public void setDailyWallpaperEnabledTimestamp(long timestamp) { - mSharedPrefs.edit() - .putLong(WallpaperPreferenceKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP, timestamp) + mNoBackupPrefs.edit() + .putLong(NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP, + timestamp) .apply(); } @Override public void clearDailyRotations() { - mSharedPrefs.edit() - .remove(WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS) - .remove(WallpaperPreferenceKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP) + mNoBackupPrefs.edit() + .remove(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS) + .remove(NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP) .apply(); } @Override public long getLastDailyLogTimestamp() { - return mSharedPrefs.getLong(WallpaperPreferenceKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, 0); + return mNoBackupPrefs.getLong( + NoBackupKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, 0); } @Override public void setLastDailyLogTimestamp(long timestamp) { - mSharedPrefs.edit() - .putLong(WallpaperPreferenceKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, timestamp) + mNoBackupPrefs.edit() + .putLong(NoBackupKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, timestamp) .apply(); } @Override public long getLastAppActiveTimestamp() { - return mSharedPrefs.getLong(WallpaperPreferenceKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, 0); + return mNoBackupPrefs.getLong( + NoBackupKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, 0); } @Override public void setLastAppActiveTimestamp(long timestamp) { - mSharedPrefs.edit() - .putLong(WallpaperPreferenceKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, timestamp) + mNoBackupPrefs.edit() + .putLong(NoBackupKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, timestamp) .apply(); } @Override public void setDailyWallpaperRotationStatus(int status, long timestamp) { - mSharedPrefs.edit() - .putInt(WallpaperPreferenceKeys.KEY_LAST_ROTATION_STATUS, status) - .putLong(WallpaperPreferenceKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP, timestamp) + mNoBackupPrefs.edit() + .putInt(NoBackupKeys.KEY_LAST_ROTATION_STATUS, status) + .putLong(NoBackupKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP, + timestamp) .apply(); } @Override public int getDailyWallpaperLastRotationStatus() { - return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_LAST_ROTATION_STATUS, -1); + return mNoBackupPrefs.getInt(NoBackupKeys.KEY_LAST_ROTATION_STATUS, -1); } @Override public long getDailyWallpaperLastRotationStatusTimestamp() { - return mSharedPrefs.getLong(WallpaperPreferenceKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP, 0); + return mNoBackupPrefs.getLong( + NoBackupKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP, 0); } @Override public long getLastSyncTimestamp() { - return mSharedPrefs.getLong(WallpaperPreferenceKeys.KEY_LAST_SYNC_TIMESTAMP, 0); + return mNoBackupPrefs.getLong(NoBackupKeys.KEY_LAST_SYNC_TIMESTAMP, 0); } @Override public void setLastSyncTimestamp(long timestamp) { - // Write synchronously via commit() to ensure this timetsamp gets written to disk immediately. - mSharedPrefs.edit() - .putLong(WallpaperPreferenceKeys.KEY_LAST_SYNC_TIMESTAMP, timestamp) + // Write synchronously via commit() to ensure this timetsamp gets written to disk + // immediately. + mNoBackupPrefs.edit() + .putLong(NoBackupKeys.KEY_LAST_SYNC_TIMESTAMP, timestamp) .commit(); } @Override public void setPendingWallpaperSetStatusSync(@PendingWallpaperSetStatus int setStatus) { - mSharedPrefs.edit() - .putInt(WallpaperPreferenceKeys.KEY_PENDING_WALLPAPER_SET_STATUS, setStatus) + mNoBackupPrefs.edit() + .putInt(NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS, + setStatus) .commit(); } @Override public int getPendingWallpaperSetStatus() { //noinspection ResourceType - return mSharedPrefs.getInt( - WallpaperPreferenceKeys.KEY_PENDING_WALLPAPER_SET_STATUS, WALLPAPER_SET_NOT_PENDING); + return mNoBackupPrefs.getInt( + NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS, + WALLPAPER_SET_NOT_PENDING); } @Override public void setPendingWallpaperSetStatus(@PendingWallpaperSetStatus int setStatus) { - mSharedPrefs.edit() - .putInt(WallpaperPreferenceKeys.KEY_PENDING_WALLPAPER_SET_STATUS, setStatus) + mNoBackupPrefs.edit() + .putInt(NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS, + setStatus) .apply(); } @Override public void setPendingDailyWallpaperUpdateStatusSync( @PendingDailyWallpaperUpdateStatus int updateStatus) { - mSharedPrefs.edit() - .putInt(WallpaperPreferenceKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS, updateStatus) + mNoBackupPrefs.edit() + .putInt(NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS, + updateStatus) .commit(); } @Override public int getPendingDailyWallpaperUpdateStatus() { //noinspection ResourceType - return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS, + return mNoBackupPrefs.getInt( + NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS, DAILY_WALLPAPER_UPDATE_NOT_PENDING); } @Override public void setPendingDailyWallpaperUpdateStatus( @PendingDailyWallpaperUpdateStatus int updateStatus) { - mSharedPrefs.edit() - .putInt(WallpaperPreferenceKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS, updateStatus) + mNoBackupPrefs.edit() + .putInt(NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS, + updateStatus) .apply(); } @Override public void incrementNumDaysDailyRotationFailed() { - mSharedPrefs.edit() - .putInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, + mNoBackupPrefs.edit() + .putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, getNumDaysDailyRotationFailed() + 1) .apply(); } @Override public int getNumDaysDailyRotationFailed() { - return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, 0); + return mNoBackupPrefs.getInt( + NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, 0); } @Override public void resetNumDaysDailyRotationFailed() { - mSharedPrefs.edit() - .putInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, 0) + mNoBackupPrefs.edit() + .putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, 0) .apply(); } @Override public void incrementNumDaysDailyRotationNotAttempted() { - mSharedPrefs.edit() - .putInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, + mNoBackupPrefs.edit() + .putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, getNumDaysDailyRotationNotAttempted() + 1) .apply(); } @Override public int getNumDaysDailyRotationNotAttempted() { - return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, 0); + return mNoBackupPrefs.getInt( + NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, 0); } @Override public void resetNumDaysDailyRotationNotAttempted() { - mSharedPrefs.edit() - .putInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, 0) + mNoBackupPrefs.edit() + .putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, 0) .apply(); } } diff --git a/src/com/android/wallpaper/module/DefaultWallpaperRefresher.java b/src/com/android/wallpaper/module/DefaultWallpaperRefresher.java index c97d2b9..d85e04e 100755 --- a/src/com/android/wallpaper/module/DefaultWallpaperRefresher.java +++ b/src/com/android/wallpaper/module/DefaultWallpaperRefresher.java @@ -32,7 +32,6 @@ import com.android.wallpaper.compat.WallpaperManagerCompat; import com.android.wallpaper.model.WallpaperMetadata; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -50,9 +49,6 @@ public class DefaultWallpaperRefresher implements WallpaperRefresher { private final Context mAppContext; private final WallpaperPreferences mWallpaperPreferences; private final WallpaperManager mWallpaperManager; - private final LiveWallpaperStatusChecker mLiveWallpaperStatusChecker; - private final UserEventLogger mUserEventLogger; - private final Context mDeviceProtectedContext; /** * @param context The application's context. @@ -62,13 +58,10 @@ public class DefaultWallpaperRefresher implements WallpaperRefresher { Injector injector = InjectorProvider.getInjector(); mWallpaperPreferences = injector.getPreferences(mAppContext); - mLiveWallpaperStatusChecker = injector.getLiveWallpaperStatusChecker(mAppContext); - mUserEventLogger = injector.getUserEventLogger(mAppContext); // Retrieve WallpaperManager using Context#getSystemService instead of // WallpaperManager#getInstance so it can be mocked out in test. mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE); - mDeviceProtectedContext = mAppContext.createDeviceProtectedStorageContext(); } @Override @@ -203,8 +196,7 @@ public class DefaultWallpaperRefresher implements WallpaperRefresher { * current system wallpaper. */ private boolean isHomeScreenMetadataCurrent() { - return (mWallpaperManager.getWallpaperInfo() == null - || mLiveWallpaperStatusChecker.isNoBackupImageWallpaperSet()) + return (mWallpaperManager.getWallpaperInfo() == null) ? isHomeScreenImageWallpaperCurrent() : isHomeScreenLiveWallpaperCurrent(); } @@ -221,36 +213,6 @@ public class DefaultWallpaperRefresher implements WallpaperRefresher { private long getCurrentHomeWallpaperHashCode() { if (mCurrentHomeWallpaperHashCode == 0) { - if (mLiveWallpaperStatusChecker.isNoBackupImageWallpaperSet()) { - - synchronized (RotatingWallpaperLockProvider.getInstance()) { - Bitmap bitmap = null; - try { - FileInputStream fis = - mDeviceProtectedContext.openFileInput( - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH); - bitmap = BitmapFactory.decodeStream(fis); - fis.close(); - } catch (FileNotFoundException e) { - Log.e(TAG, "Rotating wallpaper file not found at path: " - + mDeviceProtectedContext.getFileStreamPath( - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH), - e); - } catch (IOException e) { - Log.e(TAG, "IOException when closing FileInputStream " + e); - } - - if (bitmap != null) { - mCurrentHomeWallpaperHashCode = BitmapUtils.generateHashCode(bitmap); - mUserEventLogger.logDailyWallpaperDecodes(true); - } else { - // If an error occurred decoding the stream then we should just assume the current - // home wallpaper remained intact. - mCurrentHomeWallpaperHashCode = mWallpaperPreferences.getHomeWallpaperHashCode(); - mUserEventLogger.logDailyWallpaperDecodes(false); - } - } - } else { BitmapDrawable wallpaperDrawable = (BitmapDrawable) mWallpaperManagerCompat.getDrawable(); Bitmap wallpaperBitmap = wallpaperDrawable.getBitmap(); mCurrentHomeWallpaperHashCode = BitmapUtils.generateHashCode(wallpaperBitmap); @@ -258,7 +220,6 @@ public class DefaultWallpaperRefresher implements WallpaperRefresher { // Manually request that WallpaperManager loses its reference to the current wallpaper // bitmap, which can occupy a large memory allocation for the lifetime of the app. mWallpaperManager.forgetLoadedWallpaper(); - } } return mCurrentHomeWallpaperHashCode; } diff --git a/src/com/android/wallpaper/module/Injector.java b/src/com/android/wallpaper/module/Injector.java index 3afaeb4..ca64b9d 100755 --- a/src/com/android/wallpaper/module/Injector.java +++ b/src/com/android/wallpaper/module/Injector.java @@ -44,8 +44,6 @@ public interface Injector { FormFactorChecker getFormFactorChecker(Context context); - LiveWallpaperStatusChecker getLiveWallpaperStatusChecker(Context context); - LoggingOptInStatusProvider getLoggingOptInStatusProvider(Context context); NetworkStatusNotifier getNetworkStatusNotifier(Context context); @@ -56,8 +54,6 @@ public interface Injector { Requester getRequester(Context context); - RotatingWallpaperComponentChecker getRotatingWallpaperComponentChecker(); - SystemFeatureChecker getSystemFeatureChecker(); UserEventLogger getUserEventLogger(Context context); diff --git a/src/com/android/wallpaper/module/NoBackupImageWallpaper.java b/src/com/android/wallpaper/module/NoBackupImageWallpaper.java deleted file mode 100755 index a76d881..0000000 --- a/src/com/android/wallpaper/module/NoBackupImageWallpaper.java +++ /dev/null @@ -1,1018 +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 static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT; -import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE; - -import android.annotation.SuppressLint; -import android.app.WallpaperColors; -import android.app.WallpaperManager; -import android.content.BroadcastReceiver; -import android.content.ComponentCallbacks2; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.opengl.GLES20; -import android.opengl.GLUtils; -import android.os.AsyncTask; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; -import android.os.Handler; -import android.renderscript.Matrix4f; -import android.service.wallpaper.WallpaperService; -import android.util.Log; -import android.view.Display; -import android.view.MotionEvent; -import android.view.SurfaceHolder; -import android.view.WindowManager; - -import com.android.wallpaper.util.ScreenSizeCalculator; - -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLSurface; - -import androidx.annotation.RequiresApi; - -/** - * Live wallpaper service which simply renders a wallpaper from internal storage. Designed as a - * workaround to WallpaperManager not having an allowBackup=false option on pre-N builds of Android. - * <p> - * Adapted from {@code com.android.systemui.ImageWallpaper}. - */ -@SuppressLint("ServiceCast") -public class NoBackupImageWallpaper extends WallpaperService { - - public static final String ACTION_ROTATING_WALLPAPER_CHANGED = - ".ACTION_ROTATING_WALLPAPER_CHANGED"; - public static final String PERMISSION_NOTIFY_ROTATING_WALLPAPER_CHANGED = - ".NOTIFY_ROTATING_WALLPAPER_CHANGED"; - public static final String PREVIEW_WALLPAPER_FILE_PATH = "preview_wallpaper.jpg"; - public static final String ROTATING_WALLPAPER_FILE_PATH = "rotating_wallpaper.jpg"; - - private static final String TAG = "NoBackupImageWallpaper"; - private static final String GL_LOG_TAG = "ImageWallpaperGL"; - private static final boolean DEBUG = false; - private static final boolean FIXED_SIZED_SURFACE = false; - - private final Handler mHandler = new Handler(); - - private int mOpenGlContextCounter; - private WallpaperManager mWallpaperManager; - private DrawableEngine mEngine; - private boolean mIsHardwareAccelerated; - - @Override - public void onCreate() { - super.onCreate(); - - mOpenGlContextCounter = 0; - mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE); - - // By default, use OpenGL for drawing the static wallpaper image. - mIsHardwareAccelerated = true; - } - - @Override - public void onTrimMemory(int level) { - if (mEngine != null) { - mEngine.trimMemory(level); - } - } - - @Override - public Engine onCreateEngine() { - mEngine = new DrawableEngine(); - return mEngine; - } - - private class DrawableEngine extends Engine { - static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - static final int EGL_OPENGL_ES2_BIT = 4; - private static final String S_SIMPLE_VS = - "attribute vec4 position;\n" - + "attribute vec2 texCoords;\n" - + "varying vec2 outTexCoords;\n" - + "uniform mat4 projection;\n" - + "\nvoid main(void) {\n" - + " outTexCoords = texCoords;\n" - + " gl_Position = projection * position;\n" - + "}\n\n"; - private static final String S_SIMPLE_FS = - "precision mediump float;\n\n" - + "varying vec2 outTexCoords;\n" - + "uniform sampler2D texture;\n" - + "\nvoid main(void) {\n" - + " gl_FragColor = texture2D(texture, outTexCoords);\n" - + "}\n\n"; - private static final int FLOAT_SIZE_BYTES = 4; - private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; - private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; - private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; - Bitmap mBackground; - WallpaperColors mCachedWallpaperColors; - int mBackgroundWidth = -1, mBackgroundHeight = -1; - int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1; - int mLastRotation = -1; - float mXOffset = 0.5f; - float mYOffset = 0.5f; - float mScale = 1f; - boolean mVisible = true; - boolean mOffsetsChanged; - int mLastXTranslation; - int mLastYTranslation; - private Display mDefaultDisplay; - private EGL10 mEgl; - private EGLDisplay mEglDisplay; - private EGLConfig mEglConfig; - private EGLContext mEglContext; - private EGLSurface mEglSurface; - private int mTexture; - private int mProgram; - private boolean mIsOpenGlTextureLoaded; - private int mRotationAtLastSurfaceSizeUpdate = -1; - private int mDisplayWidthAtLastSurfaceSizeUpdate = -1; - private int mDisplayHeightAtLastSurfaceSizeUpdate = -1; - - private int mLastRequestedWidth = -1; - private int mLastRequestedHeight = -1; - private AsyncTask<Void, Void, Bitmap> mLoader; - private boolean mNeedsDrawAfterLoadingWallpaper; - private boolean mSurfaceValid; - - private BroadcastReceiver mReceiver; - - public DrawableEngine() { - super(); - } - - public void trimMemory(int level) { - if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW - && mBackground != null) { - if (DEBUG) { - Log.d(TAG, "trimMemory"); - } - mBackground.recycle(); - mBackground = null; - mBackgroundWidth = -1; - mBackgroundHeight = -1; - } - } - - @Override - public void onCreate(SurfaceHolder surfaceHolder) { - if (DEBUG) { - Log.d(TAG, "onCreate"); - } - - super.onCreate(surfaceHolder); - - mIsOpenGlTextureLoaded = false; - - mDefaultDisplay = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)) - .getDefaultDisplay(); - - updateSurfaceSize(surfaceHolder, mDefaultDisplay, false /* forDraw */); - - // Enable offset notifications to pan wallpaper for parallax effect. - setOffsetNotificationsEnabled(true); - - // If not a preview, then register a local broadcast receiver for listening to changes in the - // rotating wallpaper file. - if (!isPreview()) { - IntentFilter filter = new IntentFilter(); - filter.addAction(getPackageName() + ACTION_ROTATING_WALLPAPER_CHANGED); - - mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (DEBUG) { - Log.i(TAG, "Broadcast received with intent: " + intent); - } - - String action = intent.getAction(); - if (action.equals(getPackageName() + ACTION_ROTATING_WALLPAPER_CHANGED)) { - DrawableEngine.this.invalidateAndRedrawWallpaper(); - } - } - }; - - registerReceiver(mReceiver, filter, getPackageName() - + PERMISSION_NOTIFY_ROTATING_WALLPAPER_CHANGED, null /* handler */); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - mBackground = null; - mWallpaperManager.forgetLoadedWallpaper(); - - if (!isPreview() && mReceiver != null) { - unregisterReceiver(mReceiver); - } - } - - boolean updateSurfaceSize(SurfaceHolder surfaceHolder, Display display, boolean forDraw) { - boolean hasWallpaper = true; - Point displaySize = ScreenSizeCalculator.getInstance().getScreenSize(display); - - // Load background image dimensions, if we haven't saved them yet - if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) { - // Need to load the image to get dimensions - loadWallpaper(forDraw); - if (DEBUG) { - Log.d(TAG, "Reloading, redoing updateSurfaceSize later."); - } - hasWallpaper = false; - } - - // Force the wallpaper to cover the screen in both dimensions - int surfaceWidth = Math.max(displaySize.x, mBackgroundWidth); - int surfaceHeight = Math.max(displaySize.y, mBackgroundHeight); - - if (FIXED_SIZED_SURFACE) { - // Used a fixed size surface, because we are special. We can do - // this because we know the current design of window animations doesn't - // cause this to break. - surfaceHolder.setFixedSize(surfaceWidth, surfaceHeight); - mLastRequestedWidth = surfaceWidth; - mLastRequestedHeight = surfaceHeight; - } else { - surfaceHolder.setSizeFromLayout(); - } - return hasWallpaper; - } - - @Override - public void onVisibilityChanged(boolean visible) { - if (DEBUG) { - Log.d(TAG, "onVisibilityChanged: mVisible, visible=" + mVisible + ", " + visible); - } - - if (mVisible != visible) { - if (DEBUG) { - Log.d(TAG, "Visibility changed to visible=" + visible); - } - mVisible = visible; - drawFrame(false /* forceRedraw */); - } - } - - @Override - public void onTouchEvent(MotionEvent event) { - super.onTouchEvent(event); - } - - @Override - public void onOffsetsChanged(float xOffset, float yOffset, - float xOffsetStep, float yOffsetStep, - int xPixels, int yPixels) { - if (DEBUG) { - Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset - + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep - + ", xPixels=" + xPixels + ", yPixels=" + yPixels); - } - - if (mXOffset != xOffset || mYOffset != yOffset) { - if (DEBUG) { - Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ")."); - } - mXOffset = xOffset; - mYOffset = yOffset; - mOffsetsChanged = true; - } - mHandler.post(new Runnable() { - @Override - public void run() { - drawFrame(false /* forceRedraw */); - } - }); - } - - @Override - public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { - if (DEBUG) { - Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height); - } - - super.onSurfaceChanged(holder, format, width, height); - - // Retrieve buffer in new size. - if (mEgl != null) { - mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); - } - drawFrame(false /* forceRedraw */); - } - - @Override - public void onSurfaceDestroyed(SurfaceHolder holder) { - super.onSurfaceDestroyed(holder); - if (DEBUG) { - Log.d(TAG, "onSurfaceDestroyed"); - } - mLastSurfaceWidth = mLastSurfaceHeight = -1; - mSurfaceValid = false; - - if (mIsHardwareAccelerated) { - finishGL(mTexture, mProgram); - } - } - - @Override - public void onSurfaceCreated(SurfaceHolder holder) { - super.onSurfaceCreated(holder); - if (DEBUG) { - Log.d(TAG, "onSurfaceCreated"); - } - mLastSurfaceWidth = mLastSurfaceHeight = -1; - mSurfaceValid = true; - - if (mIsHardwareAccelerated) { - if (!initGL(holder)) { - // Fall back to canvas drawing if initializing OpenGL failed. - mIsHardwareAccelerated = false; - mEgl = null; - } - } - } - - @Override - public void onSurfaceRedrawNeeded(SurfaceHolder holder) { - if (DEBUG) { - Log.d(TAG, "onSurfaceRedrawNeeded"); - } - super.onSurfaceRedrawNeeded(holder); - - drawFrame(true /* forceRedraw */); - } - - @RequiresApi(VERSION_CODES.O_MR1) - @Override - public WallpaperColors onComputeColors() { - // It's OK to return null here. - return mCachedWallpaperColors; - } - - /** - * Invalidates the currently-drawn wallpaper image, causing the engine to reload the image from - * disk and draw the new wallpaper image. - */ - public void invalidateAndRedrawWallpaper() { - // If a wallpaper load was already in flight, cancel it and restart a load in order to decode - // the new image. - if (mLoader != null) { - mLoader.cancel(true /* mayInterruptIfRunning */); - mLoader = null; - } - - loadWallpaper(true /* needsDraw */); - } - - void drawFrame(boolean forceRedraw) { - if (!mSurfaceValid) { - return; - } - - Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(mDefaultDisplay); - int newRotation = mDefaultDisplay.getRotation(); - - // Sometimes a wallpaper is not large enough to cover the screen in one dimension. - // Call updateSurfaceSize -- it will only actually do the update if the dimensions - // should change - if (newRotation != mLastRotation) { - // Update surface size (if necessary) - if (!updateSurfaceSize(getSurfaceHolder(), mDefaultDisplay, true /* forDraw */)) { - return; - } - mRotationAtLastSurfaceSizeUpdate = newRotation; - mDisplayWidthAtLastSurfaceSizeUpdate = screenSize.x; - mDisplayHeightAtLastSurfaceSizeUpdate = screenSize.y; - } - SurfaceHolder sh = getSurfaceHolder(); - final Rect frame = sh.getSurfaceFrame(); - final int dw = frame.width(); - final int dh = frame.height(); - boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth - || dh != mLastSurfaceHeight; - - boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation - || forceRedraw; - if (!redrawNeeded && !mOffsetsChanged) { - if (DEBUG) { - Log.d(TAG, "Suppressed drawFrame since redraw is not needed " - + "and offsets have not changed."); - } - return; - } - mLastRotation = newRotation; - - // Load bitmap if its null and we're not using hardware acceleration. - if ((mIsHardwareAccelerated && !mIsOpenGlTextureLoaded) // Using OpenGL but texture not loaded - || (!mIsHardwareAccelerated && mBackground == null)) { // Draw with Canvas but no bitmap - if (DEBUG) { - Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " - + mBackground + ", " + ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " - + ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " + dw + ", " + dh); - } - loadWallpaper(true /* needDraw */); - if (DEBUG) { - Log.d(TAG, "Reloading, resuming draw later"); - } - return; - } - - // Center the scaled image - mScale = Math.max(1f, Math.max(dw / (float) mBackgroundWidth, - dh / (float) mBackgroundHeight)); - final int availw = dw - (int) (mBackgroundWidth * mScale); - final int availh = dh - (int) (mBackgroundHeight * mScale); - int xPixels = availw / 2; - int yPixels = availh / 2; - - // Adjust the image for xOffset/yOffset values. If window manager is handling offsets, - // mXOffset and mYOffset are set to 0.5f by default and therefore xPixels and yPixels - // will remain unchanged - final int availwUnscaled = dw - mBackgroundWidth; - final int availhUnscaled = dh - mBackgroundHeight; - if (availwUnscaled < 0) { - xPixels += (int) (availwUnscaled * (mXOffset - .5f) + .5f); - } - if (availhUnscaled < 0) { - yPixels += (int) (availhUnscaled * (mYOffset - .5f) + .5f); - } - - mOffsetsChanged = false; - if (surfaceDimensionsChanged) { - mLastSurfaceWidth = dw; - mLastSurfaceHeight = dh; - } - if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) { - if (DEBUG) { - Log.d(TAG, "Suppressed drawFrame since the image has not " - + "actually moved an integral number of pixels."); - } - return; - } - mLastXTranslation = xPixels; - mLastYTranslation = yPixels; - - if (DEBUG) { - Log.d(TAG, "Redrawing wallpaper"); - } - - if (mIsHardwareAccelerated) { - if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) { - drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels); - } else { - // If OpenGL drawing was successful, then we can safely discard a reference to the - // wallpaper bitmap to save memory (since a copy has already been loaded into an OpenGL - // texture). - mBackground = null; - } - } else { - drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels); - } - } - - /** - * Loads the wallpaper on background thread and schedules updating the surface frame, - * and if {@param needsDraw} is set also draws a frame. - * <p> - * If loading is already in-flight, subsequent loads are ignored (but needDraw is or-ed to - * the active request). - * <p> - * If {@param needsReset} is set also clears the cache in WallpaperManager first. - */ - private void loadWallpaper(boolean needsDraw) { - mNeedsDrawAfterLoadingWallpaper |= needsDraw; - if (mLoader != null) { - if (DEBUG) { - Log.d(TAG, "Skipping loadWallpaper, already in flight "); - } - return; - } - mLoader = new AsyncTask<Void, Void, Bitmap>() { - @Override - protected Bitmap doInBackground(Void... params) { - Throwable exception = null; - try { - // Decode bitmap of rotating image wallpaper. - String wallpaperFilePath = isPreview() - ? PREVIEW_WALLPAPER_FILE_PATH : ROTATING_WALLPAPER_FILE_PATH; - Context context = isPreview() ? getApplicationContext() - : getApplicationContext().createDeviceProtectedStorageContext(); - FileInputStream fileInputStream = context.openFileInput(wallpaperFilePath); - Bitmap bitmap = BitmapFactory.decodeStream(fileInputStream); - fileInputStream.close(); - return bitmap; - } catch (RuntimeException | FileNotFoundException | OutOfMemoryError e) { - Log.i(TAG, "couldn't decode stream: ", e); - exception = e; - } catch (IOException e) { - Log.i(TAG, "couldn't close stream: ", e); - exception = e; - } - - if (isCancelled()) { - return null; - } - - if (exception != null) { - // Note that if we do fail at this, and the default wallpaper can't - // be loaded, we will go into a cycle. Don't do a build where the - // default wallpaper can't be loaded. - Log.w(TAG, "Unable to load wallpaper!", exception); - try { - return ((BitmapDrawable) getFallbackDrawable()).getBitmap(); - } catch (OutOfMemoryError ex) { - // now we're really screwed. - Log.w(TAG, "Unable reset to default wallpaper!", ex); - } - - if (isCancelled()) { - return null; - } - } - return null; - } - - @Override - protected void onPostExecute(Bitmap b) { - mBackground = null; - mBackgroundWidth = -1; - mBackgroundHeight = -1; - - if (b != null) { - mBackground = b; - mBackgroundWidth = mBackground.getWidth(); - mBackgroundHeight = mBackground.getHeight(); - - if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) { - mCachedWallpaperColors = WallpaperColors.fromBitmap(mBackground); - notifyColorsChanged(); - } - } - - if (DEBUG) { - Log.d(TAG, "Wallpaper loaded: " + mBackground); - } - updateSurfaceSize(getSurfaceHolder(), mDefaultDisplay, - false /* forDraw */); - if (mTexture != 0 && mEgl != null) { - deleteTexture(mTexture); - } - // If background is absent (due to an error decoding the bitmap) then don't try to load - // a texture. - if (mEgl != null && mBackground != null) { - mTexture = loadTexture(mBackground); - } - if (mNeedsDrawAfterLoadingWallpaper) { - drawFrame(true /* forceRedraw */); - } - - mLoader = null; - mNeedsDrawAfterLoadingWallpaper = false; - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private Drawable getFallbackDrawable() { - Drawable drawable; - try { - drawable = mWallpaperManager.getDrawable(); - } catch (java.lang.Exception e) { - // Work around Samsung bug where SecurityException is thrown if device is still using its - // default wallpaper, and around Android 7.0 bug where SELinux issues can cause a perfectly - // valid access of the current wallpaper to cause a failed Binder transaction manifest here - // as a RuntimeException. - drawable = mWallpaperManager.getBuiltInDrawable(); - } - return drawable; - } - - @Override - protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { - super.dump(prefix, fd, out, args); - - out.print(prefix); - out.println("ImageWallpaper.DrawableEngine:"); - out.print(prefix); - out.print(" mBackground="); - out.print(mBackground); - out.print(" mBackgroundWidth="); - out.print(mBackgroundWidth); - out.print(" mBackgroundHeight="); - out.println(mBackgroundHeight); - - out.print(prefix); - out.print(" mLastRotation="); - out.print(mLastRotation); - out.print(" mLastSurfaceWidth="); - out.print(mLastSurfaceWidth); - out.print(" mLastSurfaceHeight="); - out.println(mLastSurfaceHeight); - - out.print(prefix); - out.print(" mXOffset="); - out.print(mXOffset); - out.print(" mYOffset="); - out.println(mYOffset); - - out.print(prefix); - out.print(" mVisible="); - out.print(mVisible); - out.print(" mOffsetsChanged="); - out.println(mOffsetsChanged); - - out.print(prefix); - out.print(" mLastXTranslation="); - out.print(mLastXTranslation); - out.print(" mLastYTranslation="); - out.print(mLastYTranslation); - out.print(" mScale="); - out.println(mScale); - - out.print(prefix); - out.print(" mLastRequestedWidth="); - out.print(mLastRequestedWidth); - out.print(" mLastRequestedHeight="); - out.println(mLastRequestedHeight); - - out.print(prefix); - out.println(" DisplayInfo at last updateSurfaceSize:"); - out.print(prefix); - out.print(" rotation="); - out.print(mRotationAtLastSurfaceSizeUpdate); - out.print(" width="); - out.print(mDisplayWidthAtLastSurfaceSizeUpdate); - out.print(" height="); - out.println(mDisplayHeightAtLastSurfaceSizeUpdate); - } - - private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int left, int top) { - Canvas c = sh.lockCanvas(); - if (c != null) { - try { - if (DEBUG) { - Log.d(TAG, "Redrawing: left=" + left + ", top=" + top); - } - - final float right = left + mBackgroundWidth * mScale; - final float bottom = top + mBackgroundHeight * mScale; - if (w < 0 || h < 0) { - c.save(); - c.clipOutRect(left, top, right, bottom); - c.drawColor(0xff000000); - c.restore(); - } - if (mBackground != null) { - RectF dest = new RectF(left, top, right, bottom); - // add a filter bitmap? - c.drawBitmap(mBackground, null, dest, null); - } - } finally { - sh.unlockCanvasAndPost(c); - } - } - } - - private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) { - - mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); - - final float right = left + mBackgroundWidth * mScale; - final float bottom = top + mBackgroundHeight * mScale; - - final Rect frame = sh.getSurfaceFrame(); - final Matrix4f ortho = new Matrix4f(); - ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f); - - final FloatBuffer triangleVertices = createMesh(left, top, right, bottom); - - final int attribPosition = GLES20.glGetAttribLocation(mProgram, "position"); - final int attribTexCoords = GLES20.glGetAttribLocation(mProgram, "texCoords"); - final int uniformTexture = GLES20.glGetUniformLocation(mProgram, "texture"); - final int uniformProjection = GLES20.glGetUniformLocation(mProgram, "projection"); - - checkGlError(); - - GLES20.glViewport(0, 0, frame.width(), frame.height()); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture); - - GLES20.glUseProgram(mProgram); - GLES20.glEnableVertexAttribArray(attribPosition); - GLES20.glEnableVertexAttribArray(attribTexCoords); - GLES20.glUniform1i(uniformTexture, 0); - GLES20.glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0); - - checkGlError(); - - if (w > 0 || h > 0) { - GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); - } - - // drawQuad - triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); - GLES20.glVertexAttribPointer(attribPosition, 3, GLES20.GL_FLOAT, false, - TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); - - triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); - GLES20.glVertexAttribPointer(attribTexCoords, 3, GLES20.GL_FLOAT, false, - TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); - - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - - boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); - checkEglError(); - - return status; - } - - private FloatBuffer createMesh(int left, int top, float right, float bottom) { - final float[] verticesData = { - // X, Y, Z, U, V - left, bottom, 0.0f, 0.0f, 1.0f, - right, bottom, 0.0f, 1.0f, 1.0f, - left, top, 0.0f, 0.0f, 0.0f, - right, top, 0.0f, 1.0f, 0.0f, - }; - - final int bytes = verticesData.length * FLOAT_SIZE_BYTES; - final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order( - ByteOrder.nativeOrder()).asFloatBuffer(); - triangleVertices.put(verticesData).position(0); - return triangleVertices; - } - - private int loadTexture(Bitmap bitmap) { - mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); - - int[] textures = new int[1]; - - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glGenTextures(1, textures, 0); - checkGlError(); - - int texture = textures[0]; - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); - checkGlError(); - - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); - - GLES20.glTexParameteri( - GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameteri( - GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - - GLUtils.texImage2D( - GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap, GLES20.GL_UNSIGNED_BYTE, 0); - checkGlError(); - - mIsOpenGlTextureLoaded = true; - - return texture; - } - - private int buildProgram(String vertex, String fragment) { - int vertexShader = buildShader(vertex, GLES20.GL_VERTEX_SHADER); - if (vertexShader == 0) { - return 0; - } - - int fragmentShader = buildShader(fragment, GLES20.GL_FRAGMENT_SHADER); - if (fragmentShader == 0) { - return 0; - } - - int program = GLES20.glCreateProgram(); - GLES20.glAttachShader(program, vertexShader); - GLES20.glAttachShader(program, fragmentShader); - GLES20.glLinkProgram(program); - checkGlError(); - - GLES20.glDeleteShader(vertexShader); - GLES20.glDeleteShader(fragmentShader); - - int[] status = new int[1]; - GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, status, 0); - if (status[0] != GLES20.GL_TRUE) { - String error = GLES20.glGetProgramInfoLog(program); - Log.d(GL_LOG_TAG, "Error while linking program:\n" + error); - GLES20.glDeleteProgram(program); - return 0; - } - - return program; - } - - private int buildShader(String source, int type) { - int shader = GLES20.glCreateShader(type); - - GLES20.glShaderSource(shader, source); - checkGlError(); - - GLES20.glCompileShader(shader); - checkGlError(); - - int[] status = new int[1]; - GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, status, 0); - if (status[0] != GLES20.GL_TRUE) { - String error = GLES20.glGetShaderInfoLog(shader); - Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error); - GLES20.glDeleteShader(shader); - return 0; - } - - return shader; - } - - private void checkEglError() { - int error = mEgl.eglGetError(); - if (error != EGL10.EGL_SUCCESS) { - Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error)); - } - } - - private void checkGlError() { - int error = GLES20.glGetError(); - if (error != GLES20.GL_NO_ERROR) { - Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable()); - } - } - - private void deleteTexture(int texture) { - int[] textures = new int[1]; - textures[0] = texture; - - mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); - GLES20.glDeleteTextures(1, textures, 0); - mTexture = 0; - } - - private void finishGL(int texture, int program) { - if (mEgl == null) { - return; - } - - mOpenGlContextCounter--; - - mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); - deleteTexture(mTexture); - GLES20.glDeleteProgram(program); - mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - mEgl.eglDestroySurface(mEglDisplay, mEglSurface); - mEgl.eglDestroyContext(mEglDisplay, mEglContext); - if (mOpenGlContextCounter == 0) { - mEgl.eglTerminate(mEglDisplay); - } - - mEgl = null; - } - - private boolean initGL(SurfaceHolder surfaceHolder) { - mEgl = (EGL10) EGLContext.getEGL(); - - mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { - throw new RuntimeException("eglGetDisplay failed " - + GLUtils.getEGLErrorString(mEgl.eglGetError())); - } - - int[] version = new int[2]; - if (!mEgl.eglInitialize(mEglDisplay, version)) { - throw new RuntimeException("eglInitialize failed " - + GLUtils.getEGLErrorString(mEgl.eglGetError())); - } - - mOpenGlContextCounter++; - - mEglConfig = chooseEglConfig(); - if (mEglConfig == null) { - throw new RuntimeException("eglConfig not initialized"); - } - - mEglContext = createContext(mEgl, mEglDisplay, mEglConfig); - if (mEglContext == EGL_NO_CONTEXT) { - throw new RuntimeException("createContext failed " - + GLUtils.getEGLErrorString(mEgl.eglGetError())); - } - - int attribs[] = { - EGL10.EGL_WIDTH, 1, - EGL10.EGL_HEIGHT, 1, - EGL10.EGL_NONE - }; - EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); - mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext); - - int[] maxSize = new int[1]; - Rect frame = surfaceHolder.getSurfaceFrame(); - GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxSize, 0); - - mEgl.eglMakeCurrent( - mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - mEgl.eglDestroySurface(mEglDisplay, tmpSurface); - - if (frame.width() > maxSize[0] || frame.height() > maxSize[0]) { - mEgl.eglDestroyContext(mEglDisplay, mEglContext); - mEgl.eglTerminate(mEglDisplay); - Log.e(GL_LOG_TAG, "requested texture size " + frame.width() + "x" + frame.height() - + " exceeds the support maximum of " + maxSize[0] + "x" + maxSize[0]); - return false; - } - - mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null); - if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) { - int error = mEgl.eglGetError(); - if (error == EGL10.EGL_BAD_NATIVE_WINDOW || error == EGL10.EGL_BAD_ALLOC) { - Log.e(GL_LOG_TAG, "createWindowSurface returned " + GLUtils.getEGLErrorString(error) - + "."); - return false; - } - throw new RuntimeException("createWindowSurface failed " - + GLUtils.getEGLErrorString(error)); - } - - if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { - throw new RuntimeException("eglMakeCurrent failed " - + GLUtils.getEGLErrorString(mEgl.eglGetError())); - } - - mProgram = buildProgram(S_SIMPLE_VS, S_SIMPLE_FS); - if (mBackground != null) { - mTexture = loadTexture(mBackground); - } - - return true; - } - - - EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { - int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; - return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attribList); - } - - private EGLConfig chooseEglConfig() { - int[] configsCount = new int[1]; - EGLConfig[] configs = new EGLConfig[1]; - int[] configSpec = getConfig(); - if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) { - throw new IllegalArgumentException("eglChooseConfig failed " - + GLUtils.getEGLErrorString(mEgl.eglGetError())); - } else if (configsCount[0] > 0) { - return configs[0]; - } - return null; - } - - private int[] getConfig() { - return new int[]{ - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL10.EGL_RED_SIZE, 8, - EGL10.EGL_GREEN_SIZE, 8, - EGL10.EGL_BLUE_SIZE, 8, - EGL10.EGL_ALPHA_SIZE, 0, - EGL10.EGL_DEPTH_SIZE, 0, - EGL10.EGL_STENCIL_SIZE, 0, - EGL10.EGL_CONFIG_CAVEAT, EGL10.EGL_NONE, - EGL10.EGL_NONE - }; - } - } -} diff --git a/src/com/android/wallpaper/module/RotatingWallpaperComponentChecker.java b/src/com/android/wallpaper/module/RotatingWallpaperComponentChecker.java deleted file mode 100755 index 8a2908a..0000000 --- a/src/com/android/wallpaper/module/RotatingWallpaperComponentChecker.java +++ /dev/null @@ -1,69 +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 androidx.annotation.IntDef; - -/** - * Checks what component is responsible for presenting the rotating wallpaper image under the - * device's launcher or keyguard for current and future rotations. - */ -public interface RotatingWallpaperComponentChecker { - - int ROTATING_WALLPAPER_COMPONENT_LIVE = 1; - int ROTATING_WALLPAPER_COMPONENT_STATIC = 2; - - int ROTATING_WALLPAPER_SUPPORT_NOT_SUPPORTED = 0; - int ROTATING_WALLPAPER_SUPPORT_SUPPORTED = 1; - - /** - * Returns the rotating wallpaper component for the current wallpaper rotation. - */ - @RotatingWallpaperComponent - int getCurrentRotatingWallpaperComponent(Context context); - - /** - * Returns the rotating wallpaper component for future wallpaper rotations. - */ - @RotatingWallpaperComponent - int getNextRotatingWallpaperComponent(Context context); - - /** - * Returns the support level for rotating wallpaper. - */ - @RotatingWallpaperSupport - int getRotatingWallpaperSupport(Context context); - - /** - * Possible components for presenting the rotating wallpaper image. - */ - @IntDef({ - ROTATING_WALLPAPER_COMPONENT_LIVE, - ROTATING_WALLPAPER_COMPONENT_STATIC}) - @interface RotatingWallpaperComponent { - } - - /** - * Possible support levels for rotating wallpaper. - */ - @IntDef({ - ROTATING_WALLPAPER_SUPPORT_NOT_SUPPORTED, - ROTATING_WALLPAPER_SUPPORT_SUPPORTED}) - @interface RotatingWallpaperSupport { - } -} diff --git a/src/com/android/wallpaper/module/RotationWallpaperMoveReceiver.java b/src/com/android/wallpaper/module/RotationWallpaperMoveReceiver.java deleted file mode 100644 index 975b0db..0000000 --- a/src/com/android/wallpaper/module/RotationWallpaperMoveReceiver.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.android.wallpaper.module; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -import com.android.wallpaper.compat.BuildCompat; -import com.android.wallpaper.util.DiskBasedLogger; -import com.android.wallpaper.util.FileMover; - -import java.io.File; -import java.io.IOException; - -/** - * Receiver to run when the app was updated or on first boot to migrate previously existing rotating - * wallpaper file to device protected storage so it can be read in direct-boot. - * This is basically a no-op if there's no file in - * {@link NoBackupImageWallpaper#ROTATING_WALLPAPER_FILE_PATH}. - */ -public class RotationWallpaperMoveReceiver extends BroadcastReceiver { - - private static final String TAG = "RotationWallpaperMoveReceiver"; - - @Override - public void onReceive(Context context, Intent intent) { - // This receiver is a no-op on pre-N Android and should only respond to a - // MY_PACKAGE_REPLACED intent. - if (intent.getAction() == null - || !(intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED) - || intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) - || !BuildCompat.isAtLeastN()) { - DiskBasedLogger.e( - TAG, - "Unexpected action or Android version!", - context); - throw new IllegalStateException( - "Unexpected broadcast action or unsupported Android version"); - } - - // This is a no-op if there is no rotating wallpaper file in the files directory. - if (!context.getFileStreamPath( - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH).exists()) { - return; - } - - PendingResult broadcastResult = goAsync(); - new Thread(() -> { - Context appContext = context.getApplicationContext(); - Context deviceProtectedContext = appContext.createDeviceProtectedStorageContext(); - try { - File movedFile = FileMover.moveFileBetweenContexts(appContext, - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH, - deviceProtectedContext, - NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH); - - if (movedFile != null) { - // Notify NoBackupImageWallpaper of the change in case that's the currently - // set wallpaper - Intent intent1 = new Intent(appContext.getPackageName() - + NoBackupImageWallpaper.ACTION_ROTATING_WALLPAPER_CHANGED); - // Handled by a runtime-registered receiver in NoBackupImageWallpaper. - intent1.setPackage(appContext.getPackageName()); - appContext.sendBroadcast(intent1); - } - } catch (IOException e) { - DiskBasedLogger.e( - TAG, - "Failed to move rotating wallpaper file to device protected storage: " - + e.getMessage(), - appContext); - } finally { - broadcastResult.finish(); - } - }).start(); - - } -} diff --git a/src/com/android/wallpaper/module/RotationWallpaperUpdateReceiver.java b/src/com/android/wallpaper/module/RotationWallpaperUpdateReceiver.java new file mode 100644 index 0000000..4fbc20d --- /dev/null +++ b/src/com/android/wallpaper/module/RotationWallpaperUpdateReceiver.java @@ -0,0 +1,111 @@ +/* + * 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.module; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import com.android.wallpaper.compat.BuildCompat; +import com.android.wallpaper.util.DiskBasedLogger; +import com.android.wallpaper.util.FileMover; + +import java.io.File; + +/** + * Receiver to run when the app was updated or on first boot to switch from live rotating wallpaper + * to static rotating wallpaper. + */ +public class RotationWallpaperUpdateReceiver extends BroadcastReceiver { + + + // Earlier versions of rotating wallpaper save the current rotation image as a file. + // We can infer from the extistance of this file whether or not user had rotating live wallpaper + private static final String ROTATING_WALLPAPER_FILE_PATH = "rotating_wallpaper.jpg"; + private static final String TAG = "RotationWallpaperUpdateReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + // This receiver is a no-op on pre-N Android and should only respond to a + // MY_PACKAGE_REPLACED intent. + if (intent.getAction() == null + || !(intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED) + || intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) + || !BuildCompat.isAtLeastN()) { + DiskBasedLogger.e( + TAG, + "Unexpected action or Android version!", + context); + throw new IllegalStateException( + "Unexpected broadcast action or unsupported Android version"); + } + + PendingResult broadcastResult = goAsync(); + new Thread(() -> { + Context appContext = context.getApplicationContext(); + Context deviceProtectedContext = appContext.createDeviceProtectedStorageContext(); + + if (context.getFileStreamPath(ROTATING_WALLPAPER_FILE_PATH).exists()) { + moveFileToProtectedStorage(appContext, deviceProtectedContext); + } + + File wallpaperFile = deviceProtectedContext.getFileStreamPath( + ROTATING_WALLPAPER_FILE_PATH); + if (wallpaperFile.exists()) { + switchToStaticWallpaper(appContext, wallpaperFile); + } + broadcastResult.finish(); + }).start(); + } + + private void moveFileToProtectedStorage(Context context, Context deviceProtectedContext) { + try { + FileMover.moveFileBetweenContexts(context, ROTATING_WALLPAPER_FILE_PATH, + deviceProtectedContext, ROTATING_WALLPAPER_FILE_PATH); + } catch (Exception ex) { + DiskBasedLogger.e( + TAG, + "Failed to move rotating wallpaper file to device protected storage: " + + ex.getMessage(), + context); + } + } + + private void switchToStaticWallpaper(Context appContext, File wallpaperFile) { + try { + Injector injector = InjectorProvider.getInjector(); + WallpaperPreferences wallpaperPreferences = injector.getPreferences(appContext); + if (wallpaperPreferences.getWallpaperPresentationMode() + != WallpaperPreferences.PRESENTATION_MODE_ROTATING) { + return; + } + Bitmap bitmap = BitmapFactory.decodeFile(wallpaperFile.getAbsolutePath()); + + injector.getWallpaperPersister(appContext).setWallpaperInRotation(bitmap, + wallpaperPreferences.getHomeWallpaperAttributions(), + wallpaperPreferences.getHomeWallpaperActionLabelRes(), + wallpaperPreferences.getHomeWallpaperActionIconRes(), + wallpaperPreferences.getHomeWallpaperActionUrl(), + wallpaperPreferences.getHomeWallpaperCollectionId()); + wallpaperFile.delete(); + + } catch (Exception ex) { + DiskBasedLogger.e(TAG, "Unable to set static wallpaper", appContext); + } + } +} diff --git a/src/com/android/wallpaper/module/WallpaperPreferenceKeys.java b/src/com/android/wallpaper/module/WallpaperPreferenceKeys.java index f63506f..e4c6588 100755 --- a/src/com/android/wallpaper/module/WallpaperPreferenceKeys.java +++ b/src/com/android/wallpaper/module/WallpaperPreferenceKeys.java @@ -28,12 +28,7 @@ public class WallpaperPreferenceKeys { public static final String KEY_HOME_WALLPAPER_ACTION_LABEL_RES = "home_wallpaper_action_label"; public static final String KEY_HOME_WALLPAPER_ACTION_ICON_RES = "home_wallpaper_action_icon"; public static final String KEY_HOME_WALLPAPER_COLLECTION_ID = "home_wallpaper_collection_id"; - public static final String KEY_HOME_WALLPAPER_BASE_IMAGE_URL = "home_wallpaper_base_image_url"; public static final String KEY_HOME_WALLPAPER_HASH_CODE = "home_wallpaper_hash_code"; - public static final String KEY_HOME_WALLPAPER_MANAGER_ID = "home_wallpaper_id"; - public static final String KEY_HOME_WALLPAPER_PACKAGE_NAME = "home_wallpaper_package_name"; - public static final String KEY_HOME_WALLPAPER_REMOTE_ID = "home_wallpaper_remote_id"; - public static final String KEY_HOME_WALLPAPER_BACKING_FILE = "home_wallpaper_backing_file"; public static final String KEY_LOCK_WALLPAPER_ATTRIB_1 = "lock_wallpaper_attribution_line_1"; public static final String KEY_LOCK_WALLPAPER_ATTRIB_2 = "lock_wallpaper_attribution_line_2"; @@ -43,24 +38,35 @@ public class WallpaperPreferenceKeys { public static final String KEY_LOCK_WALLPAPER_ACTION_ICON_RES = "lock_wallpaper_action_icon"; public static final String KEY_LOCK_WALLPAPER_HASH_CODE = "lock_wallpaper_hash_code"; public static final String KEY_LOCK_WALLPAPER_COLLECTION_ID = "lock_wallpaper_collection_id"; - public static final String KEY_LOCK_WALLPAPER_MANAGER_ID = "lock_wallpaper_id"; - public static final String KEY_LOCK_WALLPAPER_BACKING_FILE = "lock_wallpaper_backing_file"; - public static final String KEY_DAILY_ROTATION_TIMESTAMPS = "daily_rotation_timestamps"; - public static final String KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP = - "daily_wallpaper_enabled_timestamp"; - - public static final String KEY_LAST_DAILY_LOG_TIMESTAMP = "last_daily_log_timestamp"; - public static final String KEY_LAST_APP_ACTIVE_TIMESTAMP = "last_app_active_timestamp"; - public static final String KEY_LAST_ROTATION_STATUS = "last_rotation_status"; - public static final String KEY_LAST_ROTATION_STATUS_TIMESTAMP = "last_rotation_status_timestamp"; - public static final String KEY_LAST_SYNC_TIMESTAMP = "last_sync_timestamp"; - - public static final String KEY_PENDING_WALLPAPER_SET_STATUS = "pending_wallpaper_set_status"; - public static final String KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS = - "pending_daily_wallpaper_update_status"; - - public static final String KEY_NUM_DAYS_DAILY_ROTATION_FAILED = "num_days_daily_rotation_failed"; - public static final String KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED = - "num_days_daily_rotation_not_attempted"; + /** + * Preferences with these keys should not be backed up + */ + public interface NoBackupKeys { + public static final String KEY_HOME_WALLPAPER_BASE_IMAGE_URL = + "home_wallpaper_base_image_url"; + public static final String KEY_HOME_WALLPAPER_MANAGER_ID = "home_wallpaper_id"; + public static final String KEY_HOME_WALLPAPER_REMOTE_ID = "home_wallpaper_remote_id"; + public static final String KEY_HOME_WALLPAPER_BACKING_FILE = "home_wallpaper_backing_file"; + public static final String KEY_LOCK_WALLPAPER_MANAGER_ID = "lock_wallpaper_id"; + public static final String KEY_LOCK_WALLPAPER_BACKING_FILE = "lock_wallpaper_backing_file"; + public static final String KEY_DAILY_ROTATION_TIMESTAMPS = "daily_rotation_timestamps"; + public static final String KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP = + "daily_wallpaper_enabled_timestamp"; + public static final String KEY_LAST_DAILY_LOG_TIMESTAMP = "last_daily_log_timestamp"; + public static final String KEY_LAST_APP_ACTIVE_TIMESTAMP = "last_app_active_timestamp"; + public static final String KEY_LAST_ROTATION_STATUS = "last_rotation_status"; + public static final String KEY_LAST_ROTATION_STATUS_TIMESTAMP = + "last_rotation_status_timestamp"; + public static final String KEY_LAST_SYNC_TIMESTAMP = "last_sync_timestamp"; + public static final String KEY_PENDING_WALLPAPER_SET_STATUS = + "pending_wallpaper_set_status"; + public static final String KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS = + "pending_daily_wallpaper_update_status"; + public static final String KEY_NUM_DAYS_DAILY_ROTATION_FAILED = + "num_days_daily_rotation_failed"; + public static final String KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED = + "num_days_daily_rotation_not_attempted"; + public static final String KEY_HOME_WALLPAPER_PACKAGE_NAME = "home_wallpaper_package_name"; + } } diff --git a/src/com/android/wallpaper/module/WallpaperSetter.java b/src/com/android/wallpaper/module/WallpaperSetter.java index 830d914..a33a5f1 100644 --- a/src/com/android/wallpaper/module/WallpaperSetter.java +++ b/src/com/android/wallpaper/module/WallpaperSetter.java @@ -72,8 +72,9 @@ public class WallpaperSetter { * @param callback optional callback to be notified when the wallpaper is set. */ public void setCurrentWallpaper(Activity containerActivity, WallpaperInfo wallpaper, - Asset wallpaperAsset, @Destination final int destination, float wallpaperScale, - @Nullable Rect cropRect, @Nullable SetWallpaperCallback callback) { + @Nullable Asset wallpaperAsset, @Destination final int destination, + float wallpaperScale, @Nullable Rect cropRect, + @Nullable SetWallpaperCallback callback) { if (wallpaper instanceof LiveWallpaperInfo) { setCurrentLiveWallpaper(containerActivity, (LiveWallpaperInfo) wallpaper, destination, callback); diff --git a/src/com/android/wallpaper/picker/ImagePreviewFragment.java b/src/com/android/wallpaper/picker/ImagePreviewFragment.java new file mode 100755 index 0000000..87e29c5 --- /dev/null +++ b/src/com/android/wallpaper/picker/ImagePreviewFragment.java @@ -0,0 +1,464 @@ +/* + * 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.picker; + +import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Color; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.os.Bundle; +import android.view.ContextThemeWrapper; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; + +import com.android.wallpaper.R; +import com.android.wallpaper.asset.Asset; +import com.android.wallpaper.module.WallpaperPersister.Destination; +import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback; +import com.android.wallpaper.util.ScreenSizeCalculator; +import com.android.wallpaper.util.WallpaperCropUtils; +import com.android.wallpaper.widget.MaterialProgressDrawable; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.MemoryCategory; +import com.davemorrissey.labs.subscaleview.ImageSource; +import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; +import com.google.android.material.bottomsheet.BottomSheetBehavior; + +import java.util.List; + +/** + * Fragment which displays the UI for previewing an individual static wallpaper and its attribution + * information. + */ +public class ImagePreviewFragment extends PreviewFragment { + + private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f; + + private SubsamplingScaleImageView mFullResImageView; + private Asset mWallpaperAsset; + private TextView mAttributionTitle; + private TextView mAttributionSubtitle1; + private TextView mAttributionSubtitle2; + private Button mExploreButton; + private Button mSetWallpaperButton; + + private Point mDefaultCropSurfaceSize; + private Point mScreenSize; + private Point mRawWallpaperSize; // Native size of wallpaper image. + private ImageView mLoadingIndicator; + private MaterialProgressDrawable mProgressDrawable; + private ImageView mLowResImageView; + private View mSpacer; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mWallpaperAsset = mWallpaper.getAsset(requireContext().getApplicationContext()); + } + + @Override + protected int getLayoutResId() { + return R.layout.fragment_image_preview; + } + + @Override + protected int getBottomSheetResId() { + return R.id.bottom_sheet; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); + + Activity activity = requireActivity(); + // Set toolbar as the action bar. + + mFullResImageView = view.findViewById(R.id.full_res_image); + mLoadingIndicator = view.findViewById(R.id.loading_indicator); + + mAttributionTitle = view.findViewById(R.id.preview_attribution_pane_title); + mAttributionSubtitle1 = view.findViewById(R.id.preview_attribution_pane_subtitle1); + mAttributionSubtitle2 = view.findViewById(R.id.preview_attribution_pane_subtitle2); + mExploreButton = view.findViewById(R.id.preview_attribution_pane_explore_button); + mSetWallpaperButton = view.findViewById(R.id.preview_attribution_pane_set_wallpaper_button); + + mLowResImageView = view.findViewById(R.id.low_res_image); + mSpacer = view.findViewById(R.id.spacer); + + // Trim some memory from Glide to make room for the full-size image in this fragment. + Glide.get(activity).setMemoryCategory(MemoryCategory.LOW); + + mDefaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize( + getResources(), activity.getWindowManager().getDefaultDisplay()); + mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize( + activity.getWindowManager().getDefaultDisplay()); + + // Load a low-res placeholder image if there's a thumbnail available from the asset that can + // be shown to the user more quickly than the full-sized image. + if (mWallpaperAsset.hasLowResDataSource()) { + mWallpaperAsset.loadLowResDrawable(activity, mLowResImageView, Color.BLACK, + new WallpaperPreviewBitmapTransformation(activity.getApplicationContext(), + isRtl())); + } + + mWallpaperAsset.decodeRawDimensions(getActivity(), dimensions -> { + // Don't continue loading the wallpaper if the Fragment is detached. + if (getActivity() == null) { + return; + } + + // Return early and show a dialog if dimensions are null (signaling a decoding error). + if (dimensions == null) { + showLoadWallpaperErrorDialog(); + return; + } + + mRawWallpaperSize = dimensions; + setUpExploreIntent(ImagePreviewFragment.this::initFullResView); + }); + + // Configure loading indicator with a MaterialProgressDrawable. + setUpLoadingIndicator(); + + return view; + } + + @Override + protected void setUpBottomSheetView(ViewGroup bottomSheet) { + // Nothing needed here. + } + + private void setUpLoadingIndicator() { + Context context = requireContext(); + mProgressDrawable = new MaterialProgressDrawable(context.getApplicationContext(), + mLoadingIndicator); + mProgressDrawable.setAlpha(255); + mProgressDrawable.setBackgroundColor(getResources().getColor(R.color.material_white_100, + context.getTheme())); + mProgressDrawable.setColorSchemeColors(getAttrColor( + new ContextThemeWrapper(context, getDeviceDefaultTheme()), + android.R.attr.colorAccent)); + mProgressDrawable.updateSizes(MaterialProgressDrawable.LARGE); + mLoadingIndicator.setImageDrawable(mProgressDrawable); + + // We don't want to show the spinner every time we load an image if it loads quickly; + // instead, only start showing the spinner if loading the image has taken longer than half + // of a second. + mLoadingIndicator.postDelayed(() -> { + if (mFullResImageView != null && !mFullResImageView.hasImage() + && !mTestingModeEnabled) { + mLoadingIndicator.setVisibility(View.VISIBLE); + mLoadingIndicator.setAlpha(1f); + if (mProgressDrawable != null) { + mProgressDrawable.start(); + } + } + }, 500); + } + + @Override + public void onClickOk() { + FragmentActivity activity = getActivity(); + if (activity != null) { + activity.finish(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mProgressDrawable != null) { + mProgressDrawable.stop(); + } + mFullResImageView.recycle(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet); + outState.putInt(KEY_BOTTOM_SHEET_STATE, bottomSheetBehavior.getState()); + } + + @Override + protected void setBottomSheetContentAlpha(float alpha) { + mExploreButton.setAlpha(alpha); + mAttributionTitle.setAlpha(alpha); + mAttributionSubtitle1.setAlpha(alpha); + mAttributionSubtitle2.setAlpha(alpha); + } + + private void populateAttributionPane() { + final Context context = getContext(); + + final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet); + + List<String> attributions = mWallpaper.getAttributions(context); + if (attributions.size() > 0 && attributions.get(0) != null) { + mAttributionTitle.setText(attributions.get(0)); + } + + if (attributions.size() > 1 && attributions.get(1) != null) { + mAttributionSubtitle1.setVisibility(View.VISIBLE); + mAttributionSubtitle1.setText(attributions.get(1)); + } + + if (attributions.size() > 2 && attributions.get(2) != null) { + mAttributionSubtitle2.setVisibility(View.VISIBLE); + mAttributionSubtitle2.setText(attributions.get(2)); + } + + setUpSetWallpaperButton(mSetWallpaperButton); + + setUpExploreButton(mExploreButton); + + if (mExploreButton.getVisibility() == View.VISIBLE + && mSetWallpaperButton.getVisibility() == View.VISIBLE) { + mSpacer.setVisibility(View.VISIBLE); + } else { + mSpacer.setVisibility(View.GONE); + } + + mBottomSheet.setVisibility(View.VISIBLE); + + // Initialize the state of the BottomSheet based on the current state because if the initial + // and current state are the same, the state change listener won't fire and set the correct + // arrow asset and text alpha. + if (bottomSheetBehavior.getState() == STATE_EXPANDED) { + setPreviewChecked(false); + mAttributionTitle.setAlpha(1f); + mAttributionSubtitle1.setAlpha(1f); + mAttributionSubtitle2.setAlpha(1f); + } else { + setPreviewChecked(true); + mAttributionTitle.setAlpha(0f); + mAttributionSubtitle1.setAlpha(0f); + mAttributionSubtitle2.setAlpha(0f); + } + + // Let the state change listener take care of animating a state change to the initial state + // if there's a state change. + bottomSheetBehavior.setState(mBottomSheetInitialState); + } + + /** + * Initializes MosaicView by initializing tiling, setting a fallback page bitmap, and + * initializing a zoom-scroll observer and click listener. + */ + private void initFullResView() { + mFullResImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP); + + // Set a solid black "page bitmap" so MosaicView draws a black background while waiting + // for the image to load or a transparent one if a thumbnail already loaded. + Bitmap blackBitmap = Bitmap.createBitmap(1, 1, Config.ARGB_8888); + int color = (mLowResImageView.getDrawable() == null) ? Color.BLACK : Color.TRANSPARENT; + blackBitmap.setPixel(0, 0, color); + mFullResImageView.setImage(ImageSource.bitmap(blackBitmap)); + + // Then set a fallback "page bitmap" to cover the whole MosaicView, which is an actual + // (lower res) version of the image to be displayed. + Point targetPageBitmapSize = new Point(mRawWallpaperSize); + mWallpaperAsset.decodeBitmap(targetPageBitmapSize.x, targetPageBitmapSize.y, + pageBitmap -> { + // Check that the activity is still around since the decoding task started. + if (getActivity() == null) { + return; + } + + // Some of these may be null depending on if the Fragment is paused, stopped, + // or destroyed. + if (mLoadingIndicator != null) { + mLoadingIndicator.setVisibility(View.GONE); + } + // The page bitmap may be null if there was a decoding error, so show an + // error dialog. + if (pageBitmap == null) { + showLoadWallpaperErrorDialog(); + return; + } + if (mFullResImageView != null) { + // Set page bitmap. + mFullResImageView.setImage(ImageSource.bitmap(pageBitmap)); + + setDefaultWallpaperZoomAndScroll(); + crossFadeInMosaicView(); + } + if (mProgressDrawable != null) { + mProgressDrawable.stop(); + } + getActivity().invalidateOptionsMenu(); + + populateAttributionPane(); + }); + } + + /** + * Makes the MosaicView visible with an alpha fade-in animation while fading out the loading + * indicator. + */ + private void crossFadeInMosaicView() { + long shortAnimationDuration = getResources().getInteger( + android.R.integer.config_shortAnimTime); + + mFullResImageView.setAlpha(0f); + mFullResImageView.animate() + .alpha(1f) + .setDuration(shortAnimationDuration) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Clear the thumbnail bitmap reference to save memory since it's no longer + // visible. + if (mLowResImageView != null) { + mLowResImageView.setImageBitmap(null); + } + } + }); + + mLoadingIndicator.animate() + .alpha(0f) + .setDuration(shortAnimationDuration) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (mLoadingIndicator != null) { + mLoadingIndicator.setVisibility(View.GONE); + } + } + }); + } + + /** + * Sets the default wallpaper zoom and scroll position based on a "crop surface" + * (with extra width to account for parallax) superimposed on the screen. Shows as much of the + * wallpaper as possible on the crop surface and align screen to crop surface such that the + * default preview matches what would be seen by the user in the left-most home screen. + * + * <p>This method is called once in the Fragment lifecycle after the wallpaper asset has loaded + * and rendered to the layout. + */ + private void setDefaultWallpaperZoomAndScroll() { + // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface. + float defaultWallpaperZoom = + WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mDefaultCropSurfaceSize); + float minWallpaperZoom = + WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mScreenSize); + + Point screenToCropSurfacePosition = WallpaperCropUtils.calculateCenterPosition( + mDefaultCropSurfaceSize, mScreenSize, true /* alignStart */, isRtl()); + Point zoomedWallpaperSize = new Point( + Math.round(mRawWallpaperSize.x * defaultWallpaperZoom), + Math.round(mRawWallpaperSize.y * defaultWallpaperZoom)); + Point cropSurfaceToWallpaperPosition = WallpaperCropUtils.calculateCenterPosition( + zoomedWallpaperSize, mDefaultCropSurfaceSize, false /* alignStart */, isRtl()); + + // Set min wallpaper zoom and max zoom on MosaicView widget. + mFullResImageView.setMaxScale(Math.max(DEFAULT_WALLPAPER_MAX_ZOOM, defaultWallpaperZoom)); + mFullResImageView.setMinScale(minWallpaperZoom); + + // Set center to composite positioning between scaled wallpaper and screen. + PointF centerPosition = new PointF( + mRawWallpaperSize.x / 2f, + mRawWallpaperSize.y / 2f); + centerPosition.offset(-(screenToCropSurfacePosition.x + cropSurfaceToWallpaperPosition.x), + -(screenToCropSurfacePosition.y + cropSurfaceToWallpaperPosition.y)); + + mFullResImageView.setScaleAndCenter(minWallpaperZoom, centerPosition); + } + + private Rect calculateCropRect() { + // Calculate Rect of wallpaper in physical pixel terms (i.e., scaled to current zoom). + float wallpaperZoom = mFullResImageView.getScale(); + int scaledWallpaperWidth = (int) (mRawWallpaperSize.x * wallpaperZoom); + int scaledWallpaperHeight = (int) (mRawWallpaperSize.y * wallpaperZoom); + Rect rect = new Rect(); + mFullResImageView.visibleFileRect(rect); + int scrollX = (int) (rect.left * wallpaperZoom); + int scrollY = (int) (rect.top * wallpaperZoom); + + rect.set(0, 0, scaledWallpaperWidth, scaledWallpaperHeight); + + Display defaultDisplay = requireActivity().getWindowManager().getDefaultDisplay(); + Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(defaultDisplay); + // Crop rect should start off as the visible screen and then include extra width and height + // if available within wallpaper at the current zoom. + Rect cropRect = new Rect(scrollX, scrollY, scrollX + screenSize.x, scrollY + screenSize.y); + + Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize( + getResources(), defaultDisplay); + int extraWidth = defaultCropSurfaceSize.x - screenSize.x; + int extraHeightTopAndBottom = (int) ((defaultCropSurfaceSize.y - screenSize.y) / 2f); + + // Try to increase size of screenRect to include extra width depending on the layout + // direction. + if (isRtl()) { + cropRect.left = Math.max(cropRect.left - extraWidth, rect.left); + } else { + cropRect.right = Math.min(cropRect.right + extraWidth, rect.right); + } + + // Try to increase the size of the cropRect to to include extra height. + int availableExtraHeightTop = cropRect.top - Math.max( + rect.top, + cropRect.top - extraHeightTopAndBottom); + int availableExtraHeightBottom = Math.min( + rect.bottom, + cropRect.bottom + extraHeightTopAndBottom) - cropRect.bottom; + + int availableExtraHeightTopAndBottom = + Math.min(availableExtraHeightTop, availableExtraHeightBottom); + cropRect.top -= availableExtraHeightTopAndBottom; + cropRect.bottom += availableExtraHeightTopAndBottom; + + return cropRect; + } + + @Override + protected void setCurrentWallpaper(@Destination int destination) { + mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset, + destination, mFullResImageView.getScale(), calculateCropRect(), + new SetWallpaperCallback() { + @Override + public void onSuccess() { + finishActivityWithResultOk(); + } + + @Override + public void onError(@Nullable Throwable throwable) { + showSetWallpaperErrorDialog(destination); + } + }); + } +} diff --git a/src/com/android/wallpaper/picker/LivePreviewFragment.java b/src/com/android/wallpaper/picker/LivePreviewFragment.java new file mode 100644 index 0000000..fee60a8 --- /dev/null +++ b/src/com/android/wallpaper/picker/LivePreviewFragment.java @@ -0,0 +1,655 @@ +/* + * 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.picker; + +import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.AlertDialog; +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.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.graphics.Rect; +import android.net.Uri; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.service.wallpaper.IWallpaperConnection; +import android.service.wallpaper.IWallpaperEngine; +import android.service.wallpaper.IWallpaperService; +import android.service.wallpaper.WallpaperService; +import android.service.wallpaper.WallpaperSettingsActivity; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager.LayoutParams; +import android.view.animation.AnimationUtils; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.LiveData; +import androidx.slice.Slice; +import androidx.slice.widget.SliceLiveData; +import androidx.slice.widget.SliceView; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.android.wallpaper.R; +import com.android.wallpaper.compat.BuildCompat; +import com.android.wallpaper.model.LiveWallpaperInfo; +import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback; +import com.android.wallpaper.widget.MaterialProgressDrawable; + +import com.google.android.material.bottomsheet.BottomSheetBehavior; +import com.google.android.material.tabs.TabLayout; + +import java.util.ArrayList; +import java.util.List; + +/** + * Fragment which displays the UI for previewing an individual live wallpaper, its attribution + * information and settings slices if available. + */ +public class LivePreviewFragment extends PreviewFragment { + + private static final String TAG = "LivePreviewFragment"; + + private static final String KEY_ACTION_DELETE_LIVE_WALLPAPER = "action_delete_live_wallpaper"; + private static final String EXTRA_LIVE_WALLPAPER_INFO = "android.live_wallpaper.info"; + + /** + * Instance of {@link WallpaperConnection} used to bind to the live wallpaper service to show + * it in this preview fragment. + * @see IWallpaperConnection + */ + private WallpaperConnection mWallpaperConnection; + + private Intent mWallpaperIntent; + private Intent mDeleteIntent; + private Intent mSettingsIntent; + + private List<Pair<String, View>> mPages; + private ImageView mLoadingIndicator; + private TextView mAttributionTitle; + private TextView mAttributionSubtitle1; + private TextView mAttributionSubtitle2; + private Button mExploreButton; + private Button mSetWallpaperButton; + private ViewPager mViewPager; + private TabLayout mTabLayout; + private SliceView mSettingsSliceView; + private LiveData<Slice> mSettingsLiveData; + private View mSpacer; + private View mLoadingScrim; + private MaterialProgressDrawable mProgressDrawable; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + android.app.WallpaperInfo info = mWallpaper.getWallpaperComponent(); + mWallpaperIntent = new Intent(WallpaperService.SERVICE_INTERFACE) + .setClassName(info.getPackageName(), info.getServiceName()); + setUpExploreIntent(null); + + android.app.WallpaperInfo currentWallpaper = + WallpaperManager.getInstance(requireContext()).getWallpaperInfo(); + String deleteAction = getDeleteAction(info.getServiceInfo(), + (currentWallpaper == null) ? null : currentWallpaper.getServiceInfo()); + + if (!TextUtils.isEmpty(deleteAction)) { + mDeleteIntent = new Intent(deleteAction); + mDeleteIntent.setPackage(info.getPackageName()); + mDeleteIntent.putExtra(EXTRA_LIVE_WALLPAPER_INFO, info); + } + + String settingsActivity = info.getSettingsActivity(); + if (settingsActivity != null) { + mSettingsIntent = new Intent(); + mSettingsIntent.setComponent(new ComponentName(info.getPackageName(), + settingsActivity)); + mSettingsIntent.putExtra(WallpaperSettingsActivity.EXTRA_PREVIEW_MODE, true); + PackageManager pm = requireContext().getPackageManager(); + ActivityInfo activityInfo = mSettingsIntent.resolveActivityInfo(pm, 0); + if (activityInfo == null) { + Log.i(TAG, "Couldn't find wallpaper settings activity: " + settingsActivity); + mSettingsIntent = null; + } + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mPages = new ArrayList<>(); + View view = super.onCreateView(inflater, container, savedInstanceState); + if (view == null) { + return null; + } + + Activity activity = requireActivity(); + + mLoadingScrim = view.findViewById(R.id.loading); + mLoadingIndicator = view.findViewById(R.id.loading_indicator); + setUpLoadingIndicator(); + + mWallpaperConnection = new WallpaperConnection(mWallpaperIntent, activity); + container.post(() -> { + if (!mWallpaperConnection.connect()) { + mWallpaperConnection = null; + } + }); + + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (mSettingsLiveData != null && mSettingsLiveData.hasObservers()) { + mSettingsLiveData.removeObserver(mSettingsSliceView); + mSettingsLiveData = null; + } + if (mWallpaperConnection != null) { + mWallpaperConnection.disconnect(); + } + mWallpaperConnection = null; + super.onDestroy(); + } + + @Override + protected void setUpBottomSheetView(ViewGroup bottomSheet) { + + initInfoPage(); + initSettingsPage(); + + mViewPager = bottomSheet.findViewById(R.id.viewpager); + mTabLayout = bottomSheet.findViewById(R.id.tablayout); + + // Create PagerAdapter + final PagerAdapter pagerAdapter = new PagerAdapter() { + @Override + public Object instantiateItem(ViewGroup container, int position) { + final View page = mPages.get(position).second; + container.addView(page); + return page; + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, + @NonNull Object object) { + if (object instanceof View) { + container.removeView((View) object); + } + } + + @Override + public int getCount() { + return mPages.size(); + } + + @Override + public CharSequence getPageTitle(int position) { + try { + return mPages.get(position).first; + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { + return (view == object); + } + }; + + // Add OnPageChangeListener to re-measure ViewPager's height + mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + mViewPager.requestLayout(); + } + }); + + // Set PagerAdapter + mViewPager.setAdapter(pagerAdapter); + + // Make TabLayout visible if there are more than one page + if (mPages.size() > 1) { + mTabLayout.setVisibility(View.VISIBLE); + mTabLayout.setupWithViewPager(mViewPager); + } + mViewPager.setCurrentItem(0); + } + + private void setUpLoadingIndicator() { + Context context = requireContext(); + mProgressDrawable = new MaterialProgressDrawable(context.getApplicationContext(), + mLoadingIndicator); + mProgressDrawable.setAlpha(255); + mProgressDrawable.setBackgroundColor(getResources().getColor(R.color.material_white_100, + context.getTheme())); + mProgressDrawable.setColorSchemeColors(getAttrColor( + new ContextThemeWrapper(context, getDeviceDefaultTheme()), + android.R.attr.colorAccent)); + mProgressDrawable.updateSizes(MaterialProgressDrawable.LARGE); + mLoadingIndicator.setImageDrawable(mProgressDrawable); + + // We don't want to show the spinner every time we load a wallpaper if it loads quickly; + // instead, only start showing the spinner after 100 ms + mLoadingIndicator.postDelayed(() -> { + if ((mWallpaperConnection == null || !mWallpaperConnection.isEngineReady()) + && !mTestingModeEnabled) { + mLoadingIndicator.setVisibility(View.VISIBLE); + mLoadingIndicator.setAlpha(1f); + if (mProgressDrawable != null) { + mProgressDrawable.start(); + } + } + }, 100); + } + + private void initInfoPage() { + View pageInfo = getLayoutInflater().inflate(R.layout.preview_page_info, null /* root */); + + mAttributionTitle = pageInfo.findViewById(R.id.preview_attribution_pane_title); + mAttributionSubtitle1 = pageInfo.findViewById(R.id.preview_attribution_pane_subtitle1); + mAttributionSubtitle2 = pageInfo.findViewById(R.id.preview_attribution_pane_subtitle2); + mSpacer = pageInfo.findViewById(R.id.spacer); + + mExploreButton = pageInfo.findViewById(R.id.preview_attribution_pane_explore_button); + mSetWallpaperButton = pageInfo.findViewById( + R.id.preview_attribution_pane_set_wallpaper_button); + + mPages.add(Pair.create(getString(R.string.tab_info), pageInfo)); + } + + private void initSettingsPage() { + final Uri uriSettingsSlice = getSettingsSliceUri(mWallpaper.getWallpaperComponent()); + if (uriSettingsSlice == null) { + return; + } + + final View pageSettings = getLayoutInflater().inflate(R.layout.preview_page_settings, + null /* root */); + + mSettingsSliceView = pageSettings.findViewById(R.id.settings_slice); + mSettingsSliceView.setMode(SliceView.MODE_LARGE); + mSettingsSliceView.setScrollable(false); + + // Set LiveData for SliceView + mSettingsLiveData = SliceLiveData.fromUri(requireContext() /* context */, uriSettingsSlice); + mSettingsLiveData.observeForever(mSettingsSliceView); + + mPages.add(Pair.create(getResources().getString(R.string.tab_customize), pageSettings)); + } + + private void populateAttributionPane() { + final Context context = getContext(); + + final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet); + + List<String> attributions = mWallpaper.getAttributions(context); + if (attributions.size() > 0 && attributions.get(0) != null) { + mAttributionTitle.setText(attributions.get(0)); + } + + if (mWallpaper.getWallpaperComponent().getShowMetadataInPreview()) { + + if (attributions.size() > 1 && attributions.get(1) != null) { + mAttributionSubtitle1.setVisibility(View.VISIBLE); + mAttributionSubtitle1.setText(attributions.get(1)); + } + + if (attributions.size() > 2 && attributions.get(2) != null) { + mAttributionSubtitle2.setVisibility(View.VISIBLE); + mAttributionSubtitle2.setText(attributions.get(2)); + } + + } else { + mExploreIntent = null; + } + + setUpSetWallpaperButton(mSetWallpaperButton); + + setUpExploreButton(mExploreButton); + + if (mExploreButton.getVisibility() == View.VISIBLE + && mSetWallpaperButton.getVisibility() == View.VISIBLE) { + mSpacer.setVisibility(View.VISIBLE); + } else { + mSpacer.setVisibility(View.GONE); + } + + mBottomSheet.setVisibility(View.VISIBLE); + + // Initialize the state of the BottomSheet based on the current state because if the initial + // and current state are the same, the state change listener won't fire and set the correct + // arrow asset and text alpha. + if (mBottomSheetInitialState == STATE_EXPANDED) { + setPreviewChecked(false); + mAttributionTitle.setAlpha(1f); + mAttributionSubtitle1.setAlpha(1f); + mAttributionSubtitle2.setAlpha(1f); + } else { + setPreviewChecked(true); + mAttributionTitle.setAlpha(0f); + mAttributionSubtitle1.setAlpha(0f); + mAttributionSubtitle2.setAlpha(0f); + } + + bottomSheetBehavior.setState(mBottomSheetInitialState); + } + + @SuppressLint("NewApi") //Already checking with isAtLeastQ + private Uri getSettingsSliceUri(android.app.WallpaperInfo info) { + if (BuildCompat.isAtLeastQ()) { + return info.getSettingsSliceUri(); + } + return null; + } + + @Override + protected int getLayoutResId() { + return R.layout.fragment_live_preview; + } + + @Override + protected int getBottomSheetResId() { + return R.id.bottom_sheet; + } + + @Override + protected void setCurrentWallpaper(int destination) { + mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, null, + destination, 0, null, new SetWallpaperCallback() { + @Override + public void onSuccess() { + finishActivityWithResultOk(); + } + + @Override + public void onError(@Nullable Throwable throwable) { + showSetWallpaperErrorDialog(destination); + } + }); + } + + @Override + protected void setBottomSheetContentAlpha(float alpha) { + mExploreButton.setAlpha(alpha); + mAttributionTitle.setAlpha(alpha); + mAttributionSubtitle1.setAlpha(alpha); + mAttributionSubtitle2.setAlpha(alpha); + } + + @Override + protected void setUpExploreButton(Button exploreButton) { + super.setUpExploreButton(exploreButton); + if (exploreButton.getVisibility() != View.VISIBLE) { + return; + } + Context context = requireContext(); + CharSequence exploreLabel = ((LiveWallpaperInfo) mWallpaper).getActionDescription(context); + if (!TextUtils.isEmpty(exploreLabel)) { + exploreButton.setText(exploreLabel); + } + } + + @Nullable + private String getDeleteAction(ServiceInfo serviceInfo, + @Nullable ServiceInfo currentService) { + if (!isPackagePreInstalled(serviceInfo.applicationInfo)) { + Log.d(TAG, "This wallpaper is not pre-installed: " + serviceInfo.name); + return null; + } + + // A currently set Live wallpaper should not be deleted. + if (currentService != null && TextUtils.equals(serviceInfo.name, currentService.name)) { + return null; + } + + final Bundle metaData = serviceInfo.metaData; + if (metaData != null) { + return metaData.getString(KEY_ACTION_DELETE_LIVE_WALLPAPER); + } + return null; + } + + @Override + public void onResume() { + super.onResume(); + if (mWallpaperConnection != null) { + mWallpaperConnection.setVisibility(true); + } + } + + @Override + public void onPause() { + super.onPause(); + if (mWallpaperConnection != null) { + mWallpaperConnection.setVisibility(false); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + menu.findItem(R.id.configure).setVisible(mSettingsIntent != null); + menu.findItem(R.id.delete_wallpaper).setVisible(mDeleteIntent != null); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == R.id.configure) { + if (getActivity() != null) { + startActivity(mSettingsIntent); + return true; + } + } else if (id == R.id.delete_wallpaper) { + showDeleteConfirmDialog(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void showDeleteConfirmDialog() { + final AlertDialog alertDialog = new AlertDialog.Builder( + new ContextThemeWrapper(getContext(), getDeviceDefaultTheme())) + .setMessage(R.string.delete_wallpaper_confirmation) + .setPositiveButton(R.string.delete_live_wallpaper, + (dialog, which) -> deleteLiveWallpaper()) + .setNegativeButton(android.R.string.cancel, null /* listener */) + .create(); + alertDialog.show(); + } + + private void deleteLiveWallpaper() { + if (mDeleteIntent != null) { + requireContext().startService(mDeleteIntent); + finishActivityWithResultOk(); + } + } + + private boolean isPackagePreInstalled(ApplicationInfo info) { + if (info != null && (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + return true; + } + return false; + } + + private class WallpaperConnection extends IWallpaperConnection.Stub + implements ServiceConnection { + private final Activity mActivity; + private final Intent mIntent; + private IWallpaperService mService; + private IWallpaperEngine mEngine; + private boolean mConnected; + private boolean mIsVisible; + private boolean mIsEngineVisible; + private boolean mEngineReady; + + WallpaperConnection(Intent intent, Activity activity) { + mActivity = activity; + mIntent = intent; + } + + public boolean connect() { + synchronized (this) { + if (!mActivity.bindService(mIntent, this, Context.BIND_AUTO_CREATE)) { + return false; + } + + mConnected = true; + return true; + } + } + + public void disconnect() { + synchronized (this) { + mConnected = false; + if (mEngine != null) { + try { + mEngine.destroy(); + } catch (RemoteException e) { + // Ignore + } + mEngine = null; + } + try { + mActivity.unbindService(this); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Can't unbind wallpaper service. " + + "It might have crashed, just ignoring.", e); + } + mService = null; + } + } + + public void onServiceConnected(ComponentName name, IBinder service) { + if (mWallpaperConnection == this) { + mService = IWallpaperService.Stub.asInterface(service); + try { + View root = mActivity.getWindow().getDecorView(); + int displayId = root.getDisplay().getDisplayId(); + mService.attach(this, root.getWindowToken(), + LayoutParams.TYPE_APPLICATION_MEDIA, + true, root.getWidth(), root.getHeight(), + new Rect(0, 0, 0, 0), displayId); + } catch (RemoteException e) { + Log.w(TAG, "Failed attaching wallpaper; clearing", e); + } + } + } + + public void onServiceDisconnected(ComponentName name) { + mService = null; + mEngine = null; + if (mWallpaperConnection == this) { + Log.w(TAG, "Wallpaper service gone: " + name); + } + } + + public void attachEngine(IWallpaperEngine engine, int displayId) { + synchronized (this) { + if (mConnected) { + mEngine = engine; + if (mIsVisible) { + setEngineVisibility(true); + } + } else { + try { + engine.destroy(); + } catch (RemoteException e) { + // Ignore + } + } + } + } + + public ParcelFileDescriptor setWallpaper(String name) { + return null; + } + + @Override + public void onWallpaperColorsChanged(WallpaperColors colors, int displayId) + throws RemoteException { + + } + + @Override + public void engineShown(IWallpaperEngine engine) { + mLoadingScrim.post(() -> { + mLoadingScrim.animate() + .alpha(0f) + .setDuration(220) + .setStartDelay(300) + .setInterpolator(AnimationUtils.loadInterpolator(mActivity, + android.R.interpolator.fast_out_linear_in)) + .withEndAction(() -> { + if (mLoadingIndicator != null) { + mLoadingIndicator.setVisibility(View.GONE); + } + if (mProgressDrawable != null) { + mProgressDrawable.stop(); + } + mLoadingScrim.setVisibility(View.INVISIBLE); + populateAttributionPane(); + }); + }); + mEngineReady = true; + } + + public boolean isEngineReady() { + return mEngineReady; + } + + public void setVisibility(boolean visible) { + mIsVisible = visible; + setEngineVisibility(visible); + } + + private void setEngineVisibility(boolean visible) { + if (mEngine != null && visible != mIsEngineVisible) { + try { + mEngine.setVisibility(visible); + mIsEngineVisible = visible; + } catch (RemoteException e) { + Log.w(TAG, "Failure setting wallpaper visibility ", e); + } + } + } + } +} diff --git a/src/com/android/wallpaper/picker/PreviewFragment.java b/src/com/android/wallpaper/picker/PreviewFragment.java index c698c8c..f1a1625 100755 --- a/src/com/android/wallpaper/picker/PreviewFragment.java +++ b/src/com/android/wallpaper/picker/PreviewFragment.java @@ -18,46 +18,33 @@ package com.android.wallpaper.picker; import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED; import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.res.Configuration; import android.content.res.Resources.NotFoundException; import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Color; -import android.graphics.Point; -import android.graphics.PointF; import android.graphics.PorterDuff.Mode; -import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.net.Uri; import android.os.Bundle; import android.util.Log; -import android.view.ContextThemeWrapper; -import android.view.Display; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.Button; import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.CallSuper; +import androidx.annotation.IdRes; import androidx.annotation.IntDef; +import androidx.annotation.LayoutRes; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; @@ -66,9 +53,6 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import com.android.wallpaper.R; -import com.android.wallpaper.asset.Asset; -import com.android.wallpaper.asset.Asset.BitmapReceiver; -import com.android.wallpaper.asset.Asset.DimensionsReceiver; import com.android.wallpaper.compat.BuildCompat; import com.android.wallpaper.model.LiveWallpaperInfo; import com.android.wallpaper.model.WallpaperInfo; @@ -76,19 +60,10 @@ import com.android.wallpaper.module.ExploreIntentChecker; import com.android.wallpaper.module.Injector; import com.android.wallpaper.module.InjectorProvider; import com.android.wallpaper.module.UserEventLogger; -import com.android.wallpaper.module.WallpaperPersister; import com.android.wallpaper.module.WallpaperPersister.Destination; -import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback; import com.android.wallpaper.module.WallpaperPreferences; import com.android.wallpaper.module.WallpaperSetter; -import com.android.wallpaper.util.ScreenSizeCalculator; -import com.android.wallpaper.util.WallpaperCropUtils; -import com.android.wallpaper.widget.MaterialProgressDrawable; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.MemoryCategory; -import com.davemorrissey.labs.subscaleview.ImageSource; -import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; + import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetBehavior.State; @@ -96,23 +71,22 @@ import java.util.Date; import java.util.List; /** - * Fragment which displays the UI for previewing an individual wallpaper and its attribution - * information. + * Base Fragment to display the UI for previewing an individual wallpaper */ -public class PreviewFragment extends Fragment implements +public abstract class PreviewFragment extends Fragment implements SetWallpaperDialogFragment.Listener, SetWallpaperErrorDialogFragment.Listener, LoadWallpaperErrorDialogFragment.Listener { /** * User can view wallpaper and attributions in full screen, but "Set wallpaper" button is hidden. */ - public static final int MODE_VIEW_ONLY = 0; + static final int MODE_VIEW_ONLY = 0; /** * User can view wallpaper and attributions in full screen and click "Set wallpaper" to set the * wallpaper with pan and crop position to the device. */ - public static final int MODE_CROP_AND_SET_WALLPAPER = 1; + static final int MODE_CROP_AND_SET_WALLPAPER = 1; /** * Possible preview modes for the fragment. @@ -126,52 +100,56 @@ public class PreviewFragment extends Fragment implements public static final String ARG_WALLPAPER = "wallpaper"; public static final String ARG_PREVIEW_MODE = "preview_mode"; 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 testingModeEnabled) { + + boolean isLive = wallpaperInfo instanceof LiveWallpaperInfo; + + Bundle args = new Bundle(); + args.putParcelable(ARG_WALLPAPER, wallpaperInfo); + args.putInt(ARG_PREVIEW_MODE, mode); + args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled); + + PreviewFragment fragment = isLive ? 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 = "set_wallpaper_error_dialog"; private static final int UNUSED_REQUEST_CODE = 1; - private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f; private static final String TAG = "PreviewFragment"; - private static final float PAGE_BITMAP_MAX_HEAP_RATIO = 0.25f; - private static final String KEY_BOTTOM_SHEET_STATE = "key_bottom_sheet_state"; + static final String KEY_BOTTOM_SHEET_STATE = "key_bottom_sheet_state"; @PreviewMode - private int mPreviewMode; + protected int mPreviewMode; /** * When true, enables 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. */ - private boolean mTestingModeEnabled; + protected boolean mTestingModeEnabled; - protected SubsamplingScaleImageView mFullResImageView; protected WallpaperInfo mWallpaper; - private Asset mWallpaperAsset; - private WallpaperSetter mWallpaperSetter; - private UserEventLogger mUserEventLogger; - private LinearLayout mBottomSheet; - private TextView mAttributionTitle; - private TextView mAttributionSubtitle1; - private TextView mAttributionSubtitle2; - private Button mAttributionExploreButton; - private int mCurrentScreenOrientation; - private Point mDefaultCropSurfaceSize; - private Point mScreenSize; - private Point mRawWallpaperSize; // Native size of wallpaper image. - private ImageView mLoadingIndicator; - private MaterialProgressDrawable mProgressDrawable; - private ImageView mLowResImageView; - private Button mSetWallpaperButton; - private View mSpacer; - private CheckBox mPreview; + protected WallpaperSetter mWallpaperSetter; + protected UserEventLogger mUserEventLogger; + protected ViewGroup mBottomSheet; + + protected CheckBox mPreview; @SuppressWarnings("RestrictTo") @State - private int mBottomSheetInitialState; + protected int mBottomSheetInitialState; - private Intent mExploreIntent; + protected Intent mExploreIntent; /** * Staged error dialog fragments that were unable to be shown when the hosting activity didn't @@ -180,23 +158,7 @@ public class PreviewFragment extends Fragment implements private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment; private LoadWallpaperErrorDialogFragment mStagedLoadWallpaperErrorDialogFragment; - /** - * Creates and returns new instance of {@link PreviewFragment} with the provided wallpaper set as - * an argument. - */ - public static PreviewFragment newInstance( - WallpaperInfo wallpaperInfo, @PreviewMode int mode, boolean testingModeEnabled) { - Bundle args = new Bundle(); - args.putParcelable(ARG_WALLPAPER, wallpaperInfo); - args.putInt(ARG_PREVIEW_MODE, mode); - args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled); - - PreviewFragment fragment = new PreviewFragment(); - fragment.setArguments(args); - return fragment; - } - - private static int getAttrColor(Context context, int attr) { + protected static int getAttrColor(Context context, int attr) { TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); int colorAccent = ta.getColor(0, 0); ta.recycle(); @@ -208,12 +170,12 @@ public class PreviewFragment extends Fragment implements super.onCreate(savedInstanceState); Activity activity = getActivity(); - Context appContext = activity.getApplicationContext(); + Context appContext = getContext().getApplicationContext(); Injector injector = InjectorProvider.getInjector(); mUserEventLogger = injector.getUserEventLogger(appContext); mWallpaper = getArguments().getParcelable(ARG_WALLPAPER); - mWallpaperAsset = mWallpaper.getAsset(appContext); + //noinspection ResourceType mPreviewMode = getArguments().getInt(ARG_PREVIEW_MODE); mTestingModeEnabled = getArguments().getBoolean(ARG_TESTING_MODE_ENABLED); @@ -236,14 +198,18 @@ public class PreviewFragment extends Fragment implements } } + @LayoutRes + protected abstract int getLayoutResId(); + @Override + @CallSuper public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_preview, container, false); + View view = inflater.inflate(getLayoutResId(), container, false); // Set toolbar as the action bar. Toolbar toolbar = view.findViewById(R.id.toolbar); - AppCompatActivity activity = (AppCompatActivity) getActivity(); + AppCompatActivity activity = (AppCompatActivity) requireActivity(); activity.setSupportActionBar(toolbar); activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); activity.getSupportActionBar().setDisplayShowTitleEnabled(false); @@ -270,18 +236,8 @@ public class PreviewFragment extends Fragment implements R.dimen.preview_toolbar_set_wallpaper_button_end_padding), /* bottom */ 0); - mFullResImageView = view.findViewById(R.id.full_res_image); - mLoadingIndicator = view.findViewById(R.id.loading_indicator); - - mBottomSheet = view.findViewById(R.id.bottom_sheet); - mAttributionTitle = view.findViewById(R.id.preview_attribution_pane_title); - mAttributionSubtitle1 = view.findViewById(R.id.preview_attribution_pane_subtitle1); - mAttributionSubtitle2 = view.findViewById(R.id.preview_attribution_pane_subtitle2); - mAttributionExploreButton = view.findViewById( - R.id.preview_attribution_pane_explore_button); - mLowResImageView = view.findViewById(R.id.low_res_image); - mSetWallpaperButton = view.findViewById(R.id.preview_attribution_pane_set_wallpaper_button); - mSpacer = view.findViewById(R.id.spacer); + mBottomSheet = view.findViewById(getBottomSheetResId()); + setUpBottomSheetView(mBottomSheet); // Workaround as we don't have access to bottomDialogCornerRadius, mBottomSheet radii are // set to dialogCornerRadius by default. @@ -294,93 +250,18 @@ public class PreviewFragment extends Fragment implements bottomSheetBackground.setCornerRadii(radii); mBottomSheet.setBackground(bottomSheetBackground); - // Trim some memory from Glide to make room for the full-size image in this fragment. - Glide.get(getActivity()).setMemoryCategory(MemoryCategory.LOW); - - mDefaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize( - getResources(), getActivity().getWindowManager().getDefaultDisplay()); - mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize( - getActivity().getWindowManager().getDefaultDisplay()); - - // Load a low-res placeholder image if there's a thumbnail available from the asset that can be - // shown to the user more quickly than the full-sized image. - if (mWallpaperAsset.hasLowResDataSource()) { - mWallpaperAsset.loadLowResDrawable(getActivity(), mLowResImageView, Color.BLACK, - new WallpaperPreviewBitmapTransformation(getActivity().getApplicationContext(), - isRtl())); - } - - mWallpaperAsset.decodeRawDimensions(getActivity(), new DimensionsReceiver() { - @Override - public void onDimensionsDecoded(Point dimensions) { - // Don't continue loading the wallpaper if the Fragment is detached. - Activity activity = getActivity(); - if (activity == null) { - return; - } - - // Return early and show a dialog if dimensions are null (signaling a decoding error). - if (dimensions == null) { - showLoadWallpaperErrorDialog(); - return; - } - - mRawWallpaperSize = dimensions; - ExploreIntentChecker intentChecker = - InjectorProvider.getInjector().getExploreIntentChecker(activity); - String actionUrl = mWallpaper.getActionUrl(activity); - if (actionUrl != null && !actionUrl.isEmpty()) { - Uri exploreUri = Uri.parse(mWallpaper.getActionUrl(activity)); - - intentChecker.fetchValidActionViewIntent(exploreUri, exploreIntent -> { - if (getActivity() == null) { - return; - } - - mExploreIntent = exploreIntent; - initFullResView(); - }); - } else { - initFullResView(); - } - } - }); - - // Configure loading indicator with a MaterialProgressDrawable. - mProgressDrawable = new MaterialProgressDrawable(getActivity().getApplicationContext(), - mLoadingIndicator); - mProgressDrawable.setAlpha(255); - mProgressDrawable.setBackgroundColor(getResources().getColor(R.color.material_white_100, - getContext().getTheme())); - mProgressDrawable.setColorSchemeColors(getAttrColor( - new ContextThemeWrapper(getContext(), getDeviceDefaultTheme()), - android.R.attr.colorAccent)); - mProgressDrawable.updateSizes(MaterialProgressDrawable.LARGE); - mLoadingIndicator.setImageDrawable(mProgressDrawable); - - // We don't want to show the spinner every time we load an image if it loads quickly; instead, - // only start showing the spinner if loading the image has taken longer than half of a second. - mLoadingIndicator.postDelayed(() -> { - if (mFullResImageView != null && !mFullResImageView.hasImage() - && !mTestingModeEnabled) { - mLoadingIndicator.setVisibility(View.VISIBLE); - mLoadingIndicator.setAlpha(1f); - if (mProgressDrawable != null) { - mProgressDrawable.start(); - } - } - }, 500); - - - mBottomSheetInitialState = (savedInstanceState == null) - ? STATE_EXPANDED - : savedInstanceState.getInt(KEY_BOTTOM_SHEET_STATE, - STATE_EXPANDED); + mBottomSheetInitialState = (savedInstanceState == null) ? STATE_EXPANDED + : savedInstanceState.getInt(KEY_BOTTOM_SHEET_STATE, STATE_EXPANDED); setUpBottomSheetListeners(); return view; } + protected abstract void setUpBottomSheetView(ViewGroup bottomSheet); + + @IdRes + protected abstract int getBottomSheetResId(); + protected int getDeviceDefaultTheme() { return android.R.style.Theme_DeviceDefault; } @@ -398,12 +279,12 @@ public class PreviewFragment extends Fragment implements // allow committing fragment transactions. if (mStagedLoadWallpaperErrorDialogFragment != null) { mStagedLoadWallpaperErrorDialogFragment.show( - getFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT); + requireFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT); mStagedLoadWallpaperErrorDialogFragment = null; } if (mStagedSetWallpaperErrorDialogFragment != null) { mStagedSetWallpaperErrorDialogFragment.show( - getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT); + requireFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT); mStagedSetWallpaperErrorDialogFragment = null; } } @@ -428,20 +309,20 @@ public class PreviewFragment extends Fragment implements // the IndividualPreviewActivity, the "My photos" selection (by way of // TopLevelPickerActivity), or from a system "crop and set wallpaper" intent. // Therefore, handle the Up button as a global Back. - getActivity().onBackPressed(); + requireActivity().onBackPressed(); return true; } return false; } - protected void setupPreviewMenu(Menu menu) { + private void setupPreviewMenu(Menu menu) { mPreview = (CheckBox) menu.findItem(R.id.preview).getActionView(); mPreview.setChecked(mBottomSheetInitialState == STATE_COLLAPSED); mPreview.setOnClickListener(this::setPreviewBehavior); } - private void setPreviewChecked(boolean checked) { + protected void setPreviewChecked(boolean checked) { if (mPreview != null) { mPreview.setChecked(checked); int resId = checked ? R.string.expand_attribution_panel @@ -450,7 +331,7 @@ public class PreviewFragment extends Fragment implements } } - private void setPreviewBehavior(final View v) { + private void setPreviewBehavior(View v) { CheckBox checkbox = (CheckBox) v; BottomSheetBehavior<?> behavior = BottomSheetBehavior.from(mBottomSheet); @@ -461,6 +342,61 @@ public class PreviewFragment extends Fragment implements } } + protected void setUpSetWallpaperButton(Button setWallpaperButton) { + if (mPreviewMode == MODE_VIEW_ONLY) { + setWallpaperButton.setVisibility(View.GONE); + } else { + setWallpaperButton.setVisibility(View.VISIBLE); + setWallpaperButton.setOnClickListener(this::onSetWallpaperClicked); + } + } + + protected void setUpExploreButton(Button exploreButton) { + exploreButton.setVisibility(View.GONE); + if (mExploreIntent == null) { + return; + } + Context context = requireContext(); + exploreButton.setVisibility(View.VISIBLE); + exploreButton.setText(context.getString( + mWallpaper.getActionLabelRes(context))); + + exploreButton.setOnClickListener(view -> { + mUserEventLogger.logActionClicked(mWallpaper.getCollectionId(context), + mWallpaper.getActionLabelRes(context)); + + startActivity(mExploreIntent); + }); + } + + protected void setUpExploreIntent(@Nullable Runnable callback) { + Context context = getContext(); + if (context == null) { + return; + } + String actionUrl = mWallpaper.getActionUrl(context); + if (actionUrl != null && !actionUrl.isEmpty()) { + Uri exploreUri = Uri.parse(mWallpaper.getActionUrl(context)); + ExploreIntentChecker intentChecker = + InjectorProvider.getInjector().getExploreIntentChecker(context); + + intentChecker.fetchValidActionViewIntent(exploreUri, exploreIntent -> { + if (getActivity() == null) { + return; + } + + mExploreIntent = exploreIntent; + if (callback != null) { + callback.run(); + } + }); + } else { + if (callback != null) { + callback.run(); + } + } + } + @Override public void onSet(int destination) { setCurrentWallpaper(destination); @@ -483,10 +419,6 @@ public class PreviewFragment extends Fragment implements public void onDestroy() { super.onDestroy(); mWallpaperSetter.cleanUp(); - if (mProgressDrawable != null) { - mProgressDrawable.stop(); - } - mFullResImageView.recycle(); } @Override @@ -498,41 +430,8 @@ public class PreviewFragment extends Fragment implements } private void onSetWallpaperClicked(View button) { - if (BuildCompat.isAtLeastN()) { - mWallpaperSetter.requestDestination(getContext(), getFragmentManager(), this, - mWallpaper instanceof LiveWallpaperInfo); - } else { - setCurrentWallpaper(WallpaperPersister.DEST_HOME_SCREEN); - } - } - - /** - * Returns a zoom level that is similar to the actual zoom, but that is exactly 0.5 ** n for some - * integer n. This is useful for downsampling a bitmap--we want to see the bitmap at full detail, - * or downsampled to 1 in every 2 pixels, or 1 in 4, and so on, depending on the zoom. - */ - private static float getDownsampleZoom(float actualZoom) { - if (actualZoom > 1) { - // Very zoomed in, but we can't sample more than 1 pixel per pixel. - return 1.0f; - } - float lower = 1.0f / roundUpToPower2((int) Math.ceil(1 / actualZoom)); - float upper = lower * 2; - return nearestValue(actualZoom, lower, upper); - } - - /** - * Returns the integer rounded up to the next power of 2. - */ - private static int roundUpToPower2(int value) { - return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); - } - - /** - * Returns the closer of two values a and b to the given value. - */ - private static float nearestValue(float value, float a, float b) { - return Math.abs(a - value) < Math.abs(b - value) ? a : b; + mWallpaperSetter.requestDestination(getContext(), getFragmentManager(), this, + mWallpaper instanceof LiveWallpaperInfo); } private void setUpBottomSheetListeners() { @@ -554,6 +453,8 @@ public class PreviewFragment extends Fragment implements case STATE_EXPANDED: setPreviewChecked(false /* checked */); break; + default: + Log.v(TAG, "Ignoring BottomSheet state: " + newState); } } @@ -565,264 +466,13 @@ public class PreviewFragment extends Fragment implements } else { alpha = 1f - slideOffset; } - mAttributionTitle.setAlpha(alpha); - mAttributionSubtitle1.setAlpha(alpha); - mAttributionSubtitle2.setAlpha(alpha); - mAttributionExploreButton.setAlpha(alpha); + setBottomSheetContentAlpha(alpha); } }); } - private boolean isWallpaperLoaded() { - return mFullResImageView.hasImage(); - } - - private void populateAttributionPane() { - final Context context = getContext(); - - final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet); - - List<String> attributions = mWallpaper.getAttributions(context); - if (attributions.size() > 0 && attributions.get(0) != null) { - mAttributionTitle.setText(attributions.get(0)); - } - - if (attributions.size() > 1 && attributions.get(1) != null) { - mAttributionSubtitle1.setVisibility(View.VISIBLE); - mAttributionSubtitle1.setText(attributions.get(1)); - } - - if (attributions.size() > 2 && attributions.get(2) != null) { - mAttributionSubtitle2.setVisibility(View.VISIBLE); - mAttributionSubtitle2.setText(attributions.get(2)); - } - - if (mPreviewMode == MODE_CROP_AND_SET_WALLPAPER) { - mSetWallpaperButton.setVisibility(View.VISIBLE); - mSetWallpaperButton.setOnClickListener(this::onSetWallpaperClicked); - } else { - mSetWallpaperButton.setVisibility(View.GONE); - } - - String actionUrl = mWallpaper.getActionUrl(context); - - mAttributionExploreButton.setVisibility(View.GONE); - if (actionUrl != null && !actionUrl.isEmpty()) { - if (mExploreIntent != null) { - mAttributionExploreButton.setVisibility(View.VISIBLE); - mAttributionExploreButton.setText(context.getString( - mWallpaper.getActionLabelRes(context))); - - mAttributionExploreButton.setOnClickListener(view -> { - mUserEventLogger.logActionClicked(mWallpaper.getCollectionId(context), - mWallpaper.getActionLabelRes(context)); - - startActivity(mExploreIntent); - }); - } - } - - if (mAttributionExploreButton.getVisibility() == View.VISIBLE - && mSetWallpaperButton.getVisibility() == View.VISIBLE) { - mSpacer.setVisibility(View.VISIBLE); - } else { - mSpacer.setVisibility(View.GONE); - } - - mBottomSheet.setVisibility(View.VISIBLE); - - // Initialize the state of the BottomSheet based on the current state because if the initial - // and current state are the same, the state change listener won't fire and set the correct - // arrow asset and text alpha. - if (bottomSheetBehavior.getState() == STATE_EXPANDED) { - setPreviewChecked(false); - mAttributionTitle.setAlpha(1f); - mAttributionSubtitle1.setAlpha(1f); - mAttributionSubtitle2.setAlpha(1f); - } else { - setPreviewChecked(true); - mAttributionTitle.setAlpha(0f); - mAttributionSubtitle1.setAlpha(0f); - mAttributionSubtitle2.setAlpha(0f); - } - - // Let the state change listener take care of animating a state change to the initial state - // if there's a state change. - bottomSheetBehavior.setState(mBottomSheetInitialState); - } - - /** - * Initializes MosaicView by initializing tiling, setting a fallback page bitmap, and - * initializing a zoom-scroll observer and click listener. - */ - private void initFullResView() { - mFullResImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP); - - // Set a solid black "page bitmap" so MosaicView draws a black background while waiting - // for the image to load or a transparent one if a thumbnail already loaded. - Bitmap blackBitmap = Bitmap.createBitmap(1, 1, Config.ARGB_8888); - int color = (mLowResImageView.getDrawable() == null) ? Color.BLACK : Color.TRANSPARENT; - blackBitmap.setPixel(0, 0, color); - mFullResImageView.setImage(ImageSource.bitmap(blackBitmap)); - - // Then set a fallback "page bitmap" to cover the whole MosaicView, which is an actual - // (lower res) version of the image to be displayed. - Point targetPageBitmapSize = new Point(mRawWallpaperSize); - mWallpaperAsset.decodeBitmap(targetPageBitmapSize.x, targetPageBitmapSize.y, - new BitmapReceiver() { - @Override - public void onBitmapDecoded(Bitmap pageBitmap) { - // Check that the activity is still around since the decoding task started. - if (getActivity() == null) { - return; - } - - // Some of these may be null depending on if the Fragment is paused, stopped, - // or destroyed. - if (mLoadingIndicator != null) { - mLoadingIndicator.setVisibility(View.GONE); - } - // The page bitmap may be null if there was a decoding error, so show an - // error dialog. - if (pageBitmap == null) { - showLoadWallpaperErrorDialog(); - return; - } - if (mFullResImageView != null) { - // Set page bitmap. - mFullResImageView.setImage(ImageSource.bitmap(pageBitmap)); - - setDefaultWallpaperZoomAndScroll(); - crossFadeInMosaicView(); - } - if (mProgressDrawable != null) { - mProgressDrawable.stop(); - } - getActivity().invalidateOptionsMenu(); - - populateAttributionPane(); - } - }); - } + protected void setBottomSheetContentAlpha(float alpha) { - /** - * Makes the MosaicView visible with an alpha fade-in animation while fading out the loading - * indicator. - */ - private void crossFadeInMosaicView() { - long shortAnimationDuration = getResources().getInteger( - android.R.integer.config_shortAnimTime); - - mFullResImageView.setAlpha(0f); - mFullResImageView.animate() - .alpha(1f) - .setDuration(shortAnimationDuration) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // Clear the thumbnail bitmap reference to save memory since it's no longer - // visible. - if (mLowResImageView != null) { - mLowResImageView.setImageBitmap(null); - } - } - }); - - mLoadingIndicator.animate() - .alpha(0f) - .setDuration(shortAnimationDuration) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (mLoadingIndicator != null) { - mLoadingIndicator.setVisibility(View.GONE); - } - } - }); - } - - /** - * Sets the default wallpaper zoom and scroll position based on a "crop surface" - * (with extra width to account for parallax) superimposed on the screen. Shows as much of the - * wallpaper as possible on the crop surface and align screen to crop surface such that the - * default preview matches what would be seen by the user in the left-most home screen. - * - * <p>This method is called once in the Fragment lifecycle after the wallpaper asset has loaded - * and rendered to the layout. - */ - private void setDefaultWallpaperZoomAndScroll() { - // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface. - float defaultWallpaperZoom = - WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mDefaultCropSurfaceSize); - float minWallpaperZoom = - WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mScreenSize); - - Point screenToCropSurfacePosition = WallpaperCropUtils.calculateCenterPosition( - mDefaultCropSurfaceSize, mScreenSize, true /* alignStart */, isRtl()); - Point zoomedWallpaperSize = new Point( - Math.round(mRawWallpaperSize.x * defaultWallpaperZoom), - Math.round(mRawWallpaperSize.y * defaultWallpaperZoom)); - Point cropSurfaceToWallpaperPosition = WallpaperCropUtils.calculateCenterPosition( - zoomedWallpaperSize, mDefaultCropSurfaceSize, false /* alignStart */, isRtl()); - - // Set min wallpaper zoom and max zoom on MosaicView widget. - mFullResImageView.setMaxScale(Math.max(DEFAULT_WALLPAPER_MAX_ZOOM, defaultWallpaperZoom)); - mFullResImageView.setMinScale(minWallpaperZoom); - - // Set center to composite positioning between scaled wallpaper and screen. - PointF centerPosition = new PointF( - mRawWallpaperSize.x / 2f, - mRawWallpaperSize.y / 2f); - centerPosition.offset( - (screenToCropSurfacePosition.x + cropSurfaceToWallpaperPosition.x), - - (screenToCropSurfacePosition.y + cropSurfaceToWallpaperPosition.y)); - - mFullResImageView.setScaleAndCenter(minWallpaperZoom, centerPosition); - } - - protected Rect calculateCropRect() { - // Calculate Rect of wallpaper in physical pixel terms (i.e., scaled to current zoom). - float wallpaperZoom = mFullResImageView.getScale(); - int scaledWallpaperWidth = (int) (mRawWallpaperSize.x * wallpaperZoom); - int scaledWallpaperHeight = (int) (mRawWallpaperSize.y * wallpaperZoom); - Rect rect = new Rect(); - mFullResImageView.visibleFileRect(rect); - int scrollX = (int) (rect.left * wallpaperZoom); - int scrollY = (int) (rect.top * wallpaperZoom); - - rect.set(0, 0, scaledWallpaperWidth, scaledWallpaperHeight); - Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize( - getActivity().getWindowManager().getDefaultDisplay()); - // Crop rect should start off as the visible screen and then include extra width and height - // if available within wallpaper at the current zoom. - Rect cropRect = new Rect(scrollX, scrollY, scrollX + screenSize.x, scrollY + screenSize.y); - - Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize( - getResources(), getActivity().getWindowManager().getDefaultDisplay()); - int extraWidth = defaultCropSurfaceSize.x - screenSize.x; - int extraHeightTopAndBottom = (int) ((defaultCropSurfaceSize.y - screenSize.y) / 2f); - - // Try to increase size of screenRect to include extra width depending on the layout - // direction. - if (isRtl()) { - cropRect.left = Math.max(cropRect.left - extraWidth, rect.left); - } else { - cropRect.right = Math.min(cropRect.right + extraWidth, rect.right); - } - - // Try to increase the size of the cropRect to to include extra height. - int availableExtraHeightTop = cropRect.top - Math.max( - rect.top, - cropRect.top - extraHeightTopAndBottom); - int availableExtraHeightBottom = Math.min( - rect.bottom, - cropRect.bottom + extraHeightTopAndBottom) - cropRect.bottom; - - int availableExtraHeightTopAndBottom = - Math.min(availableExtraHeightTop, availableExtraHeightBottom); - cropRect.top -= availableExtraHeightTopAndBottom; - cropRect.bottom += availableExtraHeightTopAndBottom; - - return cropRect; } /** @@ -830,35 +480,22 @@ public class PreviewFragment extends Fragment implements * * @param destination The wallpaper destination i.e. home vs. lockscreen vs. both. */ - private void setCurrentWallpaper(@Destination final int destination) { - mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset, - destination, mFullResImageView.getScale(), calculateCropRect(), - new SetWallpaperCallback() { - @Override - public void onSuccess() { - finishActivityWithResultOk(); - } - - @Override - public void onError(@Nullable Throwable throwable) { - showSetWallpaperErrorDialog(destination); - } - }); - } + protected abstract void setCurrentWallpaper(@Destination int destination); - private void finishActivityWithResultOk() { + protected void finishActivityWithResultOk() { + Activity activity = requireActivity(); try { - Toast.makeText( - getActivity(), R.string.wallpaper_set_successfully_message, Toast.LENGTH_SHORT).show(); + Toast.makeText(activity, + R.string.wallpaper_set_successfully_message, Toast.LENGTH_SHORT).show(); } catch (NotFoundException e) { Log.e(TAG, "Could not show toast " + e); } - getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out); - getActivity().setResult(Activity.RESULT_OK); - getActivity().finish(); + activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out); + activity.setResult(Activity.RESULT_OK); + activity.finish(); } - private void showSetWallpaperErrorDialog(@Destination int wallpaperDestination) { + protected void showSetWallpaperErrorDialog(@Destination int wallpaperDestination) { SetWallpaperErrorDialogFragment newFragment = SetWallpaperErrorDialogFragment.newInstance( R.string.set_wallpaper_error_message, wallpaperDestination); newFragment.setTargetFragment(this, UNUSED_REQUEST_CODE); @@ -866,9 +503,9 @@ public class PreviewFragment extends Fragment implements // Show 'set wallpaper' error dialog now if it's safe to commit fragment transactions, // otherwise stage it for later when the hosting activity is in a state to commit fragment // transactions. - BasePreviewActivity activity = (BasePreviewActivity) getActivity(); + BasePreviewActivity activity = (BasePreviewActivity) requireActivity(); if (activity.isSafeToCommitFragmentTransaction()) { - newFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT); + newFragment.show(requireFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT); } else { mStagedSetWallpaperErrorDialogFragment = newFragment; } @@ -878,17 +515,16 @@ public class PreviewFragment extends Fragment implements * Shows 'load wallpaper' error dialog now or stage it to be shown when the hosting activity is * in a state that allows committing fragment transactions. */ - private void showLoadWallpaperErrorDialog() { + protected void showLoadWallpaperErrorDialog() { LoadWallpaperErrorDialogFragment dialogFragment = LoadWallpaperErrorDialogFragment.newInstance(); - dialogFragment.setTargetFragment(PreviewFragment.this, UNUSED_REQUEST_CODE); + dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE); // Show 'load wallpaper' error dialog now or stage it to be shown when the hosting // activity is in a state that allows committing fragment transactions. BasePreviewActivity activity = (BasePreviewActivity) getActivity(); if (activity != null && activity.isSafeToCommitFragmentTransaction()) { - dialogFragment.show(PreviewFragment.this.getFragmentManager(), - TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT); + dialogFragment.show(requireFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT); } else { mStagedLoadWallpaperErrorDialogFragment = dialogFragment; } @@ -903,59 +539,10 @@ public class PreviewFragment extends Fragment implements } /** - * Gets the appropriate ActivityInfo orientation for the current configuration orientation to - * enable locking screen rotation at API levels lower than 18. - */ - @ActivityInfoScreenOrientation - private int getCompatActivityInfoOrientation() { - int configOrientation = getResources().getConfiguration().orientation; - final Display display = getActivity().getWindowManager().getDefaultDisplay(); - int naturalOrientation = Configuration.ORIENTATION_LANDSCAPE; - switch (display.getRotation()) { - case Surface.ROTATION_0: - case Surface.ROTATION_180: - // We are currently in the same basic orientation as the natural orientation. - naturalOrientation = configOrientation; - break; - case Surface.ROTATION_90: - case Surface.ROTATION_270: - // We are currently in the other basic orientation to the natural orientation. - naturalOrientation = (configOrientation == Configuration.ORIENTATION_LANDSCAPE) - ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; - break; - default: - // continue below - } - - // Since the map starts at portrait, we need to offset if this device's natural orientation - // is landscape. - int indexOffset = 0; - if (naturalOrientation == Configuration.ORIENTATION_LANDSCAPE) { - indexOffset = 1; - } - - switch ((display.getRotation() + indexOffset) % 4) { - case 0: - return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - case 1: - return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - case 2: - return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; - case 3: - return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; - default: - Log.e(TAG, "Display rotation did not correspond to a valid ActivityInfo orientation" - + "with display rotation: " + display.getRotation() + " and index offset: " - + indexOffset + "."); - return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - } - } - - /** * Returns whether layout direction is RTL (or false for LTR). Since native RTL layout support * was added in API 17, returns false for versions lower than 17. */ - private boolean isRtl() { + protected boolean isRtl() { return getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; } diff --git a/src/com/android/wallpaper/picker/StartRotationDialogFragment.java b/src/com/android/wallpaper/picker/StartRotationDialogFragment.java index d5fd017..f0e4dcd 100755 --- a/src/com/android/wallpaper/picker/StartRotationDialogFragment.java +++ b/src/com/android/wallpaper/picker/StartRotationDialogFragment.java @@ -29,8 +29,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - -import androidx.appcompat.view.ContextThemeWrapper; import androidx.fragment.app.DialogFragment; import com.android.wallpaper.R; @@ -42,29 +40,14 @@ import com.android.wallpaper.module.InjectorProvider; */ public class StartRotationDialogFragment extends DialogFragment { private static final String KEY_IS_WIFI_ONLY_CHECKED = "key_is_wifi_only_checked"; - private static final String KEY_IS_LIVE_WALLPAPER_PREVIEW_NEEDED = "key_is_live_wallpaper_needed"; private static final boolean DEFAULT_IS_WIFI_ONLY = true; private boolean mIsWifiOnlyChecked; - private boolean mIsLiveWallpaperPreviewNeeded; - - public static StartRotationDialogFragment newInstance(boolean isLiveWallpaperPreviewNeeded) { - StartRotationDialogFragment dialogFragment = new StartRotationDialogFragment(); - Bundle args = new Bundle(); - args.putBoolean(KEY_IS_LIVE_WALLPAPER_PREVIEW_NEEDED, isLiveWallpaperPreviewNeeded); - dialogFragment.setArguments(args); - return dialogFragment; - } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Bundle args = getArguments(); - if (args != null) { - mIsLiveWallpaperPreviewNeeded = args.getBoolean(KEY_IS_LIVE_WALLPAPER_PREVIEW_NEEDED); - } - if (savedInstanceState == null) { mIsWifiOnlyChecked = DEFAULT_IS_WIFI_ONLY; } else { @@ -117,14 +100,10 @@ public class StartRotationDialogFragment extends DialogFragment { } private int getBodyTextResourceId() { - return mIsLiveWallpaperPreviewNeeded - ? R.string.start_rotation_dialog_body_live_wallpaper_needed - : R.string.start_rotation_dialog_body; + return R.string.start_rotation_dialog_body; } private int getPositiveButtonTextResourceId() { - return mIsLiveWallpaperPreviewNeeded - ? R.string.start_rotation_dialog_continue - : android.R.string.ok; + return android.R.string.ok; } } diff --git a/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java b/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java index 367181a..de5ba84 100755 --- a/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java +++ b/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java @@ -16,8 +16,6 @@ package com.android.wallpaper.picker.individual; import android.app.Activity; -import android.app.WallpaperManager; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Resources.NotFoundException; @@ -44,12 +42,9 @@ import com.android.wallpaper.model.PickerIntentFactory; import com.android.wallpaper.model.WallpaperInfo; import com.android.wallpaper.module.Injector; import com.android.wallpaper.module.InjectorProvider; -import com.android.wallpaper.module.LiveWallpaperStatusChecker; -import com.android.wallpaper.module.NoBackupImageWallpaper; import com.android.wallpaper.module.WallpaperPersister; import com.android.wallpaper.picker.BaseActivity; import com.android.wallpaper.picker.PreviewActivity.PreviewActivityIntentFactory; -import com.android.wallpaper.util.ActivityUtils; import com.android.wallpaper.util.DiskBasedLogger; /** @@ -61,13 +56,11 @@ public class IndividualPickerActivity extends BaseActivity { private static final String EXTRA_CATEGORY_COLLECTION_ID = "com.android.wallpaper.category_collection_id"; private static final int PREVIEW_WALLPAPER_REQUEST_CODE = 0; - private static final int NO_BACKUP_IMAGE_WALLPAPER_REQUEST_CODE = 1; private static final int PREVIEW_LIVEWALLPAPER_REQUEST_CODE = 2; private static final String KEY_CATEGORY_COLLECTION_ID = "key_category_collection_id"; private InlinePreviewIntentFactory mPreviewIntentFactory; private WallpaperPersister mWallpaperPersister; - private LiveWallpaperStatusChecker mLiveWallpaperStatusChecker; private Category mCategory; private String mCategoryCollectionId; @@ -83,7 +76,6 @@ public class IndividualPickerActivity extends BaseActivity { mPreviewIntentFactory = new PreviewActivityIntentFactory(); Injector injector = InjectorProvider.getInjector(); mWallpaperPersister = injector.getWallpaperPersister(this); - mLiveWallpaperStatusChecker = injector.getLiveWallpaperStatusChecker(this); FragmentManager fm = getSupportFragmentManager(); Fragment fragment = fm.findFragmentById(R.id.fragment_container); @@ -160,8 +152,8 @@ public class IndividualPickerActivity extends BaseActivity { public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == android.R.id.home) { - // Handle Up as a Global back since the only entry point to IndividualPickerActivity is from - // TopLevelPickerActivity. + // Handle Up as a Global back since the only entry point to IndividualPickerActivity is + // from TopLevelPickerActivity. onBackPressed(); return true; } @@ -184,18 +176,6 @@ public class IndividualPickerActivity extends BaseActivity { finishWithResultOk(shouldShowMessage); } break; - - case NO_BACKUP_IMAGE_WALLPAPER_REQUEST_CODE: - // User clicked "Set wallpaper" in live wallpaper preview UI. - // NOTE: Don't check for the result code prior to KitKat MR2 because a bug on those versions - // caused the result code to be discarded from LivePicker so we can't rely on it. - if ((!BuildCompat.isAtLeastL() || resultCode == Activity.RESULT_OK) - && mLiveWallpaperStatusChecker.isNoBackupImageWallpaperSet() - && mCategory.getWallpaperRotationInitializer().startRotation(getApplicationContext())) { - finishWithResultOk(true); - } - break; - default: Log.e(TAG, "Invalid request code: " + requestCode); } @@ -211,18 +191,6 @@ public class IndividualPickerActivity extends BaseActivity { : PREVIEW_WALLPAPER_REQUEST_CODE); } - /** - * Shows the system live wallpaper preview for the {@link NoBackupImageWallpaper} which is used to - * draw rotating wallpapers on pre-N Android builds. - */ - public void showNoBackupImageWallpaperPreview() { - Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER); - ComponentName componentName = new ComponentName(this, NoBackupImageWallpaper.class); - intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, componentName); - ActivityUtils.startActivityForResultSafely( - this, intent, NO_BACKUP_IMAGE_WALLPAPER_REQUEST_CODE); - } - private void finishWithResultOk(boolean shouldShowMessage) { if (shouldShowMessage) { try { diff --git a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java index db21da3..c0c4ce5 100755 --- a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java +++ b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java @@ -58,13 +58,11 @@ import com.android.wallpaper.model.WallpaperRotationInitializer; import com.android.wallpaper.model.WallpaperRotationInitializer.Listener; import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference; import com.android.wallpaper.model.WallpaperRotationInitializer.RotationInitializationState; -import com.android.wallpaper.model.WallpaperRotationInitializer.RotationStateListener; import com.android.wallpaper.module.FormFactorChecker; import com.android.wallpaper.module.FormFactorChecker.FormFactor; import com.android.wallpaper.module.Injector; import com.android.wallpaper.module.InjectorProvider; import com.android.wallpaper.module.PackageStatusNotifier; -import com.android.wallpaper.module.RotatingWallpaperComponentChecker; import com.android.wallpaper.module.WallpaperChangedNotifier; import com.android.wallpaper.module.WallpaperPersister; import com.android.wallpaper.module.WallpaperPersister.Destination; @@ -116,7 +114,6 @@ public class IndividualPickerFragment extends Fragment WallpaperPreferences mWallpaperPreferences; WallpaperChangedNotifier mWallpaperChangedNotifier; - RotatingWallpaperComponentChecker mRotatingWallpaperComponentChecker; RecyclerView mImageGrid; IndividualAdapter mAdapter; WallpaperCategory mCategory; @@ -250,8 +247,6 @@ public class IndividualPickerFragment extends Fragment mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance(); mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener); - mRotatingWallpaperComponentChecker = injector.getRotatingWallpaperComponentChecker(); - mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor(); mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext); @@ -445,7 +440,8 @@ public class IndividualPickerFragment extends Fragment public void onResume() { super.onResume(); - WallpaperPreferences preferences = InjectorProvider.getInjector().getPreferences(getActivity()); + WallpaperPreferences preferences = InjectorProvider.getInjector() + .getPreferences(getActivity()); preferences.setLastAppActiveTimestamp(new Date().getTime()); // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in @@ -541,31 +537,23 @@ public class IndividualPickerFragment extends Fragment * state of the user's device and binds the state of the current category's rotation to the "start * rotation" tile. */ - private void refreshRotationHolder(final RotationHolder rotationHolder) { + private void refreshRotationHolder(RotationHolder rotationHolder) { mWallpaperRotationInitializer.fetchRotationInitializationState(getContext(), - new RotationStateListener() { - @Override - public void onRotationStateReceived( - @RotationInitializationState final int rotationInitializationState) { - - // Update the UI state of the "start rotation" tile displayed on screen. Do this in a - // Handler so it is scheduled at the end of the message queue. This is necessary to - // ensure we do not remove or add data from the adapter while the layout is still being - // computed. RecyclerView documentation therefore recommends performing such changes in - // a Handler. - new android.os.Handler().post(new Runnable() { - @Override - public void run() { - // A config change may have destroyed the activity since the refresh started, so - // check for that to avoid an NPE. - if (getActivity() == null) { - return; - } + rotationState -> { + // Update the UI state of the "start rotation" tile displayed on screen. + // Do this in a Handler so it is scheduled at the end of the message queue. + // This is necessary to ensure we do not remove or add data from the adapter + // while the layout is still being computed. RecyclerView documentation + // therefore recommends performing such changes in a Handler. + new Handler().post(() -> { + // A config change may have destroyed the activity since the refresh + // started, so check for that to avoid an NPE. + if (getActivity() == null) { + return; + } - rotationHolder.bindRotationInitializationState(rotationInitializationState); - } - }); - } + rotationHolder.bindRotationInitializationState(rotationState); + }); }); } @@ -614,34 +602,33 @@ public class IndividualPickerFragment extends Fragment // app before the first wallpaper image in rotation finishes downloading. Activity activity = getActivity(); - if (activity != null - && mWallpaperRotationInitializer - .isNoBackupImageWallpaperPreviewNeeded(appContext)) { - ((IndividualPickerActivity) activity).showNoBackupImageWallpaperPreview(); - } else { - if (mWallpaperRotationInitializer.startRotation(appContext)) { - if (activity != null && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) { - try { - Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message, - Toast.LENGTH_SHORT).show(); - } catch (NotFoundException e) { - Log.e(TAG, "Could not show toast " + e); - } - activity.setResult(Activity.RESULT_OK); - activity.finish(); - } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) { - mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION); - } - } else { // Failed to start rotation. - showStartRotationErrorDialog(networkPreference); - - if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) { - DesktopRotationHolder rotationViewHolder = - (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition( - SPECIAL_FIXED_TILE_ADAPTER_POSITION); - rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED); + if (mWallpaperRotationInitializer.startRotation(appContext)) { + if (activity != null + && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) { + try { + Toast.makeText(getActivity(), + R.string.wallpaper_set_successfully_message, + Toast.LENGTH_SHORT).show(); + } catch (NotFoundException e) { + Log.e(TAG, "Could not show toast " + e); } + + activity.setResult(Activity.RESULT_OK); + activity.finish(); + } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) { + mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION); + } + } else { // Failed to start rotation. + showStartRotationErrorDialog(networkPreference); + + if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) { + DesktopRotationHolder rotationViewHolder = + (DesktopRotationHolder) + mImageGrid.findViewHolderForAdapterPosition( + SPECIAL_FIXED_TILE_ADAPTER_POSITION); + rotationViewHolder.setSelectionState( + SelectableHolder.SELECTION_STATE_DESELECTED); } } } @@ -690,11 +677,7 @@ public class IndividualPickerFragment extends Fragment * Returns whether rotation is enabled for this category. */ boolean isRotationEnabled() { - boolean isRotationSupported = - mRotatingWallpaperComponentChecker.getRotatingWallpaperSupport(getContext()) - == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_SUPPORT_SUPPORTED; - - return isRotationSupported && mWallpaperRotationInitializer != null; + return mWallpaperRotationInitializer != null; } @Override @@ -732,10 +715,10 @@ public class IndividualPickerFragment extends Fragment super(itemView); itemView.setOnClickListener(this); - mTileLayout = (FrameLayout) itemView.findViewById(R.id.daily_refresh); - mRotationMessage = (TextView) itemView.findViewById(R.id.rotation_tile_message); - mRotationTitle = (TextView) itemView.findViewById(R.id.rotation_tile_title); - mRefreshIcon = (ImageView) itemView.findViewById(R.id.rotation_tile_refresh_icon); + mTileLayout = itemView.findViewById(R.id.daily_refresh); + mRotationMessage = itemView.findViewById(R.id.rotation_tile_message); + mRotationTitle = itemView.findViewById(R.id.rotation_tile_title); + mRefreshIcon = itemView.findViewById(R.id.rotation_tile_refresh_icon); mTileLayout.getLayoutParams().height = mTileSizePx.y; // If the feature flag for "dynamic start rotation tile" is not enabled, fall back to the @@ -749,7 +732,8 @@ public class IndividualPickerFragment extends Fragment mRotationMessage.setTextColor( getResources().getColor(R.color.rotation_tile_enabled_subtitle_text_color)); mRefreshIcon.setColorFilter( - getResources().getColor(R.color.rotation_tile_enabled_refresh_icon_color), Mode.SRC_IN); + getResources().getColor(R.color.rotation_tile_enabled_refresh_icon_color), + Mode.SRC_IN); return; } @@ -765,10 +749,7 @@ public class IndividualPickerFragment extends Fragment @Override public void onClick(View v) { - boolean isLiveWallpaperNeeded = mWallpaperRotationInitializer - .isNoBackupImageWallpaperPreviewNeeded(getActivity().getApplicationContext()); - DialogFragment startRotationDialogFragment = StartRotationDialogFragment - .newInstance(isLiveWallpaperNeeded); + DialogFragment startRotationDialogFragment = new StartRotationDialogFragment(); startRotationDialogFragment.setTargetFragment( IndividualPickerFragment.this, UNUSED_REQUEST_CODE); startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG); diff --git a/src/com/android/wallpaper/widget/ConstraintViewPager.java b/src/com/android/wallpaper/widget/ConstraintViewPager.java new file mode 100644 index 0000000..ad56750 --- /dev/null +++ b/src/com/android/wallpaper/widget/ConstraintViewPager.java @@ -0,0 +1,62 @@ +/* + * 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.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; + +/** + * When ConstraintViewPager is being measured, it will get all height of pages and makes itself + * height as the same as the maximum height. + */ +public class ConstraintViewPager extends ViewPager { + + public ConstraintViewPager(@NonNull Context context) { + this(context, null /* attrs */); + } + + public ConstraintViewPager(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + /** + * Visit all child views first and then determine the maximum height for ViewPager. + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int maxChildHeight = 0; + for (int i = 0; i < getChildCount(); i++) { + View view = getChildAt(i); + view.measure(widthMeasureSpec, + MeasureSpec.makeMeasureSpec(0 /* size */, MeasureSpec.UNSPECIFIED)); + int childHeight = view.getMeasuredHeight(); + if (childHeight > maxChildHeight) { + maxChildHeight = childHeight; + } + } + + if (maxChildHeight != 0) { + heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxChildHeight, MeasureSpec.EXACTLY); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +} diff --git a/src_override/com/android/wallpaper/module/WallpapersInjector.java b/src_override/com/android/wallpaper/module/WallpapersInjector.java index 8e3b3e9..5a39524 100755 --- a/src_override/com/android/wallpaper/module/WallpapersInjector.java +++ b/src_override/com/android/wallpaper/module/WallpapersInjector.java @@ -17,12 +17,12 @@ package com.android.wallpaper.module; import android.content.Context; +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.PreviewFragment; - -import androidx.fragment.app.Fragment; +import com.android.wallpaper.picker.ImagePreviewFragment; /** * A concrete, real implementation of the dependency provider. @@ -69,7 +69,7 @@ public class WallpapersInjector extends BaseWallpaperInjector { WallpaperInfo wallpaperInfo, int mode, boolean testingModeEnabled) { - return PreviewFragment.newInstance(wallpaperInfo, mode, testingModeEnabled); + return ImagePreviewFragment.newInstance(wallpaperInfo, mode, testingModeEnabled); } @Override diff --git a/tests/Android.mk b/tests/Android.mk new file mode 100644 index 0000000..be224e3 --- /dev/null +++ b/tests/Android.mk @@ -0,0 +1,59 @@ +# 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. +# + +LOCAL_PATH := $(call my-dir) + +# +# Build rule for WallpaperPicker2 tests +# +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_STATIC_JAVA_LIBRARIES := \ + androidx.annotation_annotation \ + androidx.test.core \ + androidx.test.runner \ + androidx.test.rules \ + androidx.test.espresso.contrib \ + androidx.test.espresso.intents \ + mockito-target-minus-junit4 \ + androidx.test.espresso.core \ + hamcrest-library \ + hamcrest + +ifneq (,$(wildcard frameworks/base)) + LOCAL_PRIVATE_PLATFORM_APIS := true +else + LOCAL_SDK_VERSION := 29 + LOCAL_MIN_SDK_VERSION := 26 +endif + +LOCAL_PROGUARD_ENABLED := disabled + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_STATIC_ANDROID_LIBRARIES := WallpaperPicker2CommonDepsLib + +LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest.xml +LOCAL_MANIFEST_FILE := AndroidManifest.xml + +LOCAL_INSTRUMENTATION_FOR := WallpaperPicker2 + +LOCAL_PACKAGE_NAME := WallpaperPicker2Tests + +include $(BUILD_PACKAGE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml new file mode 100644 index 0000000..1e35c92 --- /dev/null +++ b/tests/AndroidManifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.wallpaper.tests"> + + <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="29"/> + + <application> + <uses-library android:name="android.test.runner"/> + </application> + + <instrumentation + android:functionalTest="false" + android:handleProfiling="false" + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.wallpaper"> + </instrumentation> +</manifest> diff --git a/tests/src/com/android/wallpaper/picker/PreviewActivityTest.java b/tests/src/com/android/wallpaper/picker/PreviewActivityTest.java new file mode 100644 index 0000000..e1feba5 --- /dev/null +++ b/tests/src/com/android/wallpaper/picker/PreviewActivityTest.java @@ -0,0 +1,510 @@ +/* + * 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.picker; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static junit.framework.TestCase.assertFalse; + +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.Rect; +import android.widget.TextView; + +import androidx.test.espresso.intent.Intents; +import androidx.test.filters.MediumTest; +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; + +import com.android.wallpaper.R; +import com.android.wallpaper.module.Injector; +import com.android.wallpaper.module.InjectorProvider; +import com.android.wallpaper.module.UserEventLogger; +import com.android.wallpaper.module.WallpaperPersister; +import com.android.wallpaper.testing.TestAsset; +import com.android.wallpaper.testing.TestExploreIntentChecker; +import com.android.wallpaper.testing.TestInjector; +import com.android.wallpaper.testing.TestUserEventLogger; +import com.android.wallpaper.testing.TestWallpaperInfo; +import com.android.wallpaper.testing.TestWallpaperPersister; +import com.android.wallpaper.util.ScreenSizeCalculator; +import com.android.wallpaper.util.WallpaperCropUtils; + +import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests for {@link PreviewActivity}. + */ +@RunWith(AndroidJUnit4ClassRunner.class) +@MediumTest +public class PreviewActivityTest { + + private static final float FLOAT_ERROR_MARGIN = 0.001f; + private static final String ACTION_URL = "http://google.com"; + + private TestWallpaperInfo mMockWallpaper; + private Injector mInjector; + private TestWallpaperPersister mWallpaperPersister; + private TestUserEventLogger mEventLogger; + private TestExploreIntentChecker mExploreIntentChecker; + + @Rule + public ActivityTestRule<PreviewActivity> mActivityRule = + new ActivityTestRule<>(PreviewActivity.class, false, false); + + @Before + public void setUp() { + + mInjector = new TestInjector(); + InjectorProvider.setInjector(mInjector); + + Intents.init(); + + mMockWallpaper = new TestWallpaperInfo(TestWallpaperInfo.COLOR_BLACK); + List<String> attributions = new ArrayList<>(); + attributions.add("Title"); + attributions.add("Subtitle 1"); + attributions.add("Subtitle 2"); + mMockWallpaper.setAttributions(attributions); + mMockWallpaper.setCollectionId("collection"); + mMockWallpaper.setWallpaperId("wallpaper"); + mMockWallpaper.setActionUrl(ACTION_URL); + + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + mWallpaperPersister = (TestWallpaperPersister) mInjector.getWallpaperPersister(context); + mEventLogger = (TestUserEventLogger) mInjector.getUserEventLogger(context); + mExploreIntentChecker = (TestExploreIntentChecker) + mInjector.getExploreIntentChecker(context); + } + + @After + public void tearDown() { + Intents.release(); + mActivityRule.finishActivity(); + } + + private void launchActivityIntentWithMockWallpaper() { + Intent intent = PreviewActivity.newIntent( + InstrumentationRegistry.getInstrumentation().getTargetContext(), mMockWallpaper); + intent.putExtra(BasePreviewActivity.EXTRA_TESTING_MODE_ENABLED, true); + + mActivityRule.launchActivity(intent); + } + + private void finishSettingWallpaper() throws Throwable { + mActivityRule.runOnUiThread(() -> mWallpaperPersister.finishSettingWallpaper()); + } + + @Test + public void testRendersWallpaperDrawableFromIntent() { + launchActivityIntentWithMockWallpaper(); + PreviewActivity activity = mActivityRule.getActivity(); + SubsamplingScaleImageView mosaicView = activity.findViewById(R.id.full_res_image); + + assertTrue(mosaicView.hasImage()); + } + + @Test + public void testClickSetWallpaper_Success_HomeScreen() throws Throwable { + launchActivityIntentWithMockWallpaper(); + assertNull(mWallpaperPersister.getCurrentHomeWallpaper()); + + onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click()); + + // Destination dialog is shown; click "Home screen". + onView(withText(R.string.set_wallpaper_home_screen_destination)).perform(click()); + + assertNull(mWallpaperPersister.getCurrentHomeWallpaper()); + + finishSettingWallpaper(); + + // Mock system wallpaper bitmap should be equal to the mock WallpaperInfo's bitmap. + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap(); + assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentHomeWallpaper())); + + // The wallpaper should have been set on the home screen. + assertEquals(WallpaperPersister.DEST_HOME_SCREEN, mWallpaperPersister.getLastDestination()); + assertEquals(1, mEventLogger.getNumWallpaperSetEvents()); + + assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents()); + assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS, + mEventLogger.getLastWallpaperSetResult()); + } + + @Test + public void testClickSetWallpaper_Success_LockScreen() throws Throwable { + launchActivityIntentWithMockWallpaper(); + assertNull(mWallpaperPersister.getCurrentLockWallpaper()); + + onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click()); + + // Destination dialog is shown; click "Lock screen." + onView(withText(R.string.set_wallpaper_lock_screen_destination)).perform(click()); + + assertNull(mWallpaperPersister.getCurrentLockWallpaper()); + + finishSettingWallpaper(); + + // Mock system wallpaper bitmap should be equal to the mock WallpaperInfo's bitmap. + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap(); + assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentLockWallpaper())); + + // The wallpaper should have been set on the lock screen. + assertEquals(WallpaperPersister.DEST_LOCK_SCREEN, mWallpaperPersister.getLastDestination()); + assertEquals(1, mEventLogger.getNumWallpaperSetEvents()); + + assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents()); + assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS, + mEventLogger.getLastWallpaperSetResult()); + } + + @Test + public void testClickSetWallpaper_Success_BothHomeAndLockScreen() throws Throwable { + launchActivityIntentWithMockWallpaper(); + assertNull(mWallpaperPersister.getCurrentHomeWallpaper()); + assertNull(mWallpaperPersister.getCurrentLockWallpaper()); + + onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click()); + + // Destination dialog is shown; click "Both." + onView(withText(R.string.set_wallpaper_both_destination)).perform(click()); + + assertNull(mWallpaperPersister.getCurrentHomeWallpaper()); + assertNull(mWallpaperPersister.getCurrentLockWallpaper()); + + finishSettingWallpaper(); + + // Mock system wallpaper bitmap should be equal to the mock WallpaperInfo's bitmap. + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap(); + assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentHomeWallpaper())); + assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentLockWallpaper())); + + // The wallpaper should have been set on both the home and the lock screen. + assertEquals(WallpaperPersister.DEST_BOTH, mWallpaperPersister.getLastDestination()); + assertEquals(1, mEventLogger.getNumWallpaperSetEvents()); + + assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents()); + assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS, + mEventLogger.getLastWallpaperSetResult()); + } + + @Test + public void testClickSetWallpaper_Fails_HomeScreen_ShowsErrorDialog() + throws Throwable { + launchActivityIntentWithMockWallpaper(); + assertNull(mWallpaperPersister.getCurrentHomeWallpaper()); + + mWallpaperPersister.setFailNextCall(true); + + onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click()); + + // Destination dialog is shown; click "Home screen." + onView(withText(R.string.set_wallpaper_home_screen_destination)).perform(click()); + + finishSettingWallpaper(); + + assertNull(mWallpaperPersister.getCurrentHomeWallpaper()); + onView(withText(R.string.set_wallpaper_error_message)).check(matches(isDisplayed())); + + assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents()); + assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_FAILURE, + mEventLogger.getLastWallpaperSetResult()); + + // Set next call to succeed and current wallpaper bitmap should not be null and equals to + // the + // mock wallpaper bitmap after clicking "try again". + mWallpaperPersister.setFailNextCall(false); + + onView(withText(R.string.try_again)).perform(click()); + finishSettingWallpaper(); + + assertNotNull(mWallpaperPersister.getCurrentHomeWallpaper()); + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap(); + assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentHomeWallpaper())); + + // The wallpaper should have been set on the home screen. + assertEquals(WallpaperPersister.DEST_HOME_SCREEN, mWallpaperPersister.getLastDestination()); + } + + @Test + public void testClickSetWallpaper_Fails_LockScreen_ShowsErrorDialog() + throws Throwable { + launchActivityIntentWithMockWallpaper(); + assertNull(mWallpaperPersister.getCurrentLockWallpaper()); + + mWallpaperPersister.setFailNextCall(true); + + onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click()); + + // Destination dialog is shown; click "Lock screen." + onView(withText(R.string.set_wallpaper_lock_screen_destination)).perform(click()); + + finishSettingWallpaper(); + + assertNull(mWallpaperPersister.getCurrentLockWallpaper()); + onView(withText(R.string.set_wallpaper_error_message)).check(matches(isDisplayed())); + + assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents()); + assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_FAILURE, + mEventLogger.getLastWallpaperSetResult()); + + // Set next call to succeed and current wallpaper bitmap should not be null and equals to + // the + // mock wallpaper bitmap after clicking "try again". + mWallpaperPersister.setFailNextCall(false); + + onView(withText(R.string.try_again)).perform(click()); + finishSettingWallpaper(); + + assertNotNull(mWallpaperPersister.getCurrentLockWallpaper()); + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap(); + assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentLockWallpaper())); + + // The wallpaper should have been set on the lock screen. + assertEquals(WallpaperPersister.DEST_LOCK_SCREEN, mWallpaperPersister.getLastDestination()); + } + + @Test + public void testClickSetWallpaper_Fails_BothHomeAndLock_ShowsErrorDialog() + throws Throwable { + launchActivityIntentWithMockWallpaper(); + assertNull(mWallpaperPersister.getCurrentHomeWallpaper()); + assertNull(mWallpaperPersister.getCurrentLockWallpaper()); + + mWallpaperPersister.setFailNextCall(true); + + onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click()); + + // Destination dialog is shown; click "Both." + onView(withText(R.string.set_wallpaper_both_destination)).perform(click()); + + finishSettingWallpaper(); + + assertNull(mWallpaperPersister.getCurrentHomeWallpaper()); + assertNull(mWallpaperPersister.getCurrentLockWallpaper()); + onView(withText(R.string.set_wallpaper_error_message)).check(matches(isDisplayed())); + + assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents()); + assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_FAILURE, + mEventLogger.getLastWallpaperSetResult()); + + // Set next call to succeed and current wallpaper bitmap should not be null and equals to + // the mock wallpaper bitmap after clicking "try again". + mWallpaperPersister.setFailNextCall(false); + + onView(withText(R.string.try_again)).perform(click()); + finishSettingWallpaper(); + + assertNotNull(mWallpaperPersister.getCurrentHomeWallpaper()); + assertNotNull(mWallpaperPersister.getCurrentLockWallpaper()); + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap(); + assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentHomeWallpaper())); + assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentLockWallpaper())); + + // The wallpaper should have been set on both the home screen and the lock screen. + assertEquals(WallpaperPersister.DEST_BOTH, mWallpaperPersister.getLastDestination()); + } + + @Test + public void testClickSetWallpaper_CropsAndScalesWallpaper() { + launchActivityIntentWithMockWallpaper(); + // Scale should not have a meaningful value before clicking "set wallpaper". + assertTrue(mWallpaperPersister.getScale() < 0); + + onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click()); + + // Destination dialog is shown; click "Home screen". + onView(withText(R.string.set_wallpaper_home_screen_destination)).perform(click()); + + // WallpaperPersister's scale should match the ScaleImageView's scale. + float zoom = ((SubsamplingScaleImageView) + mActivityRule.getActivity().findViewById(R.id.full_res_image)).getScale(); + assertEquals(mWallpaperPersister.getScale(), zoom, FLOAT_ERROR_MARGIN); + + Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize( + mActivityRule.getActivity().getWindowManager().getDefaultDisplay()); + int maxDim = Math.max(screenSize.x, screenSize.y); + Rect cropRect = mWallpaperPersister.getCropRect(); + + // Crop rect should be greater or equal than screen size in both directions. + assertTrue(cropRect.width() >= maxDim); + assertTrue(cropRect.height() >= maxDim); + } + + @Test + public void testClickSetWallpaper_FailsCroppingAndScalingWallpaper_ShowsErrorDialog() + throws Throwable { + launchActivityIntentWithMockWallpaper(); + mWallpaperPersister.setFailNextCall(true); + onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click()); + // Destination dialog is shown; click "Home screen". + onView(withText(R.string.set_wallpaper_home_screen_destination)).perform(click()); + + finishSettingWallpaper(); + + onView(withText(R.string.set_wallpaper_error_message)).check(matches(isDisplayed())); + } + + /** + * Tests that tapping Set Wallpaper shows the destination dialog (i.e., choosing + * between Home screen, Lock screen, or Both). + */ + @Test + public void testClickSetWallpaper_ShowsDestinationDialog() { + launchActivityIntentWithMockWallpaper(); + onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click()); + onView(withText(R.string.set_wallpaper_dialog_message)).check(matches(isDisplayed())); + } + + @Test + public void testSetsDefaultWallpaperZoomAndScroll() { + float expectedWallpaperZoom; + int expectedWallpaperScrollX; + int expectedWallpaperScrollY; + + launchActivityIntentWithMockWallpaper(); + PreviewActivity activity = mActivityRule.getActivity(); + SubsamplingScaleImageView fullResImageView = activity.findViewById(R.id.full_res_image); + + Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize( + activity.getResources(), activity.getWindowManager().getDefaultDisplay()); + Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize( + activity.getWindowManager().getDefaultDisplay()); + TestAsset asset = (TestAsset) mMockWallpaper.getAsset(activity); + Point wallpaperSize = new Point(asset.getBitmap().getWidth(), + asset.getBitmap().getHeight()); + + expectedWallpaperZoom = WallpaperCropUtils.calculateMinZoom( + wallpaperSize, defaultCropSurfaceSize); + + // Current zoom should match the minimum zoom required to fit wallpaper + // completely on the crop surface. + assertEquals(expectedWallpaperZoom, fullResImageView.getScale(), FLOAT_ERROR_MARGIN); + + Point scaledWallpaperSize = new Point( + (int) (wallpaperSize.x * expectedWallpaperZoom), + (int) (wallpaperSize.y * expectedWallpaperZoom)); + Point cropSurfaceToScreen = WallpaperCropUtils.calculateCenterPosition( + defaultCropSurfaceSize, screenSize, true /* alignStart */, false /* isRtl */); + Point wallpaperToCropSurface = WallpaperCropUtils.calculateCenterPosition( + scaledWallpaperSize, defaultCropSurfaceSize, false /* alignStart */, + false /* isRtl */); + + expectedWallpaperScrollX = wallpaperToCropSurface.x + cropSurfaceToScreen.x; + expectedWallpaperScrollY = wallpaperToCropSurface.y + cropSurfaceToScreen.y; + + // ZoomView should be scrolled in X and Y directions such that the crop surface is centered + // relative to the wallpaper and the screen is centered (and aligned left) relative to the + // crop surface. + assertEquals(expectedWallpaperScrollX, fullResImageView.getScrollX()); + assertEquals(expectedWallpaperScrollY, fullResImageView.getScrollY()); + } + + @Test + public void testSetWallpaper_TemporarilyLocksScreenOrientation() throws Throwable { + launchActivityIntentWithMockWallpaper(); + PreviewActivity activity = mActivityRule.getActivity(); + + assertFalse(activity.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LOCKED); + + onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click()); + + // Destination dialog is shown; click "Home screen". + onView(withText(R.string.set_wallpaper_home_screen_destination)).perform(click()); + + assertEquals(ActivityInfo.SCREEN_ORIENTATION_LOCKED, activity.getRequestedOrientation()); + + // Finish setting the wallpaper to check that the screen orientation is no longer locked. + finishSettingWallpaper(); + + assertNotEquals(activity.getRequestedOrientation(), ActivityInfo.SCREEN_ORIENTATION_LOCKED); + } + @Test + public void testShowsWallpaperAttribution() { + launchActivityIntentWithMockWallpaper(); + PreviewActivity activity = mActivityRule.getActivity(); + + TextView titleView = activity.findViewById(R.id.preview_attribution_pane_title); + assertEquals("Title", titleView.getText()); + + TextView subtitle1View = activity.findViewById(R.id.preview_attribution_pane_subtitle1); + assertEquals("Subtitle 1", subtitle1View.getText()); + + TextView subtitle2View = activity.findViewById(R.id.preview_attribution_pane_subtitle2); + assertEquals("Subtitle 2", subtitle2View.getText()); + } + + /** + * Tests that if there was a failure decoding the wallpaper bitmap, then the activity shows an + * informative error dialog with an "OK" button, when clicked finishes the activity. + */ + @Test + public void testLoadWallpaper_Failed_ShowsErrorDialog() { + // Simulate a corrupted asset that fails to perform decoding operations. + mMockWallpaper.corruptAssets(); + launchActivityIntentWithMockWallpaper(); + + onView(withText(R.string.load_wallpaper_error_message)).check(matches(isDisplayed())); + + onView(withText(android.R.string.ok)).perform(click()); + + assertTrue(mActivityRule.getActivity().isFinishing()); + } + + /** + * Tests that the explore button is not visible, even if there is an action URL present, if + * there is no activity on the device which can handle such an explore action. + */ + @Test + public void testNoActionViewHandler_ExploreButtonNotVisible() { + mExploreIntentChecker.setViewHandlerExists(false); + + launchActivityIntentWithMockWallpaper(); + onView(withId(R.id.preview_attribution_pane_explore_button)).check( + matches(not(isDisplayed()))); + } +} diff --git a/tests/src/com/android/wallpaper/picker/individual/IndividualPickerActivityTest.java b/tests/src/com/android/wallpaper/picker/individual/IndividualPickerActivityTest.java new file mode 100644 index 0000000..d91c16a --- /dev/null +++ b/tests/src/com/android/wallpaper/picker/individual/IndividualPickerActivityTest.java @@ -0,0 +1,496 @@ +/* + * 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.picker.individual; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.intent.Intents.intended; +import static androidx.test.espresso.intent.Intents.intending; +import static androidx.test.espresso.intent.matcher.ComponentNameMatchers.hasClassName; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; +import static androidx.test.espresso.matcher.ViewMatchers.isChecked; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static junit.framework.TestCase.assertFalse; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.app.Activity; +import android.app.Instrumentation.ActivityResult; +import android.app.WallpaperManager; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.ColorDrawable; +import android.widget.FrameLayout; + +import androidx.recyclerview.widget.RecyclerView; +import androidx.test.espresso.contrib.RecyclerViewActions; +import androidx.test.espresso.intent.Intents; +import androidx.test.filters.MediumTest; +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; + +import com.android.wallpaper.R; +import com.android.wallpaper.config.Flags; +import com.android.wallpaper.model.Category; +import com.android.wallpaper.model.PickerIntentFactory; +import com.android.wallpaper.model.WallpaperInfo; +import com.android.wallpaper.model.WallpaperRotationInitializer; +import com.android.wallpaper.model.WallpaperRotationInitializer.RotationInitializationState; +import com.android.wallpaper.module.FormFactorChecker; +import com.android.wallpaper.module.Injector; +import com.android.wallpaper.module.InjectorProvider; +import com.android.wallpaper.testing.TestCategoryProvider; +import com.android.wallpaper.testing.TestFormFactorChecker; +import com.android.wallpaper.testing.TestInjector; +import com.android.wallpaper.testing.TestUserEventLogger; +import com.android.wallpaper.testing.TestWallpaperCategory; +import com.android.wallpaper.testing.TestWallpaperInfo; +import com.android.wallpaper.testing.TestWallpaperRotationInitializer; + +import org.hamcrest.Matcher; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Tests for {@link IndividualPickerActivity}. + */ +@RunWith(AndroidJUnit4ClassRunner.class) +@MediumTest +public class IndividualPickerActivityTest { + + private static final String EXTRA_WALLPAPER_INFO = + "com.android.wallpaper.picker.wallpaper_info"; + private static final TestWallpaperInfo sWallpaperInfo1 = new TestWallpaperInfo( + TestWallpaperInfo.COLOR_BLACK, "test-wallpaper-1"); + private static final TestWallpaperInfo sWallpaperInfo2 = new TestWallpaperInfo( + TestWallpaperInfo.COLOR_BLACK, "test-wallpaper-2"); + private static final TestWallpaperInfo sWallpaperInfo3 = new TestWallpaperInfo( + TestWallpaperInfo.COLOR_BLACK, "test-wallpaper-3"); + + private TestCategoryProvider mTestCategoryProvider; + + private TestFormFactorChecker mTestFormFactorChecker; + private Injector mInjector; + + private TestWallpaperCategory mTestCategory; + + @Rule + public ActivityTestRule<IndividualPickerActivity> mActivityRule = + new ActivityTestRule<>(IndividualPickerActivity.class, false, false); + + @Before + public void setUp() { + Intents.init(); + + mInjector = new TestInjector(); + InjectorProvider.setInjector(mInjector); + + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + mTestFormFactorChecker = (TestFormFactorChecker) mInjector.getFormFactorChecker(context); + mTestCategoryProvider = (TestCategoryProvider) mInjector.getCategoryProvider(context); + + sWallpaperInfo1.setAttributions(Arrays.asList( + "Attribution 0", "Attribution 1", "Attribution 2")); + } + + @After + public void tearDown() { + Intents.release(); + mActivityRule.finishActivity(); + } + + private IndividualPickerActivity getActivity() { + return mActivityRule.getActivity(); + } + + private void setUpFragmentForTesting() { + IndividualPickerFragment fragment = (IndividualPickerFragment) + getActivity().getSupportFragmentManager().findFragmentById(R.id.fragment_container); + fragment.setTestingMode(true); + } + + private void setActivityWithMockWallpapers(boolean isRotationEnabled, + @RotationInitializationState int rotationState) { + sWallpaperInfo1.setCollectionId("collection"); + + ArrayList<WallpaperInfo> wallpapers = new ArrayList<>(); + wallpapers.add(sWallpaperInfo1); + wallpapers.add(sWallpaperInfo2); + wallpapers.add(sWallpaperInfo3); + + mTestCategory = new TestWallpaperCategory( + "Test category", "collection", wallpapers, 0 /* priority */); + mTestCategory.setIsRotationEnabled(isRotationEnabled); + mTestCategory.setRotationInitializationState(rotationState); + + List<Category> testCategories = mTestCategoryProvider.getTestCategories(); + testCategories.set(0, mTestCategory); + + PickerIntentFactory intentFactory = + new IndividualPickerActivity.IndividualPickerActivityIntentFactory(); + Intent intent = intentFactory.newIntent( + InstrumentationRegistry.getInstrumentation().getTargetContext(), + mTestCategory.getCollectionId()); + mActivityRule.launchActivity(intent); + } + + @Test + public void testDrawsTilesForProvidedWallpapers() { + setActivityWithMockWallpapers(false /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + IndividualPickerActivity activity = getActivity(); + + RecyclerView recyclerView = activity.findViewById(R.id.wallpaper_grid); + + // There are only three wallpapers in the category, so the grid should only have three + // items. + assertNotNull(recyclerView.findViewHolderForAdapterPosition(0)); + assertNotNull(recyclerView.findViewHolderForAdapterPosition(1)); + assertNotNull(recyclerView.findViewHolderForAdapterPosition(2)); + assertNull(recyclerView.findViewHolderForAdapterPosition(3)); + } + + @Test + public void testClickTile_Mobile_LaunchesPreviewActivityWithWallpaper() { + mTestFormFactorChecker.setFormFactor(FormFactorChecker.FORM_FACTOR_MOBILE); + + setActivityWithMockWallpapers(false /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + getActivity(); + + onView(withId(R.id.wallpaper_grid)).perform( + RecyclerViewActions.actionOnItemAtPosition(0, click())); + intended(allOf( + hasComponent(hasClassName("com.android.wallpaper.picker.PreviewActivity")), + hasExtra(equalTo(EXTRA_WALLPAPER_INFO), equalTo(sWallpaperInfo1)))); + + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + TestUserEventLogger eventLogger = (TestUserEventLogger) mInjector.getUserEventLogger( + context); + assertEquals(1, eventLogger.getNumIndividualWallpaperSelectedEvents()); + assertEquals(sWallpaperInfo1.getCollectionId(context), eventLogger.getLastCollectionId()); + } + + /** + * Tests that the static daily rotation tile (with flag dynamicStartRotationTileEnabled=false) + * has a background of the light blue accent color and says "Tap to turn on". + */ + @Test + public void testRotationEnabled_StaticDailyRotationTile() { + Flags.dynamicStartRotationTileEnabled = false; + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + getActivity(); + + onView(withText(R.string.daily_refresh_tile_title)).check(matches(isDisplayed())); + onView(withText(R.string.daily_refresh_tile_subtitle)).check(matches(isDisplayed())); + + // Check that the background color of the "daily refresh" tile is the blue accent color. + FrameLayout rotationTile = getActivity().findViewById(R.id.daily_refresh); + int backgroundColor = ((ColorDrawable) rotationTile.getBackground()).getColor(); + assertEquals(getActivity().getResources().getColor(R.color.accent_color), + backgroundColor); + } + + /** + * Tests that when rotation is enabled and the rotation for this category is already in effect + * on both home & lock screens, then the "start rotation" tile reads "Home & Lock". + */ + @Test + public void testRotationEnabled_RotationInitializedHomeAndLock() { + Flags.dynamicStartRotationTileEnabled = true; + + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_HOME_AND_LOCK); + getActivity(); + + onView(withText(R.string.daily_refresh_tile_title)).check(matches(isDisplayed())); + onView(withText(R.string.home_and_lock_short_label)).check(matches(isDisplayed())); + } + + /** + * Tests that when rotation is enabled and the rotation is aleady for this category is already + * in + * effect on the home-screen only, then the "start rotation" tile reads "Home screen". + */ + @Test + public void testRotationEnabled_RotationInitializedHomeScreenOnly() { + Flags.dynamicStartRotationTileEnabled = true; + + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_HOME_ONLY); + getActivity(); + + onView(withText(R.string.daily_refresh_tile_title)).check(matches(isDisplayed())); + onView(withText(R.string.home_screen_message)).check(matches(isDisplayed())); + } + + /** + * Tests that after the IndividualPickerActivity loads, if the state of the current category's + * rotation changes while the activity is restarted, then the UI for the "start rotation" tile + * changes to reflect that new state. + */ + @Test + public void testActivityRestarted_RotationStateChanges_StartRotationTileUpdates() + throws Throwable { + Flags.dynamicStartRotationTileEnabled = true; + + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_HOME_ONLY); + + onView(withText(R.string.daily_refresh_tile_title)).check(matches(isDisplayed())); + onView(withText(R.string.home_screen_message)).check(matches(isDisplayed())); + + // Now change the rotation initialization state such that the tile should say + // "Tap to turn on" after the activity resumes. + setUpFragmentForTesting(); + TestWallpaperRotationInitializer + testWPRotationInitializer = (TestWallpaperRotationInitializer) + mTestCategory.getWallpaperRotationInitializer(); + testWPRotationInitializer.setRotationInitializationState( + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + + // Restart the activity. + IndividualPickerActivity activity = getActivity(); + mActivityRule.runOnUiThread(activity::recreate); + + onView(withText(R.string.daily_refresh_tile_title)).check(matches(isDisplayed())); + onView(withText(R.string.daily_refresh_tile_subtitle)).check(matches(isDisplayed())); + } + + @Test + public void testRotationDisabled_DoesNotRenderDailyRefreshTile() { + setActivityWithMockWallpapers(false /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + getActivity(); + + onView(withText(R.string.daily_refresh_tile_title)).check(doesNotExist()); + onView(withText(R.string.daily_refresh_tile_subtitle)).check(doesNotExist()); + } + + @Test + public void testClickDailyRefreshTile_ShowsStartRotationDialog() { + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + getActivity(); + + onView(withId(R.id.daily_refresh)).perform(click()); + + onView(withId(R.id.start_rotation_wifi_only_checkbox)) + .check(matches(isDisplayed())); + // WiFi-only option should be checked by default. + onView(withId(R.id.start_rotation_wifi_only_checkbox)) + .check(matches(isChecked())); + } + + @Test + public void testShowStartRotationDialog_WifiOnly_ClickOK_StartsRotation() throws Throwable { + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + getActivity(); + + setUpFragmentForTesting(); + TestWallpaperRotationInitializer + testWPRotationInitializer = (TestWallpaperRotationInitializer) + mTestCategory.getWallpaperRotationInitializer(); + assertFalse(testWPRotationInitializer.isRotationInitialized()); + + // Mock out the intent and response for the live wallpaper preview. + Matcher<Intent> expectedIntent = hasAction(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER); + intending(expectedIntent).respondWith(new ActivityResult(Activity.RESULT_OK, null)); + + onView(withId(R.id.daily_refresh)).perform(click()); + + onView(withText(android.R.string.ok)).perform(click()); + mActivityRule.runOnUiThread(() -> { + testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */); + assertTrue(testWPRotationInitializer.isRotationInitialized()); + assertTrue(testWPRotationInitializer.isWifiOnly()); + + // The activity should finish if starting a rotation was successful. + assertTrue(getActivity().isFinishing()); + }); + } + + @Test + public void testShowStartRotationDialog_WifiOnly_ClickOK_Fails_ShowsErrorDialog() { + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + getActivity(); + + setUpFragmentForTesting(); + TestWallpaperRotationInitializer + testWPRotationInitializer = (TestWallpaperRotationInitializer) + mTestCategory.getWallpaperRotationInitializer(); + assertFalse(testWPRotationInitializer.isRotationInitialized()); + + onView(withId(R.id.daily_refresh)).perform(click()); + onView(withText(android.R.string.ok)).perform(click()); + + testWPRotationInitializer.finishDownloadingFirstWallpaper(false /* isSuccessful */); + assertFalse(testWPRotationInitializer.isRotationInitialized()); + + // Error dialog should be shown with retry option. + onView(withText(R.string.start_rotation_error_message)).check(matches(isDisplayed())); + onView(withText(R.string.try_again)).check(matches(isDisplayed())); + } + + @Test + public void testStartRotation_WifiOnly_FailOnce_Retry() throws Throwable { + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + getActivity(); + + setUpFragmentForTesting(); + TestWallpaperRotationInitializer + testWPRotationInitializer = (TestWallpaperRotationInitializer) + mTestCategory.getWallpaperRotationInitializer(); + assertFalse(testWPRotationInitializer.isRotationInitialized()); + + onView(withId(R.id.daily_refresh)).perform(click()); + onView(withText(android.R.string.ok)).perform(click()); + + testWPRotationInitializer.finishDownloadingFirstWallpaper(false /* isSuccessful */); + assertFalse(testWPRotationInitializer.isRotationInitialized()); + + // Click try again to retry. + onView(withText(R.string.try_again)).perform(click()); + + mActivityRule.runOnUiThread(() -> { + testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */); + assertTrue(testWPRotationInitializer.isRotationInitialized()); + assertTrue(testWPRotationInitializer.isWifiOnly()); + }); + } + + @Test + public void testShowStartRotationDialog_TurnOffWifiOnly_ClickOK_StartsRotation() + throws Throwable { + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + getActivity(); + + setUpFragmentForTesting(); + TestWallpaperRotationInitializer + testWPRotationInitializer = (TestWallpaperRotationInitializer) + mTestCategory.getWallpaperRotationInitializer(); + assertFalse(testWPRotationInitializer.isRotationInitialized()); + + onView(withId(R.id.daily_refresh)).perform(click()); + // Click on WiFi-only option to toggle it off. + onView(withId(R.id.start_rotation_wifi_only_checkbox)).perform(click()); + onView(withText(android.R.string.ok)).perform(click()); + + mActivityRule.runOnUiThread(() -> { + testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */); + assertTrue(testWPRotationInitializer.isRotationInitialized()); + assertFalse(testWPRotationInitializer.isWifiOnly()); + }); + } + + @Test + public void testStartRotation_WifiOnly_FailOnce_Retry_ShouldStillHaveWifiTurnedOff() + throws Throwable { + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + getActivity(); + + setUpFragmentForTesting(); + TestWallpaperRotationInitializer + testWPRotationInitializer = (TestWallpaperRotationInitializer) + mTestCategory.getWallpaperRotationInitializer(); + assertFalse(testWPRotationInitializer.isRotationInitialized()); + + onView(withId(R.id.daily_refresh)).perform(click()); + // Click on WiFi-only option to toggle it off. + onView(withId(R.id.start_rotation_wifi_only_checkbox)).perform(click()); + onView(withText(android.R.string.ok)).perform(click()); + + testWPRotationInitializer.finishDownloadingFirstWallpaper(false /* isSuccessful */); + assertFalse(testWPRotationInitializer.isRotationInitialized()); + + // Click try again to retry. + onView(withText(R.string.try_again)).perform(click()); + + mActivityRule.runOnUiThread(() -> { + testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */); + assertTrue(testWPRotationInitializer.isRotationInitialized()); + assertFalse(testWPRotationInitializer.isWifiOnly()); + }); + } + + @Test + public void testShowStartRotationDialog_ClickCancel_DismissesDialog() { + setActivityWithMockWallpapers(true /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + getActivity(); + + setUpFragmentForTesting(); + TestWallpaperRotationInitializer + testWPRotationInitializer = (TestWallpaperRotationInitializer) + mTestCategory.getWallpaperRotationInitializer(); + assertFalse(testWPRotationInitializer.isRotationInitialized()); + + onView(withId(R.id.daily_refresh)).perform(click()); + onView(withId(R.id.start_rotation_wifi_only_checkbox)).check(matches(isDisplayed())); + // WiFi-only option should be checked by default. + onView(withId(R.id.start_rotation_wifi_only_checkbox)).check(matches(isChecked())); + + // Click "Cancel" to dismiss the dialog. + onView(withText(android.R.string.cancel)).perform(click()); + + // Rotation was not initialized and dialog is no longer visible. + assertFalse(testWPRotationInitializer.isRotationInitialized()); + onView(withId(R.id.start_rotation_wifi_only_checkbox)).check(doesNotExist()); + } + + /** + * Tests that when no title is present, a wallpaper tile's content description attribute is + * set to the first attribution. + */ + @Test + public void testWallpaperHasContentDescriptionFromAttribution() { + setActivityWithMockWallpapers(false /* isRotationEnabled */, + WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED); + IndividualPickerActivity activity = getActivity(); + + RecyclerView recyclerView = activity.findViewById(R.id.wallpaper_grid); + + RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(0); + assertEquals("Attribution 0", holder.itemView.findViewById(R.id.tile) + .getContentDescription()); + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestAlarmManagerWrapper.java b/tests/src/com/android/wallpaper/testing/TestAlarmManagerWrapper.java new file mode 100644 index 0000000..c9bbdd2 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestAlarmManagerWrapper.java @@ -0,0 +1,71 @@ +/* + * 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.PendingIntent; + +import com.android.wallpaper.module.AlarmManagerWrapper; + +/** + * Mock of {@link AlarmManagerWrapper}. + */ +public class TestAlarmManagerWrapper implements AlarmManagerWrapper { + + private int mExactAlarmSetCount; + private int mInexactAlarmSetCount; + private int mAlarmCanceledCount; + + private long mLastInexactTriggerAtMillis; + + @Override + public void set(int type, long triggerAtMillis, PendingIntent operation) { + mExactAlarmSetCount++; + } + + @Override + public void setWindow(int type, long windowStartMillis, long windowLengthMillis, + PendingIntent operation) { + mExactAlarmSetCount++; + } + + @Override + public void setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, + PendingIntent operation) { + mInexactAlarmSetCount++; + mLastInexactTriggerAtMillis = triggerAtMillis; + } + + @Override + public void cancel(PendingIntent operation) { + mAlarmCanceledCount++; + } + + public int getExactAlarmSetCount() { + return mExactAlarmSetCount; + } + + public int getInexactAlarmSetCount() { + return mInexactAlarmSetCount; + } + + public int getAlarmCanceledCount() { + return mAlarmCanceledCount; + } + + public long getLastInexactTriggerAtMillis() { + return mLastInexactTriggerAtMillis; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestAsset.java b/tests/src/com/android/wallpaper/testing/TestAsset.java new file mode 100644 index 0000000..368919a --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestAsset.java @@ -0,0 +1,99 @@ +/* + * 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.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Point; +import android.graphics.Rect; +import android.widget.ImageView; + +import androidx.annotation.Nullable; + +import com.android.wallpaper.asset.Asset; + + +/** + * Test implementation of Asset which blocks on Bitmap decoding operations. + */ +public final class TestAsset extends Asset { + + private Bitmap mBitmap; + private boolean mIsCorrupt; + + /** + * Constructs an asset underpinned by a 1x1 bitmap uniquely identifiable by the given pixel + * color. + * + * @param pixelColor Color of the asset's single pixel. + * @param isCorrupt Whether or not the asset is corrupt and fails to validly decode bitmaps and + * dimensions. + */ + public TestAsset(int pixelColor, boolean isCorrupt) { + mIsCorrupt = isCorrupt; + + if (!mIsCorrupt) { + mBitmap = Bitmap.createBitmap(1, 1, Config.ARGB_8888); + mBitmap.setPixel(0, 0, pixelColor); + } else { + mBitmap = null; + } + } + + @Override + public void decodeBitmap(int targetWidth, int targetHeight, BitmapReceiver receiver) { + receiver.onBitmapDecoded(mBitmap); + } + + @Override + public void decodeBitmapRegion(Rect unused, int targetWidth, int targetHeight, + BitmapReceiver receiver) { + receiver.onBitmapDecoded(mBitmap); + } + + @Override + public void decodeRawDimensions(Activity unused, DimensionsReceiver receiver) { + receiver.onDimensionsDecoded(mIsCorrupt ? null : new Point(1, 1)); + } + + @Override + public boolean supportsTiling() { + return false; + } + + @Override + public void loadDrawableWithTransition( + Context context, + ImageView imageView, + int transitionDurationMillis, + @Nullable DrawableLoadedListener drawableLoadedListener, + int placeholderColor) { + if (drawableLoadedListener != null) { + drawableLoadedListener.onDrawableLoaded(); + } + } + + /** Returns the bitmap synchronously. Convenience method for tests. */ + public Bitmap getBitmap() { + return mBitmap; + } + + public void setBitmap(Bitmap bitmap) { + mBitmap = bitmap; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestBitmapCropper.java b/tests/src/com/android/wallpaper/testing/TestBitmapCropper.java new file mode 100644 index 0000000..7daeeab --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestBitmapCropper.java @@ -0,0 +1,58 @@ +/* + * 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.graphics.Bitmap; +import android.graphics.Rect; + +import com.android.wallpaper.asset.Asset; +import com.android.wallpaper.asset.Asset.BitmapReceiver; +import com.android.wallpaper.module.BitmapCropper; + +/** + * Test double for BitmapCropper. + */ +public class TestBitmapCropper implements BitmapCropper { + + private boolean mFailNextCall; + + public TestBitmapCropper() { + mFailNextCall = false; + } + + @Override + public void cropAndScaleBitmap(Asset asset, float scale, Rect cropRect, + Callback callback) { + if (mFailNextCall) { + callback.onError(null /* throwable */); + return; + } + // Crop rect in pixels of source image. + Rect scaledCropRect = new Rect( + Math.round((float) cropRect.left / scale), + Math.round((float) cropRect.top / scale), + Math.round((float) cropRect.right / scale), + Math.round((float) cropRect.bottom / scale)); + + asset.decodeBitmapRegion(scaledCropRect, cropRect.width(), cropRect.height(), + new BitmapReceiver() { + @Override + public void onBitmapDecoded(Bitmap bitmap) { + callback.onBitmapCropped(bitmap); + } + }); + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestCategoryProvider.java b/tests/src/com/android/wallpaper/testing/TestCategoryProvider.java new file mode 100644 index 0000000..310840c --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestCategoryProvider.java @@ -0,0 +1,94 @@ +/* + * 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.os.Handler; + +import com.android.wallpaper.model.Category; +import com.android.wallpaper.model.CategoryProvider; +import com.android.wallpaper.model.CategoryReceiver; +import com.android.wallpaper.model.ImageCategory; +import com.android.wallpaper.model.WallpaperInfo; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test implementation of {@link CategoryProvider}. + */ +public class TestCategoryProvider implements CategoryProvider { + private final List<Category> mCategories; + + public TestCategoryProvider() { + Category category1 = new ImageCategory( + "My photos" /* title */, + "image_wallpapers" /* collection */, + 0 /* priority */); + + ArrayList<WallpaperInfo> wallpapers = new ArrayList<>(); + WallpaperInfo wallpaperInfo = new com.android.wallpaper.testing.TestWallpaperInfo(0); + wallpapers.add(wallpaperInfo); + Category category2 = new com.android.wallpaper.testing.TestWallpaperCategory( + "Test category", "init_collection", wallpapers, + 1 /* priority */); + + mCategories = new ArrayList<>(); + mCategories.add(category1); + mCategories.add(category2); + } + + @Override + public void fetchCategories(CategoryReceiver receiver, boolean forceRefresh) { + // Mimic real behavior by fetching asynchronously. + new Handler().post(new Runnable() { + @Override + public void run() { + List<Category> categories = getTestCategories(); + for (Category category : categories) { + receiver.onCategoryReceived(category); + } + receiver.doneFetchingCategories(); + } + }); + } + + @Override + public int getSize() { + return mCategories == null ? 0 : mCategories.size(); + } + + @Override + public Category getCategory(int index) { + return mCategories == null ? null : mCategories.get(index); + } + + @Override + public Category getCategory(String collectionId) { + Category category; + for (int i = 0; i < mCategories.size(); i++) { + category = mCategories.get(i); + if (category.getCollectionId().equals(collectionId)) { + return category; + } + } + return null; + } + + /** Returns a list of test Category objects used by this TestCategoryProvider. */ + public List<Category> getTestCategories() { + return mCategories; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestCurrentWallpaperInfoFactory.java b/tests/src/com/android/wallpaper/testing/TestCurrentWallpaperInfoFactory.java new file mode 100644 index 0000000..3e8c665 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestCurrentWallpaperInfoFactory.java @@ -0,0 +1,70 @@ +/* + * 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 com.android.wallpaper.compat.BuildCompat; +import com.android.wallpaper.model.WallpaperInfo; +import com.android.wallpaper.module.CurrentWallpaperInfoFactory; +import com.android.wallpaper.module.InjectorProvider; +import com.android.wallpaper.module.WallpaperRefresher; + +import java.util.List; + +/** + * Test double of {@link CurrentWallpaperInfoFactory}. + */ +public class TestCurrentWallpaperInfoFactory implements CurrentWallpaperInfoFactory { + + private WallpaperRefresher mRefresher; + + public TestCurrentWallpaperInfoFactory(Context context) { + mRefresher = InjectorProvider.getInjector().getWallpaperRefresher( + context.getApplicationContext()); + } + + @Override + public void createCurrentWallpaperInfos(final WallpaperInfoCallback callback, + boolean forceRefresh) { + mRefresher.refresh((homeWallpaperMetadata, lockWallpaperMetadata, presentationMode) -> { + + WallpaperInfo homeWallpaper = createTestWallpaperInfo( + homeWallpaperMetadata.getAttributions(), + homeWallpaperMetadata.getActionUrl(), + homeWallpaperMetadata.getCollectionId()); + + WallpaperInfo lockWallpaper = null; + if (lockWallpaperMetadata != null && BuildCompat.isAtLeastN()) { + lockWallpaper = createTestWallpaperInfo( + lockWallpaperMetadata.getAttributions(), + lockWallpaperMetadata.getActionUrl(), + lockWallpaperMetadata.getCollectionId()); + } + + callback.onWallpaperInfoCreated(homeWallpaper, lockWallpaper, presentationMode); + }); + } + + private static WallpaperInfo createTestWallpaperInfo(List<String> attributions, + String actionUrl, String collectionId) { + TestWallpaperInfo wallpaper = new TestWallpaperInfo(TestWallpaperInfo.COLOR_BLACK); + wallpaper.setAttributions(attributions); + wallpaper.setActionUrl(actionUrl); + wallpaper.setCollectionId(collectionId); + return wallpaper; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestExploreIntentChecker.java b/tests/src/com/android/wallpaper/testing/TestExploreIntentChecker.java new file mode 100644 index 0000000..dc1f1a8 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestExploreIntentChecker.java @@ -0,0 +1,44 @@ +/* + * 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.Intent; +import android.net.Uri; + +import com.android.wallpaper.module.ExploreIntentChecker; + +/** + * Test implementation of ExploreIntentChecker. + */ +public class TestExploreIntentChecker implements ExploreIntentChecker { + + private boolean mViewHandlerExists; + + public TestExploreIntentChecker() { + // True by default. + mViewHandlerExists = true; + } + + @Override + public void fetchValidActionViewIntent(Uri uri, IntentReceiver receiver) { + Intent intent = mViewHandlerExists ? new Intent(Intent.ACTION_VIEW, uri) : null; + receiver.onIntentReceived(intent); + } + + public void setViewHandlerExists(boolean exists) { + mViewHandlerExists = exists; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestFormFactorChecker.java b/tests/src/com/android/wallpaper/testing/TestFormFactorChecker.java new file mode 100644 index 0000000..ba77172 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestFormFactorChecker.java @@ -0,0 +1,41 @@ +/* + * 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 com.android.wallpaper.module.FormFactorChecker; + +/** + * Test implementation of {@code FormFactorChecker}. + */ +public class TestFormFactorChecker implements FormFactorChecker { + + @FormFactor + private int mFormFactor; + + public TestFormFactorChecker() { + mFormFactor = FORM_FACTOR_MOBILE; + } + + @FormFactor + @Override + public int getFormFactor() { + return mFormFactor; + } + + public void setFormFactor(@FormFactor int formFactor) { + mFormFactor = formFactor; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestInjector.java b/tests/src/com/android/wallpaper/testing/TestInjector.java new file mode 100644 index 0000000..0da755e --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestInjector.java @@ -0,0 +1,244 @@ +/* + * 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 androidx.fragment.app.Fragment; + +import com.android.wallpaper.compat.WallpaperManagerCompat; +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.DefaultLiveWallpaperInfoFactory; +import com.android.wallpaper.module.DrawableLayerResolver; +import com.android.wallpaper.module.ExploreIntentChecker; +import com.android.wallpaper.module.FormFactorChecker; +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.monitor.PerformanceMonitor; +import com.android.wallpaper.network.Requester; +import com.android.wallpaper.picker.ImagePreviewFragment; +import com.android.wallpaper.picker.individual.IndividualPickerFragment; + +/** + * 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 FormFactorChecker mFormFactorChecker; + 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 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 FormFactorChecker getFormFactorChecker(Context unused) { + if (mFormFactorChecker == null) { + mFormFactorChecker = new TestFormFactorChecker(); + } + return mFormFactorChecker; + } + + @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 testingModeEnabled) { + return ImagePreviewFragment.newInstance(wallpaperInfo, mode, 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 PerformanceMonitor getPerformanceMonitor() { + if (mPerformanceMonitor == null) { + mPerformanceMonitor = new TestPerformanceMonitor(); + } + return mPerformanceMonitor; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java b/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java new file mode 100644 index 0000000..f4434b1 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java @@ -0,0 +1,27 @@ +/* + * 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 com.android.wallpaper.module.LoggingOptInStatusProvider; + +/** Test implementation of {@link LoggingOptInStatusProvider}. */ +public class TestLoggingOptInStatusProvider implements LoggingOptInStatusProvider { + + @Override + public void fetchOptInValue(OptInValueReceiver receiver) { + receiver.onOptInValueReady(true /* optedIn */); + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestNetworkStatusNotifier.java b/tests/src/com/android/wallpaper/testing/TestNetworkStatusNotifier.java new file mode 100644 index 0000000..b09ee43 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestNetworkStatusNotifier.java @@ -0,0 +1,61 @@ +/* + * 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 com.android.wallpaper.module.NetworkStatusNotifier; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test implementation of {@link NetworkStatusNotifier} which enables clients to manually notify + * listeners of a network status change. + */ +public class TestNetworkStatusNotifier implements NetworkStatusNotifier { + + private List<Listener> mListeners; + @NetworkStatus + private int mNetworkStatus; + + public TestNetworkStatusNotifier() { + mListeners = new ArrayList<>(); + mNetworkStatus = NETWORK_CONNECTED; + } + + @Override + public int getNetworkStatus() { + return mNetworkStatus; + } + + @Override + public void registerListener(Listener listener) { + mListeners.add(listener); + listener.onNetworkChanged(mNetworkStatus); + } + + @Override + public void unregisterListener(Listener listener) { + mListeners.remove(listener); + } + + public void setAndNotifyNetworkStatus(@NetworkStatus int networkStatus) { + mNetworkStatus = networkStatus; + for (Listener listener : mListeners) { + listener.onNetworkChanged(mNetworkStatus); + } + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestPartnerProvider.java b/tests/src/com/android/wallpaper/testing/TestPartnerProvider.java new file mode 100644 index 0000000..fbdd0af --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestPartnerProvider.java @@ -0,0 +1,58 @@ +/* + * 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.res.Resources; + +import com.android.wallpaper.module.PartnerProvider; + +import java.io.File; + +/** + * Test implementation for PartnerProvider. + */ +public class TestPartnerProvider implements PartnerProvider { + private File mLegacyWallpaperDirectory; + + @Override + public Resources getResources() { + return null; + } + + @Override + public File getLegacyWallpaperDirectory() { + return mLegacyWallpaperDirectory; + } + + /** + * Sets the File to be returned by subsequent calls to getLegacyWallpaperDirectory(). + * + * @param dir The legacy wallpaper directory. + */ + public void setLegacyWallpaperDirectory(File dir) { + mLegacyWallpaperDirectory = dir; + } + + @Override + public String getPackageName() { + return null; + } + + @Override + public boolean shouldHideDefaultWallpaper() { + return false; + } +} diff --git a/src/com/android/wallpaper/module/LiveWallpaperStatusChecker.java b/tests/src/com/android/wallpaper/testing/TestPerformanceMonitor.java index 67d87f8..7fe3ade 100755..100644 --- a/src/com/android/wallpaper/module/LiveWallpaperStatusChecker.java +++ b/tests/src/com/android/wallpaper/testing/TestPerformanceMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * 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. @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.wallpaper.module; +package com.android.wallpaper.testing; + +import com.android.wallpaper.monitor.PerformanceMonitor; /** - * Reads whether the application's live wallpaper service is set to the device. + * No-op performance monitor for test. */ -public interface LiveWallpaperStatusChecker { +public class TestPerformanceMonitor implements PerformanceMonitor { - /** - * Returns whether the live wallpaper for daily wallpapers is set to the device. - */ - boolean isNoBackupImageWallpaperSet(); + @Override + public void recordFullResPreviewLoadedMemorySnapshot() { + } } diff --git a/tests/src/com/android/wallpaper/testing/TestSystemFeatureChecker.java b/tests/src/com/android/wallpaper/testing/TestSystemFeatureChecker.java new file mode 100644 index 0000000..775e693 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestSystemFeatureChecker.java @@ -0,0 +1,41 @@ +/* + * 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 com.android.wallpaper.module.SystemFeatureChecker; + +/** + * Test implementation of {@link SystemFeatureChecker}. + */ +public class TestSystemFeatureChecker implements SystemFeatureChecker { + + private boolean mHasTelephony; + + public TestSystemFeatureChecker() { + mHasTelephony = true; + } + + @Override + public boolean hasTelephony(Context context) { + return mHasTelephony; + } + + public void setHasTelephony(boolean hasTelephony) { + mHasTelephony = hasTelephony; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java b/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java new file mode 100644 index 0000000..9f0489e --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java @@ -0,0 +1,289 @@ +/* + * 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 com.android.wallpaper.module.UserEventLogger; +import com.android.wallpaper.module.WallpaperPersister.WallpaperPosition; + +/** + * Test implementation of {@link UserEventLogger}. + */ +public class TestUserEventLogger implements UserEventLogger { + + private int mNumDailyRefreshTurnedOnEvents; + private int mNumCurrentWallpaperPreviewedEvents; + private int mNumActionClickedEvents; + private int mNumIndividualWallpaperSelectedEvents; + private int mNumCategorySelectedEvents; + private int mNumWallpaperSetEvents; + private int mNumWallpaperSetResultEvents; + private String mLastCollectionId; + private String mLastWallpaperId; + @WallpaperSetResult + private int mLastWallpaperSetResult; + private int mLastDailyRotationHour; + private int mNum1DayActiveLogs; + private int mNum7DayActiveLogs; + private int mNum14DayActiveLogs; + private int mNum28DayActiveLogs; + private int mLastDailyWallpaperRotationStatus; + private int mNumDaysDailyRotationFailed; + private int mNumDaysDailyRotationNotAttempted; + private int mLastDailyWallpaperUpdateResult; + private int mStandalonePreviewLaunches; + private int mNumRestores; + @WallpaperPosition + private int mWallpaperPosition; + + public TestUserEventLogger() { + mLastDailyRotationHour = -1; + mLastDailyWallpaperRotationStatus = -1; + mNumDaysDailyRotationFailed = -1; + mNumDaysDailyRotationNotAttempted = -1; + } + + @Override + public void logResumed(boolean provisioned, boolean wallpaper) { + + } + + @Override + public void logStopped() { + + } + + @Override + public void logAppLaunched() { + // Do nothing. + } + + @Override + public void logDailyRefreshTurnedOn() { + mNumDailyRefreshTurnedOnEvents++; + } + + public int getNumDailyRefreshTurnedOnEvents() { + return mNumDailyRefreshTurnedOnEvents; + } + + @Override + public void logCurrentWallpaperPreviewed() { + mNumCurrentWallpaperPreviewedEvents++; + } + + @Override + public void logActionClicked(String collectionId, int actionLabelResId) { + mNumActionClickedEvents++; + mLastCollectionId = collectionId; + } + + public int getNumCurrentWallpaperPreviewedEvents() { + return mNumCurrentWallpaperPreviewedEvents; + } + + public int getNumActionClickedEvents() { + return mNumActionClickedEvents; + } + + @Override + public void logIndividualWallpaperSelected(String collectionId) { + mNumIndividualWallpaperSelectedEvents++; + mLastCollectionId = collectionId; + } + + public int getNumIndividualWallpaperSelectedEvents() { + return mNumIndividualWallpaperSelectedEvents; + } + + @Override + public void logCategorySelected(String collectionId) { + mNumCategorySelectedEvents++; + mLastCollectionId = collectionId; + } + + public int getNumCategorySelectedEvents() { + return mNumCategorySelectedEvents; + } + + @Override + public void logWallpaperSet(String collectionId, String wallpaperId) { + mNumWallpaperSetEvents++; + mLastCollectionId = collectionId; + mLastWallpaperId = wallpaperId; + } + + @Override + public void logWallpaperSetResult(@WallpaperSetResult int result) { + mNumWallpaperSetResultEvents++; + mLastWallpaperSetResult = result; + } + + @Override + public void logWallpaperSetFailureReason(@WallpaperSetFailureReason int reason) { + // No-op + } + + + @Override + public void logNumDailyWallpaperRotationsInLastWeek() { + // No-op + } + + @Override + public void logNumDailyWallpaperRotationsPreviousDay() { + // No-op + } + + @Override + public void logDailyWallpaperRotationHour(int hour) { + mLastDailyRotationHour = hour; + } + + @Override + public void logDailyWallpaperDecodes(boolean decodes) { + // No-op + } + + @Override + public void logRefreshDailyWallpaperButtonClicked() { + // No-op + } + + @Override + public void logDailyWallpaperRotationStatus(int status) { + mLastDailyWallpaperRotationStatus = status; + } + + @Override + public void logDailyWallpaperSetNextWallpaperResult(@DailyWallpaperUpdateResult int result) { + mLastDailyWallpaperUpdateResult = result; + } + + @Override + public void logDailyWallpaperSetNextWallpaperCrash(@DailyWallpaperUpdateCrash int crash) { + // No-op + } + + @Override + public void logNumDaysDailyRotationFailed(int days) { + mNumDaysDailyRotationFailed = days; + } + + @Override + public void logDailyWallpaperMetadataRequestFailure( + @DailyWallpaperMetadataFailureReason int reason) { + // No-op + } + + @Override + public void logNumDaysDailyRotationNotAttempted(int days) { + mNumDaysDailyRotationNotAttempted = days; + } + + @Override + public void logStandalonePreviewLaunched() { + mStandalonePreviewLaunches++; + } + + @Override + public void logStandalonePreviewImageUriHasReadPermission(boolean isReadPermissionGranted) { + // No-op + } + + @Override + public void logStandalonePreviewStorageDialogApproved(boolean isApproved) { + // No-op + } + + @Override + public void logWallpaperPresentationMode() { + // No-op + } + + @Override + public void logRestored() { + mNumRestores++; + } + + public int getNumWallpaperSetEvents() { + return mNumWallpaperSetEvents; + } + + public String getLastCollectionId() { + return mLastCollectionId; + } + + public String getLastWallpaperId() { + return mLastWallpaperId; + } + + public int getNumWallpaperSetResultEvents() { + return mNumWallpaperSetResultEvents; + } + + @WallpaperSetResult + public int getLastWallpaperSetResult() { + return mLastWallpaperSetResult; + } + + public int getLastDailyRotationHour() { + return mLastDailyRotationHour; + } + + public int getNum1DayActiveLogs() { + return mNum1DayActiveLogs; + } + + public int getNum7DayActiveLogs() { + return mNum7DayActiveLogs; + } + + public int getNum14DayActiveLogs() { + return mNum14DayActiveLogs; + } + + public int getNum28DayActiveLogs() { + return mNum28DayActiveLogs; + } + + public int getLastDailyWallpaperRotationStatus() { + return mLastDailyWallpaperRotationStatus; + } + + public int getNumDaysDailyRotationFailed() { + return mNumDaysDailyRotationFailed; + } + + public int getNumDaysDailyRotationNotAttempted() { + return mNumDaysDailyRotationNotAttempted; + } + + public int getLastDailyWallpaperUpdateResult() { + return mLastDailyWallpaperUpdateResult; + } + + public int getStandalonePreviewLaunches() { + return mStandalonePreviewLaunches; + } + + public int getNumRestores() { + return mNumRestores; + } + + public int getWallpaperPosition() { + return mWallpaperPosition; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperCategory.java b/tests/src/com/android/wallpaper/testing/TestWallpaperCategory.java new file mode 100644 index 0000000..c69ecf8 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestWallpaperCategory.java @@ -0,0 +1,59 @@ +/* + * 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 com.android.wallpaper.model.WallpaperCategory; +import com.android.wallpaper.model.WallpaperInfo; +import com.android.wallpaper.model.WallpaperRotationInitializer; +import com.android.wallpaper.model.WallpaperRotationInitializer.RotationInitializationState; + +import java.util.List; + +/** + * Test-only subclass of {@link WallpaperCategory} which can be configured to provide a test double + * {@link WallpaperRotationInitializer}. + */ +public class TestWallpaperCategory extends WallpaperCategory { + private boolean mIsRotationEnabled; + private TestWallpaperRotationInitializer mWallpaperRotationInitializer; + + public TestWallpaperCategory(String title, String collectionId, List<WallpaperInfo> wallpapers, + int priority) { + super(title, collectionId, wallpapers, priority); + mIsRotationEnabled = false; + mWallpaperRotationInitializer = new TestWallpaperRotationInitializer(); + } + + @Override + public WallpaperRotationInitializer getWallpaperRotationInitializer() { + return (mIsRotationEnabled) ? mWallpaperRotationInitializer : null; + } + + @Override + public List<WallpaperInfo> getMutableWallpapers() { + return super.getMutableWallpapers(); + } + + /** Sets whether rotation is enabled on this category. */ + public void setIsRotationEnabled(boolean isRotationEnabled) { + mIsRotationEnabled = isRotationEnabled; + } + + public void setRotationInitializationState(@RotationInitializationState int rotationState) { + mWallpaperRotationInitializer.setRotationInitializationState(rotationState); + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperInfo.java b/tests/src/com/android/wallpaper/testing/TestWallpaperInfo.java new file mode 100644 index 0000000..c0ccee6 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestWallpaperInfo.java @@ -0,0 +1,228 @@ +/* + * 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.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.wallpaper.asset.Asset; +import com.android.wallpaper.model.InlinePreviewIntentFactory; +import com.android.wallpaper.model.WallpaperInfo; + +import java.util.Arrays; +import java.util.List; + +/** + * Test model object for a wallpaper coming from local drawable resources. + */ +public class TestWallpaperInfo extends WallpaperInfo { + public static final int COLOR_BLACK = 0; + public static final Parcelable.Creator<TestWallpaperInfo> CREATOR = + new Parcelable.Creator<TestWallpaperInfo>() { + @Override + public TestWallpaperInfo createFromParcel(Parcel in) { + return new TestWallpaperInfo(in); + } + + @Override + public TestWallpaperInfo[] newArray(int size) { + return new TestWallpaperInfo[size]; + } + }; + private int mPixelColor; + private TestAsset mAsset; + private TestAsset mThumbAsset; + private List<String> mAttributions; + private android.app.WallpaperInfo mWallpaperComponent; + private String mActionUrl; + private String mBaseImageUrl; + private String mCollectionId; + private String mWallpaperId; + private boolean mIsAssetCorrupt; + private int mBackupPermission; + + /** Constructs a test WallpaperInfo object representing a 1x1 wallpaper of the given color. */ + public TestWallpaperInfo(int pixelColor) { + this(pixelColor, "test-wallpaper"); + } + + /** Constructs a test WallpaperInfo object representing a 1x1 wallpaper of the given color. */ + public TestWallpaperInfo(int pixelColor, String id) { + mPixelColor = pixelColor; + mAttributions = Arrays.asList("Test wallpaper"); + mWallpaperComponent = null; + mIsAssetCorrupt = false; + mBackupPermission = BACKUP_ALLOWED; + mWallpaperId = id; + } + + private TestWallpaperInfo(Parcel in) { + mPixelColor = in.readInt(); + mAttributions = in.createStringArrayList(); + mActionUrl = in.readString(); + mBaseImageUrl = in.readString(); + mCollectionId = in.readString(); + mWallpaperId = in.readString(); + mIsAssetCorrupt = in.readInt() == 1; + mBackupPermission = in.readInt(); + } + + @Override + public Drawable getOverlayIcon(Context context) { + return null; + } + + @Override + public List<String> getAttributions(Context context) { + return mAttributions; + } + + /** + * Override default "Test wallpaper" attributions for testing. + */ + public void setAttributions(List<String> attributions) { + mAttributions = attributions; + } + + @Override + public String getActionUrl(Context unused) { + return mActionUrl; + } + + /** Sets the action URL for this wallpaper. */ + public void setActionUrl(String actionUrl) { + mActionUrl = actionUrl; + } + + @Override + public String getBaseImageUrl() { + return mBaseImageUrl; + } + + /** Sets the base image URL for this wallpaper. */ + public void setBaseImageUrl(String baseImageUrl) { + mBaseImageUrl = baseImageUrl; + } + + @Override + public String getCollectionId(Context unused) { + return mCollectionId; + } + + /** Sets the collection ID for this wallpaper. */ + public void setCollectionId(String collectionId) { + mCollectionId = collectionId; + } + + @Override + public String getWallpaperId() { + return mWallpaperId; + } + + /** Sets the ID for this wallpaper. */ + public void setWallpaperId(String wallpaperId) { + mWallpaperId = wallpaperId; + } + + @Override + public Asset getAsset(Context context) { + if (mAsset == null) { + mAsset = new TestAsset(mPixelColor, mIsAssetCorrupt); + } + return mAsset; + } + + @Override + public Asset getThumbAsset(Context context) { + if (mThumbAsset == null) { + mThumbAsset = new TestAsset(mPixelColor, mIsAssetCorrupt); + } + return mThumbAsset; + } + + @Override + public void showPreview(Activity srcActivity, + InlinePreviewIntentFactory inlinePreviewIntentFactory, int requestCode) { + srcActivity.startActivityForResult( + inlinePreviewIntentFactory.newIntent(srcActivity, this), requestCode); + } + + @Override + @BackupPermission + public int getBackupPermission() { + return mBackupPermission; + } + + public void setBackupPermission(@BackupPermission int backupPermission) { + mBackupPermission = backupPermission; + } + + @Override + public android.app.WallpaperInfo getWallpaperComponent() { + return mWallpaperComponent; + } + + public void setWallpaperComponent(android.app.WallpaperInfo wallpaperComponent) { + mWallpaperComponent = wallpaperComponent; + } + + /** + * Simulates that the {@link Asset} instances returned by calls to #getAsset and #getThumbAsset + * on + * this object are "corrupt" and will fail to perform decode operations such as #decodeBitmap, + * #decodeBitmapRegion, #decodeRawDimensions, etc (these methods will call their callbacks with + * null instead of meaningful objects). + */ + public void corruptAssets() { + mIsAssetCorrupt = true; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeInt(mPixelColor); + parcel.writeStringList(mAttributions); + parcel.writeString(mActionUrl); + parcel.writeString(mBaseImageUrl); + parcel.writeString(mCollectionId); + parcel.writeString(mWallpaperId); + parcel.writeInt(mIsAssetCorrupt ? 1 : 0); + parcel.writeInt(mBackupPermission); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof TestWallpaperInfo) { + return mPixelColor == ((TestWallpaperInfo) object).mPixelColor; + } + return false; + } + + @Override + public int hashCode() { + return mPixelColor; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperManagerCompat.java b/tests/src/com/android/wallpaper/testing/TestWallpaperManagerCompat.java new file mode 100644 index 0000000..69f6839 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestWallpaperManagerCompat.java @@ -0,0 +1,135 @@ +/* + * 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.WallpaperManager; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import com.android.wallpaper.compat.WallpaperManagerCompat; + +import java.io.IOException; +import java.io.InputStream; + +/** Test double for {@link WallpaperManagerCompat}. */ +public class TestWallpaperManagerCompat extends WallpaperManagerCompat { + private static final String TAG = "TestWPManagerCompat"; + + ParcelFileDescriptor mSystemParcelFd; + ParcelFileDescriptor mLockParcelFd; + int mHomeWallpaperId = 0; + int mLockWallpaperId = 0; + + private boolean mAllowBackup; + private Context mAppContext; + private Drawable mTestDrawable; + + public TestWallpaperManagerCompat(Context appContext) { + mAppContext = appContext; + mAllowBackup = true; + } + + @Override + public int setStream(InputStream stream, Rect visibleCropHint, boolean allowBackup, + int whichWallpaper) throws IOException { + mAllowBackup = allowBackup; + return ++mHomeWallpaperId; + } + + @Override + public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, + int whichWallpaper) throws IOException { + mAllowBackup = allowBackup; + return ++mLockWallpaperId; + } + + @Override + public ParcelFileDescriptor getWallpaperFile(int whichWallpaper) { + if (whichWallpaper == WallpaperManagerCompat.FLAG_SYSTEM) { + return mSystemParcelFd; + } else if (whichWallpaper == WallpaperManagerCompat.FLAG_LOCK) { + return mLockParcelFd; + } else { + // :( + return null; + } + } + + @Override + public Drawable getDrawable() { + if (mTestDrawable != null) { + return mTestDrawable; + } + + // Retrieve WallpaperManager using Context#getSystemService instead of + // WallpaperManager#getInstance so it can be mocked out in test. + WallpaperManager wallpaperManager = + (WallpaperManager) mAppContext.getSystemService(Context.WALLPAPER_SERVICE); + return wallpaperManager.getDrawable(); + } + + @Override + public int getWallpaperId(@WallpaperLocation int whichWallpaper) { + switch (whichWallpaper) { + case WallpaperManagerCompat.FLAG_SYSTEM: + return mHomeWallpaperId; + case WallpaperManagerCompat.FLAG_LOCK: + return mLockWallpaperId; + default: + throw new IllegalArgumentException( + "Wallpaper location must be one of FLAG_SYSTEM or " + + "FLAG_LOCK but the value " + whichWallpaper + " was provided."); + } + } + + public void setWallpaperFile(int whichWallpaper, ParcelFileDescriptor file) { + if (whichWallpaper == WallpaperManagerCompat.FLAG_SYSTEM) { + mSystemParcelFd = file; + } else if (whichWallpaper == WallpaperManagerCompat.FLAG_LOCK) { + mLockParcelFd = file; + } else { + Log.e(TAG, "Called setWallpaperFile without a valid distinct 'which' argument."); + } + } + + public void setWallpaperId(@WallpaperLocation int whichWallpaper, int wallpaperId) { + switch (whichWallpaper) { + case WallpaperManagerCompat.FLAG_SYSTEM: + mHomeWallpaperId = wallpaperId; + break; + case WallpaperManagerCompat.FLAG_LOCK: + mLockWallpaperId = wallpaperId; + break; + default: + throw new IllegalArgumentException( + "Wallpaper location must be one of FLAG_SYSTEM or " + + "FLAG_LOCK but the value " + whichWallpaper + " was provided."); + } + } + + public void setDrawable(Drawable drawable) { + mTestDrawable = drawable; + } + + /** Returns whether backup is allowed for the last set wallpaper. */ + public boolean isBackupAllowed() { + return mAllowBackup; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperPersister.java b/tests/src/com/android/wallpaper/testing/TestWallpaperPersister.java new file mode 100644 index 0000000..5fc9421 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestWallpaperPersister.java @@ -0,0 +1,223 @@ +/* + * 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.graphics.Bitmap; +import android.graphics.Rect; + +import androidx.annotation.Nullable; + +import com.android.wallpaper.asset.Asset; +import com.android.wallpaper.asset.Asset.BitmapReceiver; +import com.android.wallpaper.model.WallpaperInfo; +import com.android.wallpaper.module.InjectorProvider; +import com.android.wallpaper.module.WallpaperChangedNotifier; +import com.android.wallpaper.module.WallpaperPersister; +import com.android.wallpaper.module.WallpaperPreferences; + +import java.util.List; + +/** + * Test double for {@link WallpaperPersister}. + */ +public class TestWallpaperPersister implements WallpaperPersister { + + private Context mAppContext; + private WallpaperPreferences mPrefs; + private WallpaperChangedNotifier mWallpaperChangedNotifier; + private Bitmap mCurrentHomeWallpaper; + private Bitmap mCurrentLockWallpaper; + private Bitmap mPendingHomeWallpaper; + private Bitmap mPendingLockWallpaper; + private List<String> mHomeAttributions; + private String mHomeActionUrl; + @Destination + private int mDestination; + private WallpaperPersister.SetWallpaperCallback mCallback; + private boolean mFailNextCall; + private Rect mCropRect; + private float mScale; + @WallpaperPosition + private int mWallpaperPosition; + + public TestWallpaperPersister(Context appContext) { + mAppContext = appContext; + mPrefs = InjectorProvider.getInjector().getPreferences(appContext); + mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance(); + + mCurrentHomeWallpaper = null; + mCurrentLockWallpaper = null; + mPendingHomeWallpaper = null; + mPendingLockWallpaper = null; + mFailNextCall = false; + mScale = -1.0f; + } + + @Override + public void setIndividualWallpaper(final WallpaperInfo wallpaperInfo, Asset asset, + @Nullable final Rect cropRect, final float scale, final @Destination int destination, + final WallpaperPersister.SetWallpaperCallback callback) { + asset.decodeBitmap(50, 50, bitmap -> { + if (destination == DEST_HOME_SCREEN || destination == DEST_BOTH) { + mPendingHomeWallpaper = bitmap; + mPrefs.setHomeWallpaperAttributions(wallpaperInfo.getAttributions(mAppContext)); + mPrefs.setWallpaperPresentationMode( + WallpaperPreferences.PRESENTATION_MODE_STATIC); + mPrefs.setHomeWallpaperRemoteId(wallpaperInfo.getWallpaperId()); + } + if (destination == DEST_LOCK_SCREEN || destination == DEST_BOTH) { + mPendingLockWallpaper = bitmap; + mPrefs.setLockWallpaperAttributions(wallpaperInfo.getAttributions(mAppContext)); + } + mDestination = destination; + mCallback = callback; + mCropRect = cropRect; + mScale = scale; + }); + } + + @Override + public void setIndividualWallpaperWithPosition(Activity activity, WallpaperInfo wallpaper, + @WallpaperPosition int wallpaperPosition, SetWallpaperCallback callback) { + wallpaper.getAsset(activity).decodeBitmap(50, 50, new BitmapReceiver() { + @Override + public void onBitmapDecoded(@Nullable Bitmap bitmap) { + mPendingHomeWallpaper = bitmap; + mPrefs.setHomeWallpaperAttributions(wallpaper.getAttributions(mAppContext)); + mPrefs.setHomeWallpaperBaseImageUrl(wallpaper.getBaseImageUrl()); + mPrefs.setHomeWallpaperActionUrl(wallpaper.getActionUrl(mAppContext)); + mPrefs.setHomeWallpaperCollectionId(wallpaper.getCollectionId(mAppContext)); + mPrefs.setHomeWallpaperRemoteId(wallpaper.getWallpaperId()); + mPrefs.setWallpaperPresentationMode(WallpaperPreferences.PRESENTATION_MODE_STATIC); + mPendingLockWallpaper = bitmap; + mPrefs.setLockWallpaperAttributions(wallpaper.getAttributions(mAppContext)); + + mDestination = WallpaperPersister.DEST_BOTH; + mCallback = callback; + mWallpaperPosition = wallpaperPosition; + } + }); + } + + @Override + public boolean setWallpaperInRotation(Bitmap wallpaperBitmap, List<String> attributions, + int actionLabelRes, int actionIconRes, String actionUrl, String collectionId) { + if (mFailNextCall) { + return false; + } + + mCurrentHomeWallpaper = wallpaperBitmap; + mCurrentLockWallpaper = wallpaperBitmap; + mHomeAttributions = attributions; + mHomeActionUrl = actionUrl; + return true; + } + + @Override + public int setWallpaperBitmapInNextRotation(Bitmap wallpaperBitmap) { + mCurrentHomeWallpaper = wallpaperBitmap; + mCurrentLockWallpaper = wallpaperBitmap; + return 1; + } + + @Override + public boolean finalizeWallpaperForNextRotation(List<String> attributions, String actionUrl, + int actionLabelRes, int actionIconRes, String collectionId, int wallpaperId) { + mHomeAttributions = attributions; + mHomeActionUrl = actionUrl; + return true; + } + + /** Returns mock system wallpaper bitmap. */ + public Bitmap getCurrentHomeWallpaper() { + return mCurrentHomeWallpaper; + } + + /** Returns mock lock screen wallpaper bitmap. */ + public Bitmap getCurrentLockWallpaper() { + return mCurrentLockWallpaper; + } + + /** Returns mock home attributions. */ + public List<String> getHomeAttributions() { + return mHomeAttributions; + } + + /** Returns the home wallpaper action URL. */ + public String getHomeActionUrl() { + return mHomeActionUrl; + } + + /** Returns the Destination a wallpaper was most recently set on. */ + @Destination + public int getLastDestination() { + return mDestination; + } + + /** + * Sets whether the next "set wallpaper" operation should fail or succeed. + */ + public void setFailNextCall(Boolean failNextCall) { + mFailNextCall = failNextCall; + } + + /** + * Implemented so synchronous test methods can control the completion of what would otherwise be + * an asynchronous operation. + */ + public void finishSettingWallpaper() { + if (mFailNextCall) { + mCallback.onError(null /* throwable */); + } else { + if (mDestination == DEST_HOME_SCREEN || mDestination == DEST_BOTH) { + mCurrentHomeWallpaper = mPendingHomeWallpaper; + mPendingHomeWallpaper = null; + } + if (mDestination == DEST_LOCK_SCREEN || mDestination == DEST_BOTH) { + mCurrentLockWallpaper = mPendingLockWallpaper; + mPendingLockWallpaper = null; + } + mCallback.onSuccess(); + mWallpaperChangedNotifier.notifyWallpaperChanged(); + } + } + + @Override + public void setWallpaperInfoInPreview(WallpaperInfo wallpaperInfo) { + } + + @Override + public void onLiveWallpaperSet() { + } + + /** Returns the last requested wallpaper bitmap scale. */ + public float getScale() { + return mScale; + } + + /** Returns the last requested wallpaper crop. */ + public Rect getCropRect() { + return mCropRect; + } + + /** Returns the last selected wallpaper position option. */ + @WallpaperPosition + public int getWallpaperPosition() { + return mWallpaperPosition; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperPreferences.java b/tests/src/com/android/wallpaper/testing/TestWallpaperPreferences.java new file mode 100644 index 0000000..715e81b --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestWallpaperPreferences.java @@ -0,0 +1,457 @@ +/* + * 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 androidx.annotation.Nullable; + +import com.android.wallpaper.module.WallpaperPreferences; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Test implementation of the WallpaperPreferences interface. Just keeps prefs in memory. + */ +public class TestWallpaperPreferences implements WallpaperPreferences { + + @PresentationMode + private int mWallpaperPresentationMode; + + private List<String> mHomeScreenAttributions; + private long mHomeScreenBitmapHashCode; + private int mHomeWallpaperManagerId; + private String mHomeScreenPackageName; + private String mHomeActionUrl; + private String mHomeBaseImageUrl; + private String mHomeCollectionId; + private String mHomeWallpaperRemoteId; + + private List<String> mLockScreenAttributions; + private long mLockScreenBitmapHashCode; + private int mLockWallpaperManagerId; + private String mLockActionUrl; + private String mLockCollectionId; + + private List<Long> mDailyRotations; + private long mDailyWallpaperEnabledTimestamp; + private long mLastDailyLogTimestamp; + private long mLastAppActiveTimestamp; + private int mLastDailyWallpaperRotationStatus; + private long mLastDailyWallpaperRotationStatusTimestamp; + private long mLastSyncTimestamp; + @PendingWallpaperSetStatus + private int mPendingWallpaperSetStatus; + @PendingDailyWallpaperUpdateStatus + private int mPendingDailyWallpaperUpdateStatus; + private int mNumDaysDailyRotationFailed; + private int mNumDaysDailyRotationNotAttempted; + private int mHomeWallpaperActionLabelRes; + private int mHomeWallpaperActionIconRes; + private int mLockWallpaperActionLabelRes; + private int mLockWallpaperActionIconRes; + + public TestWallpaperPreferences() { + mWallpaperPresentationMode = WallpaperPreferences.PRESENTATION_MODE_STATIC; + mHomeScreenAttributions = Arrays.asList("Android wallpaper"); + mHomeScreenBitmapHashCode = 0; + mHomeWallpaperManagerId = 0; + + mLockScreenAttributions = Arrays.asList("Android wallpaper"); + mLockScreenBitmapHashCode = 0; + mLockWallpaperManagerId = 0; + + mDailyRotations = new ArrayList<>(); + mDailyWallpaperEnabledTimestamp = -1; + mLastDailyLogTimestamp = -1; + mLastDailyWallpaperRotationStatus = -1; + mLastDailyWallpaperRotationStatusTimestamp = 0; + mLastSyncTimestamp = 0; + mPendingWallpaperSetStatus = WALLPAPER_SET_NOT_PENDING; + } + + @Override + public int getWallpaperPresentationMode() { + return mWallpaperPresentationMode; + } + + @Override + public void setWallpaperPresentationMode(@PresentationMode int presentationMode) { + mWallpaperPresentationMode = presentationMode; + } + + @Override + public List<String> getHomeWallpaperAttributions() { + return mHomeScreenAttributions; + } + + @Override + public void setHomeWallpaperAttributions(List<String> attributions) { + mHomeScreenAttributions = attributions; + } + + @Override + public String getHomeWallpaperActionUrl() { + return mHomeActionUrl; + } + + @Override + public void setHomeWallpaperActionUrl(String actionUrl) { + mHomeActionUrl = actionUrl; + } + + @Override + public int getHomeWallpaperActionLabelRes() { + return mHomeWallpaperActionLabelRes; + } + + @Override + public void setHomeWallpaperActionLabelRes(int resId) { + mHomeWallpaperActionLabelRes = resId; + } + + @Override + public int getHomeWallpaperActionIconRes() { + return mHomeWallpaperActionIconRes; + } + + @Override + public void setHomeWallpaperActionIconRes(int resId) { + mHomeWallpaperActionIconRes = resId; + } + + @Override + public String getHomeWallpaperBaseImageUrl() { + return mHomeBaseImageUrl; + } + + @Override + public void setHomeWallpaperBaseImageUrl(String baseImageUrl) { + mHomeBaseImageUrl = baseImageUrl; + } + + @Override + public String getHomeWallpaperCollectionId() { + return mHomeCollectionId; + } + + @Override + public void setHomeWallpaperCollectionId(String collectionId) { + mHomeCollectionId = collectionId; + } + + @Override + public String getHomeWallpaperBackingFileName() { + return null; + } + + @Override + public void setHomeWallpaperBackingFileName(String fileName) { + + } + + @Override + public void clearHomeWallpaperMetadata() { + mHomeScreenAttributions = null; + mWallpaperPresentationMode = WallpaperPreferences.PRESENTATION_MODE_STATIC; + mHomeScreenBitmapHashCode = 0; + mHomeScreenPackageName = null; + mHomeWallpaperManagerId = 0; + } + + @Override + public long getHomeWallpaperHashCode() { + return mHomeScreenBitmapHashCode; + } + + @Override + public void setHomeWallpaperHashCode(long hashCode) { + mHomeScreenBitmapHashCode = hashCode; + } + + @Override + public String getHomeWallpaperPackageName() { + return mHomeScreenPackageName; + } + + @Override + public void setHomeWallpaperPackageName(String packageName) { + mHomeScreenPackageName = packageName; + } + + @Override + public int getHomeWallpaperManagerId() { + return mHomeWallpaperManagerId; + } + + @Override + public void setHomeWallpaperManagerId(int homeWallpaperId) { + mHomeWallpaperManagerId = homeWallpaperId; + } + + @Override + public String getHomeWallpaperRemoteId() { + return mHomeWallpaperRemoteId; + } + + @Override + public void setHomeWallpaperRemoteId(String wallpaperRemoteId) { + mHomeWallpaperRemoteId = wallpaperRemoteId; + } + + @Override + public List<String> getLockWallpaperAttributions() { + return mLockScreenAttributions; + } + + @Override + public void setLockWallpaperAttributions(List<String> attributions) { + mLockScreenAttributions = attributions; + } + + @Override + public String getLockWallpaperActionUrl() { + return mLockActionUrl; + } + + @Override + public void setLockWallpaperActionUrl(String actionUrl) { + mLockActionUrl = actionUrl; + } + + @Override + public int getLockWallpaperActionLabelRes() { + return mLockWallpaperActionLabelRes; + } + + @Override + public void setLockWallpaperActionLabelRes(int resId) { + mLockWallpaperActionLabelRes = resId; + } + + @Override + public int getLockWallpaperActionIconRes() { + return mLockWallpaperActionIconRes; + } + + @Override + public void setLockWallpaperActionIconRes(int resId) { + mLockWallpaperActionIconRes = resId; + } + + @Override + public String getLockWallpaperCollectionId() { + return mLockCollectionId; + } + + @Override + public void setLockWallpaperCollectionId(String collectionId) { + mLockCollectionId = collectionId; + } + + @Override + public String getLockWallpaperBackingFileName() { + return null; + } + + @Override + public void setLockWallpaperBackingFileName(String fileName) { + + } + + @Override + public void clearLockWallpaperMetadata() { + mLockScreenAttributions = null; + mLockScreenBitmapHashCode = 0; + mLockWallpaperManagerId = 0; + } + + @Override + public long getLockWallpaperHashCode() { + return mLockScreenBitmapHashCode; + } + + @Override + public void setLockWallpaperHashCode(long hashCode) { + mLockScreenBitmapHashCode = hashCode; + } + + @Override + public int getLockWallpaperId() { + return mLockWallpaperManagerId; + } + + @Override + public void setLockWallpaperId(int lockWallpaperId) { + mLockWallpaperManagerId = lockWallpaperId; + } + + @Override + public void addDailyRotation(long timestamp) { + mDailyRotations.add(timestamp); + } + + @Override + public long getLastDailyRotationTimestamp() { + if (mDailyRotations.size() == 0) { + return -1; + } + + return mDailyRotations.get(mDailyRotations.size() - 1); + } + + @Override + public List<Long> getDailyRotationsInLastWeek() { + return mDailyRotations; + } + + @Nullable + @Override + public List<Long> getDailyRotationsPreviousDay() { + return null; + } + + @Override + public long getDailyWallpaperEnabledTimestamp() { + return mDailyWallpaperEnabledTimestamp; + } + + @Override + public void setDailyWallpaperEnabledTimestamp(long timestamp) { + mDailyWallpaperEnabledTimestamp = timestamp; + } + + @Override + public void clearDailyRotations() { + mDailyRotations.clear(); + } + + @Override + public long getLastDailyLogTimestamp() { + return mLastDailyLogTimestamp; + } + + @Override + public void setLastDailyLogTimestamp(long timestamp) { + mLastDailyLogTimestamp = timestamp; + } + + @Override + public long getLastAppActiveTimestamp() { + return mLastAppActiveTimestamp; + } + + @Override + public void setLastAppActiveTimestamp(long timestamp) { + mLastAppActiveTimestamp = timestamp; + } + + @Override + public void setDailyWallpaperRotationStatus(int status, long timestamp) { + mLastDailyWallpaperRotationStatus = status; + mLastDailyWallpaperRotationStatusTimestamp = timestamp; + } + + @Override + public int getDailyWallpaperLastRotationStatus() { + return mLastDailyWallpaperRotationStatus; + } + + @Override + public long getDailyWallpaperLastRotationStatusTimestamp() { + return mLastDailyWallpaperRotationStatusTimestamp; + } + + @Override + public long getLastSyncTimestamp() { + return mLastSyncTimestamp; + } + + @Override + public void setLastSyncTimestamp(long timestamp) { + mLastSyncTimestamp = timestamp; + } + + @Override + public void setPendingWallpaperSetStatusSync(@PendingWallpaperSetStatus int setStatus) { + mPendingWallpaperSetStatus = setStatus; + } + + @Override + public int getPendingWallpaperSetStatus() { + return mPendingWallpaperSetStatus; + } + + @Override + public void setPendingWallpaperSetStatus(@PendingWallpaperSetStatus int setStatus) { + mPendingWallpaperSetStatus = setStatus; + } + + @Override + public void setPendingDailyWallpaperUpdateStatusSync( + @PendingDailyWallpaperUpdateStatus int updateStatus) { + mPendingDailyWallpaperUpdateStatus = updateStatus; + } + + @Override + public int getPendingDailyWallpaperUpdateStatus() { + return mPendingDailyWallpaperUpdateStatus; + } + + @Override + public void setPendingDailyWallpaperUpdateStatus( + @PendingDailyWallpaperUpdateStatus int updateStatus) { + mPendingDailyWallpaperUpdateStatus = updateStatus; + } + + @Override + public void incrementNumDaysDailyRotationFailed() { + mNumDaysDailyRotationFailed++; + } + + @Override + public int getNumDaysDailyRotationFailed() { + return mNumDaysDailyRotationFailed; + } + + public void setNumDaysDailyRotationFailed(int days) { + mNumDaysDailyRotationFailed = days; + } + + @Override + public void resetNumDaysDailyRotationFailed() { + mNumDaysDailyRotationFailed = 0; + } + + @Override + public void incrementNumDaysDailyRotationNotAttempted() { + mNumDaysDailyRotationNotAttempted++; + } + + @Override + public int getNumDaysDailyRotationNotAttempted() { + return mNumDaysDailyRotationNotAttempted; + } + + public void setNumDaysDailyRotationNotAttempted(int days) { + mNumDaysDailyRotationNotAttempted = days; + } + + @Override + public void resetNumDaysDailyRotationNotAttempted() { + mNumDaysDailyRotationNotAttempted = 0; + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperRefresher.java b/tests/src/com/android/wallpaper/testing/TestWallpaperRefresher.java new file mode 100644 index 0000000..97961fe --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestWallpaperRefresher.java @@ -0,0 +1,81 @@ +/* + * 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.WallpaperManager; +import android.content.Context; + +import com.android.wallpaper.compat.BuildCompat; +import com.android.wallpaper.model.WallpaperMetadata; +import com.android.wallpaper.module.InjectorProvider; +import com.android.wallpaper.module.WallpaperPreferences; +import com.android.wallpaper.module.WallpaperRefresher; + +/** + * Test implementation of {@link WallpaperRefresher} which simply provides whatever metadata is + * saved in WallpaperPreferences and the image wallpaper set to {@link WallpaperManager}. + */ +public class TestWallpaperRefresher implements WallpaperRefresher { + + private final Context mAppContext; + + /** + * @param context The application's context. + */ + public TestWallpaperRefresher(Context context) { + mAppContext = context.getApplicationContext(); + } + + @Override + public void refresh(RefreshListener listener) { + + WallpaperPreferences prefs = InjectorProvider.getInjector().getPreferences(mAppContext); + + if (BuildCompat.isAtLeastN() && prefs.getLockWallpaperId() > 0) { + listener.onRefreshed( + new WallpaperMetadata( + prefs.getHomeWallpaperAttributions(), + prefs.getHomeWallpaperActionUrl(), + prefs.getHomeWallpaperActionLabelRes(), + prefs.getHomeWallpaperActionIconRes(), + prefs.getHomeWallpaperCollectionId(), + prefs.getHomeWallpaperBackingFileName(), + null /* wallpaperComponent */), + new WallpaperMetadata( + prefs.getLockWallpaperAttributions(), + prefs.getLockWallpaperActionUrl(), + prefs.getLockWallpaperActionLabelRes(), + prefs.getLockWallpaperActionIconRes(), + prefs.getLockWallpaperCollectionId(), + prefs.getLockWallpaperBackingFileName(), + null /* wallpaperComponent */), + prefs.getWallpaperPresentationMode()); + } else { + listener.onRefreshed( + new WallpaperMetadata( + prefs.getHomeWallpaperAttributions(), + prefs.getHomeWallpaperActionUrl(), + prefs.getHomeWallpaperActionLabelRes(), + prefs.getHomeWallpaperActionIconRes(), + prefs.getHomeWallpaperCollectionId(), + prefs.getHomeWallpaperBackingFileName(), + null /* wallpaperComponent */), + null, + prefs.getWallpaperPresentationMode()); + } + } +} diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperRotationInitializer.java b/tests/src/com/android/wallpaper/testing/TestWallpaperRotationInitializer.java new file mode 100644 index 0000000..9494d19 --- /dev/null +++ b/tests/src/com/android/wallpaper/testing/TestWallpaperRotationInitializer.java @@ -0,0 +1,133 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; + +import com.android.wallpaper.model.WallpaperRotationInitializer; +import com.android.wallpaper.module.InjectorProvider; +import com.android.wallpaper.module.WallpaperPreferences; + +/** + * Test implementation of {@link WallpaperRotationInitializer}. + */ +public class TestWallpaperRotationInitializer implements WallpaperRotationInitializer { + + private boolean mIsRotationInitialized; + @NetworkPreference + private int mNetworkPreference; + private Listener mListener; + @RotationInitializationState + private int mRotationInitializationState; + + TestWallpaperRotationInitializer() { + mIsRotationInitialized = false; + mNetworkPreference = NETWORK_PREFERENCE_WIFI_ONLY; + mRotationInitializationState = WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED; + } + + TestWallpaperRotationInitializer(@RotationInitializationState int rotationState) { + mIsRotationInitialized = false; + mNetworkPreference = NETWORK_PREFERENCE_WIFI_ONLY; + mRotationInitializationState = rotationState; + } + + private TestWallpaperRotationInitializer(Parcel unused) { + mIsRotationInitialized = false; + mNetworkPreference = NETWORK_PREFERENCE_WIFI_ONLY; + } + + @Override + public void setFirstWallpaperInRotation(Context context, + @NetworkPreference int networkPreference, + Listener listener) { + mListener = listener; + mNetworkPreference = networkPreference; + } + + @Override + public boolean startRotation(Context appContext) { + WallpaperPreferences wallpaperPreferences = + InjectorProvider.getInjector().getPreferences(appContext); + wallpaperPreferences.setWallpaperPresentationMode( + WallpaperPreferences.PRESENTATION_MODE_ROTATING); + return true; + } + + @Override + public void fetchRotationInitializationState(Context context, RotationStateListener listener) { + listener.onRotationStateReceived(mRotationInitializationState); + } + + @Override + public int getRotationInitializationStateDirty(Context context) { + return mRotationInitializationState; + } + + /** Sets the mocked rotation initialization state. */ + public void setRotationInitializationState(@RotationInitializationState int rotationState) { + mRotationInitializationState = rotationState; + } + + /** + * Simulates completion of the asynchronous task to download the first wallpaper in a rotation.. + * + * @param isSuccessful Whether the first wallpaper downloaded successfully. + */ + public void finishDownloadingFirstWallpaper(boolean isSuccessful) { + if (isSuccessful) { + mIsRotationInitialized = true; + mListener.onFirstWallpaperInRotationSet(); + } else { + mIsRotationInitialized = false; + mListener.onError(); + } + } + + /** Returns whether a wallpaper rotation is initialized. */ + public boolean isRotationInitialized() { + return mIsRotationInitialized; + } + + /** Returns whether a wallpaper rotation is enabled for WiFi-only. */ + public boolean isWifiOnly() { + return mNetworkPreference == NETWORK_PREFERENCE_WIFI_ONLY; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<TestWallpaperRotationInitializer> CREATOR = + new Parcelable.Creator<TestWallpaperRotationInitializer>() { + @Override + public TestWallpaperRotationInitializer createFromParcel(Parcel in) { + return new TestWallpaperRotationInitializer(in); + } + + @Override + public TestWallpaperRotationInitializer[] newArray(int size) { + return new TestWallpaperRotationInitializer[size]; + } + }; +} |