1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.android.wallpaper.weathereffects.data.repository
18 
19 import android.content.Context
20 import android.util.Log
21 import com.google.android.wallpaper.weathereffects.shared.model.WallpaperFileModel
22 import com.google.android.wallpaper.weathereffects.shared.model.WallpaperImageModel
23 import javax.inject.Inject
24 import javax.inject.Singleton
25 import kotlinx.coroutines.flow.MutableStateFlow
26 import kotlinx.coroutines.flow.StateFlow
27 import kotlinx.coroutines.flow.asStateFlow
28 
29 @Singleton
30 class WeatherEffectsRepository @Inject constructor(
31     private val context: Context,
32 ) {
33     private val _wallpaperImage = MutableStateFlow<WallpaperImageModel?>(null)
34     val wallpaperImage: StateFlow<WallpaperImageModel?> = _wallpaperImage.asStateFlow()
35 
36     /**
37      * Generates or updates a wallpaper from the provided [wallpaperFileModel].
38      */
39     suspend fun updateWallpaper(wallpaperFileModel: WallpaperFileModel) {
40         try {
41             // Use the existing images if the foreground and background are not supplied.
42             var fgBitmap = _wallpaperImage.value?.foreground
43             var bgBitmap = _wallpaperImage.value?.background
44 
45             wallpaperFileModel.foregroundAbsolutePath?.let {
46                 WallpaperFileUtils.importBitmapFromAbsolutePath(it)?.let { newFg ->
47                     fgBitmap = newFg
48                 }
49             }
50 
51             wallpaperFileModel.backgroundAbsolutePath?.let {
52                 WallpaperFileUtils.importBitmapFromAbsolutePath(it)?.let { newBg ->
53                     bgBitmap = newBg
54                 }
55             }
56 
57             if (fgBitmap == null || bgBitmap == null) {
58                 Log.w(TAG, "Cannot update wallpaper. asset: $wallpaperFileModel")
59                 return
60             }
61 
62             val foreground = fgBitmap!!
63             val background = bgBitmap!!
64 
65             var success = true
66             // TODO: Only persist assets when the wallpaper is applied.
67             success = success and WallpaperFileUtils.exportBitmap(
68                 context,
69                 WallpaperFileUtils.FG_FILE_NAME,
70                 foreground,
71             )
72             success = success and WallpaperFileUtils.exportBitmap(
73                 context,
74                 WallpaperFileUtils.BG_FILE_NAME,
75                 background,
76             )
77             if (!success) {
78                 Log.e(TAG, "Failed to export assets during wallpaper generation")
79                 return
80             }
81 
82             // We always respect the new weather.
83             val weather = wallpaperFileModel.weatherEffect
84             if (weather != null) WallpaperFileUtils.exportLastKnownWeather(weather, context)
85 
86             _wallpaperImage.value = WallpaperImageModel(
87                 foreground,
88                 background,
89                 weather
90             )
91         } catch (e: RuntimeException) {
92             Log.e(TAG, "Unable to load wallpaper: ", e)
93         } catch (e: OutOfMemoryError) {
94             Log.e(TAG, "Unable to load wallpaper: ", e)
95         }
96     }
97 
98     /**
99      * Loads wallpaper from the persisted files in local storage.
100      * This assumes wallpaper assets exist in local storage under fixed names.
101      */
102     suspend fun loadWallpaperFromLocalStorage() {
103         try {
104             val fgBitmap = WallpaperFileUtils.importBitmapFromLocalStorage(
105                 WallpaperFileUtils.FG_FILE_NAME, context
106             )
107             val bgBitmap = WallpaperFileUtils.importBitmapFromLocalStorage(
108                 WallpaperFileUtils.BG_FILE_NAME, context
109             )
110             if (fgBitmap == null || bgBitmap == null) {
111                 Log.w(TAG, "Cannot load wallpaper from local storage.")
112                 return
113             }
114 
115             val weatherEffect = WallpaperFileUtils.importLastKnownWeather(context)
116 
117             _wallpaperImage.value = WallpaperImageModel(
118                 fgBitmap,
119                 bgBitmap,
120                 weatherEffect,
121             )
122         } catch (e: RuntimeException) {
123             Log.e(TAG, "Unable to load wallpaper: ", e)
124         } catch (e: OutOfMemoryError) {
125             Log.e(TAG, "Unable to load wallpaper: ", e)
126             null
127         }
128     }
129 
130     suspend fun saveWallpaper() {
131         val foreground = _wallpaperImage.value?.foreground
132         val background = _wallpaperImage.value?.background
133 
134         var success = true
135         success = success and (foreground?.let {
136             WallpaperFileUtils.exportBitmap(
137                 context,
138                 WallpaperFileUtils.FG_FILE_NAME,
139                 it,
140             )
141         } == true)
142         success = success and (background?.let {
143             WallpaperFileUtils.exportBitmap(
144                 context,
145                 WallpaperFileUtils.BG_FILE_NAME,
146                 it,
147             )
148         } == true)
149         if (success) {
150             Log.d(TAG, "Successfully save wallpaper")
151         } else {
152             Log.e(TAG, "Failed to save wallpaper")
153         }
154     }
155 
156     companion object {
157         private const val TAG = "WeatherEffectsRepository"
158     }
159 }
160