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