diff --git a/app/build.gradle b/app/build.gradle index 25853684b..778ed52a5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -68,7 +68,7 @@ dependencies { testImplementation 'org.robolectric:robolectric:3.7.1' testImplementation 'com.nhaarman:mockito-kotlin:1.5.0' testImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1' - compile 'com.dinuscxj:circleprogressbar:1.1.1' + implementation 'com.dinuscxj:circleprogressbar:1.1.1' androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1' diff --git a/app/src/main/java/fr/free/nrw/commons/achievements/Achievements.java b/app/src/main/java/fr/free/nrw/commons/achievements/Achievements.java index ea934c19b..0cadcba64 100644 --- a/app/src/main/java/fr/free/nrw/commons/achievements/Achievements.java +++ b/app/src/main/java/fr/free/nrw/commons/achievements/Achievements.java @@ -1,5 +1,127 @@ package fr.free.nrw.commons.achievements; +/** + * represnts Achievements + */ public class Achievements { + private int uniqueUsedImages; + private int articlesUsingImages; + private int thanksReceived; + private int imagesEditedBySomeoneElse; + private int featuredImages; + private int imagesUploaded; + public Achievements(){ + + } + + public Achievements(int uniqueUsedImages, + int articlesUsingImages, + int thanksReceived, + int imagesEditedBySomeoneElse, + int featuredImages, + int imagesUploaded) { + this.uniqueUsedImages = uniqueUsedImages; + this.articlesUsingImages = articlesUsingImages; + this.thanksReceived = thanksReceived; + this.imagesEditedBySomeoneElse = imagesEditedBySomeoneElse; + this.featuredImages = featuredImages; + this.imagesUploaded = imagesUploaded; + } + public class AchievementsBuilder { + private int nestedUniqueUsedImages; + private int nestedArticlesUsingImages; + private int nestedThanksReceived; + private int nestedImagesEditedBySomeoneElse; + private int nestedFeaturedImages; + private int nestedImagesUploaded; + + public AchievementsBuilder setUniqueUsedImages(int uniqueUsedImages) { + this.nestedUniqueUsedImages = uniqueUsedImages; + return this; + } + + public AchievementsBuilder setArticlesUsingImages(int articlesUsingImages) { + this.nestedArticlesUsingImages = articlesUsingImages; + return this; + } + + public AchievementsBuilder setThanksReceived(int thanksReceived) { + this.nestedThanksReceived = thanksReceived; + return this; + } + + public AchievementsBuilder setImagesEditedBySomeoneElse(int imagesEditedBySomeoneElse) { + this.nestedImagesEditedBySomeoneElse = imagesEditedBySomeoneElse; + return this; + } + + public AchievementsBuilder setFeaturedImages(int featuredImages) { + this.nestedFeaturedImages = featuredImages; + return this; + } + + public AchievementsBuilder setImagesUploaded(int imagesUploaded) { + this.nestedImagesUploaded = imagesUploaded; + return this; + } + + public Achievements createAchievements(){ + return new Achievements(nestedUniqueUsedImages, + nestedArticlesUsingImages, + nestedThanksReceived, + nestedImagesEditedBySomeoneElse, + nestedFeaturedImages, + nestedImagesUploaded); + } + + } + + public int getImagesUploaded() { + return imagesUploaded; + } + + public int getFeaturedImages() { + return featuredImages; + } + + public int getImagesEditedBySomeoneElse() { + return imagesEditedBySomeoneElse; + } + + public int getThanksReceived() { + return thanksReceived; + } + + public int getArticlesUsingImages() { + return articlesUsingImages; + } + + public int getUniqueUsedImages() { + return uniqueUsedImages; + } + + public void setImagesUploaded(int imagesUploaded) { + this.imagesUploaded = imagesUploaded; + } + + public void setFeaturedImages(int featuredImages) { + this.featuredImages = featuredImages; + } + + public void setImagesEditedBySomeoneElse(int imagesEditedBySomeoneElse) { + this.imagesEditedBySomeoneElse = imagesEditedBySomeoneElse; + } + + public void setThanksReceived(int thanksReceived) { + this.thanksReceived = thanksReceived; + } + + public void setArticlesUsingImages(int articlesUsingImages) { + this.articlesUsingImages = articlesUsingImages; + } + + public void setUniqueUsedImages(int uniqueUsedImages) { + this.uniqueUsedImages = uniqueUsedImages; + } } diff --git a/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java b/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java index 57187be51..ec9cbf6ad 100644 --- a/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java @@ -2,20 +2,37 @@ package fr.free.nrw.commons.achievements; import android.annotation.SuppressLint; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; +import android.support.v4.content.res.ResourcesCompat; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; import android.util.DisplayMetrics; import android.util.Log; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; +import com.dinuscxj.progressbar.CircleProgressBar; + +import org.json.JSONException; import org.json.JSONObject; import java.io.File; @@ -26,6 +43,7 @@ import javax.inject.Inject; import butterknife.BindView; import butterknife.ButterKnife; +import butterknife.OnClick; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.auth.SessionManager; @@ -34,6 +52,7 @@ import fr.free.nrw.commons.theme.NavigationBaseActivity; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; /** * activity for sharing feedback on uploaded activity @@ -42,13 +61,36 @@ public class AchievementsActivity extends NavigationBaseActivity { private static final double BADGE_IMAGE_WIDTH_RATIO = 0.4; private static final double BADGE_IMAGE_HEIGHT_RATIO = 0.3; + private Boolean isUploadFetched = false; + private Boolean isStatisticsFetched = false; + private Achievements achievements = new Achievements(); + private LevelController level; + private LevelController.LevelInfo levelInfo; @BindView(R.id.achievement_badge) ImageView imageView; @BindView(R.id.achievement_level) - TextView textView; + 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_progressbar) + CircleProgressBar imagesUsedByWikiProgessbar; + @BindView(R.id.image_featured) + TextView imagesFeatured; + @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; @Inject SessionManager sessionManager; @Inject @@ -83,14 +125,32 @@ public class AchievementsActivity extends NavigationBaseActivity { imageView.getLayoutParams(); params.height = (int) (height * BADGE_IMAGE_HEIGHT_RATIO); params.width = (int) (width * BADGE_IMAGE_WIDTH_RATIO); - imageView.setImageResource(R.drawable.featured); + imageView.setImageResource(R.drawable.badge); imageView.requestLayout(); setSupportActionBar(toolbar); + progressBar.setVisibility(View.VISIBLE); + hideLayouts(); setAchievements(); + setUploadCount(); initDrawer(); } + /** + * to invoke the AlertDialog on clicking info button + */ + @OnClick(R.id.achievement_info) + public void showInfoDialog(){ + new AlertDialog.Builder(AchievementsActivity.this) + .setTitle(R.string.Achievements) + .setMessage(R.string.achievements_info_message) + .setCancelable(true) + .setNeutralButton(android.R.string.ok, (dialog, id) -> dialog.cancel()) + .create() + .show(); + + } + @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. @@ -104,7 +164,7 @@ public class AchievementsActivity extends NavigationBaseActivity { if (id == R.id.share_app_icon) { View rootView = getWindow().getDecorView().findViewById(android.R.id.content); Bitmap screenShot = Utils.getScreenShot(rootView); - shareScreen(screenShot); + showAlert(screenShot); } return super.onOptionsItemSelected(item); @@ -147,13 +207,76 @@ public class AchievementsActivity extends NavigationBaseActivity { )); } + /** + * used to the count of images uploaded by user + */ + private void setUploadCount() { + compositeDisposable.add(mediaWikiApi + .getUploadCount(sessionManager.getCurrentAccount().name) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + uploadCount -> achievements.setImagesUploaded(uploadCount), + t -> Timber.e(t, "Fetching upload count failed") + )); + isUploadFetched = true; + hideProgressBar(); + } + + /** + * used to the uploaded images progressbar + * @param uploadCount + */ + private void setUploadProgress( int uploadCount){ + + imagesUploadedProgressbar.setProgress + (100*uploadCount/levelInfo.getMaxUploadCount()); + imagesUploadedProgressbar.setProgressTextFormatPattern + (uploadCount +"/" + levelInfo.getMaxUploadCount() ); + } + /** * used to parse the JSONObject containing results * * @param object */ private void parseJson(JSONObject object) { - Log.i("json", object.toString()); + try { + achievements.setUniqueUsedImages(object.getInt("uniqueUsedImages")); + achievements.setArticlesUsingImages(object.getInt("articlesUsingImages")); + achievements.setThanksReceived(object.getInt("thanksReceived")); + achievements.setImagesEditedBySomeoneElse(object.getInt("imagesEditedBySomeoneElse")); + JSONObject featuredImages = object.getJSONObject("featuredImages"); + achievements.setFeaturedImages + (featuredImages.getInt("Quality_images") + + featuredImages.getInt("Featured_pictures_on_Wikimedia_Commons")); + } catch (JSONException e) { + e.printStackTrace(); + } + isStatisticsFetched = true; + hideProgressBar(); + } + + /** + * Used the inflate the fetched statistics of the images uploaded by user + * and assign badge and level + * @param achievements + */ + private void inflateAchievements( Achievements achievements ){ + thanksReceived.setText(Integer.toString(achievements.getThanksReceived())); + imagesUsedByWikiProgessbar.setProgress + (100*achievements.getUniqueUsedImages()/levelInfo.getMaxUniqueImages() ); + imagesUsedByWikiProgessbar.setProgressTextFormatPattern + (achievements.getUniqueUsedImages() + "/" + levelInfo.getMaxUniqueImages()); + imagesFeatured.setText(Integer.toString(achievements.getFeaturedImages())); + String levelUpInfoString = getString(R.string.level); + levelUpInfoString += " " + Integer.toString(levelInfo.getLevelNumber()); + levelNumber.setText(levelUpInfoString); + final ContextThemeWrapper wrapper = new ContextThemeWrapper(this, levelInfo.getLevelStyle()); + Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.badge, wrapper.getTheme()); + Bitmap bitmap = BitmapUtils.drawableToBitmap(drawable); + BitmapDrawable bitmapImage = BitmapUtils.writeOnDrawable(bitmap, Integer.toString(levelInfo.getLevelNumber()),this); + imageView.setImageDrawable(bitmapImage); } /** @@ -168,4 +291,61 @@ public class AchievementsActivity extends NavigationBaseActivity { context.startActivity(intent); } + /** + * to hide progressbar + */ + private void hideProgressBar() { + if (progressBar != null && isUploadFetched && isStatisticsFetched) { + levelInfo = LevelController.LevelInfo.from(achievements.getImagesUploaded(),achievements.getUniqueUsedImages()); + inflateAchievements(achievements); + setUploadProgress(achievements.getImagesUploaded()); + progressBar.setVisibility(View.GONE); + layoutImageReverts.setVisibility(View.VISIBLE); + layoutImageUploaded.setVisibility(View.VISIBLE); + layoutImageUsedByWiki.setVisibility(View.VISIBLE); + layoutStatistics.setVisibility(View.VISIBLE); + imageView.setVisibility(View.VISIBLE); + levelNumber.setVisibility(View.VISIBLE); + } + } + + /** + * used to hide the layouts while fetching results from api + */ + private void hideLayouts(){ + layoutImageUsedByWiki.setVisibility(View.INVISIBLE); + layoutImageUploaded.setVisibility(View.INVISIBLE); + layoutImageReverts.setVisibility(View.INVISIBLE); + layoutStatistics.setVisibility(View.INVISIBLE); + imageView.setVisibility(View.INVISIBLE); + 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 = (ImageView) view.findViewById(R.id.alert_image); + screenShotImage.setImageBitmap(screenshot); + TextView shareMessage = (TextView) view.findViewById(R.id.alert_text); + shareMessage.setText(R.string.achievements_share_message); + alertadd.setView(view); + alertadd.setPositiveButton("Proceed", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + shareScreen(screenshot); + } + }); + alertadd.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + alertadd.show(); + } + } diff --git a/app/src/main/java/fr/free/nrw/commons/achievements/BitmapUtils.java b/app/src/main/java/fr/free/nrw/commons/achievements/BitmapUtils.java new file mode 100644 index 000000000..b7400117d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/achievements/BitmapUtils.java @@ -0,0 +1,54 @@ +package fr.free.nrw.commons.achievements; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; + +public class BitmapUtils { + + /** + * write level Number on the badge + * @param bm + * @param text + * @return + */ + public static BitmapDrawable writeOnDrawable(Bitmap bm, String text, Context context){ + Bitmap.Config config = bm.getConfig(); + if(config == null){ + config = Bitmap.Config.ARGB_8888; + } + Bitmap bitmap = Bitmap.createBitmap(bm.getWidth(),bm.getHeight(),config); + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(bm, 0, 0, null); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setStyle(Paint.Style.FILL); + paint.setColor(Color.WHITE); + paint.setTextSize(Math.round(canvas.getHeight()/2)); + paint.setTextAlign(Paint.Align.CENTER); + Rect rectText = new Rect(); + paint.getTextBounds(text,0, text.length(),rectText); + canvas.drawText(text, Math.round(canvas.getWidth()/2),Math.round(canvas.getHeight()/1.35), paint); + return new BitmapDrawable(context.getResources(), bitmap); + } + + /** + * Convert Drawable to bitmap + * @param drawable + * @return + */ + public static Bitmap drawableToBitmap (Drawable drawable) { + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable)drawable).getBitmap(); + } + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/achievements/LevelController.java b/app/src/main/java/fr/free/nrw/commons/achievements/LevelController.java new file mode 100644 index 000000000..20176759d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/achievements/LevelController.java @@ -0,0 +1,73 @@ +package fr.free.nrw.commons.achievements; + +import fr.free.nrw.commons.R; + +/** + * calculates the level of the user + */ +public class LevelController { + + public LevelInfo level; + public enum LevelInfo{ + LEVEL_15(15,R.style.LevelFive, 80, 160), + LEVEL_14(14,R.style.LevelFour, 75 , 150), + LEVEL_13(13,R.style.LevelThree, 70, 140), + LEVEL_12(12,R.style.LevelTwo,65 , 130), + LEVEL_11(11,R.style.LevelOne, 60, 120), + LEVEL_10(10, R.style.LevelFive, 55, 110), + LEVEL_9(9, R.style.LevelFour, 50, 100), + LEVEL_8(8, R.style.LevelThree, 45, 90), + LEVEL_7(7, R.style.LevelTwo, 40, 80), + LEVEL_6(6,R.style.LevelOne,30,70), + LEVEL_5(5, R.style.LevelFive, 25, 60), + LEVEL_4(4, R.style.LevelFour,20,50), + LEVEL_3(3, R.style.LevelThree, 15,40), + LEVEL_2(2, R.style.LevelTwo, 10, 30), + LEVEL_1(1, R.style.LevelOne, 5, 20 ); + + private int levelNumber; + private int levelStyle; + private int maxUniqueImages; + private int maxUploadCount; + + LevelInfo(int levelNumber, int levelStyle, int maxUniqueImages, int maxUploadCount) { + this.levelNumber = levelNumber; + this.levelStyle = levelStyle; + this.maxUniqueImages = maxUniqueImages; + this.maxUploadCount = maxUploadCount; + } + + public static LevelInfo from(int imagesUploaded, int uniqueImagesUsed) { + LevelInfo level = LEVEL_1; + + for (LevelInfo levelInfo : LevelInfo.values()) { + if (imagesUploaded > levelInfo.maxUploadCount && uniqueImagesUsed > levelInfo.maxUniqueImages) { + level = levelInfo; + return level; + } + } + return level; + } + + public int getLevelStyle() { + return levelStyle; + } + + public int getLevelNumber() { + return levelNumber; + } + + public void setLevelStyle(int levelStyle) { + this.levelStyle = levelStyle; + } + + public int getMaxUniqueImages() { + return maxUniqueImages; + } + + public int getMaxUploadCount() { + return maxUploadCount; + } + } + +} diff --git a/app/src/main/res/drawable-mdpi/badge.xml b/app/src/main/res/drawable-mdpi/badge.xml new file mode 100644 index 000000000..3792e33cf --- /dev/null +++ b/app/src/main/res/drawable-mdpi/badge.xml @@ -0,0 +1,33 @@ + + + + + + + diff --git a/app/src/main/res/layout/activity_achievements.xml b/app/src/main/res/layout/activity_achievements.xml index 99e1d28c1..f289eafe8 100644 --- a/app/src/main/res/layout/activity_achievements.xml +++ b/app/src/main/res/layout/activity_achievements.xml @@ -71,18 +71,22 @@ android:layout_height="wrap_content" android:layout_marginLeft="@dimen/activity_margin_horizontal" android:layout_marginStart="@dimen/activity_margin_horizontal" + android:layout_marginTop="@dimen/achievements_activity_margin_vertical" android:text="@string/images_uploaded" /> @@ -103,19 +107,32 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/activity_margin_horizontal" + android:id="@+id/images_reverted_text" android:layout_marginStart="@dimen/activity_margin_horizontal" android:text="@string/image_reverts" /> + + @@ -138,26 +155,38 @@ android:layout_height="wrap_content" android:layout_marginLeft="@dimen/activity_margin_horizontal" android:layout_marginStart="@dimen/activity_margin_horizontal" + android:layout_marginTop="@dimen/achievements_activity_margin_vertical" android:text="@string/images_used_by_wiki" /> + + + @@ -242,6 +272,7 @@ android:layout_marginStart="@dimen/activity_margin_horizontal" android:layout_marginLeft="@dimen/activity_margin_horizontal" android:text="2" + android:id="@+id/thanks_received" android:layout_marginRight="44dp" /> diff --git a/app/src/main/res/layout/image_alert_layout.xml b/app/src/main/res/layout/image_alert_layout.xml new file mode 100644 index 000000000..37b2c5b67 --- /dev/null +++ b/app/src/main/res/layout/image_alert_layout.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 8d125aa61..a4c9ef716 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -23,4 +23,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 7eaf97880..a5847f704 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -4,6 +4,7 @@ 16dp 16dp + 8dp 48dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 60080d208..4645f1790 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ - AppearanceAppearance General Feedback Location @@ -292,4 +292,6 @@ Images Uploaded Images Not Reverted Images Used By Wiki + Share your achievements with your friends! + Your level increases as you meet these requirements. Items in the "statistics" section do not count towards your level. diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index a8eb1d849..26d9c357b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -57,4 +57,34 @@ @android:color/transparent + + + + + + + + + + \ No newline at end of file