mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Migrated widget module from Java to Kotlin (#5940)
* Rename .java to .kt * Migrated widget module to Kotlin
This commit is contained in:
parent
0fdb0044b9
commit
cb4ffd8ca8
7 changed files with 224 additions and 237 deletions
|
|
@ -2,7 +2,6 @@ package fr.free.nrw.commons.di;
|
|||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import fr.free.nrw.commons.actions.PageEditClient;
|
||||
import fr.free.nrw.commons.explore.categories.CategoriesModule;
|
||||
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment;
|
||||
import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment;
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
package fr.free.nrw.commons.widget;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* Created by Ilgaz Er on 8/7/2018.
|
||||
*/
|
||||
public class HeightLimitedRecyclerView extends RecyclerView {
|
||||
int height;
|
||||
public HeightLimitedRecyclerView(Context context) {
|
||||
super(context);
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
((Activity) getContext()).getWindowManager()
|
||||
.getDefaultDisplay()
|
||||
.getMetrics(displayMetrics);
|
||||
height=displayMetrics.heightPixels;
|
||||
}
|
||||
|
||||
public HeightLimitedRecyclerView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
((Activity) getContext()).getWindowManager()
|
||||
.getDefaultDisplay()
|
||||
.getMetrics(displayMetrics);
|
||||
height=displayMetrics.heightPixels;
|
||||
}
|
||||
|
||||
public HeightLimitedRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
((Activity) getContext()).getWindowManager()
|
||||
.getDefaultDisplay()
|
||||
.getMetrics(displayMetrics);
|
||||
height=displayMetrics.heightPixels;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthSpec, int heightSpec) {
|
||||
heightSpec = MeasureSpec.makeMeasureSpec((int) (height*0.3), MeasureSpec.AT_MOST);
|
||||
super.onMeasure(widthSpec, heightSpec);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package fr.free.nrw.commons.widget
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.util.DisplayMetrics
|
||||
|
||||
import androidx.annotation.Nullable
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
|
||||
/**
|
||||
* Created by Ilgaz Er on 8/7/2018.
|
||||
*/
|
||||
class HeightLimitedRecyclerView : RecyclerView {
|
||||
private var height: Int = 0
|
||||
|
||||
constructor(context: Context) : super(context) {
|
||||
initializeHeight(context)
|
||||
}
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
|
||||
initializeHeight(context)
|
||||
}
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) {
|
||||
initializeHeight(context)
|
||||
}
|
||||
|
||||
private fun initializeHeight(context: Context) {
|
||||
val displayMetrics = DisplayMetrics()
|
||||
(context as Activity).windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||
height = displayMetrics.heightPixels
|
||||
}
|
||||
|
||||
override fun onMeasure(widthSpec: Int, heightSpec: Int) {
|
||||
val limitedHeightSpec = MeasureSpec.makeMeasureSpec(
|
||||
(height * 0.3).toInt(),
|
||||
MeasureSpec.AT_MOST
|
||||
)
|
||||
super.onMeasure(widthSpec, limitedHeightSpec)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
package fr.free.nrw.commons.widget;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.widget.RemoteViews;
|
||||
import androidx.annotation.Nullable;
|
||||
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.media.MediaClient;
|
||||
import javax.inject.Inject;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.content.Intent.ACTION_VIEW;
|
||||
|
||||
/**
|
||||
* Implementation of App Widget functionality.
|
||||
*/
|
||||
public class PicOfDayAppWidget extends AppWidgetProvider {
|
||||
|
||||
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
||||
@Inject
|
||||
MediaClient mediaClient;
|
||||
|
||||
void updateAppWidget(
|
||||
final Context context,
|
||||
final AppWidgetManager appWidgetManager,
|
||||
final int appWidgetId
|
||||
) {
|
||||
final RemoteViews views = new RemoteViews(
|
||||
context.getPackageName(), R.layout.pic_of_day_app_widget);
|
||||
|
||||
// Launch App on Button Click
|
||||
final Intent viewIntent = new Intent(context, MainActivity.class);
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.M) {
|
||||
flags |= PendingIntent.FLAG_IMMUTABLE;
|
||||
}
|
||||
final PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, viewIntent, flags);
|
||||
|
||||
views.setOnClickPendingIntent(R.id.camera_button, pendingIntent);
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
|
||||
loadPictureOfTheDay(context, views, appWidgetManager, appWidgetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the picture of the day using media wiki API
|
||||
* @param context The application context.
|
||||
* @param views The RemoteViews object used to update the App Widget UI.
|
||||
* @param appWidgetManager The AppWidgetManager instance for managing the widget.
|
||||
* @param appWidgetId he ID of the App Widget to update.
|
||||
*/
|
||||
private void loadPictureOfTheDay(
|
||||
final Context context,
|
||||
final RemoteViews views,
|
||||
final AppWidgetManager appWidgetManager,
|
||||
final int appWidgetId
|
||||
) {
|
||||
compositeDisposable.add(mediaClient.getPictureOfTheDay()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
response -> {
|
||||
if (response != null) {
|
||||
views.setTextViewText(R.id.appwidget_title, response.getDisplayTitle());
|
||||
|
||||
// View in browser
|
||||
final Intent viewIntent = new Intent();
|
||||
viewIntent.setAction(ACTION_VIEW);
|
||||
viewIntent.setData(Uri.parse(response.getPageTitle().getMobileUri()));
|
||||
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.M) {
|
||||
flags |= PendingIntent.FLAG_IMMUTABLE;
|
||||
}
|
||||
final PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, viewIntent, flags);
|
||||
|
||||
views.setOnClickPendingIntent(R.id.appwidget_image, pendingIntent);
|
||||
loadImageFromUrl(response.getThumbUrl(),
|
||||
context, views, appWidgetManager, appWidgetId);
|
||||
}
|
||||
},
|
||||
t -> Timber.e(t, "Fetching picture of the day failed")
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Fresco to load an image from Url
|
||||
* @param imageUrl The URL of the image to load.
|
||||
* @param context The application context.
|
||||
* @param views The RemoteViews object used to update the App Widget UI.
|
||||
* @param appWidgetManager The AppWidgetManager instance for managing the widget.
|
||||
* @param appWidgetId he ID of the App Widget to update.
|
||||
*/
|
||||
private void loadImageFromUrl(
|
||||
final String imageUrl,
|
||||
final Context context,
|
||||
final RemoteViews views,
|
||||
final AppWidgetManager appWidgetManager,
|
||||
final int appWidgetId
|
||||
) {
|
||||
final ImageRequest request = ImageRequestBuilder
|
||||
.newBuilderWithSource(Uri.parse(imageUrl)).build();
|
||||
final ImagePipeline imagePipeline = Fresco.getImagePipeline();
|
||||
final DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline
|
||||
.fetchDecodedImage(request, context);
|
||||
|
||||
dataSource.subscribe(new BaseBitmapDataSubscriber() {
|
||||
@Override
|
||||
protected void onNewResultImpl(@Nullable final Bitmap tempBitmap) {
|
||||
Bitmap bitmap = null;
|
||||
if (tempBitmap != null) {
|
||||
bitmap = Bitmap.createBitmap(
|
||||
tempBitmap.getWidth(), tempBitmap.getHeight(), Bitmap.Config.ARGB_8888
|
||||
);
|
||||
final Canvas canvas = new Canvas(bitmap);
|
||||
canvas.drawBitmap(tempBitmap, 0f, 0f, new Paint());
|
||||
}
|
||||
views.setImageViewBitmap(R.id.appwidget_image, bitmap);
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFailureImpl(
|
||||
final DataSource<CloseableReference<CloseableImage>> dataSource
|
||||
) {
|
||||
// Ignore failure for now.
|
||||
}
|
||||
}, CallerThreadExecutor.getInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(
|
||||
final Context context,
|
||||
final AppWidgetManager appWidgetManager,
|
||||
final int[] appWidgetIds
|
||||
) {
|
||||
ApplicationlessInjection
|
||||
.getInstance(context.getApplicationContext())
|
||||
.getCommonsApplicationComponent()
|
||||
.inject(this);
|
||||
// There may be multiple widgets active, so update all of them
|
||||
for (final int appWidgetId : appWidgetIds) {
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(final Context context) {
|
||||
// Enter relevant functionality for when the first widget is created
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(final Context context) {
|
||||
// Enter relevant functionality for when the last widget is disabled
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
package fr.free.nrw.commons.widget
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.widget.RemoteViews
|
||||
import androidx.annotation.Nullable
|
||||
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.media.MediaClient
|
||||
import javax.inject.Inject
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Implementation of App Widget functionality.
|
||||
*/
|
||||
class PicOfDayAppWidget : AppWidgetProvider() {
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
@Inject
|
||||
lateinit var mediaClient: MediaClient
|
||||
|
||||
private fun updateAppWidget(
|
||||
context: Context,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
appWidgetId: Int
|
||||
) {
|
||||
val views = RemoteViews(context.packageName, R.layout.pic_of_day_app_widget)
|
||||
|
||||
// Launch App on Button Click
|
||||
val viewIntent = Intent(context, MainActivity::class.java)
|
||||
var flags = PendingIntent.FLAG_UPDATE_CURRENT
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
flags = flags or PendingIntent.FLAG_IMMUTABLE
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(context, 0, viewIntent, flags)
|
||||
views.setOnClickPendingIntent(R.id.camera_button, pendingIntent)
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||
|
||||
loadPictureOfTheDay(context, views, appWidgetManager, appWidgetId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the picture of the day using media wiki API
|
||||
* @param context The application context.
|
||||
* @param views The RemoteViews object used to update the App Widget UI.
|
||||
* @param appWidgetManager The AppWidgetManager instance for managing the widget.
|
||||
* @param appWidgetId The ID of the App Widget to update.
|
||||
*/
|
||||
private fun loadPictureOfTheDay(
|
||||
context: Context,
|
||||
views: RemoteViews,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
appWidgetId: Int
|
||||
) {
|
||||
compositeDisposable.add(
|
||||
mediaClient.getPictureOfTheDay()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ response ->
|
||||
if (response != null) {
|
||||
views.setTextViewText(R.id.appwidget_title, response.displayTitle)
|
||||
|
||||
// View in browser
|
||||
val viewIntent = Intent().apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
data = Uri.parse(response.pageTitle.mobileUri)
|
||||
}
|
||||
|
||||
var flags = PendingIntent.FLAG_UPDATE_CURRENT
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
flags = flags or PendingIntent.FLAG_IMMUTABLE
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
viewIntent,
|
||||
flags
|
||||
)
|
||||
|
||||
views.setOnClickPendingIntent(R.id.appwidget_image, pendingIntent)
|
||||
loadImageFromUrl(
|
||||
response.thumbUrl,
|
||||
context,
|
||||
views,
|
||||
appWidgetManager,
|
||||
appWidgetId
|
||||
)
|
||||
}
|
||||
},
|
||||
{ t -> Timber.e(t, "Fetching picture of the day failed") }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Fresco to load an image from Url
|
||||
* @param imageUrl The URL of the image to load.
|
||||
* @param context The application context.
|
||||
* @param views The RemoteViews object used to update the App Widget UI.
|
||||
* @param appWidgetManager The AppWidgetManager instance for managing the widget.
|
||||
* @param appWidgetId The ID of the App Widget to update.
|
||||
*/
|
||||
private fun loadImageFromUrl(
|
||||
imageUrl: String?,
|
||||
context: Context,
|
||||
views: RemoteViews,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
appWidgetId: Int
|
||||
) {
|
||||
val request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageUrl)).build()
|
||||
val imagePipeline = Fresco.getImagePipeline()
|
||||
val dataSource = imagePipeline.fetchDecodedImage(request, context)
|
||||
|
||||
dataSource.subscribe(object : BaseBitmapDataSubscriber() {
|
||||
override fun onNewResultImpl(tempBitmap: Bitmap?) {
|
||||
val bitmap = tempBitmap?.let {
|
||||
Bitmap.createBitmap(it.width, it.height, Bitmap.Config.ARGB_8888).apply {
|
||||
Canvas(this).drawBitmap(it, 0f, 0f, Paint())
|
||||
}
|
||||
}
|
||||
views.setImageViewBitmap(R.id.appwidget_image, bitmap)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||
}
|
||||
|
||||
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
|
||||
// Ignore failure for now.
|
||||
}
|
||||
}, CallerThreadExecutor.getInstance())
|
||||
}
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
ApplicationlessInjection
|
||||
.getInstance(context.applicationContext)
|
||||
.commonsApplicationComponent
|
||||
.inject(this)
|
||||
|
||||
// There may be multiple widgets active, so update all of them
|
||||
for (appWidgetId in appWidgetIds) {
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEnabled(context: Context) {
|
||||
// Enter relevant functionality for when the first widget is created
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
// Enter relevant functionality for when the last widget is disabled
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package fr.free.nrw.commons.widget;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public interface ViewHolder<T> {
|
||||
void bindModel(Context context, T model);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package fr.free.nrw.commons.widget
|
||||
|
||||
import android.content.Context
|
||||
|
||||
interface ViewHolder<T> {
|
||||
fun bindModel(context: Context, model: T)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue