Migrated AchievementFragment to kotlin

This commit is contained in:
Neel Doshi 2024-12-02 00:40:31 +05:30
parent 910d1aca40
commit 4357acb1ab
2 changed files with 338 additions and 313 deletions

View file

@ -16,7 +16,6 @@ import androidx.annotation.NonNull;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import com.google.android.material.tabs.TabLayout;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.ViewPagerAdapter; import fr.free.nrw.commons.ViewPagerAdapter;

View file

@ -1,168 +1,137 @@
package fr.free.nrw.commons.profile.achievements; package fr.free.nrw.commons.profile.achievements
import android.accounts.Account; import android.net.Uri
import android.content.Context; import android.os.Bundle
import android.net.Uri; import android.util.DisplayMetrics
import android.os.Bundle; import android.view.LayoutInflater
import android.util.DisplayMetrics; import android.view.View
import android.view.LayoutInflater; import android.view.ViewGroup
import android.view.View; import android.widget.Toast
import android.view.ViewGroup; import androidx.appcompat.view.ContextThemeWrapper
import android.widget.Toast; import androidx.constraintlayout.widget.ConstraintLayout
import androidx.annotation.Nullable; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import androidx.appcompat.view.ContextThemeWrapper; import fr.free.nrw.commons.R
import androidx.constraintlayout.widget.ConstraintLayout; import fr.free.nrw.commons.Utils
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.R; import fr.free.nrw.commons.databinding.FragmentAchievementsBinding
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.kvstore.BasicKvStore
import fr.free.nrw.commons.databinding.FragmentAchievementsBinding; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.profile.ProfileActivity
import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.profile.achievements.LevelController.LevelInfo.Companion.from
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
import fr.free.nrw.commons.utils.ConfigUtils; import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.utils.ViewUtil.showDismissibleSnackBar
import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.utils.ViewUtil.showLongToast
import fr.free.nrw.commons.profile.ProfileActivity; import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers
import io.reactivex.schedulers.Schedulers; import org.apache.commons.lang3.StringUtils
import java.util.Locale; import timber.log.Timber
import java.util.Objects; import java.util.Objects
import javax.inject.Inject; import javax.inject.Inject
import org.apache.commons.lang3.StringUtils;
import timber.log.Timber;
/** class AchievementsFragment : CommonsDaggerSupportFragment(){
* fragment for sharing feedback on uploaded activity private lateinit var levelInfo: LevelController.LevelInfo
*/
public class AchievementsFragment extends CommonsDaggerSupportFragment {
private static final double BADGE_IMAGE_WIDTH_RATIO = 0.4;
private static final double BADGE_IMAGE_HEIGHT_RATIO = 0.3;
/**
* Help link URLs
*/
private static final String IMAGES_UPLOADED_URL = "https://commons.wikimedia.org/wiki/Commons:Project_scope";
private static final String IMAGES_REVERT_URL = "https://commons.wikimedia.org/wiki/Commons:Deletion_policy#Reasons_for_deletion";
private static final String IMAGES_USED_URL = "https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style/Images";
private static final String IMAGES_NEARBY_PLACES_URL = "https://www.wikidata.org/wiki/Property:P18";
private static final String IMAGES_FEATURED_URL = "https://commons.wikimedia.org/wiki/Commons:Featured_pictures";
private static final String QUALITY_IMAGE_URL = "https://commons.wikimedia.org/wiki/Commons:Quality_images";
private static final String THANKS_URL = "https://www.mediawiki.org/wiki/Extension:Thanks";
private LevelController.LevelInfo levelInfo;
@Inject @Inject
SessionManager sessionManager; lateinit var sessionManager: SessionManager
@Inject @Inject
OkHttpJsonApiClient okHttpJsonApiClient; lateinit var okHttpJsonApiClient: OkHttpJsonApiClient
private FragmentAchievementsBinding binding; private var _binding: FragmentAchievementsBinding? = null
private val binding get() = _binding!!
private CompositeDisposable compositeDisposable = new CompositeDisposable(); // private lateinit var binding: FragmentAchievementsBinding
private val compositeDisposable = CompositeDisposable()
// To keep track of the number of wiki edits made by a user // To keep track of the number of wiki edits made by a user
private int numberOfEdits = 0; private var numberOfEdits: Int = 0
private String userName; private var userName: String? = null
@Override override fun onCreate(savedInstanceState: Bundle?) {
public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState)
super.onCreate(savedInstanceState); arguments?.let {
if (getArguments() != null) { userName = it.getString(ProfileActivity.KEY_USERNAME)
userName = getArguments().getString(ProfileActivity.KEY_USERNAME);
} }
} }
/** override fun onCreateView(
* This method helps in the creation Achievement screen and inflater: LayoutInflater,
* dynamically set the size of imageView container: ViewGroup?,
* savedInstanceState: Bundle?
* @param savedInstanceState Data bundle ): View {
*/ _binding = FragmentAchievementsBinding.inflate(inflater, container, false)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentAchievementsBinding.inflate(inflater, container, false);
View rootView = binding.getRoot();
binding.achievementInfo.setOnClickListener(view -> showInfoDialog()); binding.achievementInfo.setOnClickListener { showInfoDialog() }
binding.imagesUploadInfo.setOnClickListener(view -> showUploadInfo()); binding.imagesUploadInfo.setOnClickListener { showUploadInfo() }
binding.imagesRevertedInfo.setOnClickListener(view -> showRevertedInfo()); binding.imagesRevertedInfo.setOnClickListener { showRevertedInfo() }
binding.imagesUsedByWikiInfo.setOnClickListener(view -> showUsedByWikiInfo()); binding.imagesUsedByWikiInfo.setOnClickListener { showUsedByWikiInfo() }
binding.imagesNearbyInfo.setOnClickListener(view -> showImagesViaNearbyInfo()); binding.imagesNearbyInfo.setOnClickListener { showImagesViaNearbyInfo() }
binding.imagesFeaturedInfo.setOnClickListener(view -> showFeaturedImagesInfo()); binding.imagesFeaturedInfo.setOnClickListener { showFeaturedImagesInfo() }
binding.thanksReceivedInfo.setOnClickListener(view -> showThanksReceivedInfo()); binding.thanksReceivedInfo.setOnClickListener { showThanksReceivedInfo() }
binding.qualityImagesInfo.setOnClickListener(view -> showQualityImagesInfo()); binding.qualityImagesInfo.setOnClickListener { showQualityImagesInfo() }
// DisplayMetrics used to fetch the size of the screen // DisplayMetrics used to fetch the size of the screen
DisplayMetrics displayMetrics = new DisplayMetrics(); val displayMetrics = DisplayMetrics()
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
int height = displayMetrics.heightPixels; val height = displayMetrics.heightPixels
int width = displayMetrics.widthPixels; val width = displayMetrics.widthPixels
// Used for the setting the size of imageView at runtime // Used for the setting the size of imageView at runtime
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) val params = binding.achievementBadgeImage.layoutParams as ConstraintLayout.LayoutParams
binding.achievementBadgeImage.getLayoutParams(); params.height = (height * BADGE_IMAGE_HEIGHT_RATIO).toInt()
params.height = (int) (height * BADGE_IMAGE_HEIGHT_RATIO); params.width = (width * BADGE_IMAGE_WIDTH_RATIO).toInt()
params.width = (int) (width * BADGE_IMAGE_WIDTH_RATIO); binding.achievementBadgeImage.requestLayout()
binding.achievementBadgeImage.requestLayout(); binding.progressBar.visibility = View.VISIBLE
binding.progressBar.setVisibility(View.VISIBLE);
setHasOptionsMenu(true); setHasOptionsMenu(true)
// Set the initial value of WikiData edits to 0 // Set the initial value of WikiData edits to 0
binding.wikidataEdits.setText("0"); binding.wikidataEdits.text = "0"
if(sessionManager.getUserName() == null || sessionManager.getUserName().equals(userName)){ if (sessionManager.userName == null || sessionManager.userName == userName) {
binding.tvAchievementsOfUser.setVisibility(View.GONE); binding.tvAchievementsOfUser.visibility = View.GONE
}else{ } else {
binding.tvAchievementsOfUser.setVisibility(View.VISIBLE); binding.tvAchievementsOfUser.visibility = View.VISIBLE
binding.tvAchievementsOfUser.setText(getString(R.string.achievements_of_user,userName)); binding.tvAchievementsOfUser.text = getString(R.string.achievements_of_user, userName)
}
if (isBetaFlavour) {
binding.progressBar.visibility = View.GONE
binding.imagesUsedByWikiText.setText(R.string.no_image)
binding.imagesRevertedText.setText(R.string.no_image_reverted)
binding.imagesUploadTextParam.setText(R.string.no_image_uploaded)
binding.wikidataEdits.text = "0"
binding.imageFeatured.text = "0"
binding.qualityImages.text = "0"
binding.achievementLevel.text = "0"
setMenuVisibility(true)
return binding.root
} }
// Achievements currently unimplemented in Beta flavor. Skip all API calls.
if(ConfigUtils.isBetaFlavour()) { setWikidataEditCount()
binding.progressBar.setVisibility(View.GONE); setAchievements()
binding.imagesUsedByWikiText.setText(R.string.no_image); return binding.root
binding.imagesRevertedText.setText(R.string.no_image_reverted);
binding.imagesUploadTextParam.setText(R.string.no_image_uploaded);
binding.wikidataEdits.setText("0");
binding.imageFeatured.setText("0");
binding.qualityImages.setText("0");
binding.achievementLevel.setText("0");
setMenuVisibility(true);
return rootView;
}
setWikidataEditCount();
setAchievements();
return rootView;
} }
@Override
public void onDestroyView() { override fun onDestroyView() {
binding = null; super.onDestroyView()
super.onDestroyView(); _binding = null
} }
@Override
public void setMenuVisibility(boolean visible) { override fun setMenuVisibility(visible: Boolean) {
super.setMenuVisibility(visible); super.setMenuVisibility(visible)
// Whenever this fragment is revealed in a menu, // Whenever this fragment is revealed in a menu,
// notify Beta users the page data is unavailable // notify Beta users the page data is unavailable
if(ConfigUtils.isBetaFlavour() && visible) { if (isBetaFlavour && visible) {
Context ctx = null; val ctx = context ?: view?.context
if(getContext() != null) { ctx?.let {
ctx = getContext(); Toast.makeText(it, R.string.achievements_unavailable_beta, Toast.LENGTH_LONG).show()
} else if(getView() != null && getView().getContext() != null) {
ctx = getView().getContext();
}
if(ctx != null) {
Toast.makeText(ctx,
R.string.achievements_unavailable_beta,
Toast.LENGTH_LONG).show();
} }
} }
} }
@ -170,78 +139,86 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
/** /**
* To invoke the AlertDialog on clicking info button * To invoke the AlertDialog on clicking info button
*/ */
protected void showInfoDialog(){ protected fun showInfoDialog() {
launchAlert( launchAlert(
getResources().getString(R.string.Achievements), resources.getString(R.string.Achievements),
getResources().getString(R.string.achievements_info_message)); resources.getString(R.string.achievements_info_message)
)
} }
/** /**
* To call the API to get results in form Single<JSONObject> * To call the API to get results in form Single<JSONObject>
* which then calls parseJson when results are fetched * which then calls parseJson when results are fetched
*/ */
private void setAchievements() {
binding.progressBar.setVisibility(View.VISIBLE);
if (checkAccount()) {
try{
compositeDisposable.add(okHttpJsonApiClient private fun setAchievements() {
.getAchievements(Objects.requireNonNull(userName)) binding.progressBar.visibility = View.VISIBLE
.subscribeOn(Schedulers.io()) if (checkAccount()) {
.observeOn(AndroidSchedulers.mainThread()) try {
.subscribe( compositeDisposable.add(
response -> { okHttpJsonApiClient
if (response != null) { .getAchievements(userName ?: return)
setUploadCount(Achievements.from(response)); .subscribeOn(Schedulers.io())
} else { .observeOn(AndroidSchedulers.mainThread())
Timber.d("success"); .subscribe(
binding.layoutImageReverts.setVisibility(View.INVISIBLE); { response ->
binding.achievementBadgeImage.setVisibility(View.INVISIBLE); if (response != null) {
// If the number of edits made by the user are more than 150,000 setUploadCount(Achievements.from(response))
// in some cases such high number of wiki edit counts cause the
// achievements calculator to fail in some cases, for more details
// refer Issue: #3295
if (numberOfEdits <= 150000) {
showSnackBarWithRetry(false);
} else { } else {
showSnackBarWithRetry(true); Timber.d("Success")
binding.layoutImageReverts.visibility = View.INVISIBLE
binding.achievementBadgeImage.visibility = View.INVISIBLE
// If the number of edits made by the user are more than 150,000
// in some cases such high number of wiki edit counts cause the
// achievements calculator to fail in some cases, for more details
// refer Issue: #3295
if (numberOfEdits <= 150_000) {
showSnackBarWithRetry(false)
} else {
showSnackBarWithRetry(true)
}
}
},
{ throwable ->
Timber.e(throwable, "Fetching achievements statistics failed")
if (numberOfEdits <= 150_000) {
showSnackBarWithRetry(false)
} else {
showSnackBarWithRetry(true)
} }
} }
}, )
t -> { )
Timber.e(t, "Fetching achievements statistics failed"); } catch (e: Exception) {
if (numberOfEdits <= 150000) { Timber.d("Exception: ${e.message}")
showSnackBarWithRetry(false);
} else {
showSnackBarWithRetry(true);
}
}
));
}
catch (Exception e){
Timber.d(e+"success");
} }
} }
} }
/** /**
* To call the API to fetch the count of wiki data edits * To call the API to fetch the count of wiki data edits
* in the form of JavaRx Single object<JSONobject> * in the form of JavaRx Single object<JSONobject>
*/ </JSONobject> */
private void setWikidataEditCount() {
private fun setWikidataEditCount() {
if (StringUtils.isBlank(userName)) { if (StringUtils.isBlank(userName)) {
return; return
} }
compositeDisposable.add(okHttpJsonApiClient compositeDisposable.add(
.getWikidataEdits(userName) okHttpJsonApiClient
.subscribeOn(Schedulers.io()) .getWikidataEdits(userName)
.observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io())
.subscribe(edits -> { .observeOn(AndroidSchedulers.mainThread())
numberOfEdits = edits; .subscribe({ edits: Int ->
binding.wikidataEdits.setText(String.valueOf(edits)); numberOfEdits = edits
}, e -> { binding.wikidataEdits.text = edits.toString()
Timber.e("Error:" + e); }, { e: Throwable ->
})); Timber.e("Error:$e")
})
)
} }
/** /**
@ -251,42 +228,52 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
* user are so high that it wrecks havoc with the Achievements calculator due to which request may time * user are so high that it wrecks havoc with the Achievements calculator due to which request may time
* out. Well this is the Ultimate Achievement * out. Well this is the Ultimate Achievement
*/ */
private void showSnackBarWithRetry(boolean tooManyAchievements) { private fun showSnackBarWithRetry(tooManyAchievements: Boolean) {
if (tooManyAchievements) { if (tooManyAchievements) {
binding.progressBar.setVisibility(View.GONE); binding.progressBar.visibility = View.GONE
ViewUtil.showDismissibleSnackBar(getActivity().findViewById(android.R.id.content), showDismissibleSnackBar(
R.string.achievements_fetch_failed_ultimate_achievement, R.string.retry, view -> setAchievements()); requireView().findViewById(android.R.id.content),
R.string.achievements_fetch_failed_ultimate_achievement, R.string.retry
) { setAchievements() }
} else { } else {
binding.progressBar.setVisibility(View.GONE); binding.progressBar.visibility = View.GONE
ViewUtil.showDismissibleSnackBar(getActivity().findViewById(android.R.id.content), showDismissibleSnackBar(
R.string.achievements_fetch_failed, R.string.retry, view -> setAchievements()); requireView().findViewById(android.R.id.content),
R.string.achievements_fetch_failed, R.string.retry
) { setAchievements() }
} }
} }
/** /**
* Shows a generic error toast when error occurs while loading achievements or uploads * Shows a generic error toast when error occurs while loading achievements or uploads
*/ */
private void onError() { private fun onError() {
ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.error_occurred)); showLongToast(requireActivity(), resources.getString(R.string.error_occurred))
binding.progressBar.setVisibility(View.GONE); binding.progressBar.visibility = View.GONE
} }
/** /**
* used to the count of images uploaded by user * used to the count of images uploaded by user
*/ */
private void setUploadCount(Achievements achievements) {
private fun setUploadCount(achievements: Achievements) {
if (checkAccount()) { if (checkAccount()) {
compositeDisposable.add(okHttpJsonApiClient compositeDisposable.add(okHttpJsonApiClient
.getUploadCount(Objects.requireNonNull(userName)) .getUploadCount(Objects.requireNonNull<String>(userName))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
uploadCount -> setAchievementsUploadCount(achievements, uploadCount), { uploadCount: Int? ->
t -> { setAchievementsUploadCount(
Timber.e(t, "Fetching upload count failed"); achievements,
onError(); uploadCount ?:0
)
},
{ t: Throwable? ->
Timber.e(t, "Fetching upload count failed")
onError()
} }
)); ))
} }
} }
@ -294,68 +281,72 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
* used to set achievements upload count and call hideProgressbar * used to set achievements upload count and call hideProgressbar
* @param uploadCount * @param uploadCount
*/ */
private void setAchievementsUploadCount(Achievements achievements, int uploadCount) { private fun setAchievementsUploadCount(achievements: Achievements, uploadCount: Int) {
// Create a new instance of Achievements with updated imagesUploaded // Create a new instance of Achievements with updated imagesUploaded
Achievements updatedAchievements = new Achievements( val updatedAchievements = Achievements(
achievements.getUniqueUsedImages(), achievements.uniqueUsedImages,
achievements.getArticlesUsingImages(), achievements.articlesUsingImages,
achievements.getThanksReceived(), achievements.thanksReceived,
achievements.getFeaturedImages(), achievements.featuredImages,
achievements.getQualityImages(), achievements.qualityImages,
uploadCount, // Update imagesUploaded with new value uploadCount, // Update imagesUploaded with new value
achievements.getRevertCount() achievements.revertCount
); )
hideProgressBar(updatedAchievements); hideProgressBar(updatedAchievements)
} }
/** /**
* used to the uploaded images progressbar * used to the uploaded images progressbar
* @param uploadCount * @param uploadCount
*/ */
private void setUploadProgress(int uploadCount){ private fun setUploadProgress(uploadCount: Int) {
if (uploadCount==0){ if (uploadCount == 0) {
setZeroAchievements(); setZeroAchievements()
}else { } else {
binding.imagesUploadedProgressbar.setVisibility(View.VISIBLE); binding.imagesUploadedProgressbar.visibility = View.VISIBLE
binding.imagesUploadedProgressbar.setProgress binding.imagesUploadedProgressbar.progress =
(100*uploadCount/levelInfo.getMaxUploadCount()); 100 * uploadCount / levelInfo.maxUploadCount
binding.tvUploadedImages.setText binding.tvUploadedImages.text = uploadCount.toString() + "/" + levelInfo.maxUploadCount
(uploadCount + "/" + levelInfo.getMaxUploadCount());
} }
} }
private void setZeroAchievements() { private fun setZeroAchievements() {
String message = !Objects.equals(sessionManager.getUserName(), userName) ? val message = if (sessionManager.userName != userName) {
getString(R.string.no_achievements_yet, userName) : getString(R.string.no_achievements_yet, userName )
getString(R.string.you_have_no_achievements_yet); } else {
DialogUtil.showAlertDialog(getActivity(), getString(R.string.you_have_no_achievements_yet)
}
showAlertDialog(
requireActivity(),
null, null,
message, message,
getString(R.string.ok), getString(R.string.ok),
() -> {}, {},
true); true
)
// binding.imagesUploadedProgressbar.setVisibility(View.INVISIBLE); // binding.imagesUploadedProgressbar.setVisibility(View.INVISIBLE);
// binding.imageRevertsProgressbar.setVisibility(View.INVISIBLE); // binding.imageRevertsProgressbar.setVisibility(View.INVISIBLE);
// binding.imagesUsedByWikiProgressBar.setVisibility(View.INVISIBLE); // binding.imagesUsedByWikiProgressBar.setVisibility(View.INVISIBLE);
binding.achievementBadgeImage.setVisibility(View.INVISIBLE); binding.achievementBadgeImage.visibility = View.INVISIBLE
binding.imagesUsedByWikiText.setText(R.string.no_image); binding.imagesUsedByWikiText.setText(R.string.no_image)
binding.imagesRevertedText.setText(R.string.no_image_reverted); binding.imagesRevertedText.setText(R.string.no_image_reverted)
binding.imagesUploadTextParam.setText(R.string.no_image_uploaded); binding.imagesUploadTextParam.setText(R.string.no_image_uploaded)
binding.achievementBadgeImage.setVisibility(View.INVISIBLE); binding.achievementBadgeImage.visibility = View.INVISIBLE
} }
/** /**
* used to set the non revert image percentage * used to set the non revert image percentage
* @param notRevertPercentage * @param notRevertPercentage
*/ */
private void setImageRevertPercentage(int notRevertPercentage){ private fun setImageRevertPercentage(notRevertPercentage: Int) {
binding.imageRevertsProgressbar.setVisibility(View.VISIBLE); binding.imageRevertsProgressbar.visibility = View.VISIBLE
binding.imageRevertsProgressbar.setProgress(notRevertPercentage); binding.imageRevertsProgressbar.progress = notRevertPercentage
final String revertPercentage = Integer.toString(notRevertPercentage); val revertPercentage = notRevertPercentage.toString()
binding.tvRevertedImages.setText(revertPercentage + "%"); binding.tvRevertedImages.text = "$revertPercentage%"
binding.imagesRevertLimitText.setText(getResources().getString(R.string.achievements_revert_limit_message)+ levelInfo.getMinNonRevertPercentage() + "%"); binding.imagesRevertLimitText.text =
resources.getString(R.string.achievements_revert_limit_message) + levelInfo.minNonRevertPercentage + "%"
} }
/** /**
@ -363,87 +354,101 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
* and assign badge and level. Also stores the achievements level of the user in BasicKvStore to display in menu * and assign badge and level. Also stores the achievements level of the user in BasicKvStore to display in menu
* @param achievements * @param achievements
*/ */
private void inflateAchievements(Achievements achievements) { private fun inflateAchievements(achievements: Achievements) {
// binding.imagesUsedByWikiProgressBar.setVisibility(View.VISIBLE); // binding.imagesUsedByWikiProgressBar.setVisibility(View.VISIBLE);
binding.thanksReceived.setText(String.valueOf(achievements.getThanksReceived())); binding.thanksReceived.text = achievements.thanksReceived.toString()
binding.imagesUsedByWikiProgressBar.setProgress binding.imagesUsedByWikiProgressBar.progress =
(100 * achievements.getUniqueUsedImages() / levelInfo.getMaxUniqueImages()); 100 * achievements.uniqueUsedImages / levelInfo.maxUniqueImages
binding.tvWikiPb.setText(achievements.getUniqueUsedImages() + "/" binding.tvWikiPb.text = (achievements.uniqueUsedImages.toString() + "/"
+ levelInfo.getMaxUniqueImages()); + levelInfo.maxUniqueImages)
binding.imageFeatured.setText(String.valueOf(achievements.getFeaturedImages())); binding.imageFeatured.text = achievements.featuredImages.toString()
binding.qualityImages.setText(String.valueOf(achievements.getQualityImages())); binding.qualityImages.text = achievements.qualityImages.toString()
String levelUpInfoString = getString(R.string.level).toUpperCase(Locale.ROOT); var levelUpInfoString = getString(R.string.level).uppercase()
levelUpInfoString += " " + levelInfo.getLevelNumber(); levelUpInfoString += " " + levelInfo.levelNumber
binding.achievementLevel.setText(levelUpInfoString); binding.achievementLevel.text = levelUpInfoString
binding.achievementBadgeImage.setImageDrawable(VectorDrawableCompat.create(getResources(), R.drawable.badge, binding.achievementBadgeImage.setImageDrawable(
new ContextThemeWrapper(getActivity(), levelInfo.getLevelStyle()).getTheme())); VectorDrawableCompat.create(
binding.achievementBadgeText.setText(Integer.toString(levelInfo.getLevelNumber())); resources, R.drawable.badge,
BasicKvStore store = new BasicKvStore(this.getContext(), userName); ContextThemeWrapper(activity, levelInfo.levelStyle).theme
store.putString("userAchievementsLevel", Integer.toString(levelInfo.getLevelNumber())); )
)
binding.achievementBadgeText.text = levelInfo.levelNumber.toString()
val store = BasicKvStore(this.context, userName)
store.putString("userAchievementsLevel", levelInfo.levelNumber.toString())
} }
/** /**
* to hide progressbar * to hide progressbar
*/ */
private void hideProgressBar(Achievements achievements) { private fun hideProgressBar(achievements: Achievements) {
if (binding.progressBar != null) { if (binding.progressBar != null) {
levelInfo = LevelController.LevelInfo.from(achievements.getImagesUploaded(), levelInfo = from(
achievements.getUniqueUsedImages(), achievements.imagesUploaded,
achievements.getNotRevertPercentage()); achievements.uniqueUsedImages,
inflateAchievements(achievements); achievements.notRevertPercentage
setUploadProgress(achievements.getImagesUploaded()); )
setImageRevertPercentage(achievements.getNotRevertPercentage()); inflateAchievements(achievements)
binding.progressBar.setVisibility(View.GONE); setUploadProgress(achievements.imagesUploaded)
setImageRevertPercentage(achievements.notRevertPercentage)
binding.progressBar.visibility = View.GONE
} }
} }
protected void showUploadInfo(){ protected fun showUploadInfo() {
launchAlertWithHelpLink( launchAlertWithHelpLink(
getResources().getString(R.string.images_uploaded), resources.getString(R.string.images_uploaded),
getResources().getString(R.string.images_uploaded_explanation), resources.getString(R.string.images_uploaded_explanation),
IMAGES_UPLOADED_URL); IMAGES_UPLOADED_URL
)
} }
protected void showRevertedInfo(){ protected fun showRevertedInfo() {
launchAlertWithHelpLink( launchAlertWithHelpLink(
getResources().getString(R.string.image_reverts), resources.getString(R.string.image_reverts),
getResources().getString(R.string.images_reverted_explanation), resources.getString(R.string.images_reverted_explanation),
IMAGES_REVERT_URL); IMAGES_REVERT_URL
)
} }
protected void showUsedByWikiInfo(){ protected fun showUsedByWikiInfo() {
launchAlertWithHelpLink( launchAlertWithHelpLink(
getResources().getString(R.string.images_used_by_wiki), resources.getString(R.string.images_used_by_wiki),
getResources().getString(R.string.images_used_explanation), resources.getString(R.string.images_used_explanation),
IMAGES_USED_URL); IMAGES_USED_URL
)
} }
protected void showImagesViaNearbyInfo(){ protected fun showImagesViaNearbyInfo() {
launchAlertWithHelpLink( launchAlertWithHelpLink(
getResources().getString(R.string.statistics_wikidata_edits), resources.getString(R.string.statistics_wikidata_edits),
getResources().getString(R.string.images_via_nearby_explanation), resources.getString(R.string.images_via_nearby_explanation),
IMAGES_NEARBY_PLACES_URL); IMAGES_NEARBY_PLACES_URL
)
} }
protected void showFeaturedImagesInfo(){ protected fun showFeaturedImagesInfo() {
launchAlertWithHelpLink( launchAlertWithHelpLink(
getResources().getString(R.string.statistics_featured), resources.getString(R.string.statistics_featured),
getResources().getString(R.string.images_featured_explanation), resources.getString(R.string.images_featured_explanation),
IMAGES_FEATURED_URL); IMAGES_FEATURED_URL
)
} }
protected void showThanksReceivedInfo(){ protected fun showThanksReceivedInfo() {
launchAlertWithHelpLink( launchAlertWithHelpLink(
getResources().getString(R.string.statistics_thanks), resources.getString(R.string.statistics_thanks),
getResources().getString(R.string.thanks_received_explanation), resources.getString(R.string.thanks_received_explanation),
THANKS_URL); THANKS_URL
)
} }
public void showQualityImagesInfo() { fun showQualityImagesInfo() {
launchAlertWithHelpLink( launchAlertWithHelpLink(
getResources().getString(R.string.statistics_quality), resources.getString(R.string.statistics_quality),
getResources().getString(R.string.quality_images_info), resources.getString(R.string.quality_images_info),
QUALITY_IMAGE_URL); QUALITY_IMAGE_URL
)
} }
/** /**
@ -451,42 +456,63 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
* @param title * @param title
* @param message * @param message
*/ */
private void launchAlert(String title, String message){ private fun launchAlert(title: String, message: String) {
DialogUtil.showAlertDialog(getActivity(), showAlertDialog(
requireActivity(),
title, title,
message, message,
getString(R.string.ok), getString(R.string.ok),
() -> {}, {},
true); true
)
} }
/** /**
* Launch Alert with a READ MORE button and clicking it open a custom webpage * Launch Alert with a READ MORE button and clicking it open a custom webpage
*/ */
private void launchAlertWithHelpLink(String title, String message, String helpLinkUrl) { private fun launchAlertWithHelpLink(title: String, message: String, helpLinkUrl: String) {
DialogUtil.showAlertDialog(getActivity(), showAlertDialog(
requireActivity(),
title, title,
message, message,
getString(R.string.ok), getString(R.string.ok),
getString(R.string.read_help_link), getString(R.string.read_help_link),
() -> {}, {},
() -> Utils.handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)), { Utils.handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) },
null, null,
true); true
)
} }
/** /**
* check to ensure that user is logged in * check to ensure that user is logged in
* @return * @return
*/ */
private boolean checkAccount(){ private fun checkAccount(): Boolean {
Account currentAccount = sessionManager.getCurrentAccount(); val currentAccount = sessionManager.currentAccount
if (currentAccount == null) { if (currentAccount == null) {
Timber.d("Current account is null"); Timber.d("Current account is null")
ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.user_not_logged_in)); showLongToast(requireActivity(), resources.getString(R.string.user_not_logged_in))
sessionManager.forceLogin(getActivity()); sessionManager.forceLogin(activity)
return false; return false
} }
return true; return true
}
companion object{
private const val BADGE_IMAGE_WIDTH_RATIO = 0.4
private const val BADGE_IMAGE_HEIGHT_RATIO = 0.3
/**
* Help link URLs
*/
private const val IMAGES_UPLOADED_URL = "https://commons.wikimedia.org/wiki/Commons:Project_scope"
private const val IMAGES_REVERT_URL = "https://commons.wikimedia.org/wiki/Commons:Deletion_policy#Reasons_for_deletion"
private const val IMAGES_USED_URL = "https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style/Images"
private const val IMAGES_NEARBY_PLACES_URL = "https://www.wikidata.org/wiki/Property:P18"
private const val IMAGES_FEATURED_URL = "https://commons.wikimedia.org/wiki/Commons:Featured_pictures"
private const val QUALITY_IMAGE_URL = "https://commons.wikimedia.org/wiki/Commons:Quality_images"
private const val THANKS_URL = "https://www.mediawiki.org/wiki/Extension:Thanks"
} }
} }