From 5540fe6ee54c8f89b8acddd50744417699b118a1 Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Thu, 19 Dec 2024 21:44:44 -0600 Subject: [PATCH] Convert UploadCategoriesFragment to kotlin --- .../categories/UploadCategoriesFragment.java | 425 ------------------ .../categories/UploadCategoriesFragment.kt | 397 ++++++++++++++++ 2 files changed, 397 insertions(+), 425 deletions(-) delete mode 100644 app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java deleted file mode 100644 index 1436ab714..000000000 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java +++ /dev/null @@ -1,425 +0,0 @@ -package fr.free.nrw.commons.upload.categories; - -import static fr.free.nrw.commons.wikidata.WikidataConstants.SELECTED_NEARBY_PLACE_CATEGORY; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.Context; -import android.os.Bundle; -import android.text.Editable; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.Toast; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.LinearLayoutManager; -import com.jakewharton.rxbinding2.view.RxView; -import com.jakewharton.rxbinding2.widget.RxTextView; -import fr.free.nrw.commons.CommonsApplication; -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.auth.SessionManager; -import fr.free.nrw.commons.category.CategoryItem; -import fr.free.nrw.commons.contributions.ContributionsFragment; -import fr.free.nrw.commons.databinding.UploadCategoriesFragmentBinding; -import fr.free.nrw.commons.media.MediaDetailFragment; -import fr.free.nrw.commons.upload.UploadActivity; -import fr.free.nrw.commons.upload.UploadBaseFragment; -import fr.free.nrw.commons.utils.DialogUtil; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.TimeUnit; -import javax.inject.Inject; -import kotlin.Unit; -import timber.log.Timber; - -public class UploadCategoriesFragment extends UploadBaseFragment implements CategoriesContract.View { - - @Inject - CategoriesContract.UserActionListener presenter; - @Inject - SessionManager sessionManager; - private UploadCategoryAdapter adapter; - private Disposable subscribe; - /** - * Current media - */ - private Media media; - /** - * Progress Dialog for showing background process - */ - private ProgressDialog progressDialog; - /** - * WikiText from the server - */ - private String wikiText; - private String nearbyPlaceCategory; - - private UploadCategoriesFragmentBinding binding; - - @Nullable - @Override - public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - binding = UploadCategoriesFragmentBinding.inflate(inflater, container, false); - return binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - final Bundle bundle = getArguments(); - if (bundle != null) { - media = bundle.getParcelable("Existing_Categories"); - wikiText = bundle.getString("WikiText"); - nearbyPlaceCategory = bundle.getString(SELECTED_NEARBY_PLACE_CATEGORY); - } - init(); - presenter.getCategories().observe(getViewLifecycleOwner(), this::setCategories); - - } - - private void init() { - if (binding == null) { - return; - } - if (media == null) { - if (callback != null) { - binding.tvTitle.setText(getString(R.string.step_count, callback.getIndexInViewFlipper(this) + 1, - callback.getTotalNumberOfSteps(), getString(R.string.categories_activity_title))); - } - } else { - binding.tvTitle.setText(R.string.edit_categories); - binding.tvSubtitle.setVisibility(View.GONE); - binding.btnNext.setText(R.string.menu_save_categories); - binding.btnPrevious.setText(R.string.menu_cancel_upload); - } - - setTvSubTitle(); - binding.tooltip.setOnClickListener(new OnClickListener() { - @Override - public void onClick(final View v) { - DialogUtil.showAlertDialog(requireActivity(), - getString(R.string.categories_activity_title), - getString(R.string.categories_tooltip), - getString(android.R.string.ok), - null); - } - }); - if (media == null) { - presenter.onAttachView(this); - } else { - presenter.onAttachViewWithMedia(this, media); - } - binding.btnNext.setOnClickListener(v -> onNextButtonClicked()); - binding.btnPrevious.setOnClickListener(v -> onPreviousButtonClicked()); - - initRecyclerView(); - addTextChangeListenerToEtSearch(); - } - - private void addTextChangeListenerToEtSearch() { - if (binding == null) { - return; - } - subscribe = RxTextView.textChanges(binding.etSearch) - .doOnEach(v -> binding.tilContainerSearch.setError(null)) - .takeUntil(RxView.detaches(binding.etSearch)) - .debounce(500, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(filter -> searchForCategory(filter.toString()), Timber::e); - } - - /** - * Removes the tv subtitle If the activity is the instance of [UploadActivity] and - * if multiple files aren't selected. - */ - private void setTvSubTitle() { - final Activity activity = getActivity(); - if (activity instanceof UploadActivity) { - final boolean isMultipleFileSelected = ((UploadActivity) activity).getIsMultipleFilesSelected(); - if (!isMultipleFileSelected) { - binding.tvSubtitle.setVisibility(View.GONE); - } - } - } - - private void searchForCategory(final String query) { - presenter.searchForCategories(query); - } - - private void initRecyclerView() { - adapter = new UploadCategoryAdapter(categoryItem -> { - presenter.onCategoryItemClicked(categoryItem); - return Unit.INSTANCE; - }, nearbyPlaceCategory); - - if (binding!=null) { - binding.rvCategories.setLayoutManager(new LinearLayoutManager(getContext())); - binding.rvCategories.setAdapter(adapter); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - presenter.onDetachView(); - subscribe.dispose(); - } - - @Override - public void showProgress(final boolean shouldShow) { - if (binding != null) { - binding.pbCategories.setVisibility(shouldShow ? View.VISIBLE : View.GONE); - } - } - - @Override - public void showError(final String error) { - if (binding != null) { - binding.tilContainerSearch.setError(error); - } - } - - @Override - public void showError(final int stringResourceId) { - if (binding != null) { - binding.tilContainerSearch.setError(getString(stringResourceId)); - } - } - - @Override - public void setCategories(final List categories) { - if (categories == null) { - adapter.clear(); - } else { - adapter.setItems(categories); - } - adapter.notifyDataSetChanged(); - - - if (binding == null) { - return; - } - // Nested waiting for search result data to load into the category - // list and smoothly scroll to the top of the search result list. - binding.rvCategories.post(new Runnable() { - @Override - public void run() { - binding.rvCategories.smoothScrollToPosition(0); - binding.rvCategories.post(new Runnable() { - @Override - public void run() { - binding.rvCategories.smoothScrollToPosition(0); - } - }); - } - }); - } - - @Override - public void goToNextScreen() { - if (callback != null){ - callback.onNextButtonClicked(callback.getIndexInViewFlipper(this)); - } - } - - @Override - public void showNoCategorySelected() { - if (media == null) { - DialogUtil.showAlertDialog(requireActivity(), - getString(R.string.no_categories_selected), - getString(R.string.no_categories_selected_warning_desc), - getString(R.string.continue_message), - getString(R.string.cancel), - this::goToNextScreen, - null); - } else { - Toast.makeText(requireContext(), getString(R.string.no_categories_selected), - Toast.LENGTH_SHORT).show(); - presenter.clearPreviousSelection(); - goBackToPreviousScreen(); - } - - } - - /** - * Gets existing categories from media - */ - @Override - public List getExistingCategories() { - return (media == null) ? null : media.getCategories(); - } - - /** - * Returns required context - */ - @NonNull - @Override - public Context getFragmentContext() { - return requireContext(); - } - - /** - * Returns to previous fragment - */ - @Override - public void goBackToPreviousScreen() { - getFragmentManager().popBackStack(); - } - - /** - * Shows the progress dialog - */ - @Override - public void showProgressDialog() { - progressDialog = new ProgressDialog(requireContext()); - progressDialog.setMessage(getString(R.string.please_wait)); - progressDialog.show(); - } - - /** - * Hides the progress dialog - */ - @Override - public void dismissProgressDialog() { - if (progressDialog != null) { - progressDialog.dismiss(); - } - } - - /** - * Refreshes the categories - */ - @Override - public void refreshCategories() { - final MediaDetailFragment mediaDetailFragment = (MediaDetailFragment) getParentFragment(); - assert mediaDetailFragment != null; - mediaDetailFragment.updateCategories(); - } - - /** - * - */ - @Override - public void navigateToLoginScreen() { - final String username = sessionManager.getUserName(); - final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener( - requireActivity(), - requireActivity().getString(R.string.invalid_login_message), - username - ); - - CommonsApplication.getInstance().clearApplicationData( - requireActivity(), logoutListener); - } - - public void onNextButtonClicked() { - if (media != null) { - presenter.updateCategories(media, wikiText); - } else { - presenter.verifyCategories(); - } - } - - public void onPreviousButtonClicked() { - if (media != null) { - presenter.clearPreviousSelection(); - adapter.setItems(null); - final MediaDetailFragment mediaDetailFragment = (MediaDetailFragment) getParentFragment(); - assert mediaDetailFragment != null; - mediaDetailFragment.onResume(); - goBackToPreviousScreen(); - } else { - if (callback != null) { - callback.onPreviousButtonClicked(callback.getIndexInViewFlipper(this)); - } - } - } - - @Override - protected void onBecameVisible() { - super.onBecameVisible(); - if (binding == null) { - return; - } - presenter.selectCategories(); - final Editable text = binding.etSearch.getText(); - if (text != null) { - presenter.searchForCategories(text.toString()); - } - } - - /** - * Hides the action bar while opening editing fragment - */ - @Override - public void onResume() { - super.onResume(); - - if (media != null) { - binding.etSearch.setOnKeyListener((v, keyCode, event) -> { - if (keyCode == KeyEvent.KEYCODE_BACK) { - binding.etSearch.clearFocus(); - presenter.clearPreviousSelection(); - final MediaDetailFragment mediaDetailFragment = (MediaDetailFragment) getParentFragment(); - assert mediaDetailFragment != null; - mediaDetailFragment.onResume(); - goBackToPreviousScreen(); - return true; - } - return false; - }); - - requireView().setFocusableInTouchMode(true); - getView().requestFocus(); - getView().setOnKeyListener((v, keyCode, event) -> { - if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { - presenter.clearPreviousSelection(); - final MediaDetailFragment mediaDetailFragment = (MediaDetailFragment) getParentFragment(); - assert mediaDetailFragment != null; - mediaDetailFragment.onResume(); - goBackToPreviousScreen(); - return true; - } - return false; - }); - - Objects.requireNonNull( - ((AppCompatActivity) requireActivity()).getSupportActionBar()) - .hide(); - - if (getParentFragment().getParentFragment().getParentFragment() - instanceof ContributionsFragment) { - ((ContributionsFragment) (getParentFragment() - .getParentFragment().getParentFragment())).binding.cardViewNearby - .setVisibility(View.GONE); - } - } - } - - /** - * Shows the action bar while closing editing fragment - */ - @Override - public void onStop() { - super.onStop(); - if (media != null) { - Objects.requireNonNull( - ((AppCompatActivity) requireActivity()).getSupportActionBar()) - .show(); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - binding = null; - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt new file mode 100644 index 000000000..845f0548f --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt @@ -0,0 +1,397 @@ +package fr.free.nrw.commons.upload.categories + +import android.app.Activity +import android.app.ProgressDialog +import android.content.Context +import android.os.Bundle +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import com.jakewharton.rxbinding2.view.RxView +import com.jakewharton.rxbinding2.widget.RxTextView +import fr.free.nrw.commons.CommonsApplication +import fr.free.nrw.commons.CommonsApplication.Companion.instance +import fr.free.nrw.commons.Media +import fr.free.nrw.commons.R +import fr.free.nrw.commons.auth.SessionManager +import fr.free.nrw.commons.category.CategoryItem +import fr.free.nrw.commons.contributions.ContributionsFragment +import fr.free.nrw.commons.databinding.UploadCategoriesFragmentBinding +import fr.free.nrw.commons.media.MediaDetailFragment +import fr.free.nrw.commons.upload.UploadActivity +import fr.free.nrw.commons.upload.UploadBaseFragment +import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import fr.free.nrw.commons.wikidata.WikidataConstants.SELECTED_NEARBY_PLACE_CATEGORY +import io.reactivex.Notification +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import timber.log.Timber +import java.util.Objects +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +class UploadCategoriesFragment : UploadBaseFragment(), CategoriesContract.View { + @JvmField + @Inject + var presenter: CategoriesContract.UserActionListener? = null + + @JvmField + @Inject + var sessionManager: SessionManager? = null + private var adapter: UploadCategoryAdapter? = null + private var subscribe: Disposable? = null + + /** + * Current media + */ + private var media: Media? = null + + /** + * Progress Dialog for showing background process + */ + private var progressDialog: ProgressDialog? = null + + /** + * WikiText from the server + */ + private var wikiText: String? = null + private var nearbyPlaceCategory: String? = null + + private var binding: UploadCategoriesFragmentBinding? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = UploadCategoriesFragmentBinding.inflate(inflater, container, false) + return binding!!.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val bundle = arguments + if (bundle != null) { + media = bundle.getParcelable("Existing_Categories") + wikiText = bundle.getString("WikiText") + nearbyPlaceCategory = bundle.getString(SELECTED_NEARBY_PLACE_CATEGORY) + } + init() + presenter!!.getCategories().observe( + viewLifecycleOwner + ) { categories: List? -> + this.setCategories( + categories + ) + } + } + + private fun init() { + if (binding == null) { + return + } + if (media == null) { + if (callback != null) { + binding!!.tvTitle.text = getString( + R.string.step_count, callback.getIndexInViewFlipper( + this + ) + 1, + callback.totalNumberOfSteps, getString(R.string.categories_activity_title) + ) + } + } else { + binding!!.tvTitle.setText(R.string.edit_categories) + binding!!.tvSubtitle.visibility = View.GONE + binding!!.btnNext.setText(R.string.menu_save_categories) + binding!!.btnPrevious.setText(R.string.menu_cancel_upload) + } + + setTvSubTitle() + binding!!.tooltip.setOnClickListener { + showAlertDialog( + requireActivity(), + getString(R.string.categories_activity_title), + getString(R.string.categories_tooltip), + getString(android.R.string.ok), + null + ) + } + if (media == null) { + presenter!!.onAttachView(this) + } else { + presenter!!.onAttachViewWithMedia(this, media!!) + } + binding!!.btnNext.setOnClickListener { v: View? -> onNextButtonClicked() } + binding!!.btnPrevious.setOnClickListener { v: View? -> onPreviousButtonClicked() } + + initRecyclerView() + addTextChangeListenerToEtSearch() + } + + private fun addTextChangeListenerToEtSearch() { + if (binding == null) { + return + } + subscribe = RxTextView.textChanges(binding!!.etSearch) + .doOnEach { v: Notification? -> + binding!!.tilContainerSearch.error = + null + } + .takeUntil(RxView.detaches(binding!!.etSearch)) + .debounce(500, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { filter: CharSequence -> searchForCategory(filter.toString()) }, + { t: Throwable? -> Timber.e(t) }) + } + + /** + * Removes the tv subtitle If the activity is the instance of [UploadActivity] and + * if multiple files aren't selected. + */ + private fun setTvSubTitle() { + val activity: Activity? = activity + if (activity is UploadActivity) { + val isMultipleFileSelected = activity.isMultipleFilesSelected + if (!isMultipleFileSelected) { + binding!!.tvSubtitle.visibility = View.GONE + } + } + } + + private fun searchForCategory(query: String) { + presenter!!.searchForCategories(query) + } + + private fun initRecyclerView() { + adapter = UploadCategoryAdapter({ categoryItem: CategoryItem? -> + presenter!!.onCategoryItemClicked(categoryItem!!) + Unit + }, nearbyPlaceCategory) + + if (binding != null) { + binding!!.rvCategories.layoutManager = LinearLayoutManager(context) + binding!!.rvCategories.adapter = adapter + } + } + + override fun onDestroyView() { + super.onDestroyView() + presenter!!.onDetachView() + subscribe!!.dispose() + } + + override fun showProgress(shouldShow: Boolean) { + binding?.pbCategories?.setVisibility(if (shouldShow) View.VISIBLE else View.GONE) + } + + override fun showError(error: String?) { + binding?.tilContainerSearch?.error = error + } + + override fun showError(stringResourceId: Int) { + binding?.tilContainerSearch?.error = getString(stringResourceId) + } + + override fun setCategories(categories: List?) { + if (categories == null) { + adapter!!.clear() + } else { + adapter!!.items = categories + } + adapter!!.notifyDataSetChanged() + + if (binding == null) { + return + } + // Nested waiting for search result data to load into the category + // list and smoothly scroll to the top of the search result list. + binding!!.rvCategories.post { + binding!!.rvCategories.smoothScrollToPosition(0) + binding!!.rvCategories.post { + binding!!.rvCategories.smoothScrollToPosition( + 0 + ) + } + } + } + + override fun goToNextScreen() { + callback?.onNextButtonClicked(callback.getIndexInViewFlipper(this)) + } + + override fun showNoCategorySelected() { + if (media == null) { + showAlertDialog( + requireActivity(), + getString(R.string.no_categories_selected), + getString(R.string.no_categories_selected_warning_desc), + getString(R.string.continue_message), + getString(R.string.cancel), + { this.goToNextScreen() }, + null + ) + } else { + Toast.makeText( + requireContext(), getString(R.string.no_categories_selected), + Toast.LENGTH_SHORT + ).show() + presenter!!.clearPreviousSelection() + goBackToPreviousScreen() + } + } + + /** + * Gets existing categories from media + */ + override fun getExistingCategories(): List? { + return media?.categories + } + + /** + * Returns required context + */ + override fun getFragmentContext(): Context { + return requireContext() + } + + /** + * Returns to previous fragment + */ + override fun goBackToPreviousScreen() { + fragmentManager?.popBackStack() + } + + /** + * Shows the progress dialog + */ + override fun showProgressDialog() { + progressDialog = ProgressDialog(requireContext()).apply { + setMessage(getString(R.string.please_wait)) + }.also { + it.show() + } + } + + /** + * Hides the progress dialog + */ + override fun dismissProgressDialog() { + progressDialog?.dismiss() + } + + /** + * Refreshes the categories + */ + override fun refreshCategories() { + (parentFragment as MediaDetailFragment?)?.updateCategories() + } + + /** + * + */ + override fun navigateToLoginScreen() { + val username = sessionManager!!.userName + val logoutListener = CommonsApplication.BaseLogoutListener( + requireActivity(), + requireActivity().getString(R.string.invalid_login_message), + username + ) + + instance.clearApplicationData( + requireActivity(), logoutListener + ) + } + + fun onNextButtonClicked() { + if (media != null) { + presenter!!.updateCategories(media!!, wikiText!!) + } else { + presenter!!.verifyCategories() + } + } + + fun onPreviousButtonClicked() { + if (media != null) { + presenter!!.clearPreviousSelection() + adapter!!.items = null + val mediaDetailFragment = checkNotNull(parentFragment as MediaDetailFragment?) + mediaDetailFragment.onResume() + goBackToPreviousScreen() + } else { + callback?.onPreviousButtonClicked(callback.getIndexInViewFlipper(this)) + } + } + + override fun onBecameVisible() { + super.onBecameVisible() + if (binding == null) { + return + } + presenter!!.selectCategories() + val text = binding!!.etSearch.text + if (text != null) { + presenter!!.searchForCategories(text.toString()) + } + } + + /** + * Hides the action bar while opening editing fragment + */ + override fun onResume() { + super.onResume() + + if (media != null) { + binding!!.etSearch.setOnKeyListener { v: View?, keyCode: Int, event: KeyEvent? -> + if (keyCode == KeyEvent.KEYCODE_BACK) { + binding!!.etSearch.clearFocus() + presenter!!.clearPreviousSelection() + val mediaDetailFragment = + checkNotNull(parentFragment as MediaDetailFragment?) + mediaDetailFragment.onResume() + goBackToPreviousScreen() + return@setOnKeyListener true + } + false + } + + requireView().isFocusableInTouchMode = true + requireView().requestFocus() + requireView().setOnKeyListener { v: View?, keyCode: Int, event: KeyEvent -> + if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { + presenter!!.clearPreviousSelection() + val mediaDetailFragment = + checkNotNull(parentFragment as MediaDetailFragment?) + mediaDetailFragment.onResume() + goBackToPreviousScreen() + return@setOnKeyListener true + } + false + } + + (requireActivity() as AppCompatActivity).supportActionBar?.hide() + + if (parentFragment?.parentFragment?.parentFragment is ContributionsFragment) { + ((parentFragment?.parentFragment?.parentFragment) as ContributionsFragment).binding.cardViewNearby.visibility = View.GONE + } + } + } + + /** + * Shows the action bar while closing editing fragment + */ + override fun onStop() { + super.onStop() + if (media != null) { + (requireActivity() as AppCompatActivity).supportActionBar?.show() + } + } + + override fun onDestroy() { + super.onDestroy() + binding = null + } +}