From b381d40517817be180857badaff4d37df95f51c8 Mon Sep 17 00:00:00 2001
From: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com>
Date: Thu, 4 Jun 2020 20:59:20 +0530
Subject: [PATCH 1/2] [GSoC] Fixes #3789 Updated UI of achievements activity to
display level in first tab & Leaderboard in the second tab (#3794)
* Updated UI of achievements activity to display level in first tab and Leaderboard in the second tab
* Removed hardcoded string
---
.../nrw/commons/AchievementsActivityTest.kt | 5 +-
app/src/main/AndroidManifest.xml | 4 +-
.../nrw/commons/delete/ReasonBuilder.java | 2 +-
.../nrw/commons/di/ActivityBuilderModule.java | 4 +-
.../nrw/commons/di/FragmentBuilderModule.java | 8 +
.../commons/mwapi/OkHttpJsonApiClient.java | 4 +-
.../nrw/commons/profile/ProfileActivity.java | 79 +++
.../nrw/commons/profile/ViewPagerAdapter.java | 54 ++
.../achievements/Achievements.kt | 16 +-
.../achievements/AchievementsFragment.java} | 190 ++++---
.../achievements/FeaturedImages.kt | 8 +-
.../achievements/FeedbackResponse.kt | 6 +-
.../achievements/LevelController.kt | 2 +-
.../leaderboard/LeaderboardFragment.java | 18 +
.../commons/theme/NavigationBaseActivity.java | 4 +-
.../main/res/layout/activity_achievements.xml | 495 ------------------
app/src/main/res/layout/activity_profile.xml | 41 ++
.../main/res/layout/fragment_achievements.xml | 482 +++++++++++++++++
.../main/res/layout/fragment_leaderboard.xml | 6 +
app/src/main/res/values/strings.xml | 3 +
.../nrw/commons/delete/ReasonBuilderTest.kt | 3 +-
21 files changed, 810 insertions(+), 624 deletions(-)
create mode 100644 app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.java
create mode 100644 app/src/main/java/fr/free/nrw/commons/profile/ViewPagerAdapter.java
rename app/src/main/java/fr/free/nrw/commons/{ => profile}/achievements/Achievements.kt (85%)
rename app/src/main/java/fr/free/nrw/commons/{achievements/AchievementsActivity.java => profile/achievements/AchievementsFragment.java} (86%)
rename app/src/main/java/fr/free/nrw/commons/{ => profile}/achievements/FeaturedImages.kt (58%)
rename app/src/main/java/fr/free/nrw/commons/{ => profile}/achievements/FeedbackResponse.kt (66%)
rename app/src/main/java/fr/free/nrw/commons/{ => profile}/achievements/LevelController.kt (97%)
create mode 100644 app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java
delete mode 100644 app/src/main/res/layout/activity_achievements.xml
create mode 100644 app/src/main/res/layout/activity_profile.xml
create mode 100644 app/src/main/res/layout/fragment_achievements.xml
create mode 100644 app/src/main/res/layout/fragment_leaderboard.xml
diff --git a/app/src/androidTest/java/fr/free/nrw/commons/AchievementsActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/AchievementsActivityTest.kt
index 198578f2f..6bded4351 100644
--- a/app/src/androidTest/java/fr/free/nrw/commons/AchievementsActivityTest.kt
+++ b/app/src/androidTest/java/fr/free/nrw/commons/AchievementsActivityTest.kt
@@ -7,10 +7,9 @@ import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.test.espresso.matcher.ViewMatchers.withId
-import androidx.test.filters.MediumTest
import androidx.test.runner.AndroidJUnit4
-import fr.free.nrw.commons.achievements.AchievementsActivity
import fr.free.nrw.commons.auth.LoginActivity
+import fr.free.nrw.commons.profile.ProfileActivity
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -32,6 +31,6 @@ class AchievementsActivityTest {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
onView(withId(R.id.user_icon)).perform(click())
- Intents.intended(hasComponent(AchievementsActivity::class.java.name))
+ Intents.intended(hasComponent(ProfileActivity::class.java.name))
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index da4f147c8..a657904e1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -137,8 +137,8 @@
/>
+ android:name=".profile.ProfileActivity"
+ android:label="@string/Profile" />
fragmentList = new ArrayList<>();
+ List titleList = new ArrayList<>();
+ achievementsFragment = new AchievementsFragment();
+ fragmentList.add(achievementsFragment);
+ titleList.add(getResources().getString(R.string.achievements_tab_title).toUpperCase());
+ leaderboardFragment = new LeaderboardFragment();
+ fragmentList.add(leaderboardFragment);
+ titleList.add(getResources().getString(R.string.leaderboard_tab_title).toUpperCase());
+ viewPagerAdapter.setTabData(fragmentList, titleList);
+ viewPagerAdapter.notifyDataSetChanged();
+
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ compositeDisposable.clear();
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/profile/ViewPagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/profile/ViewPagerAdapter.java
new file mode 100644
index 000000000..d9db05696
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/profile/ViewPagerAdapter.java
@@ -0,0 +1,54 @@
+package fr.free.nrw.commons.profile;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ViewPagerAdapter extends FragmentPagerAdapter {
+ private List fragmentList = new ArrayList<>();
+ private List fragmentTitleList = new ArrayList<>();
+
+ public ViewPagerAdapter(FragmentManager manager) {
+ super(manager);
+ }
+
+ /**
+ * This method returns the fragment of the viewpager at a particular position
+ * @param position
+ */
+ @Override
+ public Fragment getItem(int position) {
+ return fragmentList.get(position);
+ }
+
+ /**
+ * This method returns the total number of fragments in the viewpager.
+ * @return size
+ */
+ @Override
+ public int getCount() {
+ return fragmentList.size();
+ }
+
+ /**
+ * This method sets the fragment and title list in the viewpager
+ * @param fragmentList List of all fragments to be displayed in the viewpager
+ * @param fragmentTitleList List of all titles of the fragments
+ */
+ public void setTabData(List fragmentList, List fragmentTitleList) {
+ this.fragmentList = fragmentList;
+ this.fragmentTitleList = fragmentTitleList;
+ }
+
+ /**
+ * This method returns the title of the page at a particular position
+ * @param position
+ */
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return fragmentTitleList.get(position);
+ }
+}
+
diff --git a/app/src/main/java/fr/free/nrw/commons/achievements/Achievements.kt b/app/src/main/java/fr/free/nrw/commons/profile/achievements/Achievements.kt
similarity index 85%
rename from app/src/main/java/fr/free/nrw/commons/achievements/Achievements.kt
rename to app/src/main/java/fr/free/nrw/commons/profile/achievements/Achievements.kt
index 3de252af8..081fe7e5f 100644
--- a/app/src/main/java/fr/free/nrw/commons/achievements/Achievements.kt
+++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/Achievements.kt
@@ -1,4 +1,4 @@
-package fr.free.nrw.commons.achievements
+package fr.free.nrw.commons.profile.achievements
/**
* Represents Achievements class and stores all the parameters
@@ -87,12 +87,14 @@ class Achievements {
*/
@JvmStatic
fun from(response: FeedbackResponse): Achievements {
- return Achievements(response.uniqueUsedImages,
- response.articlesUsingImages,
- response.thanksReceived,
- response.featuredImages.qualityImages
- + response.featuredImages.featuredPicturesOnWikimediaCommons, 0,
- response.deletedUploads)
+ return Achievements(
+ response.uniqueUsedImages,
+ response.articlesUsingImages,
+ response.thanksReceived,
+ response.featuredImages.qualityImages
+ + response.featuredImages.featuredPicturesOnWikimediaCommons, 0,
+ response.deletedUploads
+ )
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java b/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.java
similarity index 86%
rename from app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java
rename to app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.java
index d12ec942e..703f14f2f 100644
--- a/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.java
@@ -1,61 +1,52 @@
-package fr.free.nrw.commons.achievements;
+package fr.free.nrw.commons.profile.achievements;
import android.accounts.Account;
-import android.annotation.SuppressLint;
-import android.content.Context;
+import android.app.AlertDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.util.DisplayMetrics;
-import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
-
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.widget.Toolbar;
+import androidx.appcompat.view.ContextThemeWrapper;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.FileProvider;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
-
-import com.dinuscxj.progressbar.CircleProgressBar;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Objects;
-
-import javax.inject.Inject;
-
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
+import com.dinuscxj.progressbar.CircleProgressBar;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.auth.SessionManager;
+import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
-import fr.free.nrw.commons.theme.NavigationBaseActivity;
import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+import javax.inject.Inject;
+import org.apache.commons.lang3.StringUtils;
import timber.log.Timber;
-
-
/**
- * activity for sharing feedback on uploaded activity
+ * fragment for sharing feedback on uploaded activity
*/
-public class AchievementsActivity extends NavigationBaseActivity {
+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;
@@ -64,55 +55,71 @@ public class AchievementsActivity extends NavigationBaseActivity {
@BindView(R.id.achievement_badge_image)
ImageView imageView;
+
@BindView(R.id.achievement_badge_text)
TextView badgeText;
+
@BindView(R.id.achievement_level)
TextView levelNumber;
- @BindView(R.id.toolbar)
- Toolbar toolbar;
+
@BindView(R.id.thanks_received)
TextView thanksReceived;
+
@BindView(R.id.images_uploaded_progressbar)
CircleProgressBar imagesUploadedProgressbar;
+
@BindView(R.id.images_used_by_wiki_progress_bar)
CircleProgressBar imagesUsedByWikiProgressBar;
+
@BindView(R.id.image_reverts_progressbar)
CircleProgressBar imageRevertsProgressbar;
+
@BindView(R.id.image_featured)
TextView imagesFeatured;
+
@BindView(R.id.images_revert_limit_text)
TextView imagesRevertLimitText;
+
@BindView(R.id.progressBar)
ProgressBar progressBar;
+
@BindView(R.id.layout_image_uploaded)
RelativeLayout layoutImageUploaded;
+
@BindView(R.id.layout_image_reverts)
RelativeLayout layoutImageReverts;
+
@BindView(R.id.layout_image_used_by_wiki)
RelativeLayout layoutImageUsedByWiki;
+
@BindView(R.id.layout_statistics)
LinearLayout layoutStatistics;
+
@BindView(R.id.images_used_by_wiki_text)
TextView imageByWikiText;
+
@BindView(R.id.images_reverted_text)
TextView imageRevertedText;
+
@BindView(R.id.images_upload_text_param)
TextView imageUploadedText;
+
@BindView(R.id.wikidata_edits)
TextView wikidataEditsText;
-
@Inject
SessionManager sessionManager;
+
@Inject
OkHttpJsonApiClient okHttpJsonApiClient;
- MenuItem item;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
// To keep track of the number of wiki edits made by a user
private int numberOfEdits = 0;
+ MenuItem item;
+
/**
* This method helps in the creation Achievement screen and
* dynamically set the size of imageView
@@ -120,15 +127,13 @@ public class AchievementsActivity extends NavigationBaseActivity {
* @param savedInstanceState Data bundle
*/
@Override
- @SuppressLint("StringFormatInvalid")
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_achievements);
- ButterKnife.bind(this);
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_achievements, container, false);
+ ButterKnife.bind(this, rootView);
// DisplayMetrics used to fetch the size of the screen
DisplayMetrics displayMetrics = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
@@ -139,37 +144,23 @@ public class AchievementsActivity extends NavigationBaseActivity {
params.width = (int) (width * BADGE_IMAGE_WIDTH_RATIO);
imageView.requestLayout();
- setSupportActionBar(toolbar);
progressBar.setVisibility(View.VISIBLE);
+ setHasOptionsMenu(true);
+
hideLayouts();
setWikidataEditCount();
setAchievements();
- initDrawer();
+ return rootView;
}
@Override
- public void onDestroy() {
- super.onDestroy();
- compositeDisposable.clear();
- }
-
- /**
- * To invoke the AlertDialog on clicking info button
- */
- @OnClick(R.id.achievement_info)
- public void showInfoDialog(){
- launchAlert(getResources().getString(R.string.Achievements)
- ,getResources().getString(R.string.achievements_info_message));
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
// Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.menu_about, menu);
- item=menu.getItem(0);
+ super.onCreateOptionsMenu(menu, menuInflater);
+ menuInflater.inflate(R.menu.menu_about, menu);
+ item = menu.getItem(0);
item.setVisible(false);
- return true;
}
/**
@@ -180,7 +171,7 @@ public class AchievementsActivity extends NavigationBaseActivity {
int id = item.getItemId();
// take screenshot in form of bitmap and show it in Alert Dialog
if (id == R.id.share_app_icon) {
- View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
+ View rootView = getActivity().getWindow().getDecorView().findViewById(android.R.id.content);
Bitmap screenShot = Utils.getScreenShot(rootView);
showAlert(screenShot);
}
@@ -188,20 +179,39 @@ public class AchievementsActivity extends NavigationBaseActivity {
return super.onOptionsItemSelected(item);
}
+ /**
+ * It display the alertDialog with Image of screenshot
+ * @param screenshot
+ */
+ public void showAlert(Bitmap screenshot){
+ AlertDialog.Builder alertadd = new AlertDialog.Builder(getActivity());
+ LayoutInflater factory = LayoutInflater.from(getActivity());
+ final View view = factory.inflate(R.layout.image_alert_layout, null);
+ ImageView screenShotImage = view.findViewById(R.id.alert_image);
+ screenShotImage.setImageBitmap(screenshot);
+ TextView shareMessage = view.findViewById(R.id.alert_text);
+ shareMessage.setText(R.string.achievements_share_message);
+ alertadd.setView(view);
+ alertadd.setPositiveButton(R.string.about_translate_proceed, (dialog, which) -> shareScreen(screenshot));
+ alertadd.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.cancel());
+ alertadd.show();
+ }
+
/**
* To take bitmap and store it temporary storage and share it
* @param bitmap
*/
void shareScreen(Bitmap bitmap) {
try {
- File file = new File(this.getExternalCacheDir(), "screen.png");
+ File file = new File(getActivity().getExternalCacheDir(), "screen.png");
FileOutputStream fOut = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
file.setReadable(true, false);
- Uri fileUri = FileProvider.getUriForFile(getApplicationContext(), getPackageName()+".provider", file);
- grantUriPermission(getPackageName(), fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ Uri fileUri = FileProvider
+ .getUriForFile(getActivity().getApplicationContext(), getActivity().getPackageName()+".provider", file);
+ getActivity().grantUriPermission(getActivity().getPackageName(), fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
final Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
@@ -212,6 +222,15 @@ public class AchievementsActivity extends NavigationBaseActivity {
}
}
+ /**
+ * To invoke the AlertDialog on clicking info button
+ */
+ @OnClick(R.id.achievement_info)
+ public void showInfoDialog(){
+ launchAlert(getResources().getString(R.string.Achievements)
+ ,getResources().getString(R.string.achievements_info_message));
+ }
+
/**
* To call the API to get results in form Single
* which then calls parseJson when results are fetched
@@ -234,7 +253,7 @@ public class AchievementsActivity extends NavigationBaseActivity {
layoutImageReverts.setVisibility(View.INVISIBLE);
imageView.setVisibility(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
+ // 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) {
@@ -264,7 +283,6 @@ public class AchievementsActivity extends NavigationBaseActivity {
* To call the API to fetch the count of wiki data edits
* in the form of JavaRx Single object
*/
- @SuppressLint("CheckResult")
private void setWikidataEditCount() {
String userName = sessionManager.getUserName();
if (StringUtils.isBlank(userName)) {
@@ -285,18 +303,18 @@ public class AchievementsActivity extends NavigationBaseActivity {
/**
* Shows a snack bar which has an action button which on click dismisses the snackbar and invokes the
* listener passed
- * @param tooManyAchievements if this value is true it means that the number of achievements of the
- * user are so high that it wrecks havoc with the Achievements calculator due to which request may time
+ * @param tooManyAchievements if this value is true it means that the number of achievements of the
+ * 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
*/
private void showSnackBarWithRetry(boolean tooManyAchievements) {
if (tooManyAchievements) {
progressBar.setVisibility(View.GONE);
- ViewUtil.showDismissibleSnackBar(findViewById(android.R.id.content),
+ ViewUtil.showDismissibleSnackBar(getActivity().findViewById(android.R.id.content),
R.string.achievements_fetch_failed_ultimate_achievement, R.string.retry, view -> setAchievements());
} else {
progressBar.setVisibility(View.GONE);
- ViewUtil.showDismissibleSnackBar(findViewById(android.R.id.content),
+ ViewUtil.showDismissibleSnackBar(getActivity().findViewById(android.R.id.content),
R.string.achievements_fetch_failed, R.string.retry, view -> setAchievements());
}
}
@@ -305,7 +323,7 @@ public class AchievementsActivity extends NavigationBaseActivity {
* Shows a generic error toast when error occurs while loading achievements or uploads
*/
private void onError() {
- ViewUtil.showLongToast(this, getResources().getString(R.string.error_occurred));
+ ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.error_occurred));
progressBar.setVisibility(View.GONE);
}
@@ -355,7 +373,7 @@ public class AchievementsActivity extends NavigationBaseActivity {
}
private void setZeroAchievements() {
- AlertDialog.Builder builder=new AlertDialog.Builder(this)
+ AlertDialog.Builder builder=new AlertDialog.Builder(getActivity())
.setMessage(getString(R.string.no_achievements_yet))
.setPositiveButton(getString(R.string.ok), (dialog, which) -> {
});
@@ -399,20 +417,10 @@ public class AchievementsActivity extends NavigationBaseActivity {
levelUpInfoString += " " + levelInfo.getLevelNumber();
levelNumber.setText(levelUpInfoString);
imageView.setImageDrawable(VectorDrawableCompat.create(getResources(), R.drawable.badge,
- new ContextThemeWrapper(this, levelInfo.getLevelStyle()).getTheme()));
+ new ContextThemeWrapper(getActivity(), levelInfo.getLevelStyle()).getTheme()));
badgeText.setText(Integer.toString(levelInfo.getLevelNumber()));
}
- /**
- * Creates a way to change current activity to AchievementActivity
- * @param context
- */
- public static void startYourself(Context context) {
- Intent intent = new Intent(context, AchievementsActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- context.startActivity(intent);
- }
-
/**
* to hide progressbar
*/
@@ -447,24 +455,6 @@ public class AchievementsActivity extends NavigationBaseActivity {
levelNumber.setVisibility(View.INVISIBLE);
}
- /**
- * It display the alertDialog with Image of screenshot
- * @param screenshot
- */
- public void showAlert(Bitmap screenshot){
- AlertDialog.Builder alertadd = new AlertDialog.Builder(AchievementsActivity.this);
- LayoutInflater factory = LayoutInflater.from(AchievementsActivity.this);
- final View view = factory.inflate(R.layout.image_alert_layout, null);
- ImageView screenShotImage = view.findViewById(R.id.alert_image);
- screenShotImage.setImageBitmap(screenshot);
- TextView shareMessage = view.findViewById(R.id.alert_text);
- shareMessage.setText(R.string.achievements_share_message);
- alertadd.setView(view);
- alertadd.setPositiveButton(R.string.about_translate_proceed, (dialog, which) -> shareScreen(screenshot));
- alertadd.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.cancel());
- alertadd.show();
- }
-
@OnClick(R.id.images_upload_info)
public void showUploadInfo(){
launchAlert(getResources().getString(R.string.images_uploaded)
@@ -507,7 +497,7 @@ public class AchievementsActivity extends NavigationBaseActivity {
* @param message
*/
private void launchAlert(String title, String message){
- new AlertDialog.Builder(AchievementsActivity.this)
+ new AlertDialog.Builder(getActivity())
.setTitle(title)
.setMessage(message)
.setCancelable(true)
@@ -524,11 +514,11 @@ public class AchievementsActivity extends NavigationBaseActivity {
Account currentAccount = sessionManager.getCurrentAccount();
if (currentAccount == null) {
Timber.d("Current account is null");
- ViewUtil.showLongToast(this, getResources().getString(R.string.user_not_logged_in));
- sessionManager.forceLogin(this);
+ ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.user_not_logged_in));
+ sessionManager.forceLogin(getActivity());
return false;
}
return true;
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/achievements/FeaturedImages.kt b/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeaturedImages.kt
similarity index 58%
rename from app/src/main/java/fr/free/nrw/commons/achievements/FeaturedImages.kt
rename to app/src/main/java/fr/free/nrw/commons/profile/achievements/FeaturedImages.kt
index 4f5351e3c..0a54a778f 100644
--- a/app/src/main/java/fr/free/nrw/commons/achievements/FeaturedImages.kt
+++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeaturedImages.kt
@@ -1,11 +1,11 @@
-package fr.free.nrw.commons.achievements
+package fr.free.nrw.commons.profile.achievements
import com.google.gson.annotations.SerializedName
/**
-* Represents Featured Images on WikiMedia Commons platform
-* Used by Achievements and FeedbackResponse (objects) of the user
-*/
+ * Represents Featured Images on WikiMedia Commons platform
+ * Used by Achievements and FeedbackResponse (objects) of the user
+ */
class FeaturedImages(
@field:SerializedName("Quality_images") val qualityImages: Int,
@field:SerializedName("Featured_pictures_on_Wikimedia_Commons") val featuredPicturesOnWikimediaCommons: Int
diff --git a/app/src/main/java/fr/free/nrw/commons/achievements/FeedbackResponse.kt b/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeedbackResponse.kt
similarity index 66%
rename from app/src/main/java/fr/free/nrw/commons/achievements/FeedbackResponse.kt
rename to app/src/main/java/fr/free/nrw/commons/profile/achievements/FeedbackResponse.kt
index 8d5d8b7bd..f86ca3e9b 100644
--- a/app/src/main/java/fr/free/nrw/commons/achievements/FeedbackResponse.kt
+++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeedbackResponse.kt
@@ -1,8 +1,8 @@
-package fr.free.nrw.commons.achievements
+package fr.free.nrw.commons.profile.achievements
/**
-* Represent the Feedback Response of the user
-*/
+ * Represent the Feedback Response of the user
+ */
data class FeedbackResponse(val uniqueUsedImages: Int,
val articlesUsingImages: Int,
val deletedUploads: Int,
diff --git a/app/src/main/java/fr/free/nrw/commons/achievements/LevelController.kt b/app/src/main/java/fr/free/nrw/commons/profile/achievements/LevelController.kt
similarity index 97%
rename from app/src/main/java/fr/free/nrw/commons/achievements/LevelController.kt
rename to app/src/main/java/fr/free/nrw/commons/profile/achievements/LevelController.kt
index 772f716bd..414bf271d 100644
--- a/app/src/main/java/fr/free/nrw/commons/achievements/LevelController.kt
+++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/LevelController.kt
@@ -1,4 +1,4 @@
-package fr.free.nrw.commons.achievements
+package fr.free.nrw.commons.profile.achievements
import fr.free.nrw.commons.R
diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java
new file mode 100644
index 000000000..76cbc51a3
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java
@@ -0,0 +1,18 @@
+package fr.free.nrw.commons.profile.leaderboard;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import fr.free.nrw.commons.R;
+import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
+
+public class LeaderboardFragment extends CommonsDaggerSupportFragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_leaderboard, container, false);
+ return rootView;
+ }
+
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java
index c2c2d82f1..f3629261f 100644
--- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java
@@ -26,6 +26,7 @@ import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.navigation.NavigationView;
+import fr.free.nrw.commons.profile.ProfileActivity;
import org.wikipedia.dataclient.Service;
import javax.inject.Inject;
@@ -37,7 +38,6 @@ import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.WelcomeActivity;
-import fr.free.nrw.commons.achievements.AchievementsActivity;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.auth.LogoutClient;
import fr.free.nrw.commons.bookmarks.BookmarksActivity;
@@ -140,7 +140,7 @@ public abstract class NavigationBaseActivity extends BaseActivity
LinearLayout userIcon = navHeaderView.findViewById(R.id.user_details);
userIcon.setOnClickListener(v -> {
drawerLayout.closeDrawer(navigationView);
- AchievementsActivity.startYourself(NavigationBaseActivity.this);
+ ProfileActivity.startYourself(NavigationBaseActivity.this);
});
}
diff --git a/app/src/main/res/layout/activity_achievements.xml b/app/src/main/res/layout/activity_achievements.xml
deleted file mode 100644
index 01bc98bbd..000000000
--- a/app/src/main/res/layout/activity_achievements.xml
+++ /dev/null
@@ -1,495 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_profile.xml b/app/src/main/res/layout/activity_profile.xml
new file mode 100644
index 000000000..3ce7386c4
--- /dev/null
+++ b/app/src/main/res/layout/activity_profile.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_achievements.xml b/app/src/main/res/layout/fragment_achievements.xml
new file mode 100644
index 000000000..506d34418
--- /dev/null
+++ b/app/src/main/res/layout/fragment_achievements.xml
@@ -0,0 +1,482 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_leaderboard.xml b/app/src/main/res/layout/fragment_leaderboard.xml
new file mode 100644
index 000000000..28f5b725c
--- /dev/null
+++ b/app/src/main/res/layout/fragment_leaderboard.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 500c2db30..823c165a2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -386,6 +386,7 @@
Delete
Achievements
+ Profile
Statistics
Thanks Received
Featured Images
@@ -630,4 +631,6 @@ Upload your first media by tapping on the add button.
Did you shoot these two pictures at the same place? Do you want to use the latitude/longitude of the picture on the right?
Load More
No places found, try changing your search criteria.
+ Achievements
+ Leaderboard
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt
index 6b21d15b6..d139b8c68 100644
--- a/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt
+++ b/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt
@@ -3,9 +3,9 @@ package fr.free.nrw.commons.delete
import android.content.Context
import android.content.res.Resources
import fr.free.nrw.commons.Media
-import fr.free.nrw.commons.achievements.FeedbackResponse
import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
+import fr.free.nrw.commons.profile.achievements.FeedbackResponse
import fr.free.nrw.commons.utils.ViewUtilWrapper
import io.reactivex.Single
import org.junit.Before
@@ -13,7 +13,6 @@ import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.InjectMocks
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
import java.util.*
From 196b9141d2c593a656e00db8d6b6653b88bebdaa Mon Sep 17 00:00:00 2001
From: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com>
Date: Wed, 8 Jul 2020 19:51:39 +0530
Subject: [PATCH 2/2] =?UTF-8?q?Fixes=20#3861=20Use=20the=20APIs=20to=20fet?=
=?UTF-8?q?ch=20leaderboard=E2=80=99s=20based=20on=20uploads=20via=20mobil?=
=?UTF-8?q?e=20app=20(all=20time)=20and=20display=20it=20in=20the=20Leader?=
=?UTF-8?q?board=20screen.=20(#3865)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../free/nrw/commons/di/NetworkingModule.java | 10 ++
.../commons/mwapi/OkHttpJsonApiClient.java | 43 ++++++
.../leaderboard/LeaderboardFragment.java | 144 ++++++++++++++++++
.../profile/leaderboard/LeaderboardList.java | 53 +++++++
.../leaderboard/LeaderboardListAdapter.java | 73 +++++++++
.../leaderboard/LeaderboardResponse.java | 120 +++++++++++++++
.../main/res/layout/fragment_leaderboard.xml | 110 ++++++++++++-
.../res/layout/leaderboard_list_element.xml | 55 +++++++
app/src/main/res/values/strings.xml | 5 +
9 files changed, 610 insertions(+), 3 deletions(-)
create mode 100644 app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardList.java
create mode 100644 app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardListAdapter.java
create mode 100644 app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardResponse.java
create mode 100644 app/src/main/res/layout/leaderboard_list_element.xml
diff --git a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java
index 495a8d01e..84fdfe53a 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java
@@ -85,11 +85,13 @@ public class NetworkingModule {
public OkHttpJsonApiClient provideOkHttpJsonApiClient(OkHttpClient okHttpClient,
DepictsClient depictsClient,
@Named("tools_forge") HttpUrl toolsForgeUrl,
+ @Named("test_tools_forge") HttpUrl testToolsForgeUrl,
@Named("default_preferences") JsonKvStore defaultKvStore,
Gson gson) {
return new OkHttpJsonApiClient(okHttpClient,
depictsClient,
toolsForgeUrl,
+ testToolsForgeUrl,
WIKIDATA_SPARQL_QUERY_URL,
BuildConfig.WIKIMEDIA_CAMPAIGNS_URL,
gson);
@@ -124,6 +126,14 @@ public class NetworkingModule {
return HttpUrl.parse(TOOLS_FORGE_URL);
}
+ @Provides
+ @Named("test_tools_forge")
+ @NonNull
+ @SuppressWarnings("ConstantConditions")
+ public HttpUrl provideTestToolsForgeUrl() {
+ return HttpUrl.parse(TEST_TOOLS_FORGE_URL);
+ }
+
@Provides
@Singleton
@Named(NAMED_COMMONS_WIKI_SITE)
diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.java b/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.java
index eeaba0a73..d07667b48 100644
--- a/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.java
+++ b/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.java
@@ -11,6 +11,7 @@ import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.model.NearbyResponse;
import fr.free.nrw.commons.nearby.model.NearbyResultItem;
+import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse;
import fr.free.nrw.commons.upload.FileUtils;
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
import fr.free.nrw.commons.utils.ConfigUtils;
@@ -40,6 +41,7 @@ public class OkHttpJsonApiClient {
private final OkHttpClient okHttpClient;
private final DepictsClient depictsClient;
private final HttpUrl wikiMediaToolforgeUrl;
+ private final HttpUrl wikiMediaTestToolforgeUrl;
private final String sparqlQueryUrl;
private final String campaignsUrl;
private final Gson gson;
@@ -49,17 +51,58 @@ public class OkHttpJsonApiClient {
public OkHttpJsonApiClient(OkHttpClient okHttpClient,
DepictsClient depictsClient,
HttpUrl wikiMediaToolforgeUrl,
+ HttpUrl wikiMediaTestToolforgeUrl,
String sparqlQueryUrl,
String campaignsUrl,
Gson gson) {
this.okHttpClient = okHttpClient;
this.depictsClient = depictsClient;
this.wikiMediaToolforgeUrl = wikiMediaToolforgeUrl;
+ this.wikiMediaTestToolforgeUrl = wikiMediaTestToolforgeUrl;
this.sparqlQueryUrl = sparqlQueryUrl;
this.campaignsUrl = campaignsUrl;
this.gson = gson;
}
+ @NonNull
+ public Single getLeaderboard(String userName, String duration, String category, String limit, String offset) {
+ final String fetchLeaderboardUrlTemplate = wikiMediaTestToolforgeUrl
+ + "/leaderboard.py";
+ return Single.fromCallable(() -> {
+ String url = String.format(Locale.ENGLISH,
+ fetchLeaderboardUrlTemplate,
+ userName,
+ duration,
+ category,
+ limit,
+ offset);
+ HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
+ urlBuilder.addQueryParameter("user", userName);
+ urlBuilder.addQueryParameter("duration", duration);
+ urlBuilder.addQueryParameter("category", category);
+ urlBuilder.addQueryParameter("limit", limit);
+ urlBuilder.addQueryParameter("offset", offset);
+ Timber.i("Url %s", urlBuilder.toString());
+ Request request = new Request.Builder()
+ .url(urlBuilder.toString())
+ .build();
+ Response response = okHttpClient.newCall(request).execute();
+ if (response != null && response.body() != null && response.isSuccessful()) {
+ String json = response.body().string();
+ if (json == null) {
+ return null;
+ }
+ Timber.d("Response for leaderboard is %s", json);
+ try {
+ return gson.fromJson(json, LeaderboardResponse.class);
+ } catch (Exception e) {
+ return new LeaderboardResponse();
+ }
+ }
+ return null;
+ });
+ }
+
@NonNull
public Single getUploadCount(String userName) {
HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder();
diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java
index 76cbc51a3..e536a950a 100644
--- a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java
@@ -1,18 +1,162 @@
package fr.free.nrw.commons.profile.leaderboard;
+import android.accounts.Account;
+import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import com.facebook.drawee.view.SimpleDraweeView;
import fr.free.nrw.commons.R;
+import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
+import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
+import fr.free.nrw.commons.utils.ViewUtil;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.schedulers.Schedulers;
+import java.util.List;
+import java.util.Objects;
+import javax.inject.Inject;
+import timber.log.Timber;
public class LeaderboardFragment extends CommonsDaggerSupportFragment {
+ @BindView(R.id.avatar)
+ SimpleDraweeView avatar;
+
+ @BindView(R.id.username)
+ TextView username;
+
+ @BindView(R.id.rank)
+ TextView rank;
+
+ @BindView(R.id.count)
+ TextView count;
+
+ @BindView(R.id.leaderboard_list)
+ RecyclerView leaderboardListRecyclerView;
+
+ @BindView(R.id.progressBar)
+ ProgressBar progressBar;
+
+ @Inject
+ SessionManager sessionManager;
+
+ @Inject
+ OkHttpJsonApiClient okHttpJsonApiClient;
+
+ private String avatarSourceURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/%s/1024px-%s.png";
+
+ private CompositeDisposable compositeDisposable = new CompositeDisposable();
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_leaderboard, container, false);
+ ButterKnife.bind(this, rootView);
+ progressBar.setVisibility(View.VISIBLE);
+ hideLayouts();
+ setLeaderboard();
return rootView;
}
+ /**
+ * To call the API to get results in form Single
+ * which then calls parseJson when results are fetched
+ */
+ private void setLeaderboard() {
+ if (checkAccount()) {
+ try{
+ compositeDisposable.add(okHttpJsonApiClient
+ .getLeaderboard(Objects.requireNonNull(sessionManager.getCurrentAccount()).name,
+ "all_time", "upload", null, null)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ response -> {
+ if (response != null && response.getStatus() == 200) {
+ setLeaderboardUser(response);
+ setLeaderboardList(response.getLeaderboardList());
+ }
+ },
+ t -> {
+ Timber.e(t, "Fetching leaderboard statistics failed");
+ onError();
+ }
+ ));
+ }
+ catch (Exception e){
+ Timber.d(e+"success");
+ }
+ }
+ }
+
+ private void setLeaderboardUser(LeaderboardResponse response) {
+ hideProgressBar();
+ avatar.setImageURI(
+ Uri.parse(String.format(avatarSourceURL, response.getAvatar(), response.getAvatar())));
+ username.setText(response.getUsername());
+ rank.setText(String.format("%s %d", getString(R.string.rank_prefix), response.getRank()));
+ count.setText(String.format("%s %d", getString(R.string.count_prefix), response.getCategoryCount()));
+ }
+
+ private void setLeaderboardList(List leaderboardList) {
+ LeaderboardListAdapter leaderboardListAdapter = new LeaderboardListAdapter(leaderboardList);
+ LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
+ leaderboardListRecyclerView.setLayoutManager(linearLayoutManager);
+ leaderboardListRecyclerView.setAdapter(leaderboardListAdapter);
+ }
+
+ /**
+ * to hide progressbar
+ */
+ private void hideProgressBar() {
+ if (progressBar != null) {
+ progressBar.setVisibility(View.GONE);
+ avatar.setVisibility(View.VISIBLE);
+ username.setVisibility(View.VISIBLE);
+ rank.setVisibility(View.VISIBLE);
+ leaderboardListRecyclerView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * used to hide the layouts while fetching results from api
+ */
+ private void hideLayouts(){
+ avatar.setVisibility(View.INVISIBLE);
+ username.setVisibility(View.INVISIBLE);
+ rank.setVisibility(View.INVISIBLE);
+ leaderboardListRecyclerView.setVisibility(View.INVISIBLE);
+ }
+
+ /**
+ * check to ensure that user is logged in
+ * @return
+ */
+ private boolean checkAccount(){
+ Account currentAccount = sessionManager.getCurrentAccount();
+ if (currentAccount == null) {
+ Timber.d("Current account is null");
+ ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.user_not_logged_in));
+ sessionManager.forceLogin(getActivity());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Shows a generic error toast when error occurs while loading leaderboard
+ */
+ private void onError() {
+ ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.error_occurred));
+ progressBar.setVisibility(View.GONE);
+ }
+
}
diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardList.java b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardList.java
new file mode 100644
index 000000000..d6b86a22b
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardList.java
@@ -0,0 +1,53 @@
+package fr.free.nrw.commons.profile.leaderboard;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class LeaderboardList {
+
+ @SerializedName("username")
+ @Expose
+ private String username;
+ @SerializedName("category_count")
+ @Expose
+ private Integer categoryCount;
+ @SerializedName("avatar")
+ @Expose
+ private String avatar;
+ @SerializedName("rank")
+ @Expose
+ private Integer rank;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Integer getCategoryCount() {
+ return categoryCount;
+ }
+
+ public void setCategoryCount(Integer categoryCount) {
+ this.categoryCount = categoryCount;
+ }
+
+ public String getAvatar() {
+ return avatar;
+ }
+
+ public void setAvatar(String avatar) {
+ this.avatar = avatar;
+ }
+
+ public Integer getRank() {
+ return rank;
+ }
+
+ public void setRank(Integer rank) {
+ this.rank = rank;
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardListAdapter.java b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardListAdapter.java
new file mode 100644
index 000000000..4a61a1cb1
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardListAdapter.java
@@ -0,0 +1,73 @@
+package fr.free.nrw.commons.profile.leaderboard;
+
+import android.content.Context;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import com.facebook.drawee.view.SimpleDraweeView;
+import fr.free.nrw.commons.R;
+import java.util.List;
+
+public class LeaderboardListAdapter extends RecyclerView.Adapter {
+
+ private List leaderboardList;
+
+ private String avatarSourceURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/%s/1024px-%s.png";
+
+ public class ListViewHolder extends RecyclerView.ViewHolder {
+ TextView rank;
+ SimpleDraweeView avatar;
+ TextView username;
+ TextView count;
+
+ public ListViewHolder(View itemView) {
+ super(itemView);
+ this.rank = itemView.findViewById(R.id.user_rank);
+ this.avatar = itemView.findViewById(R.id.user_avatar);
+ this.username = itemView.findViewById(R.id.user_name);
+ this.count = itemView.findViewById(R.id.user_count);
+ }
+
+ public Context getContext() {
+ return itemView.getContext();
+ }
+ }
+
+ public LeaderboardListAdapter(List leaderboardList) {
+ this.leaderboardList = leaderboardList;
+ }
+
+ @NonNull
+ @Override
+ public LeaderboardListAdapter.ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.leaderboard_list_element, parent, false);
+
+ return new ListViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull LeaderboardListAdapter.ListViewHolder holder, int position) {
+ TextView rank = holder.rank;
+ SimpleDraweeView avatar = holder.avatar;
+ TextView username = holder.username;
+ TextView count = holder.count;
+
+ rank.setText(leaderboardList.get(position).getRank().toString());
+
+ avatar.setImageURI(
+ Uri.parse(String.format(avatarSourceURL, leaderboardList.get(position).getAvatar(),
+ leaderboardList.get(position).getAvatar())));
+ username.setText(leaderboardList.get(position).getUsername());
+ count.setText(leaderboardList.get(position).getCategoryCount().toString());
+ }
+
+ @Override
+ public int getItemCount() {
+ return leaderboardList.size();
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardResponse.java b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardResponse.java
new file mode 100644
index 000000000..6624ebfd0
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardResponse.java
@@ -0,0 +1,120 @@
+package fr.free.nrw.commons.profile.leaderboard;
+
+import java.util.List;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class LeaderboardResponse {
+
+ @SerializedName("status")
+ @Expose
+ private Integer status;
+ @SerializedName("username")
+ @Expose
+ private String username;
+ @SerializedName("category_count")
+ @Expose
+ private Integer categoryCount;
+ @SerializedName("limit")
+ @Expose
+ private Object limit;
+ @SerializedName("avatar")
+ @Expose
+ private String avatar;
+ @SerializedName("offset")
+ @Expose
+ private Object offset;
+ @SerializedName("duration")
+ @Expose
+ private String duration;
+ @SerializedName("leaderboard_list")
+ @Expose
+ private List leaderboardList = null;
+ @SerializedName("category")
+ @Expose
+ private String category;
+ @SerializedName("rank")
+ @Expose
+ private Integer rank;
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Integer getCategoryCount() {
+ return categoryCount;
+ }
+
+ public void setCategoryCount(Integer categoryCount) {
+ this.categoryCount = categoryCount;
+ }
+
+ public Object getLimit() {
+ return limit;
+ }
+
+ public void setLimit(Object limit) {
+ this.limit = limit;
+ }
+
+ public String getAvatar() {
+ return avatar;
+ }
+
+ public void setAvatar(String avatar) {
+ this.avatar = avatar;
+ }
+
+ public Object getOffset() {
+ return offset;
+ }
+
+ public void setOffset(Object offset) {
+ this.offset = offset;
+ }
+
+ public String getDuration() {
+ return duration;
+ }
+
+ public void setDuration(String duration) {
+ this.duration = duration;
+ }
+
+ public List getLeaderboardList() {
+ return leaderboardList;
+ }
+
+ public void setLeaderboardList(List leaderboardList) {
+ this.leaderboardList = leaderboardList;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public Integer getRank() {
+ return rank;
+ }
+
+ public void setRank(Integer rank) {
+ this.rank = rank;
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_leaderboard.xml b/app/src/main/res/layout/fragment_leaderboard.xml
index 28f5b725c..b965e83db 100644
--- a/app/src/main/res/layout/fragment_leaderboard.xml
+++ b/app/src/main/res/layout/fragment_leaderboard.xml
@@ -1,6 +1,110 @@
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/leaderboard_list_element.xml b/app/src/main/res/layout/leaderboard_list_element.xml
new file mode 100644
index 000000000..d779fe32a
--- /dev/null
+++ b/app/src/main/res/layout/leaderboard_list_element.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6f2a3a0c6..64459cbf6 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -651,4 +651,9 @@ Upload your first media by tapping on the add button.
Copy wikicode to clipboard
Achievements
Leaderboard
+ Rank:
+ Count:
+ Rank
+ User
+ Count