converted/Migrated

This commit is contained in:
Sujal-Gupta-SG 2025-02-03 16:46:28 +05:30
parent 2d1834aa38
commit adf15f2fbd
17 changed files with 1098 additions and 1130 deletions

View file

@ -1,110 +1,96 @@
package fr.free.nrw.commons.contributions;
package fr.free.nrw.commons.contributions
import android.net.Uri;
import android.text.TextUtils;
import android.view.View;
import android.webkit.URLUtil;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AlertDialog.Builder;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.ContributionsListAdapter.Callback;
import fr.free.nrw.commons.databinding.LayoutContributionBinding;
import fr.free.nrw.commons.media.MediaClient;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import java.io.File;
import android.net.Uri
import android.text.TextUtils
import android.view.View
import android.webkit.URLUtil
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.RecyclerView
import com.facebook.imagepipeline.request.ImageRequest
import com.facebook.imagepipeline.request.ImageRequestBuilder
import fr.free.nrw.commons.R
import fr.free.nrw.commons.databinding.LayoutContributionBinding
import fr.free.nrw.commons.media.MediaClient
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import java.io.File
public class ContributionViewHolder extends RecyclerView.ViewHolder {
class ContributionViewHolder internal constructor(
private val parent: View, private val callback: ContributionsListAdapter.Callback,
private val mediaClient: MediaClient
) : RecyclerView.ViewHolder(parent) {
var binding: LayoutContributionBinding = LayoutContributionBinding.bind(parent)
private final Callback callback;
private var position = 0
private var contribution: Contribution? = null
private val compositeDisposable = CompositeDisposable()
private var isWikipediaButtonDisplayed = false
private val pausingPopUp: AlertDialog
var imageRequest: ImageRequest? = null
private set
LayoutContributionBinding binding;
private int position;
private Contribution contribution;
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
private final MediaClient mediaClient;
private boolean isWikipediaButtonDisplayed;
private AlertDialog pausingPopUp;
private View parent;
private ImageRequest imageRequest;
ContributionViewHolder(final View parent, final Callback callback,
final MediaClient mediaClient) {
super(parent);
this.parent = parent;
this.mediaClient = mediaClient;
this.callback = callback;
binding = LayoutContributionBinding.bind(parent);
binding.contributionImage.setOnClickListener(v -> imageClicked());
binding.wikipediaButton.setOnClickListener(v -> wikipediaButtonClicked());
init {
binding.contributionImage.setOnClickListener { v: View? -> imageClicked() }
binding.wikipediaButton.setOnClickListener { v: View? -> wikipediaButtonClicked() }
/* Set a dialog indicating that the upload is being paused. This is needed because pausing
an upload might take a dozen seconds. */
AlertDialog.Builder builder = new Builder(parent.getContext());
builder.setCancelable(false);
builder.setView(R.layout.progress_dialog);
pausingPopUp = builder.create();
val builder = AlertDialog.Builder(
parent.context
)
builder.setCancelable(false)
builder.setView(R.layout.progress_dialog)
pausingPopUp = builder.create()
}
public void init(final int position, final Contribution contribution) {
fun init(position: Int, contribution: Contribution?) {
//handling crashes when the contribution is null.
if (null == contribution) {
return;
return
}
this.contribution = contribution;
this.position = position;
binding.contributionTitle.setText(contribution.getMedia().getMostRelevantCaption());
binding.authorView.setText(contribution.getMedia().getAuthor());
this.contribution = contribution
this.position = position
binding.contributionTitle.text = contribution.media.mostRelevantCaption
binding.authorView.text = contribution.media.author
//Removes flicker of loading image.
binding.contributionImage.getHierarchy().setFadeDuration(0);
binding.contributionImage.hierarchy.fadeDuration = 0
binding.contributionImage.getHierarchy().setPlaceholderImage(R.drawable.image_placeholder);
binding.contributionImage.getHierarchy().setFailureImage(R.drawable.image_placeholder);
binding.contributionImage.hierarchy.setPlaceholderImage(R.drawable.image_placeholder)
binding.contributionImage.hierarchy.setFailureImage(R.drawable.image_placeholder)
final String imageSource = chooseImageSource(contribution.getMedia().getThumbUrl(),
contribution.getLocalUri());
val imageSource = chooseImageSource(
contribution.media.thumbUrl,
contribution.localUri
)
if (!TextUtils.isEmpty(imageSource)) {
if (URLUtil.isHttpsUrl(imageSource)) {
imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageSource))
.setProgressiveRenderingEnabled(true)
.build();
.build()
} else if (URLUtil.isFileUrl(imageSource)) {
imageRequest = ImageRequest.fromUri(Uri.parse(imageSource));
imageRequest = ImageRequest.fromUri(Uri.parse(imageSource))
} else if (imageSource != null) {
final File file = new File(imageSource);
imageRequest = ImageRequest.fromFile(file);
val file = File(imageSource)
imageRequest = ImageRequest.fromFile(file)
}
if (imageRequest != null) {
binding.contributionImage.setImageRequest(imageRequest);
binding.contributionImage.setImageRequest(imageRequest)
}
}
binding.contributionSequenceNumber.setText(String.valueOf(position + 1));
binding.contributionSequenceNumber.setVisibility(View.VISIBLE);
binding.wikipediaButton.setVisibility(View.GONE);
binding.contributionState.setVisibility(View.GONE);
binding.contributionProgress.setVisibility(View.GONE);
binding.imageOptions.setVisibility(View.GONE);
binding.contributionState.setText("");
checkIfMediaExistsOnWikipediaPage(contribution);
binding.contributionSequenceNumber.text = (position + 1).toString()
binding.contributionSequenceNumber.visibility = View.VISIBLE
binding.wikipediaButton.visibility = View.GONE
binding.contributionState.visibility = View.GONE
binding.contributionProgress.visibility = View.GONE
binding.imageOptions.visibility = View.GONE
binding.contributionState.text = ""
checkIfMediaExistsOnWikipediaPage(contribution)
}
/**
@ -113,18 +99,20 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
*
* @param contribution
*/
private void checkIfMediaExistsOnWikipediaPage(final Contribution contribution) {
if (contribution.getWikidataPlace() == null
|| contribution.getWikidataPlace().getWikipediaArticle() == null) {
return;
private fun checkIfMediaExistsOnWikipediaPage(contribution: Contribution) {
if (contribution.wikidataPlace == null
|| contribution.wikidataPlace!!.wikipediaArticle == null
) {
return
}
final String wikipediaArticle = contribution.getWikidataPlace().getWikipediaPageTitle();
compositeDisposable.add(mediaClient.doesPageContainMedia(wikipediaArticle)
val wikipediaArticle = contribution.wikidataPlace!!.getWikipediaPageTitle()
compositeDisposable.add(
mediaClient.doesPageContainMedia(wikipediaArticle)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(mediaExists -> {
displayWikipediaButton(mediaExists);
}));
.subscribe { mediaExists: Boolean ->
displayWikipediaButton(mediaExists)
})
}
/**
@ -134,11 +122,11 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
*
* @param mediaExists
*/
private void displayWikipediaButton(Boolean mediaExists) {
private fun displayWikipediaButton(mediaExists: Boolean) {
if (!mediaExists) {
binding.wikipediaButton.setVisibility(View.VISIBLE);
isWikipediaButtonDisplayed = true;
binding.imageOptions.setVisibility(View.VISIBLE);
binding.wikipediaButton.visibility = View.VISIBLE
isWikipediaButtonDisplayed = true
binding.imageOptions.visibility = View.VISIBLE
}
}
@ -150,22 +138,15 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
* @param localUri
* @return
*/
@Nullable
private String chooseImageSource(final String thumbUrl, final Uri localUri) {
return !TextUtils.isEmpty(thumbUrl) ? thumbUrl :
localUri != null ? localUri.toString() :
null;
private fun chooseImageSource(thumbUrl: String?, localUri: Uri?): String? {
return if (!TextUtils.isEmpty(thumbUrl)) thumbUrl else localUri?.toString()
}
public void imageClicked() {
callback.openMediaDetail(position, isWikipediaButtonDisplayed);
fun imageClicked() {
callback.openMediaDetail(position, isWikipediaButtonDisplayed)
}
public void wikipediaButtonClicked() {
callback.addImageToWikipedia(contribution);
}
public ImageRequest getImageRequest() {
return imageRequest;
fun wikipediaButtonClicked() {
callback.addImageToWikipedia(contribution)
}
}

View file

@ -500,7 +500,7 @@ class ContributionsFragment
private fun setUploadCount() {
okHttpJsonApiClient
?.getUploadCount((activity as MainActivity).sessionManager.currentAccount!!.name)
?.getUploadCount((activity as MainActivity).sessionManager?.currentAccount!!.name)
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())?.let {
compositeDisposable.add(
@ -838,7 +838,7 @@ class ContributionsFragment
mediaDetailPagerFragment!!.showImage(position, isWikipediaButtonDisplayed)
}
override fun getMediaAtPosition(i: Int): Media {
override fun getMediaAtPosition(i: Int): Media? {
return contributionsListFragment!!.getMediaAtPosition(i)
}

View file

@ -1,273 +1,286 @@
package fr.free.nrw.commons.contributions;
package fr.free.nrw.commons.contributions
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE;
import android.Manifest.permission;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions;
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
import androidx.recyclerview.widget.RecyclerView.ItemAnimator;
import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
import androidx.recyclerview.widget.SimpleItemAnimator;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.contributions.ContributionsListAdapter.Callback;
import fr.free.nrw.commons.databinding.FragmentContributionsListBinding;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.profile.ProfileActivity;
import fr.free.nrw.commons.utils.DialogUtil;
import fr.free.nrw.commons.utils.SystemThemeUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import java.util.Map;
import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
import fr.free.nrw.commons.wikidata.model.WikiSite;
import android.Manifest.permission
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.LinearLayout
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.VisibleForTesting
import androidx.paging.PagedList
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener
import androidx.recyclerview.widget.SimpleItemAnimator
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.R
import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.contributions.WikipediaInstructionsDialogFragment.Companion.newInstance
import fr.free.nrw.commons.databinding.FragmentContributionsListBinding
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
import fr.free.nrw.commons.di.NetworkingModule
import fr.free.nrw.commons.filepicker.FilePicker
import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.profile.ProfileActivity
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
import fr.free.nrw.commons.utils.SystemThemeUtils
import fr.free.nrw.commons.utils.ViewUtil.showShortToast
import fr.free.nrw.commons.wikidata.model.WikiSite
import org.apache.commons.lang3.StringUtils
import javax.inject.Inject
import javax.inject.Named
/**
* Created by root on 01.06.2018.
*/
class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsListContract.View,
ContributionsListAdapter.Callback, WikipediaInstructionsDialogFragment.Callback {
@JvmField
@Inject
var systemThemeUtils: SystemThemeUtils? = null
public class ContributionsListFragment extends CommonsDaggerSupportFragment implements
ContributionsListContract.View, Callback,
WikipediaInstructionsDialogFragment.Callback {
@JvmField
@Inject
var controller: ContributionController? = null
private static final String RV_STATE = "rv_scroll_state";
@JvmField
@Inject
var mediaClient: MediaClient? = null
@JvmField
@Named(NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE)
@Inject
SystemThemeUtils systemThemeUtils;
@Inject
ContributionController controller;
@Inject
MediaClient mediaClient;
@Named(NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE)
@Inject
WikiSite languageWikipediaSite;
@Inject
ContributionsListPresenter contributionsListPresenter;
@Inject
SessionManager sessionManager;
var languageWikipediaSite: WikiSite? = null
private FragmentContributionsListBinding binding;
private Animation fab_close;
private Animation fab_open;
private Animation rotate_forward;
private Animation rotate_backward;
private boolean isFabOpen;
@VisibleForTesting
protected RecyclerView rvContributionsList;
@JvmField
@Inject
var contributionsListPresenter: ContributionsListPresenter? = null
@JvmField
@Inject
var sessionManager: SessionManager? = null
private var binding: FragmentContributionsListBinding? = null
private var fab_close: Animation? = null
private var fab_open: Animation? = null
private var rotate_forward: Animation? = null
private var rotate_backward: Animation? = null
private var isFabOpen = false
private lateinit var inAppCameraLocationPermissionLauncher: ActivityResultLauncher<Array<String>>
@VisibleForTesting
protected ContributionsListAdapter adapter;
var rvContributionsList: RecyclerView? = null
@Nullable
@VisibleForTesting
protected Callback callback;
var adapter: ContributionsListAdapter? = null
private final int SPAN_COUNT_LANDSCAPE = 3;
private final int SPAN_COUNT_PORTRAIT = 1;
@VisibleForTesting
var callback: Callback? = null
private int contributionsSize;
private String userName;
private val SPAN_COUNT_LANDSCAPE = 3
private val SPAN_COUNT_PORTRAIT = 1
private final ActivityResultLauncher<Intent> galleryPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks);
});
});
private var contributionsSize = 0
private var userName: String? = null
private final ActivityResultLauncher<Intent> customSelectorLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromCustomSelector(result, requireActivity(), callbacks);
});
});
private final ActivityResultLauncher<Intent> cameraPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
});
});
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(
new RequestMultiplePermissions(),
new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
boolean areAllGranted = true;
for (final boolean b : result.values()) {
areAllGranted = areAllGranted && b;
}
if (areAllGranted) {
controller.locationPermissionCallback.onLocationPermissionGranted();
} else {
if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
controller.handleShowRationaleFlowCameraLocation(getActivity(),
inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
} else {
controller.locationPermissionCallback.onLocationPermissionDenied(
getActivity().getString(
R.string.in_app_camera_location_permission_denied));
private val galleryPickLauncherForResult = registerForActivityResult<Intent, ActivityResult>(
StartActivityForResult()
) { result: ActivityResult? ->
controller!!.handleActivityResultWithCallback(requireActivity()
) { callbacks: FilePicker.Callbacks? ->
controller!!.onPictureReturnedFromGallery(
result!!, requireActivity(), callbacks!!
)
}
}
private val customSelectorLauncherForResult = registerForActivityResult<Intent, ActivityResult>(
StartActivityForResult()
) { result: ActivityResult? ->
controller!!.handleActivityResultWithCallback(requireActivity()
) { callbacks: FilePicker.Callbacks? ->
controller!!.onPictureReturnedFromCustomSelector(
result!!, requireActivity(), callbacks!!
)
}
}
});
private val cameraPickLauncherForResult = registerForActivityResult<Intent, ActivityResult>(
StartActivityForResult()
) { result: ActivityResult? ->
controller!!.handleActivityResultWithCallback(requireActivity()
) { callbacks: FilePicker.Callbacks? ->
controller!!.onPictureReturnedFromCamera(
result!!, requireActivity(), callbacks!!
)
}
}
@Override
public void onCreate(
@Nullable @org.jetbrains.annotations.Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@SuppressLint("NewApi")
override fun onCreate(
savedInstanceState: Bundle?
) {
super.onCreate(savedInstanceState)
//Now that we are allowing this fragment to be started for
// any userName- we expect it to be passed as an argument
if (getArguments() != null) {
userName = getArguments().getString(ProfileActivity.KEY_USERNAME);
if (arguments != null) {
userName = requireArguments().getString(ProfileActivity.KEY_USERNAME)
}
if (StringUtils.isEmpty(userName)) {
userName = sessionManager.getUserName();
userName = sessionManager!!.userName
}
inAppCameraLocationPermissionLauncher =
registerForActivityResult(RequestMultiplePermissions()) { result ->
val areAllGranted = result.values.all { it }
if (areAllGranted) {
controller?.locationPermissionCallback?.onLocationPermissionGranted()
} else {
activity?.let { currentActivity ->
if (currentActivity.shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
controller?.handleShowRationaleFlowCameraLocation(
currentActivity,
inAppCameraLocationPermissionLauncher, // Pass launcher
cameraPickLauncherForResult
)
} else {
controller?.locationPermissionCallback?.onLocationPermissionDenied(
currentActivity.getString(R.string.in_app_camera_location_permission_denied)
)
}
}
}
}
}
@Override
public View onCreateView(
final LayoutInflater inflater, @Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentContributionsListBinding.inflate(
inflater, container, false
);
rvContributionsList = binding.contributionsList;
)
rvContributionsList = binding!!.contributionsList
contributionsListPresenter.onAttachView(this);
binding.fabCustomGallery.setOnClickListener(v -> launchCustomSelector());
binding.fabCustomGallery.setOnLongClickListener(view -> {
ViewUtil.showShortToast(getContext(), R.string.custom_selector_title);
return true;
});
if (Objects.equals(sessionManager.getUserName(), userName)) {
binding.tvContributionsOfUser.setVisibility(GONE);
binding.fabLayout.setVisibility(VISIBLE);
} else {
binding.tvContributionsOfUser.setVisibility(VISIBLE);
binding.tvContributionsOfUser.setText(
getString(R.string.contributions_of_user, userName));
binding.fabLayout.setVisibility(GONE);
contributionsListPresenter!!.onAttachView(this)
binding!!.fabCustomGallery.setOnClickListener { v: View? -> launchCustomSelector() }
binding!!.fabCustomGallery.setOnLongClickListener { view: View? ->
showShortToast(context, fr.free.nrw.commons.R.string.custom_selector_title)
true
}
initAdapter();
if (sessionManager!!.userName == userName) {
binding!!.tvContributionsOfUser.visibility = View.GONE
binding!!.fabLayout.visibility = View.VISIBLE
} else {
binding!!.tvContributionsOfUser.visibility = View.VISIBLE
binding!!.tvContributionsOfUser.text =
getString(fr.free.nrw.commons.R.string.contributions_of_user, userName)
binding!!.fabLayout.visibility = View.GONE
}
initAdapter()
// pull down to refresh only enabled for self user.
if(Objects.equals(sessionManager.getUserName(), userName)){
binding.swipeRefreshLayout.setOnRefreshListener(() -> {
contributionsListPresenter.refreshList(binding.swipeRefreshLayout);
});
if (sessionManager!!.userName == userName) {
binding!!.swipeRefreshLayout.setOnRefreshListener {
contributionsListPresenter!!.refreshList(
binding!!.swipeRefreshLayout
)
}
} else {
binding.swipeRefreshLayout.setEnabled(false);
binding!!.swipeRefreshLayout.isEnabled = false
}
return binding.getRoot();
return binding!!.root
}
@Override
public void onDestroyView() {
binding = null;
super.onDestroyView();
override fun onDestroyView() {
binding = null
super.onDestroyView()
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (getParentFragment() != null && getParentFragment() instanceof ContributionsFragment) {
callback = ((ContributionsFragment) getParentFragment());
override fun onAttach(context: Context) {
super.onAttach(context)
if (parentFragment != null && parentFragment is ContributionsFragment) {
callback = (parentFragment as ContributionsFragment)
}
}
@Override
public void onDetach() {
super.onDetach();
callback = null;//To avoid possible memory leak
override fun onDetach() {
super.onDetach()
callback = null //To avoid possible memory leak
}
private void initAdapter() {
adapter = new ContributionsListAdapter(this, mediaClient);
private fun initAdapter() {
adapter = ContributionsListAdapter(this, mediaClient!!)
}
@Override
public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initRecyclerView();
initializeAnimations();
setListeners();
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initRecyclerView()
initializeAnimations()
setListeners()
}
private void initRecyclerView() {
final GridLayoutManager layoutManager = new GridLayoutManager(getContext(),
getSpanCount(getResources().getConfiguration().orientation));
rvContributionsList.setLayoutManager(layoutManager);
private fun initRecyclerView() {
val layoutManager = GridLayoutManager(
context,
getSpanCount(resources.configuration.orientation)
)
rvContributionsList!!.layoutManager = layoutManager
//Setting flicker animation of recycler view to false.
final ItemAnimator animator = rvContributionsList.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
val animator = rvContributionsList!!.itemAnimator
if (animator is SimpleItemAnimator) {
animator.supportsChangeAnimations = false
}
contributionsListPresenter.setup(userName,
Objects.equals(sessionManager.getUserName(), userName));
contributionsListPresenter.contributionList.observe(getViewLifecycleOwner(), list -> {
contributionsSize = list.size();
adapter.submitList(list);
if (callback != null) {
callback.notifyDataSetChanged();
contributionsListPresenter!!.setup(
userName,
sessionManager!!.userName == userName
)
contributionsListPresenter!!.contributionList?.observe(
viewLifecycleOwner
) { list: PagedList<Contribution>? ->
if (list != null) {
contributionsSize = list.size
}
});
rvContributionsList.setAdapter(adapter);
adapter.registerAdapterDataObserver(new AdapterDataObserver() {
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
contributionsSize = adapter.getItemCount();
adapter!!.submitList(list)
if (callback != null) {
callback.notifyDataSetChanged();
callback!!.notifyDataSetChanged()
}
}
rvContributionsList!!.adapter = adapter
adapter!!.registerAdapterDataObserver(object : AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
super.onItemRangeInserted(positionStart, itemCount)
contributionsSize = adapter!!.itemCount
if (callback != null) {
callback!!.notifyDataSetChanged()
}
if (itemCount > 0 && positionStart == 0) {
if (adapter.getContributionForPosition(positionStart) != null) {
rvContributionsList
.scrollToPosition(0);//Newly upload items are always added to the top
if (adapter!!.getContributionForPosition(positionStart) != null) {
rvContributionsList!!
.scrollToPosition(0) //Newly upload items are always added to the top
}
}
}
@ -276,146 +289,148 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
* Called whenever items in the list have changed
* Calls viewPagerNotifyDataSetChanged() that will notify the viewpager
*/
@Override
public void onItemRangeChanged(final int positionStart, final int itemCount) {
super.onItemRangeChanged(positionStart, itemCount);
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
super.onItemRangeChanged(positionStart, itemCount)
if (callback != null) {
callback.viewPagerNotifyDataSetChanged();
callback!!.viewPagerNotifyDataSetChanged()
}
}
});
})
//Fab close on touch outside (Scrolling or taping on item triggers this action).
rvContributionsList.addOnItemTouchListener(new OnItemTouchListener() {
rvContributionsList!!.addOnItemTouchListener(object : OnItemTouchListener {
/**
* Silently observe and/or take over touch events sent to the RecyclerView before
* they are handled by either the RecyclerView itself or its child views.
*/
@Override
public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
if (e.action == MotionEvent.ACTION_DOWN) {
if (isFabOpen) {
animateFAB(isFabOpen);
animateFAB(isFabOpen)
}
}
return false;
return false
}
/**
* Process a touch event as part of a gesture that was claimed by returning true
* from a previous call to {@link #onInterceptTouchEvent}.
* from a previous call to [.onInterceptTouchEvent].
*
* @param rv
* @param e MotionEvent describing the touch event. All coordinates are in the
* RecyclerView's coordinate system.
*/
@Override
public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
//required abstract method DO NOT DELETE
}
/**
* Called when a child of RecyclerView does not want RecyclerView and its ancestors
* to intercept touch events with {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
* to intercept touch events with [ViewGroup.onInterceptTouchEvent].
*
* @param disallowIntercept True if the child does not want the parent to intercept
* touch events.
*/
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
//required abstract method DO NOT DELETE
}
});
})
}
private int getSpanCount(final int orientation) {
return orientation == Configuration.ORIENTATION_LANDSCAPE ?
SPAN_COUNT_LANDSCAPE : SPAN_COUNT_PORTRAIT;
private fun getSpanCount(orientation: Int): Int {
return if (orientation == Configuration.ORIENTATION_LANDSCAPE) SPAN_COUNT_LANDSCAPE else SPAN_COUNT_PORTRAIT
}
@Override
public void onConfigurationChanged(final Configuration newConfig) {
super.onConfigurationChanged(newConfig);
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// check orientation
binding.fabLayout.setOrientation(
newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ?
LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
binding!!.fabLayout.orientation =
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) LinearLayout.HORIZONTAL else LinearLayout.VERTICAL
rvContributionsList
.setLayoutManager(
new GridLayoutManager(getContext(), getSpanCount(newConfig.orientation)));
?.setLayoutManager(
GridLayoutManager(context, getSpanCount(newConfig.orientation))
)
}
private void initializeAnimations() {
fab_open = AnimationUtils.loadAnimation(getActivity(), R.anim.fab_open);
fab_close = AnimationUtils.loadAnimation(getActivity(), R.anim.fab_close);
rotate_forward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_forward);
rotate_backward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_backward);
private fun initializeAnimations() {
fab_open = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.fab_open)
fab_close = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.fab_close)
rotate_forward = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.rotate_forward)
rotate_backward = AnimationUtils.loadAnimation(activity, fr.free.nrw.commons.R.anim.rotate_backward)
}
private void setListeners() {
binding.fabPlus.setOnClickListener(view -> animateFAB(isFabOpen));
binding.fabCamera.setOnClickListener(view -> {
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
animateFAB(isFabOpen);
});
binding.fabCamera.setOnLongClickListener(view -> {
ViewUtil.showShortToast(getContext(), R.string.add_contribution_from_camera);
return true;
});
binding.fabGallery.setOnClickListener(view -> {
controller.initiateGalleryPick(getActivity(), galleryPickLauncherForResult, true);
animateFAB(isFabOpen);
});
binding.fabGallery.setOnLongClickListener(view -> {
ViewUtil.showShortToast(getContext(), R.string.menu_from_gallery);
return true;
});
private fun setListeners() {
binding!!.fabPlus.setOnClickListener { view: View? -> animateFAB(isFabOpen) }
binding!!.fabCamera.setOnClickListener { view: View? ->
controller!!.initiateCameraPick(
requireActivity(),
inAppCameraLocationPermissionLauncher,
cameraPickLauncherForResult
)
animateFAB(isFabOpen)
}
binding!!.fabCamera.setOnLongClickListener { view: View? ->
showShortToast(
context,
fr.free.nrw.commons.R.string.add_contribution_from_camera
)
true
}
binding!!.fabGallery.setOnClickListener { view: View? ->
controller!!.initiateGalleryPick(requireActivity(), galleryPickLauncherForResult, true)
animateFAB(isFabOpen)
}
binding!!.fabGallery.setOnLongClickListener { view: View? ->
showShortToast(context, fr.free.nrw.commons.R.string.menu_from_gallery)
true
}
}
/**
* Launch Custom Selector.
*/
protected void launchCustomSelector() {
controller.initiateCustomGalleryPickWithPermission(getActivity(), customSelectorLauncherForResult);
animateFAB(isFabOpen);
protected fun launchCustomSelector() {
controller!!.initiateCustomGalleryPickWithPermission(
requireActivity(),
customSelectorLauncherForResult
)
animateFAB(isFabOpen)
}
public void scrollToTop() {
rvContributionsList.smoothScrollToPosition(0);
fun scrollToTop() {
rvContributionsList!!.smoothScrollToPosition(0)
}
private void animateFAB(final boolean isFabOpen) {
this.isFabOpen = !isFabOpen;
if (binding.fabPlus.isShown()) {
private fun animateFAB(isFabOpen: Boolean) {
this.isFabOpen = !isFabOpen
if (binding!!.fabPlus.isShown) {
if (isFabOpen) {
binding.fabPlus.startAnimation(rotate_backward);
binding.fabCamera.startAnimation(fab_close);
binding.fabGallery.startAnimation(fab_close);
binding.fabCustomGallery.startAnimation(fab_close);
binding.fabCamera.hide();
binding.fabGallery.hide();
binding.fabCustomGallery.hide();
binding!!.fabPlus.startAnimation(rotate_backward)
binding!!.fabCamera.startAnimation(fab_close)
binding!!.fabGallery.startAnimation(fab_close)
binding!!.fabCustomGallery.startAnimation(fab_close)
binding!!.fabCamera.hide()
binding!!.fabGallery.hide()
binding!!.fabCustomGallery.hide()
} else {
binding.fabPlus.startAnimation(rotate_forward);
binding.fabCamera.startAnimation(fab_open);
binding.fabGallery.startAnimation(fab_open);
binding.fabCustomGallery.startAnimation(fab_open);
binding.fabCamera.show();
binding.fabGallery.show();
binding.fabCustomGallery.show();
binding!!.fabPlus.startAnimation(rotate_forward)
binding!!.fabCamera.startAnimation(fab_open)
binding!!.fabGallery.startAnimation(fab_open)
binding!!.fabCustomGallery.startAnimation(fab_open)
binding!!.fabCamera.show()
binding!!.fabGallery.show()
binding!!.fabCustomGallery.show()
}
this.isFabOpen = !isFabOpen;
this.isFabOpen = !isFabOpen
}
}
/**
* Shows welcome message if user has no contributions yet i.e. new user.
*/
@Override
public void showWelcomeTip(final boolean shouldShow) {
binding.noContributionsYet.setVisibility(shouldShow ? VISIBLE : GONE);
override fun showWelcomeTip(shouldShow: Boolean) {
binding!!.noContributionsYet.visibility =
if (shouldShow) View.VISIBLE else View.GONE
}
/**
@ -423,37 +438,34 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
*
* @param shouldShow True when contributions list should be hidden.
*/
@Override
public void showProgress(final boolean shouldShow) {
binding.loadingContributionsProgressBar.setVisibility(shouldShow ? VISIBLE : GONE);
override fun showProgress(shouldShow: Boolean) {
binding!!.loadingContributionsProgressBar.visibility =
if (shouldShow) View.VISIBLE else View.GONE
}
@Override
public void showNoContributionsUI(final boolean shouldShow) {
binding.noContributionsYet.setVisibility(shouldShow ? VISIBLE : GONE);
override fun showNoContributionsUI(shouldShow: Boolean) {
binding!!.noContributionsYet.visibility =
if (shouldShow) View.VISIBLE else View.GONE
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
final GridLayoutManager layoutManager = (GridLayoutManager) rvContributionsList
.getLayoutManager();
outState.putParcelable(RV_STATE, layoutManager.onSaveInstanceState());
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val layoutManager = rvContributionsList
?.getLayoutManager() as GridLayoutManager?
outState.putParcelable(RV_STATE, layoutManager!!.onSaveInstanceState())
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
if (null != savedInstanceState) {
final Parcelable savedRecyclerLayoutState = savedInstanceState.getParcelable(RV_STATE);
rvContributionsList.getLayoutManager().onRestoreInstanceState(savedRecyclerLayoutState);
val savedRecyclerLayoutState = savedInstanceState.getParcelable<Parcelable>(RV_STATE)
rvContributionsList!!.layoutManager!!.onRestoreInstanceState(savedRecyclerLayoutState)
}
}
@Override
public void openMediaDetail(final int position, boolean isWikipediaButtonDisplayed) {
override fun openMediaDetail(position: Int, isWikipediaButtonDisplayed: Boolean) {
if (null != callback) { //Just being safe, ideally they won't be called when detached
callback.showDetail(position, isWikipediaButtonDisplayed);
callback!!.showDetail(position, isWikipediaButtonDisplayed)
}
}
@ -462,16 +474,16 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
*
* @param contribution
*/
@Override
public void addImageToWikipedia(Contribution contribution) {
DialogUtil.showAlertDialog(getActivity(),
getString(R.string.add_picture_to_wikipedia_article_title),
getString(R.string.add_picture_to_wikipedia_article_desc),
() -> {
showAddImageToWikipediaInstructions(contribution);
}, () -> {
// do nothing
});
override fun addImageToWikipedia(contribution: Contribution?) {
showAlertDialog(
requireActivity(),
getString(fr.free.nrw.commons.R.string.add_picture_to_wikipedia_article_title),
getString(fr.free.nrw.commons.R.string.add_picture_to_wikipedia_article_desc),
{
if (contribution != null) {
showAddImageToWikipediaInstructions(contribution)
}
}, {})
}
/**
@ -479,56 +491,61 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
*
* @param contribution
*/
private void showAddImageToWikipediaInstructions(Contribution contribution) {
FragmentManager fragmentManager = getFragmentManager();
WikipediaInstructionsDialogFragment fragment = WikipediaInstructionsDialogFragment
.newInstance(contribution);
fragment.setCallback(this::onConfirmClicked);
fragment.show(fragmentManager, "WikimediaFragment");
private fun showAddImageToWikipediaInstructions(contribution: Contribution) {
val fragmentManager = fragmentManager
val fragment = newInstance(contribution)
fragment.callback =
WikipediaInstructionsDialogFragment.Callback { contribution: Contribution?, copyWikicode: Boolean ->
this.onConfirmClicked(
contribution,
copyWikicode
)
}
fragment.show(fragmentManager!!, "WikimediaFragment")
}
public Media getMediaAtPosition(final int i) {
if (adapter.getContributionForPosition(i) != null) {
return adapter.getContributionForPosition(i).getMedia();
fun getMediaAtPosition(i: Int): Media? {
if (adapter!!.getContributionForPosition(i) != null) {
return adapter!!.getContributionForPosition(i)!!.media
}
return null;
return null
}
public int getTotalMediaCount() {
return contributionsSize;
}
val totalMediaCount: Int
get() = contributionsSize
/**
* Open the editor for the language Wikipedia
*
* @param contribution
*/
@Override
public void onConfirmClicked(@Nullable Contribution contribution, boolean copyWikicode) {
override fun onConfirmClicked(contribution: Contribution?, copyWikicode: Boolean) {
if (copyWikicode) {
String wikicode = contribution.getMedia().getWikiCode();
Utils.copy("wikicode", wikicode, getContext());
val wikicode = contribution!!.media.wikiCode
Utils.copy("wikicode", wikicode, context)
}
final String url =
languageWikipediaSite.mobileUrl() + "/wiki/" + contribution.getWikidataPlace()
.getWikipediaPageTitle();
Utils.handleWebUrl(getContext(), Uri.parse(url));
val url =
languageWikipediaSite!!.mobileUrl() + "/wiki/" + (contribution!!.wikidataPlace
?.getWikipediaPageTitle())
Utils.handleWebUrl(context, Uri.parse(url))
}
public Integer getContributionStateAt(int position) {
return adapter.getContributionForPosition(position).getState();
fun getContributionStateAt(position: Int): Int {
return adapter!!.getContributionForPosition(position)!!.state
}
public interface Callback {
interface Callback {
fun notifyDataSetChanged()
void notifyDataSetChanged();
void showDetail(int position, boolean isWikipediaButtonDisplayed);
fun showDetail(position: Int, isWikipediaButtonDisplayed: Boolean)
// Notify the viewpager that number of items have changed.
void viewPagerNotifyDataSetChanged();
fun viewPagerNotifyDataSetChanged()
}
companion object {
private const val RV_STATE = "rv_scroll_state"
}
}

View file

@ -1,52 +1,30 @@
package fr.free.nrw.commons.contributions;
package fr.free.nrw.commons.contributions
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.paging.DataSource;
import androidx.paging.DataSource.Factory;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import fr.free.nrw.commons.contributions.ContributionsListContract.UserActionListener;
import io.reactivex.Scheduler;
import io.reactivex.disposables.CompositeDisposable;
import java.util.Collections;
import javax.inject.Inject;
import javax.inject.Named;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import androidx.lifecycle.LiveData
import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import fr.free.nrw.commons.di.CommonsApplicationModule
import io.reactivex.Scheduler
import io.reactivex.disposables.CompositeDisposable
import javax.inject.Inject
import javax.inject.Named
/**
* The presenter class for Contributions
*/
public class ContributionsListPresenter implements UserActionListener {
class ContributionsListPresenter @Inject internal constructor(
private val contributionBoundaryCallback: ContributionBoundaryCallback,
private val contributionsRemoteDataSource: ContributionsRemoteDataSource,
private val repository: ContributionsRepository,
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler
) : ContributionsListContract.UserActionListener {
private val compositeDisposable = CompositeDisposable()
private final ContributionBoundaryCallback contributionBoundaryCallback;
private final ContributionsRepository repository;
private final Scheduler ioThreadScheduler;
var contributionList: LiveData<PagedList<Contribution>>? = null
private final CompositeDisposable compositeDisposable;
private final ContributionsRemoteDataSource contributionsRemoteDataSource;
LiveData<PagedList<Contribution>> contributionList;
@Inject
ContributionsListPresenter(
final ContributionBoundaryCallback contributionBoundaryCallback,
final ContributionsRemoteDataSource contributionsRemoteDataSource,
final ContributionsRepository repository,
@Named(IO_THREAD) final Scheduler ioThreadScheduler) {
this.contributionBoundaryCallback = contributionBoundaryCallback;
this.repository = repository;
this.ioThreadScheduler = ioThreadScheduler;
this.contributionsRemoteDataSource = contributionsRemoteDataSource;
compositeDisposable = new CompositeDisposable();
}
@Override
public void onAttachView(final ContributionsListContract.View view) {
override fun onAttachView(view: ContributionsListContract.View) {
}
/**
@ -54,46 +32,46 @@ public class ContributionsListPresenter implements UserActionListener {
* the live data object. This method can be tweaked to update the lazy loading behavior of the
* contributions list
*/
void setup(String userName, boolean isSelf) {
final PagedList.Config pagedListConfig =
(new PagedList.Config.Builder())
fun setup(userName: String?, isSelf: Boolean) {
val pagedListConfig =
(PagedList.Config.Builder())
.setPrefetchDistance(50)
.setPageSize(10).build();
Factory<Integer, Contribution> factory;
boolean shouldSetBoundaryCallback;
.setPageSize(10).build()
val factory: DataSource.Factory<Int, Contribution>
val shouldSetBoundaryCallback: Boolean
if (!isSelf) {
//We don't want to persist contributions for other user's, therefore
// creating a new DataSource for them
contributionsRemoteDataSource.setUserName(userName);
factory = new Factory<Integer, Contribution>() {
@NonNull
@Override
public DataSource<Integer, Contribution> create() {
return contributionsRemoteDataSource;
contributionsRemoteDataSource.userName = userName
factory = object : DataSource.Factory<Int, Contribution>() {
override fun create(): DataSource<Int, Contribution> {
return contributionsRemoteDataSource
}
};
shouldSetBoundaryCallback = false;
}
shouldSetBoundaryCallback = false
} else {
contributionBoundaryCallback.setUserName(userName);
shouldSetBoundaryCallback = true;
contributionBoundaryCallback.userName = userName
shouldSetBoundaryCallback = true
factory = repository.fetchContributionsWithStates(
Collections.singletonList(Contribution.STATE_COMPLETED));
listOf(Contribution.STATE_COMPLETED)
)
}
LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory,
pagedListConfig);
val livePagedListBuilder: LivePagedListBuilder<Int, Contribution> = LivePagedListBuilder(
factory,
pagedListConfig
)
if (shouldSetBoundaryCallback) {
livePagedListBuilder.setBoundaryCallback(contributionBoundaryCallback);
livePagedListBuilder.setBoundaryCallback(contributionBoundaryCallback)
}
contributionList = livePagedListBuilder.build();
contributionList = livePagedListBuilder.build()
}
@Override
public void onDetachView() {
compositeDisposable.clear();
contributionsRemoteDataSource.dispose();
contributionBoundaryCallback.dispose();
override fun onDetachView() {
compositeDisposable.clear()
contributionsRemoteDataSource.dispose()
contributionBoundaryCallback.dispose()
}
/**
@ -102,11 +80,12 @@ public class ContributionsListPresenter implements UserActionListener {
* @param swipeRefreshLayout used to stop refresh animation when
* refresh finishes.
*/
@Override
public void refreshList(final SwipeRefreshLayout swipeRefreshLayout) {
contributionBoundaryCallback.refreshList(() -> {
swipeRefreshLayout.setRefreshing(false);
return Unit.INSTANCE;
});
override fun refreshList(swipeRefreshLayout: SwipeRefreshLayout?) {
contributionBoundaryCallback.refreshList {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.isRefreshing = false
}
Unit
}
}
}

View file

@ -1,42 +1,31 @@
package fr.free.nrw.commons.contributions;
package fr.free.nrw.commons.contributions
import androidx.paging.DataSource.Factory;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import io.reactivex.Completable;
import io.reactivex.Single;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import androidx.paging.DataSource
import fr.free.nrw.commons.kvstore.JsonKvStore
import io.reactivex.Completable
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Named
/**
* The LocalDataSource class for Contributions
*/
class ContributionsLocalDataSource {
private final ContributionDao contributionDao;
private final JsonKvStore defaultKVStore;
@Inject
public ContributionsLocalDataSource(
@Named("default_preferences") final JsonKvStore defaultKVStore,
final ContributionDao contributionDao) {
this.defaultKVStore = defaultKVStore;
this.contributionDao = contributionDao;
class ContributionsLocalDataSource @Inject constructor(
@param:Named("default_preferences") private val defaultKVStore: JsonKvStore,
private val contributionDao: ContributionDao
) {
/**
* Fetch default number of contributions to be show, based on user preferences
*/
fun getString(key: String): String? {
return defaultKVStore.getString(key)
}
/**
* Fetch default number of contributions to be show, based on user preferences
*/
public String getString(final String key) {
return defaultKVStore.getString(key);
}
/**
* Fetch default number of contributions to be show, based on user preferences
*/
public long getLong(final String key) {
return defaultKVStore.getLong(key);
fun getLong(key: String): Long {
return defaultKVStore.getLong(key)
}
/**
@ -45,13 +34,14 @@ class ContributionsLocalDataSource {
* @param uri
* @return
*/
public Contribution getContributionWithFileName(final String uri) {
final List<Contribution> contributionWithUri = contributionDao.getContributionWithTitle(
uri);
fun getContributionWithFileName(uri: String): Contribution? {
val contributionWithUri = contributionDao.getContributionWithTitle(
uri
)
if (!contributionWithUri.isEmpty()) {
return contributionWithUri.get(0);
return contributionWithUri[0]
}
return null;
return null
}
/**
@ -60,8 +50,8 @@ class ContributionsLocalDataSource {
* @param contribution
* @return
*/
public Completable deleteContribution(final Contribution contribution) {
return contributionDao.delete(contribution);
fun deleteContribution(contribution: Contribution): Completable {
return contributionDao.delete(contribution)
}
/**
@ -70,13 +60,12 @@ class ContributionsLocalDataSource {
* @param states The states of the contributions to delete.
* @return A Completable indicating the result of the operation.
*/
public Completable deleteContributionsWithStates(List<Integer> states) {
return contributionDao.deleteContributionsWithStates(states);
fun deleteContributionsWithStates(states: List<Int>): Completable {
return contributionDao.deleteContributionsWithStates(states)
}
public Factory<Integer, Contribution> getContributions() {
return contributionDao.fetchContributions();
}
val contributions: DataSource.Factory<Int, Contribution>
get() = contributionDao.fetchContributions()
/**
* Fetches contributions with specific states.
@ -84,8 +73,8 @@ class ContributionsLocalDataSource {
* @param states The states of the contributions to fetch.
* @return A DataSource factory for paginated contributions with the specified states.
*/
public Factory<Integer, Contribution> getContributionsWithStates(List<Integer> states) {
return contributionDao.getContributions(states);
fun getContributionsWithStates(states: List<Int>): DataSource.Factory<Int, Contribution> {
return contributionDao.getContributions(states)
}
/**
@ -95,37 +84,39 @@ class ContributionsLocalDataSource {
* @return A DataSource factory for paginated contributions with the specified states sorted by
* date upload started.
*/
public Factory<Integer, Contribution> getContributionsWithStatesSortedByDateUploadStarted(
List<Integer> states) {
return contributionDao.getContributionsSortedByDateUploadStarted(states);
fun getContributionsWithStatesSortedByDateUploadStarted(
states: List<Int>
): DataSource.Factory<Int, Contribution> {
return contributionDao.getContributionsSortedByDateUploadStarted(states)
}
public Single<List<Long>> saveContributions(final List<Contribution> contributions) {
final List<Contribution> contributionList = new ArrayList<>();
for (final Contribution contribution : contributions) {
final Contribution oldContribution = contributionDao.getContribution(
contribution.getPageId());
fun saveContributions(contributions: List<Contribution>): Single<List<Long>> {
val contributionList: MutableList<Contribution> = ArrayList()
for (contribution in contributions) {
val oldContribution = contributionDao.getContribution(
contribution.pageId
)
if (oldContribution != null) {
contribution.setWikidataPlace(oldContribution.getWikidataPlace());
contribution.wikidataPlace = oldContribution.wikidataPlace
}
contributionList.add(contribution);
contributionList.add(contribution)
}
return contributionDao.save(contributionList);
return contributionDao.save(contributionList)
}
public Completable saveContributions(Contribution contribution) {
return contributionDao.save(contribution);
fun saveContributions(contribution: Contribution): Completable {
return contributionDao.save(contribution)
}
public void set(final String key, final long value) {
defaultKVStore.putLong(key, value);
fun set(key: String, value: Long) {
defaultKVStore.putLong(key, value)
}
public Completable updateContribution(final Contribution contribution) {
return contributionDao.update(contribution);
fun updateContribution(contribution: Contribution): Completable {
return contributionDao.update(contribution)
}
public Completable updateContributionsWithStates(List<Integer> states, int newState) {
return contributionDao.updateContributionsWithStates(states, newState);
fun updateContributionsWithStates(states: List<Int>, newState: Int): Completable {
return contributionDao.updateContributionsWithStates(states, newState)
}
}

View file

@ -1,26 +1,16 @@
package fr.free.nrw.commons.contributions;
package fr.free.nrw.commons.contributions
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import dagger.Binds
import dagger.Module
/**
* The Dagger Module for contributions-related presenters and other dependencies
*/
@Module
public abstract class ContributionsModule {
abstract class ContributionsModule {
@Binds
public abstract ContributionsContract.UserActionListener bindsContributionsPresenter(
ContributionsPresenter presenter
);
@Provides
static JsonKvStore providesApplicationKvStore(
@Named("default_preferences") JsonKvStore kvStore
) {
return kvStore;
}
abstract fun bindsContributionsPresenter(
presenter: ContributionsPresenter?
): ContributionsContract.UserActionListener?
}

View file

@ -1,58 +1,45 @@
package fr.free.nrw.commons.contributions;
package fr.free.nrw.commons.contributions
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
import androidx.work.ExistingWorkPolicy;
import fr.free.nrw.commons.MediaDataExtractor;
import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener;
import fr.free.nrw.commons.di.CommonsApplicationModule;
import fr.free.nrw.commons.repository.UploadRepository;
import fr.free.nrw.commons.upload.worker.WorkRequestHelper;
import io.reactivex.Scheduler;
import io.reactivex.disposables.CompositeDisposable;
import javax.inject.Inject;
import javax.inject.Named;
import timber.log.Timber;
import fr.free.nrw.commons.utils.ImageUtils
import androidx.work.ExistingWorkPolicy
import fr.free.nrw.commons.MediaDataExtractor
import fr.free.nrw.commons.di.CommonsApplicationModule
import fr.free.nrw.commons.repository.UploadRepository
import fr.free.nrw.commons.upload.worker.WorkRequestHelper.Companion.makeOneTimeWorkRequest
import io.reactivex.Scheduler
import io.reactivex.disposables.CompositeDisposable
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
/**
* The presenter class for Contributions
*/
public class ContributionsPresenter implements UserActionListener {
private final ContributionsRepository contributionsRepository;
private final UploadRepository uploadRepository;
private final Scheduler ioThreadScheduler;
private CompositeDisposable compositeDisposable;
private ContributionsContract.View view;
class ContributionsPresenter @Inject internal constructor(
private val contributionsRepository: ContributionsRepository,
private val uploadRepository: UploadRepository,
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler
) : ContributionsContract.UserActionListener {
private var compositeDisposable: CompositeDisposable? = null
private var view: ContributionsContract.View? = null
@JvmField
@Inject
MediaDataExtractor mediaDataExtractor;
var mediaDataExtractor: MediaDataExtractor? = null
@Inject
ContributionsPresenter(ContributionsRepository repository,
UploadRepository uploadRepository,
@Named(IO_THREAD) Scheduler ioThreadScheduler) {
this.contributionsRepository = repository;
this.uploadRepository = uploadRepository;
this.ioThreadScheduler = ioThreadScheduler;
override fun onAttachView(view: ContributionsContract.View) {
this.view = view
compositeDisposable = CompositeDisposable()
}
@Override
public void onAttachView(ContributionsContract.View view) {
this.view = view;
compositeDisposable = new CompositeDisposable();
override fun onDetachView() {
this.view = null
compositeDisposable!!.clear()
}
@Override
public void onDetachView() {
this.view = null;
compositeDisposable.clear();
}
@Override
public Contribution getContributionsWithTitle(String title) {
return contributionsRepository.getContributionWithFileName(title);
override fun getContributionsWithTitle(title: String): Contribution {
return contributionsRepository.getContributionWithFileName(title)
?: throw IllegalArgumentException("Contribution not found for title: $title")
}
/**
@ -60,22 +47,25 @@ public class ContributionsPresenter implements UserActionListener {
*
* @param contribution The contribution to check and potentially restart.
*/
public void checkDuplicateImageAndRestartContribution(Contribution contribution) {
compositeDisposable.add(uploadRepository
.checkDuplicateImage(contribution.getLocalUriPath().getPath())
fun checkDuplicateImageAndRestartContribution(contribution: Contribution) {
compositeDisposable!!.add(
uploadRepository
.checkDuplicateImage(contribution.localUriPath!!.path)
.subscribeOn(ioThreadScheduler)
.subscribe(imageCheckResult -> {
if (imageCheckResult == IMAGE_OK) {
contribution.setState(Contribution.STATE_QUEUED);
saveContribution(contribution);
.subscribe { imageCheckResult: Int ->
if (imageCheckResult == ImageUtils.IMAGE_OK) {
contribution.state = Contribution.STATE_QUEUED
saveContribution(contribution)
} else {
Timber.e("Contribution already exists");
compositeDisposable.add(contributionsRepository
Timber.e("Contribution already exists")
compositeDisposable!!.add(
contributionsRepository
.deleteContributionFromDB(contribution)
.subscribeOn(ioThreadScheduler)
.subscribe());
.subscribe()
)
}
}));
})
}
/**
@ -84,11 +74,14 @@ public class ContributionsPresenter implements UserActionListener {
*
* @param contribution
*/
public void saveContribution(Contribution contribution) {
compositeDisposable.add(contributionsRepository
fun saveContribution(contribution: Contribution) {
compositeDisposable!!.add(contributionsRepository
.save(contribution)
.subscribeOn(ioThreadScheduler)
.subscribe(() -> WorkRequestHelper.Companion.makeOneTimeWorkRequest(
view.getContext().getApplicationContext(), ExistingWorkPolicy.KEEP)));
.subscribe {
makeOneTimeWorkRequest(
view!!.getContext().applicationContext, ExistingWorkPolicy.KEEP
)
})
}
}

View file

@ -0,0 +1,28 @@
package fr.free.nrw.commons.contributions
import dagger.Module
import dagger.Provides
import fr.free.nrw.commons.kvstore.JsonKvStore
import fr.free.nrw.commons.wikidata.model.WikiSite
import javax.inject.Named
/**
* The Dagger Module for contributions-related providers
*/
@Module
class ContributionsProvidesModule {
@Provides
fun providesApplicationKvStore(
@Named("default_preferences") kvStore: JsonKvStore
): JsonKvStore {
return kvStore
}
@Provides
fun providesLanguageWikipediaSite(
@Named("language-wikipedia-wikisite") languageWikipediaSite: WikiSite
): WikiSite {
return languageWikipediaSite
}
}

View file

@ -1,30 +1,19 @@
package fr.free.nrw.commons.contributions;
package fr.free.nrw.commons.contributions
import androidx.paging.DataSource.Factory;
import io.reactivex.Completable;
import java.util.List;
import javax.inject.Inject;
import io.reactivex.Single;
import androidx.paging.DataSource
import io.reactivex.Completable
import io.reactivex.Single
import javax.inject.Inject
/**
* The repository class for contributions
*/
public class ContributionsRepository {
private ContributionsLocalDataSource localDataSource;
@Inject
public ContributionsRepository(ContributionsLocalDataSource localDataSource) {
this.localDataSource = localDataSource;
}
class ContributionsRepository @Inject constructor(private val localDataSource: ContributionsLocalDataSource) {
/**
* Fetch default number of contributions to be show, based on user preferences
*/
public String getString(String key) {
return localDataSource.getString(key);
fun getString(key: String): String? {
return localDataSource.getString(key)
}
/**
@ -33,8 +22,8 @@ public class ContributionsRepository {
* @param contribution
* @return
*/
public Completable deleteContributionFromDB(Contribution contribution) {
return localDataSource.deleteContribution(contribution);
fun deleteContributionFromDB(contribution: Contribution): Completable {
return localDataSource.deleteContribution(contribution)
}
/**
@ -43,8 +32,8 @@ public class ContributionsRepository {
* @param states The states of the contributions to delete.
* @return A Completable indicating the result of the operation.
*/
public Completable deleteContributionsFromDBWithStates(List<Integer> states) {
return localDataSource.deleteContributionsWithStates(states);
fun deleteContributionsFromDBWithStates(states: List<Int>): Completable {
return localDataSource.deleteContributionsWithStates(states)
}
/**
@ -53,12 +42,12 @@ public class ContributionsRepository {
* @param fileName
* @return
*/
public Contribution getContributionWithFileName(String fileName) {
return localDataSource.getContributionWithFileName(fileName);
fun getContributionWithFileName(fileName: String): Contribution? {
return localDataSource.getContributionWithFileName(fileName)
}
public Factory<Integer, Contribution> fetchContributions() {
return localDataSource.getContributions();
fun fetchContributions(): DataSource.Factory<Int, Contribution> {
return localDataSource.contributions
}
/**
@ -67,8 +56,8 @@ public class ContributionsRepository {
* @param states The states of the contributions to fetch.
* @return A DataSource factory for paginated contributions with the specified states.
*/
public Factory<Integer, Contribution> fetchContributionsWithStates(List<Integer> states) {
return localDataSource.getContributionsWithStates(states);
fun fetchContributionsWithStates(states: List<Int>): DataSource.Factory<Int, Contribution> {
return localDataSource.getContributionsWithStates(states)
}
/**
@ -78,25 +67,26 @@ public class ContributionsRepository {
* @return A DataSource factory for paginated contributions with the specified states sorted by
* date upload started.
*/
public Factory<Integer, Contribution> fetchContributionsWithStatesSortedByDateUploadStarted(
List<Integer> states) {
return localDataSource.getContributionsWithStatesSortedByDateUploadStarted(states);
fun fetchContributionsWithStatesSortedByDateUploadStarted(
states: List<Int>
): DataSource.Factory<Int, Contribution> {
return localDataSource.getContributionsWithStatesSortedByDateUploadStarted(states)
}
public Single<List<Long>> save(List<Contribution> contributions) {
return localDataSource.saveContributions(contributions);
fun save(contributions: List<Contribution>): Single<List<Long>> {
return localDataSource.saveContributions(contributions)
}
public Completable save(Contribution contributions) {
return localDataSource.saveContributions(contributions);
fun save(contributions: Contribution): Completable {
return localDataSource.saveContributions(contributions)
}
public void set(String key, long value) {
localDataSource.set(key, value);
operator fun set(key: String, value: Long) {
localDataSource.set(key, value)
}
public Completable updateContribution(Contribution contribution) {
return localDataSource.updateContribution(contribution);
fun updateContribution(contribution: Contribution): Completable {
return localDataSource.updateContribution(contribution)
}
/**
@ -106,7 +96,7 @@ public class ContributionsRepository {
* @param newState The new state to set.
* @return A Completable indicating the result of the operation.
*/
public Completable updateContributionsWithStates(List<Integer> states, int newState) {
return localDataSource.updateContributionsWithStates(states, newState);
fun updateContributionsWithStates(states: List<Int>, newState: Int): Completable {
return localDataSource.updateContributionsWithStates(states, newState)
}
}

View file

@ -1,128 +1,125 @@
package fr.free.nrw.commons.contributions;
package fr.free.nrw.commons.contributions
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.work.ExistingWorkPolicy;
import fr.free.nrw.commons.databinding.MainBinding;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.bookmarks.BookmarkFragment;
import fr.free.nrw.commons.explore.ExploreFragment;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LocationServiceManager;
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment;
import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment;
import fr.free.nrw.commons.navtab.NavTab;
import fr.free.nrw.commons.navtab.NavTabLayout;
import fr.free.nrw.commons.navtab.NavTabLoggedOut;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment;
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.NearbyParentFragmentInstanceReadyCallback;
import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.notification.NotificationController;
import fr.free.nrw.commons.quiz.QuizChecker;
import fr.free.nrw.commons.settings.SettingsFragment;
import fr.free.nrw.commons.theme.BaseActivity;
import fr.free.nrw.commons.upload.UploadProgressActivity;
import fr.free.nrw.commons.upload.worker.WorkRequestHelper;
import fr.free.nrw.commons.utils.PermissionUtils;
import fr.free.nrw.commons.utils.ViewUtilWrapper;
import io.reactivex.Completable;
import io.reactivex.schedulers.Schedulers;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import timber.log.Timber;
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.work.ExistingWorkPolicy
import com.google.android.material.bottomnavigation.BottomNavigationView
import fr.free.nrw.commons.R
import fr.free.nrw.commons.WelcomeActivity
import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.bookmarks.BookmarkFragment
import fr.free.nrw.commons.contributions.ContributionsFragment.Companion.newInstance
import fr.free.nrw.commons.databinding.MainBinding
import fr.free.nrw.commons.explore.ExploreFragment
import fr.free.nrw.commons.kvstore.JsonKvStore
import fr.free.nrw.commons.location.LocationServiceManager
import fr.free.nrw.commons.media.MediaDetailPagerFragment
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment
import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment
import fr.free.nrw.commons.navtab.NavTab
import fr.free.nrw.commons.navtab.NavTabLayout
import fr.free.nrw.commons.navtab.NavTabLoggedOut
import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment
import fr.free.nrw.commons.notification.NotificationActivity.Companion.startYourself
import fr.free.nrw.commons.notification.NotificationController
import fr.free.nrw.commons.quiz.QuizChecker
import fr.free.nrw.commons.settings.SettingsFragment
import fr.free.nrw.commons.theme.BaseActivity
import fr.free.nrw.commons.upload.UploadProgressActivity
import fr.free.nrw.commons.upload.worker.WorkRequestHelper.Companion.makeOneTimeWorkRequest
import fr.free.nrw.commons.utils.ViewUtilWrapper
import io.reactivex.Completable
import io.reactivex.functions.Consumer
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
import java.util.Calendar
import javax.inject.Inject
import javax.inject.Named
public class MainActivity extends BaseActivity
implements FragmentManager.OnBackStackChangedListener {
class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener {
@JvmField
@Inject
SessionManager sessionManager;
@Inject
ContributionController controller;
@Inject
ContributionDao contributionDao;
var sessionManager: SessionManager? = null
private ContributionsFragment contributionsFragment;
private NearbyParentFragment nearbyParentFragment;
private ExploreFragment exploreFragment;
private BookmarkFragment bookmarkFragment;
public ActiveFragment activeFragment;
private MediaDetailPagerFragment mediaDetailPagerFragment;
private NavTabLayout.OnNavigationItemSelectedListener navListener;
@JvmField
@Inject
var controller: ContributionController? = null
@JvmField
@Inject
public LocationServiceManager locationManager;
var contributionDao: ContributionDao? = null
private var contributionsFragment: ContributionsFragment? = null
private var nearbyParentFragment: NearbyParentFragment? = null
private var exploreFragment: ExploreFragment? = null
private var bookmarkFragment: BookmarkFragment? = null
@JvmField
var activeFragment: ActiveFragment? = null
private val mediaDetailPagerFragment: MediaDetailPagerFragment? = null
var navListener: BottomNavigationView.OnNavigationItemSelectedListener? = null
private set
@JvmField
@Inject
NotificationController notificationController;
var locationManager: LocationServiceManager? = null
@JvmField
@Inject
QuizChecker quizChecker;
var notificationController: NotificationController? = null
@JvmField
@Inject
var quizChecker: QuizChecker? = null
@JvmField
@Inject
@Named("default_preferences")
public
JsonKvStore applicationKvStore;
var applicationKvStore: JsonKvStore? = null
@JvmField
@Inject
ViewUtilWrapper viewUtilWrapper;
var viewUtilWrapper: ViewUtilWrapper? = null
public Menu menu;
var menu: Menu? = null
public MainBinding binding;
@JvmField
var binding: MainBinding? = null
NavTabLayout tabLayout;
var tabLayout: NavTabLayout? = null
/**
* Consumers should be simply using this method to use this activity.
*
* @param context A Context of the application package implementing this class.
*/
public static void startYourself(Context context) {
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
@Override
public boolean onSupportNavigateUp() {
override fun onSupportNavigateUp(): Boolean {
if (activeFragment == ActiveFragment.CONTRIBUTIONS) {
if (!contributionsFragment.backButtonClicked()) {
return false;
if (!contributionsFragment!!.backButtonClicked()) {
return false
}
} else {
onBackPressed();
showTabs();
onBackPressed()
showTabs()
}
return true;
return true
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = MainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbarBinding.toolbar);
tabLayout = binding.fragmentMainNavTabLayout;
loadLocale();
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = MainBinding.inflate(layoutInflater)
setContentView(binding!!.root)
setSupportActionBar(binding!!.toolbarBinding.toolbar)
tabLayout = binding!!.fragmentMainNavTabLayout
loadLocale()
binding.toolbarBinding.toolbar.setNavigationOnClickListener(view -> {
onSupportNavigateUp();
});
binding!!.toolbarBinding.toolbar.setNavigationOnClickListener { view: View? ->
onSupportNavigateUp()
}
/*
"first_edit_depict" is a key for getting information about opening the depiction editor
screen for the first time after opening the app.
@ -132,28 +129,28 @@ public class MainActivity extends BaseActivity
Getting false by the key means the depiction editor screen is not opened for the first time
after opening the app.
*/
applicationKvStore.putBoolean("first_edit_depict", true);
if (applicationKvStore.getBoolean("login_skipped") == true) {
setTitle(getString(R.string.navigation_item_explore));
setUpLoggedOutPager();
applicationKvStore!!.putBoolean("first_edit_depict", true)
if (applicationKvStore!!.getBoolean("login_skipped") == true) {
title = getString(R.string.navigation_item_explore)
setUpLoggedOutPager()
} else {
if (applicationKvStore.getBoolean("firstrun", true)) {
applicationKvStore.putBoolean("hasAlreadyLaunchedBigMultiupload", false);
applicationKvStore.putBoolean("hasAlreadyLaunchedCategoriesDialog", false);
if (applicationKvStore!!.getBoolean("firstrun", true)) {
applicationKvStore!!.putBoolean("hasAlreadyLaunchedBigMultiupload", false)
applicationKvStore!!.putBoolean("hasAlreadyLaunchedCategoriesDialog", false)
}
if (savedInstanceState == null) {
//starting a fresh fragment.
// Open Last opened screen if it is Contributions or Nearby, otherwise Contributions
if (applicationKvStore.getBoolean("last_opened_nearby")) {
setTitle(getString(R.string.nearby_fragment));
showNearby();
loadFragment(NearbyParentFragment.newInstance(), false);
if (applicationKvStore!!.getBoolean("last_opened_nearby")) {
title = getString(R.string.nearby_fragment)
showNearby()
loadFragment(NearbyParentFragment.newInstance(), false)
} else {
setTitle(getString(R.string.contributions_fragment));
loadFragment(ContributionsFragment.newInstance(), false);
title = getString(R.string.contributions_fragment)
loadFragment(newInstance(), false)
}
}
setUpPager();
setUpPager()
/**
* Ask the user for media location access just after login
* so that location in the EXIF metadata of the images shared by the user
@ -169,99 +166,107 @@ public class MainActivity extends BaseActivity
// R.string.add_location_manually,
// permission.ACCESS_MEDIA_LOCATION);
// }
checkAndResumeStuckUploads();
checkAndResumeStuckUploads()
}
}
public void setSelectedItemId(int id) {
binding.fragmentMainNavTabLayout.setSelectedItemId(id);
fun setSelectedItemId(id: Int) {
binding!!.fragmentMainNavTabLayout.selectedItemId = id
}
private void setUpPager() {
binding.fragmentMainNavTabLayout.setOnNavigationItemSelectedListener(
navListener = (item) -> {
if (!item.getTitle().equals(getString(R.string.more))) {
private fun setUpPager() {
binding!!.fragmentMainNavTabLayout.setOnNavigationItemSelectedListener(
BottomNavigationView.OnNavigationItemSelectedListener { item: MenuItem ->
if (item.title != getString(R.string.more)) {
// do not change title for more fragment
setTitle(item.getTitle());
title = item.title
}
// set last_opened_nearby true if item is nearby screen else set false
applicationKvStore.putBoolean("last_opened_nearby",
item.getTitle().equals(getString(R.string.nearby_fragment)));
final Fragment fragment = NavTab.of(item.getOrder()).newInstance();
return loadFragment(fragment, true);
});
applicationKvStore!!.putBoolean(
"last_opened_nearby",
item.title == getString(R.string.nearby_fragment)
)
val fragment = NavTab.of(item.order).newInstance()
loadFragment(fragment, true)
}.also { navListener = it })
}
private void setUpLoggedOutPager() {
loadFragment(ExploreFragment.newInstance(), false);
binding.fragmentMainNavTabLayout.setOnNavigationItemSelectedListener(item -> {
if (!item.getTitle().equals(getString(R.string.more))) {
private fun setUpLoggedOutPager() {
loadFragment(ExploreFragment.newInstance(), false)
binding!!.fragmentMainNavTabLayout.setOnNavigationItemSelectedListener { item: MenuItem ->
if (item.title != getString(R.string.more)) {
// do not change title for more fragment
setTitle(item.getTitle());
title = item.title
}
val fragment =
NavTabLoggedOut.of(item.order).newInstance()
loadFragment(fragment, true)
}
Fragment fragment = NavTabLoggedOut.of(item.getOrder()).newInstance();
return loadFragment(fragment, true);
});
}
private boolean loadFragment(Fragment fragment, boolean showBottom) {
private fun loadFragment(fragment: Fragment?, showBottom: Boolean): Boolean {
//showBottom so that we do not show the bottom tray again when constructing
//from the saved instance state.
if (fragment instanceof ContributionsFragment) {
if (fragment is ContributionsFragment) {
if (activeFragment == ActiveFragment.CONTRIBUTIONS) {
// scroll to top if already on the Contributions tab
contributionsFragment.scrollToTop();
return true;
contributionsFragment!!.scrollToTop()
return true
}
contributionsFragment = (ContributionsFragment) fragment;
activeFragment = ActiveFragment.CONTRIBUTIONS;
} else if (fragment instanceof NearbyParentFragment) {
contributionsFragment = fragment
activeFragment = ActiveFragment.CONTRIBUTIONS
} else if (fragment is NearbyParentFragment) {
if (activeFragment == ActiveFragment.NEARBY) { // Do nothing if same tab
return true;
return true
}
nearbyParentFragment = (NearbyParentFragment) fragment;
activeFragment = ActiveFragment.NEARBY;
} else if (fragment instanceof ExploreFragment) {
nearbyParentFragment = fragment
activeFragment = ActiveFragment.NEARBY
} else if (fragment is ExploreFragment) {
if (activeFragment == ActiveFragment.EXPLORE) { // Do nothing if same tab
return true;
return true
}
exploreFragment = (ExploreFragment) fragment;
activeFragment = ActiveFragment.EXPLORE;
} else if (fragment instanceof BookmarkFragment) {
exploreFragment = fragment
activeFragment = ActiveFragment.EXPLORE
} else if (fragment is BookmarkFragment) {
if (activeFragment == ActiveFragment.BOOKMARK) { // Do nothing if same tab
return true;
return true
}
bookmarkFragment = (BookmarkFragment) fragment;
activeFragment = ActiveFragment.BOOKMARK;
bookmarkFragment = fragment
activeFragment = ActiveFragment.BOOKMARK
} else if (fragment == null && showBottom) {
if (applicationKvStore.getBoolean("login_skipped")
== true) { // If logged out, more sheet is different
MoreBottomSheetLoggedOutFragment bottomSheet = new MoreBottomSheetLoggedOutFragment();
bottomSheet.show(getSupportFragmentManager(),
"MoreBottomSheetLoggedOut");
if (applicationKvStore!!.getBoolean("login_skipped")
== true
) { // If logged out, more sheet is different
val bottomSheet = MoreBottomSheetLoggedOutFragment()
bottomSheet.show(
supportFragmentManager,
"MoreBottomSheetLoggedOut"
)
} else {
MoreBottomSheetFragment bottomSheet = new MoreBottomSheetFragment();
bottomSheet.show(getSupportFragmentManager(),
"MoreBottomSheet");
val bottomSheet = MoreBottomSheetFragment()
bottomSheet.show(
supportFragmentManager,
"MoreBottomSheet"
)
}
}
if (fragment != null) {
getSupportFragmentManager()
supportFragmentManager
.beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.commit();
return true;
.commit()
return true
}
return false;
return false
}
public void hideTabs() {
binding.fragmentMainNavTabLayout.setVisibility(View.GONE);
fun hideTabs() {
binding!!.fragmentMainNavTabLayout.visibility = View.GONE
}
public void showTabs() {
binding.fragmentMainNavTabLayout.setVisibility(View.VISIBLE);
fun showTabs() {
binding!!.fragmentMainNavTabLayout.visibility = View.VISIBLE
}
/**
@ -270,120 +275,121 @@ public class MainActivity extends BaseActivity
*
* @param uploadCount
*/
public void setNumOfUploads(int uploadCount) {
fun setNumOfUploads(uploadCount: Int) {
if (activeFragment == ActiveFragment.CONTRIBUTIONS) {
setTitle(getResources().getString(R.string.contributions_fragment) + " " + (
!(uploadCount == 0) ?
getResources()
.getQuantityString(R.plurals.contributions_subtitle,
uploadCount, uploadCount)
: getString(R.string.contributions_subtitle_zero)));
title =
resources.getString(R.string.contributions_fragment) + " " + (if (uploadCount != 0)
resources
.getQuantityString(
R.plurals.contributions_subtitle,
uploadCount, uploadCount
)
else
getString(R.string.contributions_subtitle_zero))
}
}
/**
* Resume the uploads that got stuck because of the app being killed or the device being
* rebooted.
* <p>
*
*
* When the app is terminated or the device is restarted, contributions remain in the
* 'STATE_IN_PROGRESS' state. This status persists and doesn't change during these events. So,
* retrieving contributions labeled as 'STATE_IN_PROGRESS' from the database will provide the
* list of uploads that appear as stuck on opening the app again
*/
@SuppressLint("CheckResult")
private void checkAndResumeStuckUploads() {
List<Contribution> stuckUploads = contributionDao.getContribution(
Collections.singletonList(Contribution.STATE_IN_PROGRESS))
private fun checkAndResumeStuckUploads() {
val stuckUploads = contributionDao!!.getContribution(
listOf(Contribution.STATE_IN_PROGRESS)
)
.subscribeOn(Schedulers.io())
.blockingGet();
Timber.d("Resuming " + stuckUploads.size() + " uploads...");
.blockingGet()
Timber.d("Resuming " + stuckUploads.size + " uploads...")
if (!stuckUploads.isEmpty()) {
for (Contribution contribution : stuckUploads) {
contribution.setState(Contribution.STATE_QUEUED);
contribution.setDateUploadStarted(Calendar.getInstance().getTime());
Completable.fromAction(() -> contributionDao.saveSynchronous(contribution))
for (contribution in stuckUploads) {
contribution.state = Contribution.STATE_QUEUED
contribution.dateUploadStarted = Calendar.getInstance().time
Completable.fromAction { contributionDao!!.saveSynchronous(contribution) }
.subscribeOn(Schedulers.io())
.subscribe();
.subscribe()
}
WorkRequestHelper.Companion.makeOneTimeWorkRequest(
this, ExistingWorkPolicy.APPEND_OR_REPLACE);
makeOneTimeWorkRequest(
this, ExistingWorkPolicy.APPEND_OR_REPLACE
)
}
}
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
//quizChecker.initQuizCheck(this);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("viewPagerCurrentItem", binding.pager.getCurrentItem());
outState.putString("activeFragment", activeFragment.name());
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("viewPagerCurrentItem", binding!!.pager.currentItem)
outState.putString("activeFragment", activeFragment!!.name)
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String activeFragmentName = savedInstanceState.getString("activeFragment");
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
val activeFragmentName = savedInstanceState.getString("activeFragment")
if (activeFragmentName != null) {
restoreActiveFragment(activeFragmentName);
restoreActiveFragment(activeFragmentName)
}
}
private void restoreActiveFragment(@NonNull String fragmentName) {
if (fragmentName.equals(ActiveFragment.CONTRIBUTIONS.name())) {
setTitle(getString(R.string.contributions_fragment));
loadFragment(ContributionsFragment.newInstance(), false);
} else if (fragmentName.equals(ActiveFragment.NEARBY.name())) {
setTitle(getString(R.string.nearby_fragment));
loadFragment(NearbyParentFragment.newInstance(), false);
} else if (fragmentName.equals(ActiveFragment.EXPLORE.name())) {
setTitle(getString(R.string.navigation_item_explore));
loadFragment(ExploreFragment.newInstance(), false);
} else if (fragmentName.equals(ActiveFragment.BOOKMARK.name())) {
setTitle(getString(R.string.bookmarks));
loadFragment(BookmarkFragment.newInstance(), false);
private fun restoreActiveFragment(fragmentName: String) {
if (fragmentName == ActiveFragment.CONTRIBUTIONS.name) {
title = getString(R.string.contributions_fragment)
loadFragment(newInstance(), false)
} else if (fragmentName == ActiveFragment.NEARBY.name) {
title = getString(R.string.nearby_fragment)
loadFragment(NearbyParentFragment.newInstance(), false)
} else if (fragmentName == ActiveFragment.EXPLORE.name) {
title = getString(R.string.navigation_item_explore)
loadFragment(ExploreFragment.newInstance(), false)
} else if (fragmentName == ActiveFragment.BOOKMARK.name) {
title = getString(R.string.bookmarks)
loadFragment(BookmarkFragment.newInstance(), false)
}
}
@Override
public void onBackPressed() {
override fun onBackPressed() {
if (contributionsFragment != null && activeFragment == ActiveFragment.CONTRIBUTIONS) {
// Means that contribution fragment is visible
if (!contributionsFragment.backButtonClicked()) {//If this one does not wan't to handle
if (!contributionsFragment!!.backButtonClicked()) { //If this one does not wan't to handle
// the back press, let the activity do so
super.onBackPressed();
super.onBackPressed()
}
} else if (nearbyParentFragment != null && activeFragment == ActiveFragment.NEARBY) {
// Means that nearby fragment is visible
/* If function nearbyParentFragment.backButtonClick() returns false, it means that the bottomsheet is
not expanded. So if the back button is pressed, then go back to the Contributions tab */
if (!nearbyParentFragment.backButtonClicked()) {
getSupportFragmentManager().beginTransaction().remove(nearbyParentFragment)
.commit();
setSelectedItemId(NavTab.CONTRIBUTIONS.code());
if (!nearbyParentFragment!!.backButtonClicked()) {
supportFragmentManager.beginTransaction().remove(nearbyParentFragment!!)
.commit()
setSelectedItemId(NavTab.CONTRIBUTIONS.code())
}
} else if (exploreFragment != null && activeFragment == ActiveFragment.EXPLORE) {
// Means that explore fragment is visible
if (!exploreFragment.onBackPressed()) {
if (applicationKvStore.getBoolean("login_skipped")) {
super.onBackPressed();
if (!exploreFragment!!.onBackPressed()) {
if (applicationKvStore!!.getBoolean("login_skipped")) {
super.onBackPressed()
} else {
setSelectedItemId(NavTab.CONTRIBUTIONS.code());
setSelectedItemId(NavTab.CONTRIBUTIONS.code())
}
}
} else if (bookmarkFragment != null && activeFragment == ActiveFragment.BOOKMARK) {
// Means that bookmark fragment is visible
bookmarkFragment.onBackPressed();
bookmarkFragment!!.onBackPressed()
} else {
super.onBackPressed();
super.onBackPressed()
}
}
@Override
public void onBackStackChanged() {
override fun onBackStackChanged() {
//initBackButton();
}
@ -391,77 +397,76 @@ public class MainActivity extends BaseActivity
* Retry all failed uploads as soon as the user returns to the app
*/
@SuppressLint("CheckResult")
private void retryAllFailedUploads() {
contributionDao.
getContribution(Collections.singletonList(Contribution.STATE_FAILED))
.subscribeOn(Schedulers.io())
.subscribe(failedUploads -> {
for (Contribution contribution : failedUploads) {
contributionsFragment.retryUpload(contribution);
private fun retryAllFailedUploads() {
contributionDao
?.getContribution(listOf(Contribution.STATE_FAILED))
?.subscribeOn(Schedulers.io())
?.subscribe { failedUploads ->
failedUploads.forEach { contribution ->
contributionsFragment?.retryUpload(contribution)
}
}
});
}
/**
* Handles item selection in the options menu. This method is called when a user interacts with
* the options menu in the Top Bar.
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.upload_tab:
startActivity(new Intent(this, UploadProgressActivity.class));
return true;
case R.id.notifications:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.upload_tab -> {
startActivity(Intent(this, UploadProgressActivity::class.java))
return true
}
R.id.notifications -> {
// Starts notification activity on click to notification icon
NotificationActivity.Companion.startYourself(this, "unread");
return true;
default:
return super.onOptionsItemSelected(item);
startYourself(this, "unread")
return true
}
else -> return super.onOptionsItemSelected(item)
}
}
public void centerMapToPlace(Place place) {
setSelectedItemId(NavTab.NEARBY.code());
nearbyParentFragment.setNearbyParentFragmentInstanceReadyCallback(
new NearbyParentFragmentInstanceReadyCallback() {
@Override
public void onReady() {
nearbyParentFragment.centerMapToPlace(place);
fun centerMapToPlace(place: Place?) {
setSelectedItemId(NavTab.NEARBY.code())
nearbyParentFragment!!.setNearbyParentFragmentInstanceReadyCallback {
nearbyParentFragment!!.centerMapToPlace(
place
)
}
});
}
@Override
protected void onResume() {
super.onResume();
override fun onResume() {
super.onResume()
if ((applicationKvStore.getBoolean("firstrun", true)) &&
(!applicationKvStore.getBoolean("login_skipped"))) {
defaultKvStore.putBoolean("inAppCameraFirstRun", true);
WelcomeActivity.startYourself(this);
if ((applicationKvStore!!.getBoolean("firstrun", true)) &&
(!applicationKvStore!!.getBoolean("login_skipped"))
) {
defaultKvStore.putBoolean("inAppCameraFirstRun", true)
WelcomeActivity.startYourself(this)
}
retryAllFailedUploads();
retryAllFailedUploads()
}
@Override
protected void onDestroy() {
quizChecker.cleanup();
locationManager.unregisterLocationManager();
override fun onDestroy() {
quizChecker!!.cleanup()
locationManager!!.unregisterLocationManager()
// Remove ourself from hashmap to prevent memory leaks
locationManager = null;
super.onDestroy();
locationManager = null
super.onDestroy()
}
/**
* Public method to show nearby from the reference of this.
*/
public void showNearby() {
binding.fragmentMainNavTabLayout.setSelectedItemId(NavTab.NEARBY.code());
fun showNearby() {
binding!!.fragmentMainNavTabLayout.selectedItemId = NavTab.NEARBY.code()
}
public enum ActiveFragment {
enum class ActiveFragment {
CONTRIBUTIONS,
NEARBY,
EXPLORE,
@ -472,15 +477,26 @@ public class MainActivity extends BaseActivity
/**
* Load default language in onCreate from SharedPreferences
*/
private void loadLocale() {
final SharedPreferences preferences = getSharedPreferences("Settings",
Activity.MODE_PRIVATE);
final String language = preferences.getString("language", "");
final SettingsFragment settingsFragment = new SettingsFragment();
settingsFragment.setLocale(this, language);
private fun loadLocale() {
val preferences = getSharedPreferences(
"Settings",
MODE_PRIVATE
)
val language = preferences.getString("language", "")!!
val settingsFragment = SettingsFragment()
settingsFragment.setLocale(this, language)
}
public NavTabLayout.OnNavigationItemSelectedListener getNavListener() {
return navListener;
companion object {
/**
* Consumers should be simply using this method to use this activity.
*
* @param context A Context of the application package implementing this class.
*/
fun startYourself(context: Context) {
val intent = Intent(context, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP)
context.startActivity(intent)
}
}
}

View file

@ -1,126 +1,113 @@
package fr.free.nrw.commons.contributions;
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.net.Uri;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
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 timber.log.Timber;
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.WallpaperManager
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import androidx.core.app.NotificationCompat
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.datasource.BaseBitmapDataSubscriber
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.request.ImageRequestBuilder
import fr.free.nrw.commons.R
import timber.log.Timber
public class SetWallpaperWorker extends Worker {
class SetWallpaperWorker(context: Context, params: WorkerParameters) :
Worker(context, params) {
override fun doWork(): Result {
val context = applicationContext
createNotificationChannel(context)
showProgressNotification(context)
private static final String NOTIFICATION_CHANNEL_ID = "set_wallpaper_channel";
private static final int NOTIFICATION_ID = 1;
val imageUrl = inputData.getString("imageUrl") ?: return Result.failure()
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
val imageRequest = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(imageUrl))
.build();
.build()
ImagePipeline imagePipeline = Fresco.getImagePipeline();
final DataSource<CloseableReference<CloseableImage>>
dataSource = imagePipeline.fetchDecodedImage(imageRequest, context);
val imagePipeline = Fresco.getImagePipeline()
val 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();
dataSource.subscribe(object : BaseBitmapDataSubscriber() {
public override fun onNewResultImpl(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();
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>?) {
Timber.d("Error getting bitmap from image url %s", imageUrl.toString())
showNotification(context, "Setting Wallpaper Failed", "Failed to download image.")
dataSource?.close()
}
}
}, CallerThreadExecutor.getInstance());
}, CallerThreadExecutor.getInstance())
return Result.success();
return Result.success()
}
private void setWallpaper(Context context, Bitmap bitmap) {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
private fun setWallpaper(context: Context, bitmap: Bitmap) {
val 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());
wallpaperManager.setBitmap(bitmap)
showNotification(context, "Wallpaper Set", "Wallpaper has been updated successfully.")
} catch (e: Exception) {
Timber.e(e, "Error setting wallpaper")
showNotification(context, "Setting Wallpaper Failed", " " + e.localizedMessage)
}
}
private void showProgressNotification(Context context) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
private fun showProgressNotification(context: Context) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val builder = 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());
.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)
private fun showNotification(context: Context, title: String, content: String) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val builder = 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());
.setOngoing(false)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
private void createNotificationChannel(Context context) {
private fun 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);
val name: CharSequence = "Wallpaper Setting"
val description = "Notifications for wallpaper setting progress"
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance)
channel.description = description
val notificationManager = context.getSystemService(
NotificationManager::class.java
)
notificationManager.createNotificationChannel(channel)
}
}
companion object {
private const val NOTIFICATION_CHANNEL_ID = "set_wallpaper_channel"
private const val NOTIFICATION_ID = 1
}
}

View file

@ -1,31 +1,22 @@
package fr.free.nrw.commons.contributions;
package fr.free.nrw.commons.contributions
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.viewpager.widget.ViewPager
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;
class UnswipableViewPager : ViewPager {
constructor(context: Context) : super(context)
public class UnswipableViewPager extends ViewPager{
public UnswipableViewPager(@NonNull Context context) {
super(context);
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
public UnswipableViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
// Unswipable
return false;
return false
}
@Override
public boolean onTouchEvent(MotionEvent event) {
override fun onTouchEvent(event: MotionEvent): Boolean {
// Unswipable
return false;
return false
}
}

View file

@ -43,7 +43,7 @@ class WikipediaInstructionsDialogFragment : DialogFragment() {
/**
* Callback for handling confirm button clicked
*/
interface Callback {
fun interface Callback {
fun onConfirmClicked(
contribution: Contribution?,
copyWikicode: Boolean,

View file

@ -9,6 +9,7 @@ import fr.free.nrw.commons.CommonsApplication
import fr.free.nrw.commons.activity.SingleWebViewActivity
import fr.free.nrw.commons.auth.LoginActivity
import fr.free.nrw.commons.contributions.ContributionsModule
import fr.free.nrw.commons.contributions.ContributionsProvidesModule
import fr.free.nrw.commons.explore.SearchModule
import fr.free.nrw.commons.explore.categories.CategoriesModule
import fr.free.nrw.commons.explore.depictions.DepictionModule
@ -40,6 +41,7 @@ import javax.inject.Singleton
ContentProviderBuilderModule::class,
UploadModule::class,
ContributionsModule::class,
ContributionsProvidesModule::class,
SearchModule::class,
DepictionModule::class,
CategoriesModule::class

View file

@ -426,7 +426,7 @@ object FilePicker : Constants {
fun onCanceled(source: ImageSource, type: Int)
}
interface HandleActivityResult {
fun interface HandleActivityResult {
fun onHandleActivityResult(callbacks: Callbacks)
}
}

View file

@ -31,8 +31,8 @@ class NavTabLayout : BottomNavigationView {
private fun setTabViews() {
val isLoginSkipped = (context as MainActivity)
.applicationKvStore.getBoolean("login_skipped")
if (isLoginSkipped) {
.applicationKvStore?.getBoolean("login_skipped")
if (isLoginSkipped == true) {
for (i in 0 until NavTabLoggedOut.size()) {
val navTab = NavTabLoggedOut.of(i)
menu.add(Menu.NONE, i, i, navTab.text()).setIcon(navTab.icon())

View file

@ -28,8 +28,7 @@ import javax.inject.Named
/**
* The presenter class for PendingUploadsFragment and FailedUploadsFragment
*/
class PendingUploadsPresenter @Inject internal constructor(
*/ class PendingUploadsPresenter @Inject internal constructor(
private val contributionBoundaryCallback: ContributionBoundaryCallback,
private val contributionsRemoteDataSource: ContributionsRemoteDataSource,
private val contributionsRepository: ContributionsRepository,
@ -89,13 +88,17 @@ class PendingUploadsPresenter @Inject internal constructor(
* @param context The context in which the operation is being performed.
*/
override fun deleteUpload(contribution: Contribution?, context: Context?) {
compositeDisposable.add(
contribution?.let {
contributionsRepository
.deleteContributionFromDB(contribution)
.deleteContributionFromDB(it)
.subscribeOn(ioThreadScheduler)
.subscribe()
}?.let {
compositeDisposable.add(
it
)
}
}
/**
* Pauses all the uploads by changing the state of contributions from STATE_QUEUED and