diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.java b/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.java new file mode 100644 index 000000000..c9b55a83c --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.java @@ -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> + 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); + } + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index b5fea4422..451423e9e 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -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(); diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java index d772cabb5..99155a5e3 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java @@ -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> - 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); diff --git a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailPagerFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailPagerFragmentUnitTests.kt index 0480b8e21..33cfc3c45 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailPagerFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailPagerFragmentUnitTests.kt @@ -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() diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/ImageUtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/ImageUtilsTest.kt index ccae14226..08d95d60d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/ImageUtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/ImageUtilsTest.kt @@ -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) } - -} \ No newline at end of file +}