Skip to content

Commit 4c687b4

Browse files
Set Wallpaper in background (commons-app#5665)
* Worker added for setting up wallpaper * Fix crash * Fix test * Fix test
1 parent a7a2125 commit 4c687b4

File tree

5 files changed

+191
-62
lines changed

5 files changed

+191
-62
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package fr.free.nrw.commons.contributions;
2+
3+
import android.app.NotificationChannel;
4+
import android.app.NotificationManager;
5+
import android.app.WallpaperManager;
6+
import android.content.Context;
7+
import android.graphics.Bitmap;
8+
import android.graphics.BitmapFactory;
9+
import android.net.Uri;
10+
import android.os.Build;
11+
import android.util.Log;
12+
import androidx.annotation.NonNull;
13+
import androidx.annotation.Nullable;
14+
import androidx.core.app.NotificationCompat;
15+
import androidx.work.Data;
16+
import androidx.work.Worker;
17+
import androidx.work.WorkerParameters;
18+
import com.facebook.common.executors.CallerThreadExecutor;
19+
import com.facebook.common.references.CloseableReference;
20+
import com.facebook.datasource.DataSource;
21+
import com.facebook.drawee.backends.pipeline.Fresco;
22+
import com.facebook.imagepipeline.core.ImagePipeline;
23+
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
24+
import com.facebook.imagepipeline.image.CloseableImage;
25+
import com.facebook.imagepipeline.request.ImageRequest;
26+
import com.facebook.imagepipeline.request.ImageRequestBuilder;
27+
import fr.free.nrw.commons.R;
28+
import java.io.IOException;
29+
import timber.log.Timber;
30+
31+
public class SetWallpaperWorker extends Worker {
32+
33+
private static final String NOTIFICATION_CHANNEL_ID = "set_wallpaper_channel";
34+
private static final int NOTIFICATION_ID = 1;
35+
36+
public SetWallpaperWorker(@NonNull Context context, @NonNull WorkerParameters params) {
37+
super(context, params);
38+
}
39+
40+
@NonNull
41+
@Override
42+
public Result doWork() {
43+
Context context = getApplicationContext();
44+
createNotificationChannel(context);
45+
showProgressNotification(context);
46+
47+
String imageUrl = getInputData().getString("imageUrl");
48+
if (imageUrl == null) {
49+
return Result.failure();
50+
}
51+
52+
ImageRequest imageRequest = ImageRequestBuilder
53+
.newBuilderWithSource(Uri.parse(imageUrl))
54+
.build();
55+
56+
ImagePipeline imagePipeline = Fresco.getImagePipeline();
57+
final DataSource<CloseableReference<CloseableImage>>
58+
dataSource = imagePipeline.fetchDecodedImage(imageRequest, context);
59+
60+
dataSource.subscribe(new BaseBitmapDataSubscriber() {
61+
@Override
62+
public void onNewResultImpl(@Nullable Bitmap bitmap) {
63+
if (dataSource.isFinished() && bitmap != null) {
64+
Timber.d("Bitmap loaded from url %s", imageUrl.toString());
65+
setWallpaper(context, Bitmap.createBitmap(bitmap));
66+
dataSource.close();
67+
}
68+
}
69+
70+
@Override
71+
public void onFailureImpl(DataSource dataSource) {
72+
Timber.d("Error getting bitmap from image url %s", imageUrl.toString());
73+
showNotification(context, "Setting Wallpaper Failed", "Failed to download image.");
74+
if (dataSource != null) {
75+
dataSource.close();
76+
}
77+
}
78+
}, CallerThreadExecutor.getInstance());
79+
80+
return Result.success();
81+
}
82+
83+
private void setWallpaper(Context context, Bitmap bitmap) {
84+
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
85+
86+
try {
87+
wallpaperManager.setBitmap(bitmap);
88+
showNotification(context, "Wallpaper Set", "Wallpaper has been updated successfully.");
89+
90+
} catch (Exception e) {
91+
Timber.e(e, "Error setting wallpaper");
92+
showNotification(context, "Setting Wallpaper Failed", " "+e.getLocalizedMessage());
93+
}
94+
}
95+
96+
private void showProgressNotification(Context context) {
97+
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
98+
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
99+
.setSmallIcon(R.drawable.commons_logo)
100+
.setContentTitle("Setting Wallpaper")
101+
.setContentText("Please wait...")
102+
.setPriority(NotificationCompat.PRIORITY_HIGH)
103+
.setOngoing(true)
104+
.setProgress(0, 0, true);
105+
notificationManager.notify(NOTIFICATION_ID, builder.build());
106+
}
107+
108+
private void showNotification(Context context, String title, String content) {
109+
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
110+
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
111+
.setSmallIcon(R.drawable.commons_logo)
112+
.setContentTitle(title)
113+
.setContentText(content)
114+
.setPriority(NotificationCompat.PRIORITY_HIGH)
115+
.setOngoing(false);
116+
notificationManager.notify(NOTIFICATION_ID, builder.build());
117+
}
118+
119+
private void createNotificationChannel(Context context) {
120+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
121+
CharSequence name = "Wallpaper Setting";
122+
String description = "Notifications for wallpaper setting progress";
123+
int importance = NotificationManager.IMPORTANCE_HIGH;
124+
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance);
125+
channel.setDescription(description);
126+
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
127+
notificationManager.createNotificationChannel(channel);
128+
}
129+
}
130+
}

app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,7 @@ public void onCreate(Bundle savedInstanceState) {
164164
if (savedInstanceState != null) {
165165
editable = savedInstanceState.getBoolean("editable", false);
166166
isFeaturedImage = savedInstanceState.getBoolean("isFeaturedImage", false);
167-
if(null != binding.mediaDetailsPager) {
168-
binding.mediaDetailsPager.setCurrentItem(savedInstanceState.getInt("current-page", 0), false);
169-
}
167+
170168
}
171169
setHasOptionsMenu(true);
172170
initProvider();

app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package fr.free.nrw.commons.utils;
22

3+
import android.app.NotificationChannel;
4+
import android.app.NotificationManager;
35
import android.app.ProgressDialog;
46
import android.app.WallpaperManager;
57
import android.content.Context;
@@ -8,10 +10,14 @@
810
import android.graphics.Canvas;
911
import android.graphics.Color;
1012
import android.net.Uri;
13+
import android.os.Build;
1114
import androidx.annotation.IntDef;
1215
import androidx.annotation.Nullable;
1316
import androidx.core.content.ContextCompat;
1417
import androidx.exifinterface.media.ExifInterface;
18+
import androidx.work.Data;
19+
import androidx.work.OneTimeWorkRequest;
20+
import androidx.work.WorkManager;
1521
import com.facebook.common.executors.CallerThreadExecutor;
1622
import com.facebook.common.references.CloseableReference;
1723
import com.facebook.datasource.DataSource;
@@ -22,6 +28,7 @@
2228
import com.facebook.imagepipeline.request.ImageRequest;
2329
import com.facebook.imagepipeline.request.ImageRequestBuilder;
2430
import fr.free.nrw.commons.R;
31+
import fr.free.nrw.commons.contributions.SetWallpaperWorker;
2532
import fr.free.nrw.commons.___location.LatLng;
2633
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
2734
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -195,38 +202,24 @@ private static boolean checkIfImageIsDark(Bitmap bitmap) {
195202
* @param imageUrl Url of the image
196203
*/
197204
public static void setWallpaperFromImageUrl(Context context, Uri imageUrl) {
198-
showSettingWallpaperProgressBar(context);
199-
Timber.d("Trying to set wallpaper from url %s", imageUrl.toString());
200-
ImageRequest imageRequest = ImageRequestBuilder
201-
.newBuilderWithSource(imageUrl)
202-
.setAutoRotateEnabled(true)
203-
.build();
204-
205-
ImagePipeline imagePipeline = Fresco.getImagePipeline();
206-
final DataSource<CloseableReference<CloseableImage>>
207-
dataSource = imagePipeline.fetchDecodedImage(imageRequest, context);
208-
209-
dataSource.subscribe(new BaseBitmapDataSubscriber() {
210-
211-
@Override
212-
public void onNewResultImpl(@Nullable Bitmap bitmap) {
213-
if (dataSource.isFinished() && bitmap != null) {
214-
Timber.d("Bitmap loaded from url %s", imageUrl.toString());
215-
setWallpaper(context, Bitmap.createBitmap(bitmap));
216-
dataSource.close();
217-
}
218-
}
219205

220-
@Override
221-
public void onFailureImpl(DataSource dataSource) {
222-
Timber.d("Error getting bitmap from image url %s", imageUrl.toString());
223-
if (dataSource != null) {
224-
dataSource.close();
225-
}
226-
}
227-
}, CallerThreadExecutor.getInstance());
206+
enqueueSetWallpaperWork(context, imageUrl);
207+
228208
}
229209

210+
private static void createNotificationChannel(Context context) {
211+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
212+
CharSequence name = "Wallpaper Setting";
213+
String description = "Notifications for wallpaper setting progress";
214+
int importance = NotificationManager.IMPORTANCE_DEFAULT;
215+
NotificationChannel channel = new NotificationChannel("set_wallpaper_channel", name, importance);
216+
channel.setDescription(description);
217+
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
218+
notificationManager.createNotificationChannel(channel);
219+
}
220+
}
221+
222+
230223
/**
231224
* Calls the set avatar api to set the image url as user's avatar
232225
* @param context
@@ -272,23 +265,21 @@ public static void setAvatarFromImageUrl(Context context, String url, String use
272265

273266
}
274267

275-
private static void setWallpaper(Context context, Bitmap bitmap) {
276-
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
277-
try {
278-
wallpaperManager.setBitmap(bitmap);
279-
ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_successfully));
280-
if (progressDialogWallpaper != null && progressDialogWallpaper.isShowing()) {
281-
progressDialogWallpaper.dismiss();
282-
}
283-
} catch (IOException e) {
284-
Timber.e(e, "Error setting wallpaper");
285-
ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_unsuccessfully));
286-
if (progressDialogWallpaper != null) {
287-
progressDialogWallpaper.cancel();
288-
}
289-
}
268+
public static void enqueueSetWallpaperWork(Context context, Uri imageUrl) {
269+
createNotificationChannel(context); // Ensure the notification channel is created
270+
271+
Data inputData = new Data.Builder()
272+
.putString("imageUrl", imageUrl.toString())
273+
.build();
274+
275+
OneTimeWorkRequest setWallpaperWork = new OneTimeWorkRequest.Builder(SetWallpaperWorker.class)
276+
.setInputData(inputData)
277+
.build();
278+
279+
WorkManager.getInstance(context).enqueue(setWallpaperWork);
290280
}
291281

282+
292283
private static void showSettingWallpaperProgressBar(Context context) {
293284
progressDialogWallpaper = ProgressDialog.show(context, context.getString(R.string.setting_wallpaper_dialog_title),
294285
context.getString(R.string.setting_wallpaper_dialog_message), true);

app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailPagerFragmentUnitTests.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import android.view.MenuItem
99
import androidx.fragment.app.FragmentManager
1010
import androidx.fragment.app.FragmentTransaction
1111
import androidx.test.core.app.ApplicationProvider
12+
import androidx.work.testing.WorkManagerTestInitHelper
1213
import com.facebook.drawee.backends.pipeline.Fresco
1314
import com.facebook.soloader.SoLoader
1415
import com.nhaarman.mockitokotlin2.any
@@ -79,8 +80,11 @@ class MediaDetailPagerFragmentUnitTests {
7980

8081
MockitoAnnotations.openMocks(this)
8182

83+
8284
context = ApplicationProvider.getApplicationContext()
8385

86+
WorkManagerTestInitHelper.initializeTestWorkManager(context)
87+
8488
OkHttpConnectionFactory.CLIENT = createTestClient()
8589

8690
SoLoader.setInTestMode()

app/src/test/kotlin/fr/free/nrw/commons/utils/ImageUtilsTest.kt

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,22 @@ package fr.free.nrw.commons.utils
33
import android.app.ProgressDialog
44
import android.content.Context
55
import android.graphics.Bitmap
6+
import android.net.Uri
67
import androidx.test.core.app.ApplicationProvider
8+
import androidx.work.Data
9+
import androidx.work.ListenableWorker
10+
import androidx.work.WorkManager
11+
import androidx.work.testing.TestListenableWorkerBuilder
12+
import androidx.work.testing.WorkManagerTestInitHelper
713
import fr.free.nrw.commons.TestCommonsApplication
14+
import fr.free.nrw.commons.contributions.SetWallpaperWorker
815
import fr.free.nrw.commons.___location.LatLng
916
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
1017
import io.reactivex.disposables.CompositeDisposable
1118
import org.junit.Assert
1219
import org.junit.Before
1320
import org.junit.Test
21+
import org.junit.jupiter.api.Assertions.assertTrue
1422
import org.junit.runner.RunWith
1523
import org.mockito.Mock
1624
import org.mockito.Mockito.mock
@@ -24,16 +32,14 @@ import java.lang.reflect.Field
2432
import java.lang.reflect.Method
2533

2634

35+
2736
@RunWith(RobolectricTestRunner::class)
2837
@Config(sdk = [21], application = TestCommonsApplication::class)
2938
@LooperMode(LooperMode.Mode.PAUSED)
3039
class ImageUtilsTest {
3140

3241
private lateinit var context: Context
3342

34-
@Mock
35-
private lateinit var bitmap: Bitmap
36-
3743
@Mock
3844
private lateinit var progressDialogWallpaper: ProgressDialog
3945

@@ -43,10 +49,18 @@ class ImageUtilsTest {
4349
@Mock
4450
private lateinit var compositeDisposable: CompositeDisposable
4551

52+
@Mock
53+
private lateinit var imageUri: Uri
54+
55+
private lateinit var workManager: WorkManager
56+
4657
@Before
4758
fun setup() {
4859
MockitoAnnotations.openMocks(this)
4960
context = ApplicationProvider.getApplicationContext()
61+
62+
WorkManagerTestInitHelper.initializeTestWorkManager(context)
63+
workManager = WorkManager.getInstance(context)
5064
}
5165

5266
@Test
@@ -87,20 +101,13 @@ class ImageUtilsTest {
87101
fun testSetWallpaper() {
88102
val mockImageUtils = mock(ImageUtils::class.java)
89103
val method: Method = ImageUtils::class.java.getDeclaredMethod(
90-
"setWallpaper",
104+
"enqueueSetWallpaperWork",
91105
Context::class.java,
92-
Bitmap::class.java
106+
Uri::class.java
93107
)
94108
method.isAccessible = true
95109

96-
`when`(progressDialogWallpaper.isShowing).thenReturn(true)
97-
98-
val progressDialogWallpaperField: Field =
99-
ImageUtils::class.java.getDeclaredField("progressDialogWallpaper")
100-
progressDialogWallpaperField.isAccessible = true
101-
progressDialogWallpaperField.set(mockImageUtils, progressDialogWallpaper)
102-
103-
method.invoke(mockImageUtils, context, bitmap)
110+
method.invoke(mockImageUtils, context, imageUri)
104111
}
105112

106113
@Test
@@ -190,5 +197,4 @@ class ImageUtilsTest {
190197
method.isAccessible = true
191198
method.invoke(mockImageUtils, null)
192199
}
193-
194-
}
200+
}

0 commit comments

Comments
 (0)