Set Wallpaper in background (#5665)

* Worker added for setting up wallpaper

* Fix crash

* Fix test

* Fix test
This commit is contained in:
Shashank Kumar 2024-03-30 05:34:07 +05:30 committed by GitHub
parent a7a2125e1d
commit 4c687b4335
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 190 additions and 61 deletions

View file

@ -0,0 +1,130 @@
package fr.free.nrw.commons.contributions;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import com.facebook.common.executors.CallerThreadExecutor;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import fr.free.nrw.commons.R;
import java.io.IOException;
import timber.log.Timber;
public class SetWallpaperWorker extends Worker {
private static final String NOTIFICATION_CHANNEL_ID = "set_wallpaper_channel";
private static final int NOTIFICATION_ID = 1;
public SetWallpaperWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public Result doWork() {
Context context = getApplicationContext();
createNotificationChannel(context);
showProgressNotification(context);
String imageUrl = getInputData().getString("imageUrl");
if (imageUrl == null) {
return Result.failure();
}
ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(imageUrl))
.build();
ImagePipeline imagePipeline = Fresco.getImagePipeline();
final DataSource<CloseableReference<CloseableImage>>
dataSource = imagePipeline.fetchDecodedImage(imageRequest, context);
dataSource.subscribe(new BaseBitmapDataSubscriber() {
@Override
public void onNewResultImpl(@Nullable Bitmap bitmap) {
if (dataSource.isFinished() && bitmap != null) {
Timber.d("Bitmap loaded from url %s", imageUrl.toString());
setWallpaper(context, Bitmap.createBitmap(bitmap));
dataSource.close();
}
}
@Override
public void onFailureImpl(DataSource dataSource) {
Timber.d("Error getting bitmap from image url %s", imageUrl.toString());
showNotification(context, "Setting Wallpaper Failed", "Failed to download image.");
if (dataSource != null) {
dataSource.close();
}
}
}, CallerThreadExecutor.getInstance());
return Result.success();
}
private void setWallpaper(Context context, Bitmap bitmap) {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
try {
wallpaperManager.setBitmap(bitmap);
showNotification(context, "Wallpaper Set", "Wallpaper has been updated successfully.");
} catch (Exception e) {
Timber.e(e, "Error setting wallpaper");
showNotification(context, "Setting Wallpaper Failed", " "+e.getLocalizedMessage());
}
}
private void showProgressNotification(Context context) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.commons_logo)
.setContentTitle("Setting Wallpaper")
.setContentText("Please wait...")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setOngoing(true)
.setProgress(0, 0, true);
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
private void showNotification(Context context, String title, String content) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.commons_logo)
.setContentTitle(title)
.setContentText(content)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setOngoing(false);
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
private void createNotificationChannel(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Wallpaper Setting";
String description = "Notifications for wallpaper setting progress";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
}

View file

@ -164,9 +164,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
if (savedInstanceState != null) {
editable = savedInstanceState.getBoolean("editable", false);
isFeaturedImage = savedInstanceState.getBoolean("isFeaturedImage", false);
if(null != binding.mediaDetailsPager) {
binding.mediaDetailsPager.setCurrentItem(savedInstanceState.getInt("current-page", 0), false);
}
}
setHasOptionsMenu(true);
initProvider();

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons.utils;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.app.WallpaperManager;
import android.content.Context;
@ -8,10 +10,14 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.exifinterface.media.ExifInterface;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import com.facebook.common.executors.CallerThreadExecutor;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
@ -22,6 +28,7 @@ import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.SetWallpaperWorker;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import io.reactivex.android.schedulers.AndroidSchedulers;
@ -195,38 +202,24 @@ public class ImageUtils {
* @param imageUrl Url of the image
*/
public static void setWallpaperFromImageUrl(Context context, Uri imageUrl) {
showSettingWallpaperProgressBar(context);
Timber.d("Trying to set wallpaper from url %s", imageUrl.toString());
ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(imageUrl)
.setAutoRotateEnabled(true)
.build();
ImagePipeline imagePipeline = Fresco.getImagePipeline();
final DataSource<CloseableReference<CloseableImage>>
dataSource = imagePipeline.fetchDecodedImage(imageRequest, context);
enqueueSetWallpaperWork(context, imageUrl);
dataSource.subscribe(new BaseBitmapDataSubscriber() {
@Override
public void onNewResultImpl(@Nullable Bitmap bitmap) {
if (dataSource.isFinished() && bitmap != null) {
Timber.d("Bitmap loaded from url %s", imageUrl.toString());
setWallpaper(context, Bitmap.createBitmap(bitmap));
dataSource.close();
}
}
@Override
public void onFailureImpl(DataSource dataSource) {
Timber.d("Error getting bitmap from image url %s", imageUrl.toString());
if (dataSource != null) {
dataSource.close();
}
}
}, CallerThreadExecutor.getInstance());
}
private static void createNotificationChannel(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Wallpaper Setting";
String description = "Notifications for wallpaper setting progress";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel("set_wallpaper_channel", name, importance);
channel.setDescription(description);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
/**
* Calls the set avatar api to set the image url as user's avatar
* @param context
@ -272,23 +265,21 @@ public class ImageUtils {
}
private static void setWallpaper(Context context, Bitmap bitmap) {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
try {
wallpaperManager.setBitmap(bitmap);
ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_successfully));
if (progressDialogWallpaper != null && progressDialogWallpaper.isShowing()) {
progressDialogWallpaper.dismiss();
}
} catch (IOException e) {
Timber.e(e, "Error setting wallpaper");
ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_unsuccessfully));
if (progressDialogWallpaper != null) {
progressDialogWallpaper.cancel();
}
}
public static void enqueueSetWallpaperWork(Context context, Uri imageUrl) {
createNotificationChannel(context); // Ensure the notification channel is created
Data inputData = new Data.Builder()
.putString("imageUrl", imageUrl.toString())
.build();
OneTimeWorkRequest setWallpaperWork = new OneTimeWorkRequest.Builder(SetWallpaperWorker.class)
.setInputData(inputData)
.build();
WorkManager.getInstance(context).enqueue(setWallpaperWork);
}
private static void showSettingWallpaperProgressBar(Context context) {
progressDialogWallpaper = ProgressDialog.show(context, context.getString(R.string.setting_wallpaper_dialog_title),
context.getString(R.string.setting_wallpaper_dialog_message), true);

View file

@ -9,6 +9,7 @@ import android.view.MenuItem
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.test.core.app.ApplicationProvider
import androidx.work.testing.WorkManagerTestInitHelper
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.soloader.SoLoader
import com.nhaarman.mockitokotlin2.any
@ -79,8 +80,11 @@ class MediaDetailPagerFragmentUnitTests {
MockitoAnnotations.openMocks(this)
context = ApplicationProvider.getApplicationContext()
WorkManagerTestInitHelper.initializeTestWorkManager(context)
OkHttpConnectionFactory.CLIENT = createTestClient()
SoLoader.setInTestMode()

View file

@ -3,14 +3,22 @@ package fr.free.nrw.commons.utils
import android.app.ProgressDialog
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import androidx.test.core.app.ApplicationProvider
import androidx.work.Data
import androidx.work.ListenableWorker
import androidx.work.WorkManager
import androidx.work.testing.TestListenableWorkerBuilder
import androidx.work.testing.WorkManagerTestInitHelper
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.contributions.SetWallpaperWorker
import fr.free.nrw.commons.location.LatLng
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
import io.reactivex.disposables.CompositeDisposable
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
@ -24,6 +32,7 @@ import java.lang.reflect.Field
import java.lang.reflect.Method
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class)
@LooperMode(LooperMode.Mode.PAUSED)
@ -31,9 +40,6 @@ class ImageUtilsTest {
private lateinit var context: Context
@Mock
private lateinit var bitmap: Bitmap
@Mock
private lateinit var progressDialogWallpaper: ProgressDialog
@ -43,10 +49,18 @@ class ImageUtilsTest {
@Mock
private lateinit var compositeDisposable: CompositeDisposable
@Mock
private lateinit var imageUri: Uri
private lateinit var workManager: WorkManager
@Before
fun setup() {
MockitoAnnotations.openMocks(this)
context = ApplicationProvider.getApplicationContext()
WorkManagerTestInitHelper.initializeTestWorkManager(context)
workManager = WorkManager.getInstance(context)
}
@Test
@ -87,20 +101,13 @@ class ImageUtilsTest {
fun testSetWallpaper() {
val mockImageUtils = mock(ImageUtils::class.java)
val method: Method = ImageUtils::class.java.getDeclaredMethod(
"setWallpaper",
"enqueueSetWallpaperWork",
Context::class.java,
Bitmap::class.java
Uri::class.java
)
method.isAccessible = true
`when`(progressDialogWallpaper.isShowing).thenReturn(true)
val progressDialogWallpaperField: Field =
ImageUtils::class.java.getDeclaredField("progressDialogWallpaper")
progressDialogWallpaperField.isAccessible = true
progressDialogWallpaperField.set(mockImageUtils, progressDialogWallpaper)
method.invoke(mockImageUtils, context, bitmap)
method.invoke(mockImageUtils, context, imageUri)
}
@Test
@ -190,5 +197,4 @@ class ImageUtilsTest {
method.isAccessible = true
method.invoke(mockImageUtils, null)
}
}
}