diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.kt index 568ac9a37..d1dbf4509 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.kt @@ -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(); +an upload might take a dozen seconds. */ + 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) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(mediaExists -> { - displayWikipediaButton(mediaExists); - })); + val wikipediaArticle = contribution.wikidataPlace!!.getWikipediaPageTitle() + compositeDisposable.add( + mediaClient.doesPageContainMedia(wikipediaArticle) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .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) } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt index d22bba6c6..0b7736bab 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt @@ -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) } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt index df65a91cc..bfe1161c7 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt @@ -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> @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 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 customSelectorLauncherForResult = - registerForActivityResult(new StartActivityForResult(), - result -> { - controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { - controller.onPictureReturnedFromCustomSelector(result, requireActivity(), callbacks); - }); - }); + private val galleryPickLauncherForResult = registerForActivityResult( + StartActivityForResult() + ) { result: ActivityResult? -> + controller!!.handleActivityResultWithCallback(requireActivity() + ) { callbacks: FilePicker.Callbacks? -> + controller!!.onPictureReturnedFromGallery( + result!!, requireActivity(), callbacks!! + ) + } + } - private final ActivityResultLauncher cameraPickLauncherForResult = - registerForActivityResult(new StartActivityForResult(), - result -> { - controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { - controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks); - }); - }); + private val customSelectorLauncherForResult = registerForActivityResult( + StartActivityForResult() + ) { result: ActivityResult? -> + controller!!.handleActivityResultWithCallback(requireActivity() + ) { callbacks: FilePicker.Callbacks? -> + controller!!.onPictureReturnedFromCustomSelector( + result!!, requireActivity(), callbacks!! + ) + } + } - private ActivityResultLauncher inAppCameraLocationPermissionLauncher = registerForActivityResult( - new RequestMultiplePermissions(), - new ActivityResultCallback>() { - @Override - public void onActivityResult(Map result) { - boolean areAllGranted = true; - for (final boolean b : result.values()) { - areAllGranted = areAllGranted && b; - } + private val cameraPickLauncherForResult = registerForActivityResult( + StartActivityForResult() + ) { result: ActivityResult? -> + controller!!.handleActivityResultWithCallback(requireActivity() + ) { callbacks: FilePicker.Callbacks? -> + controller!!.onPictureReturnedFromCamera( + result!!, requireActivity(), callbacks!! + ) + } + } - 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)); - } - } - } - }); - - - @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? -> + 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() + } + } + 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(); + 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. + * 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. + * 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(RV_STATE) + rvContributionsList!!.layoutManager!!.onRestoreInstanceState(savedRecyclerLayoutState) } } - @Override - public void openMediaDetail(final int position, boolean isWikipediaButtonDisplayed) { - if (null != callback) {//Just being safe, ideally they won't be called when detached - callback.showDetail(position, 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) } } @@ -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" } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.kt index 100c8be03..1421c1757 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.kt @@ -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>? = null - private final CompositeDisposable compositeDisposable; - private final ContributionsRemoteDataSource contributionsRemoteDataSource; - - LiveData> 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 factory; - boolean shouldSetBoundaryCallback; + .setPageSize(10).build() + val factory: DataSource.Factory + 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() { - @NonNull - @Override - public DataSource create() { - return contributionsRemoteDataSource; + contributionsRemoteDataSource.userName = userName + factory = object : DataSource.Factory() { + override fun create(): DataSource { + 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 = 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 + } } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.kt index 77dcd5df9..a64581eb8 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.kt @@ -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 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 states) { - return contributionDao.deleteContributionsWithStates(states); + fun deleteContributionsWithStates(states: List): Completable { + return contributionDao.deleteContributionsWithStates(states) } - public Factory getContributions() { - return contributionDao.fetchContributions(); - } + val contributions: DataSource.Factory + 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 getContributionsWithStates(List states) { - return contributionDao.getContributions(states); + fun getContributionsWithStates(states: List): DataSource.Factory { + 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 getContributionsWithStatesSortedByDateUploadStarted( - List states) { - return contributionDao.getContributionsSortedByDateUploadStarted(states); + fun getContributionsWithStatesSortedByDateUploadStarted( + states: List + ): DataSource.Factory { + return contributionDao.getContributionsSortedByDateUploadStarted(states) } - public Single> saveContributions(final List contributions) { - final List contributionList = new ArrayList<>(); - for (final Contribution contribution : contributions) { - final Contribution oldContribution = contributionDao.getContribution( - contribution.getPageId()); + fun saveContributions(contributions: List): Single> { + val contributionList: MutableList = 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 states, int newState) { - return contributionDao.updateContributionsWithStates(states, newState); + fun updateContributionsWithStates(states: List, newState: Int): Completable { + return contributionDao.updateContributionsWithStates(states, newState) } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.kt index da4fb58f5..0e27dbade 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.kt @@ -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? +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.kt index 495a4bc64..212e96c15 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.kt @@ -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()) - .subscribeOn(ioThreadScheduler) - .subscribe(imageCheckResult -> { - if (imageCheckResult == IMAGE_OK) { - contribution.setState(Contribution.STATE_QUEUED); - saveContribution(contribution); - } else { - Timber.e("Contribution already exists"); - compositeDisposable.add(contributionsRepository - .deleteContributionFromDB(contribution) - .subscribeOn(ioThreadScheduler) - .subscribe()); - } - })); + fun checkDuplicateImageAndRestartContribution(contribution: Contribution) { + compositeDisposable!!.add( + uploadRepository + .checkDuplicateImage(contribution.localUriPath!!.path) + .subscribeOn(ioThreadScheduler) + .subscribe { imageCheckResult: Int -> + if (imageCheckResult == ImageUtils.IMAGE_OK) { + contribution.state = Contribution.STATE_QUEUED + saveContribution(contribution) + } else { + Timber.e("Contribution already exists") + compositeDisposable!!.add( + contributionsRepository + .deleteContributionFromDB(contribution) + .subscribeOn(ioThreadScheduler) + .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 + ) + }) } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsProvidesModule.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsProvidesModule.kt new file mode 100644 index 000000000..67e8f50b5 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsProvidesModule.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.kt index 3808eba8e..a9714bf62 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.kt @@ -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 states) { - return localDataSource.deleteContributionsWithStates(states); + fun deleteContributionsFromDBWithStates(states: List): 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 fetchContributions() { - return localDataSource.getContributions(); + fun fetchContributions(): DataSource.Factory { + 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 fetchContributionsWithStates(List states) { - return localDataSource.getContributionsWithStates(states); + fun fetchContributionsWithStates(states: List): DataSource.Factory { + 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 fetchContributionsWithStatesSortedByDateUploadStarted( - List states) { - return localDataSource.getContributionsWithStatesSortedByDateUploadStarted(states); + fun fetchContributionsWithStatesSortedByDateUploadStarted( + states: List + ): DataSource.Factory { + return localDataSource.getContributionsWithStatesSortedByDateUploadStarted(states) } - public Single> save(List contributions) { - return localDataSource.saveContributions(contributions); + fun save(contributions: List): Single> { + 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 states, int newState) { - return localDataSource.updateContributionsWithStates(states, newState); + fun updateContributionsWithStates(states: List, newState: Int): Completable { + return localDataSource.updateContributionsWithStates(states, newState) } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt index 03027f287..f7a4c0f77 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt @@ -1,159 +1,156 @@ -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. +"first_edit_depict" is a key for getting information about opening the depiction editor +screen for the first time after opening the app. - Getting true by the key means the depiction editor screen is opened for the first time - after opening the app. - 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(); +Getting true by the key means the depiction editor screen is opened for the first time +after opening the app. +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) { + 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 } - Fragment fragment = NavTabLoggedOut.of(item.getOrder()).newInstance(); - return loadFragment(fragment, true); - }); + val fragment = + NavTabLoggedOut.of(item.order).newInstance() + 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. - *

+ * + * * 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 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) + } } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.kt b/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.kt index 0f18c300b..06c31fede 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.kt @@ -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> - 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>?) { + 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 + } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/UnswipableViewPager.kt b/app/src/main/java/fr/free/nrw/commons/contributions/UnswipableViewPager.kt index 898a36a99..dd6ae661a 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/UnswipableViewPager.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/UnswipableViewPager.kt @@ -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 } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt b/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt index 86cda2cf3..f16a48b4c 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt @@ -43,7 +43,7 @@ class WikipediaInstructionsDialogFragment : DialogFragment() { /** * Callback for handling confirm button clicked */ - interface Callback { + fun interface Callback { fun onConfirmClicked( contribution: Contribution?, copyWikicode: Boolean, diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.kt b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.kt index 94319060b..9e569982f 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.kt +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.kt @@ -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 diff --git a/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.kt b/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.kt index 2ed573740..acf072f02 100644 --- a/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.kt +++ b/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.kt @@ -426,7 +426,7 @@ object FilePicker : Constants { fun onCanceled(source: ImageSource, type: Int) } - interface HandleActivityResult { + fun interface HandleActivityResult { fun onHandleActivityResult(callbacks: Callbacks) } } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/navtab/NavTabLayout.kt b/app/src/main/java/fr/free/nrw/commons/navtab/NavTabLayout.kt index 8d5298cac..73d030ed0 100644 --- a/app/src/main/java/fr/free/nrw/commons/navtab/NavTabLayout.kt +++ b/app/src/main/java/fr/free/nrw/commons/navtab/NavTabLayout.kt @@ -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()) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.kt index 324f988d4..9f1daa686 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.kt @@ -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,12 +88,16 @@ 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 + ) + } } /**