mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 21:03:54 +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 com.google.gson.Gson;
|
||||||
|
|
||||||
import fr.free.nrw.commons.actions.PageEditClient;
|
|
||||||
import fr.free.nrw.commons.explore.categories.CategoriesModule;
|
import fr.free.nrw.commons.explore.categories.CategoriesModule;
|
||||||
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment;
|
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment;
|
||||||
import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment;
|
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