mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Merge ffae85b18c into 91ca2e6672
This commit is contained in:
commit
e3b65ef20a
11 changed files with 304 additions and 257 deletions
|
|
@ -53,11 +53,26 @@ dependencies {
|
|||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
|
||||
|
||||
def lifecycle_version = "2.8.7"
|
||||
// ViewModel
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||
// ViewModel utilities for Compose
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
|
||||
|
||||
// Lifecycles only (without ViewModel or LiveData)
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
|
||||
// Lifecycle utilities for Compose
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version"
|
||||
|
||||
// Saved state module for ViewModel
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
|
||||
|
||||
// Annotation processor
|
||||
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
|
||||
// Jetpack Compose
|
||||
def composeBom = platform('androidx.compose:compose-bom:2024.11.00')
|
||||
|
||||
implementation "androidx.activity:activity-compose:1.9.3"
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.4"
|
||||
implementation (composeBom)
|
||||
implementation "androidx.compose.runtime:runtime"
|
||||
implementation "androidx.compose.ui:ui"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import fr.free.nrw.commons.media.PageMediaInterface
|
|||
import fr.free.nrw.commons.media.WikidataMediaInterface
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.mwapi.UserInterface
|
||||
import fr.free.nrw.commons.network.APIService
|
||||
import fr.free.nrw.commons.notification.NotificationInterface
|
||||
import fr.free.nrw.commons.review.ReviewInterface
|
||||
import fr.free.nrw.commons.upload.UploadInterface
|
||||
|
|
@ -42,6 +43,8 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import okhttp3.logging.HttpLoggingInterceptor.Level
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
|
@ -295,6 +298,32 @@ class NetworkingModule {
|
|||
fun provideLanguageWikipediaSite(): WikiSite =
|
||||
WikiSite.forDefaultLocaleLanguageCode()
|
||||
|
||||
@Provides
|
||||
@Named("tool_wmflabs_base_url")
|
||||
fun provideToolWmflabsBaseUrl() : HttpUrl =
|
||||
"https://tools.wmflabs.org/commons-android-app/tool-commons-android-app/".toHttpUrlOrNull()!!
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
@Named("tool_wmflabs_retrofit")
|
||||
fun provideToolWmflabsBaseUrlRetrofit(
|
||||
@Named("tool_wmflabs_base_url") baseUrl: HttpUrl,
|
||||
okHttpClient: OkHttpClient
|
||||
) : Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
// .addCallAdapterFactory(CoroutineCallAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAPIService(
|
||||
@Named("tool_wmflabs_retrofit") retrofit: Retrofit
|
||||
): APIService = retrofit.create(APIService::class.java)
|
||||
|
||||
companion object {
|
||||
private const val WIKIDATA_SPARQL_QUERY_URL = "https://query.wikidata.org/sparql"
|
||||
private const val TOOLS_FORGE_URL =
|
||||
|
|
|
|||
23
app/src/main/java/fr/free/nrw/commons/network/APIService.kt
Normal file
23
app/src/main/java/fr/free/nrw/commons/network/APIService.kt
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package fr.free.nrw.commons.network
|
||||
|
||||
import fr.free.nrw.commons.profile.model.AchievementResponse
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
|
||||
interface APIService {
|
||||
|
||||
// https://tools.wmflabs.org/commons-android-app/tool-commons-android-app/uploadsbyuser.py?user=Devanonymous
|
||||
@GET("uploadsbyuser.py")
|
||||
suspend fun getImageUploadCount(
|
||||
@Query("user") username : String
|
||||
) : Response<Int>
|
||||
|
||||
|
||||
// https://tools.wmflabs.org/commons-android-app/tool-commons-android-app//feedback.py?user=Devanonymous
|
||||
@GET("feedback.py")
|
||||
suspend fun getUserAchievements(
|
||||
@Query("user") username: String
|
||||
) : Response<AchievementResponse>
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package fr.free.nrw.commons.profile.achievements
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import fr.free.nrw.commons.profile.model.UserAchievements
|
||||
import fr.free.nrw.commons.repository.ProfileRepository
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class AchievementViewModel @Inject constructor(
|
||||
private val repository: ProfileRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private val _achievements = MutableStateFlow(UserAchievements(
|
||||
LevelController.LevelInfo.LEVEL_1,
|
||||
articlesUsingImagesCount = 0,
|
||||
thanksReceivedCount = 0,
|
||||
featuredImagesCount = 0,
|
||||
qualityImagesCount = 0,
|
||||
imagesUploadedCount = 0,
|
||||
revertedCount = 0,
|
||||
uniqueImagesCount = 0,
|
||||
imagesEditedBySomeoneElseCount = 0
|
||||
)
|
||||
)
|
||||
val achievements : StateFlow<UserAchievements> = _achievements
|
||||
|
||||
private val _loading = MutableStateFlow(true)
|
||||
val loading : StateFlow<Boolean> = _loading
|
||||
|
||||
fun getUserAchievements(username: String){
|
||||
viewModelScope.launch {
|
||||
repository.getUserLevel(username = username).collect {
|
||||
_loading.value = false
|
||||
_achievements.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package fr.free.nrw.commons.profile.achievements
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
/**
|
||||
* This class extends the ViewModelProvider.Factory and creates a ViewModelFactory class
|
||||
* for AchievementViewModel
|
||||
*/
|
||||
class AchievementViewModelFactory @Inject constructor(
|
||||
private val viewModelProvider: Provider<AchievementViewModel>
|
||||
): ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(AchievementViewModel::class.java)) {
|
||||
(@Suppress("UNCHECKED_CAST")
|
||||
return viewModelProvider.get() as T)
|
||||
} else {
|
||||
throw IllegalArgumentException("Unknown class name")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,16 @@
|
|||
package fr.free.nrw.commons.profile.achievements
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.google.android.material.badge.BadgeUtils
|
||||
|
|
@ -22,21 +23,20 @@ import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
|||
import fr.free.nrw.commons.kvstore.BasicKvStore
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.profile.ProfileActivity
|
||||
import fr.free.nrw.commons.profile.achievements.LevelController.LevelInfo.Companion.from
|
||||
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
||||
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showDismissibleSnackBar
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.util.Objects
|
||||
import javax.inject.Inject
|
||||
|
||||
class AchievementsFragment : CommonsDaggerSupportFragment(){
|
||||
private lateinit var levelInfo: LevelController.LevelInfo
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: AchievementViewModelFactory
|
||||
lateinit var viewModel: AchievementViewModel
|
||||
@Inject
|
||||
lateinit var sessionManager: SessionManager
|
||||
|
||||
|
|
@ -45,11 +45,8 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
|
||||
private var _binding: FragmentAchievementsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
// To keep track of the number of wiki edits made by a user
|
||||
private var numberOfEdits: Int = 0
|
||||
|
||||
private var userName: String? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
arguments?.let {
|
||||
|
|
@ -64,6 +61,8 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
): View {
|
||||
_binding = FragmentAchievementsBinding.inflate(inflater, container, false)
|
||||
|
||||
viewModel = ViewModelProvider(
|
||||
this@AchievementsFragment, viewModelFactory)[AchievementViewModel::class.java]
|
||||
binding.achievementInfo.setOnClickListener { showInfoDialog() }
|
||||
binding.imagesUploadInfoIcon.setOnClickListener { showUploadInfo() }
|
||||
binding.imagesRevertedInfoIcon.setOnClickListener { showRevertedInfo() }
|
||||
|
|
@ -73,19 +72,15 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
binding.thanksImageIcon.setOnClickListener { showThanksReceivedInfo() }
|
||||
binding.qualityImageIcon.setOnClickListener { showQualityImagesInfo() }
|
||||
|
||||
// DisplayMetrics used to fetch the size of the screen
|
||||
val displayMetrics = DisplayMetrics()
|
||||
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||
val height = displayMetrics.heightPixels
|
||||
val width = displayMetrics.widthPixels
|
||||
|
||||
// Used for the setting the size of imageView at runtime
|
||||
// TODO REMOVE
|
||||
val params = binding.achievementBadgeImage.layoutParams as ConstraintLayout.LayoutParams
|
||||
params.height = (height * BADGE_IMAGE_HEIGHT_RATIO).toInt()
|
||||
params.width = (width * BADGE_IMAGE_WIDTH_RATIO).toInt()
|
||||
binding.achievementBadgeImage.requestLayout()
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
lifecycleScope.launch {
|
||||
viewModel.loading.collectLatest {
|
||||
if (it){
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.progressBar.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setHasOptionsMenu(true)
|
||||
if (sessionManager.userName == null || sessionManager.userName == userName) {
|
||||
|
|
@ -100,8 +95,6 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
return binding.root
|
||||
}
|
||||
|
||||
|
||||
setWikidataEditCount()
|
||||
setAchievements()
|
||||
return binding.root
|
||||
|
||||
|
|
@ -145,74 +138,58 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
* which then calls parseJson when results are fetched
|
||||
*/
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun setAchievements() {
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
if (checkAccount()) {
|
||||
try {
|
||||
compositeDisposable.add(
|
||||
okHttpJsonApiClient
|
||||
.getAchievements(userName ?: return)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ response ->
|
||||
if (response != null) {
|
||||
setUploadCount(Achievements.from(response))
|
||||
} else {
|
||||
Timber.d("Success")
|
||||
// TODO Create a Method to Hide all the Statistics
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
viewModel.getUserAchievements(username = userName.toString())
|
||||
|
||||
lifecycleScope.launch {
|
||||
viewModel.achievements.collect{
|
||||
|
||||
binding.achievementLevel.text = getString(R.string.level,it.level.levelNumber)
|
||||
val store = BasicKvStore(requireContext(), userName)
|
||||
store.putString("userAchievementsLevel", it.level.levelNumber.toString())
|
||||
|
||||
binding.achievementBadgeImage.setImageDrawable(
|
||||
VectorDrawableCompat.create(
|
||||
resources, R.drawable.badge,
|
||||
ContextThemeWrapper(activity, it.level.levelStyle).theme
|
||||
)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Timber.d("Exception: ${e.message}")
|
||||
)
|
||||
binding.achievementBadgeText.text = it.level.levelNumber.toString()
|
||||
|
||||
// TODO(use String Format)
|
||||
binding.imageUploadedTVCount.text =
|
||||
it.imagesUploadedCount.toString() + "/" + it.level.maxUploadCount
|
||||
binding.imagesUploadedProgressbar.progress =
|
||||
100 * it.imagesUploadedCount / it.level.maxUploadCount
|
||||
|
||||
// Revert
|
||||
binding.imageRevertTVCount.text = it.revertedCount.toString() + "%"
|
||||
binding.imageRevertsProgressbar.progress = it.revertedCount
|
||||
binding.imagesRevertLimitText.text =
|
||||
resources.getString(R.string.achievements_revert_limit_message) + it.level.minNonRevertPercentage + "%"
|
||||
|
||||
// Images Used
|
||||
binding.imagesUsedProgressbar.progress = (100 * it.uniqueImagesCount) / it.level.maxUniqueImages
|
||||
binding.imagesUsedCount.text = (it.uniqueImagesCount.toString() + "/"
|
||||
+ it.level.maxUniqueImages)
|
||||
|
||||
// Thanks Received Badge
|
||||
showBadgesWithCount(view = binding.thanksImageIcon, count = it.thanksReceivedCount)
|
||||
|
||||
// Featured Images Badge
|
||||
showBadgesWithCount(view = binding.featuredImageIcon, count = it.featuredImagesCount)
|
||||
|
||||
// Quality Images Badge
|
||||
showBadgesWithCount(view = binding.qualityImageIcon, count = it.qualityImagesCount)
|
||||
|
||||
showBadgesWithCount(view = binding.wikidataEditsIcon, count = it.imagesEditedBySomeoneElseCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To call the API to fetch the count of wiki data edits
|
||||
* in the form of JavaRx Single object<JSONobject>
|
||||
</JSONobject> */
|
||||
|
||||
private fun setWikidataEditCount() {
|
||||
if (StringUtils.isBlank(userName)) {
|
||||
return
|
||||
}
|
||||
compositeDisposable.add(
|
||||
okHttpJsonApiClient
|
||||
.getWikidataEdits(userName)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ edits: Int ->
|
||||
numberOfEdits = edits
|
||||
showBadgesWithCount(view = binding.wikidataEditsIcon, count = edits)
|
||||
}, { e: Throwable ->
|
||||
Timber.e("Error:$e")
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a snack bar which has an action button which on click dismisses the snackbar and invokes the
|
||||
* listener passed
|
||||
|
|
@ -253,49 +230,6 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
binding.progressBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
/**
|
||||
* used to the count of images uploaded by user
|
||||
*/
|
||||
|
||||
private fun setUploadCount(achievements: Achievements) {
|
||||
if (checkAccount()) {
|
||||
compositeDisposable.add(okHttpJsonApiClient
|
||||
.getUploadCount(Objects.requireNonNull<String>(userName))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ uploadCount: Int? ->
|
||||
setAchievementsUploadCount(
|
||||
achievements,
|
||||
uploadCount ?:0
|
||||
)
|
||||
},
|
||||
{ t: Throwable? ->
|
||||
Timber.e(t, "Fetching upload count failed")
|
||||
onError()
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* used to set achievements upload count and call hideProgressbar
|
||||
* @param uploadCount
|
||||
*/
|
||||
private fun setAchievementsUploadCount(achievements: Achievements, uploadCount: Int) {
|
||||
// Create a new instance of Achievements with updated imagesUploaded
|
||||
val updatedAchievements = Achievements(
|
||||
achievements.uniqueUsedImages,
|
||||
achievements.articlesUsingImages,
|
||||
achievements.thanksReceived,
|
||||
achievements.featuredImages,
|
||||
achievements.qualityImages,
|
||||
uploadCount, // Update imagesUploaded with new value
|
||||
achievements.revertCount
|
||||
)
|
||||
|
||||
hideProgressBar(updatedAchievements)
|
||||
}
|
||||
|
||||
/**
|
||||
* used to the uploaded images progressbar
|
||||
|
|
@ -306,9 +240,6 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
setZeroAchievements()
|
||||
} else {
|
||||
binding.imagesUploadedProgressbar.visibility = View.VISIBLE
|
||||
binding.imagesUploadedProgressbar.progress =
|
||||
100 * uploadCount / levelInfo.maxUploadCount
|
||||
binding.imageUploadedTVCount.text = uploadCount.toString() + "/" + levelInfo.maxUploadCount
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,7 +256,7 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
getString(R.string.ok),
|
||||
{}
|
||||
)
|
||||
|
||||
binding.layout.visibility = View.INVISIBLE
|
||||
// binding.imagesUploadedProgressbar.setVisibility(View.INVISIBLE);
|
||||
// binding.imageRevertsProgressbar.setVisibility(View.INVISIBLE);
|
||||
// binding.imagesUsedByWikiProgressBar.setVisibility(View.INVISIBLE);
|
||||
|
|
@ -335,52 +266,6 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
binding.imagesUploadTextParam.setText(R.string.no_image_uploaded)
|
||||
}
|
||||
|
||||
/**
|
||||
* used to set the non revert image percentage
|
||||
* @param notRevertPercentage
|
||||
*/
|
||||
private fun setImageRevertPercentage(notRevertPercentage: Int) {
|
||||
binding.imageRevertsProgressbar.visibility = View.VISIBLE
|
||||
binding.imageRevertsProgressbar.progress = notRevertPercentage
|
||||
val revertPercentage = notRevertPercentage.toString()
|
||||
binding.imageRevertTVCount.text = "$revertPercentage%"
|
||||
binding.imagesRevertLimitText.text =
|
||||
resources.getString(R.string.achievements_revert_limit_message) + levelInfo.minNonRevertPercentage + "%"
|
||||
}
|
||||
|
||||
/**
|
||||
* Used the inflate the fetched statistics of the images uploaded by user
|
||||
* and assign badge and level. Also stores the achievements level of the user in BasicKvStore to display in menu
|
||||
* @param achievements
|
||||
*/
|
||||
private fun inflateAchievements(achievements: Achievements) {
|
||||
|
||||
// Thanks Received Badge
|
||||
showBadgesWithCount(view = binding.thanksImageIcon, count = achievements.thanksReceived)
|
||||
|
||||
// Featured Images Badge
|
||||
showBadgesWithCount(view = binding.featuredImageIcon, count = achievements.featuredImages)
|
||||
|
||||
// Quality Images Badge
|
||||
showBadgesWithCount(view = binding.qualityImageIcon, count = achievements.qualityImages)
|
||||
|
||||
binding.imagesUsedByWikiProgressBar.progress =
|
||||
100 * achievements.uniqueUsedImages / levelInfo.maxUniqueImages
|
||||
binding.imagesUsedCount.text = (achievements.uniqueUsedImages.toString() + "/"
|
||||
+ levelInfo.maxUniqueImages)
|
||||
|
||||
binding.achievementLevel.text = getString(R.string.level,levelInfo.levelNumber)
|
||||
binding.achievementBadgeImage.setImageDrawable(
|
||||
VectorDrawableCompat.create(
|
||||
resources, R.drawable.badge,
|
||||
ContextThemeWrapper(activity, levelInfo.levelStyle).theme
|
||||
)
|
||||
)
|
||||
binding.achievementBadgeText.text = levelInfo.levelNumber.toString()
|
||||
val store = BasicKvStore(requireContext(), userName)
|
||||
store.putString("userAchievementsLevel", levelInfo.levelNumber.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to show badge on any view (button, imageView, etc)
|
||||
* @param view The View on which the badge will be displayed eg (button, imageView, etc)
|
||||
|
|
@ -425,22 +310,6 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* to hide progressbar
|
||||
*/
|
||||
private fun hideProgressBar(achievements: Achievements) {
|
||||
if (binding.progressBar != null) {
|
||||
levelInfo = from(
|
||||
achievements.imagesUploaded,
|
||||
achievements.uniqueUsedImages,
|
||||
achievements.notRevertPercentage
|
||||
)
|
||||
inflateAchievements(achievements)
|
||||
setUploadProgress(achievements.imagesUploaded)
|
||||
setImageRevertPercentage(achievements.notRevertPercentage)
|
||||
binding.progressBar.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
fun showUploadInfo() {
|
||||
launchAlertWithHelpLink(
|
||||
|
|
@ -546,9 +415,6 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
|
||||
|
||||
companion object{
|
||||
private const val BADGE_IMAGE_WIDTH_RATIO = 0.4
|
||||
private const val BADGE_IMAGE_HEIGHT_RATIO = 0.3
|
||||
|
||||
/**
|
||||
* Help link URLs
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package fr.free.nrw.commons.profile.model
|
||||
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AchievementResponse(
|
||||
@SerializedName("articlesUsingImages")
|
||||
val articlesUsingImages: Int,
|
||||
@SerializedName("database")
|
||||
val database: String,
|
||||
@SerializedName("deletedUploads")
|
||||
val deletedUploads: Int,
|
||||
@SerializedName("featuredImages")
|
||||
val featuredImages: FeaturedImages,
|
||||
@SerializedName("imagesEditedBySomeoneElse")
|
||||
val imagesEditedBySomeoneElse: Int,
|
||||
@SerializedName("labs")
|
||||
val labs: Boolean,
|
||||
@SerializedName("status")
|
||||
val status: String,
|
||||
@SerializedName("thanksReceived")
|
||||
val thanksReceived: Int,
|
||||
@SerializedName("uniqueUsedImages")
|
||||
val uniqueUsedImages: Int,
|
||||
@SerializedName("user")
|
||||
val user: String
|
||||
)
|
||||
|
||||
data class FeaturedImages(
|
||||
@SerializedName("Featured_pictures_on_Wikimedia_Commons")
|
||||
val featuredPicturesOnWikimediaCommons: Int,
|
||||
@SerializedName("Quality_images")
|
||||
val qualityImages: Int
|
||||
)
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package fr.free.nrw.commons.profile.model
|
||||
|
||||
import fr.free.nrw.commons.profile.achievements.LevelController
|
||||
|
||||
data class UserAchievements(
|
||||
val level: LevelController.LevelInfo,
|
||||
val articlesUsingImagesCount: Int = 0,
|
||||
val thanksReceivedCount: Int = 0,
|
||||
val featuredImagesCount: Int = 0,
|
||||
val qualityImagesCount: Int = 0,
|
||||
val imagesUploadedCount: Int = 0,
|
||||
val revertedCount: Int = 0,
|
||||
val uniqueImagesCount: Int = 0,
|
||||
val imagesEditedBySomeoneElseCount: Int = 0
|
||||
)
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package fr.free.nrw.commons.repository
|
||||
|
||||
import fr.free.nrw.commons.network.APIService
|
||||
import fr.free.nrw.commons.profile.achievements.LevelController
|
||||
import fr.free.nrw.commons.profile.model.UserAchievements
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class ProfileRepository @Inject constructor(private val apiService: APIService) {
|
||||
|
||||
fun getUserLevel(username: String) : Flow<UserAchievements> = flow {
|
||||
try {
|
||||
val uploadCountResponse = apiService.getImageUploadCount(username)
|
||||
val imagesUploaded = uploadCountResponse.body() ?:0
|
||||
|
||||
val achievementResponse = apiService.getUserAchievements(username)
|
||||
|
||||
val uniqueImages = achievementResponse.body()?.uniqueUsedImages ?: 0
|
||||
val articlesUsingImages = achievementResponse.body()?.articlesUsingImages ?: 0
|
||||
val thanksReceived = achievementResponse.body()?.thanksReceived ?: 0
|
||||
val featuredImages = achievementResponse.body()?.featuredImages?.featuredPicturesOnWikimediaCommons ?:0
|
||||
val qualityImages = achievementResponse.body()?.featuredImages?.qualityImages ?: 0
|
||||
val deletedUploads = achievementResponse.body()?.deletedUploads ?:0
|
||||
val revertCount = (imagesUploaded - deletedUploads) * 100 / imagesUploaded
|
||||
val imagesEditedBySomeoneElse = achievementResponse.body()?.imagesEditedBySomeoneElse ?:0
|
||||
|
||||
val level = LevelController.LevelInfo.from(
|
||||
imagesUploaded = imagesUploaded,
|
||||
uniqueImagesUsed = uniqueImages,
|
||||
nonRevertRate = revertCount)
|
||||
|
||||
emit(
|
||||
UserAchievements(
|
||||
level = level,
|
||||
articlesUsingImagesCount = articlesUsingImages,
|
||||
featuredImagesCount = featuredImages,
|
||||
imagesUploadedCount = imagesUploaded,
|
||||
qualityImagesCount = qualityImages,
|
||||
revertedCount = revertCount,
|
||||
thanksReceivedCount = thanksReceived,
|
||||
uniqueImagesCount = uniqueImages,
|
||||
imagesEditedBySomeoneElseCount = imagesEditedBySomeoneElse
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
catch(e : Exception) {
|
||||
Timber.e(e.printStackTrace().toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
android:id="@+id/achievement_badge_image"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginTop="100dp"
|
||||
android:background="@drawable/badge"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
|
|
@ -263,7 +263,7 @@
|
|||
app:layout_constraintTop_toBottomOf="@+id/images_used_tv">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/images_used_by_wiki_progress_bar"
|
||||
android:id="@+id/images_used_progressbar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -362,7 +362,7 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.5"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
android:visibility="gone"/>
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
|
|
@ -173,33 +173,6 @@ class AchievementsFragmentUnitTests {
|
|||
method.invoke(fragment, "", "")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testHideProgressBar() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
val method: Method =
|
||||
AchievementsFragment::class.java.getDeclaredMethod(
|
||||
"hideProgressBar",
|
||||
Achievements::class.java,
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment, achievements)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testSetAchievementsUploadCount() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
val method: Method =
|
||||
AchievementsFragment::class.java.getDeclaredMethod(
|
||||
"setAchievementsUploadCount",
|
||||
Achievements::class.java,
|
||||
Int::class.java,
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment, achievements, 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testCheckAccount() {
|
||||
|
|
@ -211,20 +184,7 @@ class AchievementsFragmentUnitTests {
|
|||
method.isAccessible = true
|
||||
method.invoke(fragment)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testSetUploadCount() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
val method: Method =
|
||||
AchievementsFragment::class.java.getDeclaredMethod(
|
||||
"setUploadCount",
|
||||
Achievements::class.java,
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment, achievements)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnError() {
|
||||
|
|
@ -263,18 +223,6 @@ class AchievementsFragmentUnitTests {
|
|||
method.invoke(fragment, false)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testSetWikidataEditCount() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
val method: Method =
|
||||
AchievementsFragment::class.java.getDeclaredMethod(
|
||||
"setWikidataEditCount",
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testSetAchievements() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue