Feedback module (#1742)

*  Implemented Statistics

*  Basic Structure Implemented

*  Layout made screen independent and menu inflated

*  Share Screenshot using cache

*  Improved the Image Bound and added strings

*  Improved the quality of Pr

*  Wired to navigation drawer

*  Changed the bounds of the image

*  Added Info icon

* Removed the unecessary functionality

*  Updated JavaDocs and fetch the username

*  Fetch JsonObject from the api using JavaRx and OkHttp

* Added JavaDocs and improved quality

* fixed strings file

*  Improved the quality of pr

*  Render thanks , images used in articles on screen

*  fetch and rendered the upload count

*  FeaturedImages statistics rendered and Javadocs added

*  added ProgressBar

*  Added Class for calculating level

*  added level info and returned level info

*  level up info rendered on achievement activity

*  Inflated Level Number

*  Added the structure for badge

*  Added LevelUpInfo Programmetically on Drawable

*  aligned the text

*  changed the text

*  Implemented the structure for changing colour of drawable

*  Added functionality to change colours of badge during runtime

*  Added custom alert for share option

*  Improved the UI of screen

*  Added the alertDialog for info button

*  Improved the quality of PR

*  Added Builder model

*  Added Enum Model and increased levels to 15

* removed redundant class

*  Changed strings and added subtext

* Feedback Module: Add reverts rate parameter (#1649)

* Fetched Revert Count

* Refactored Achievements class and display the fetched results

* Refactored the levelController to include revert as parameter

*  Fixed error

*  Fixed bug

*  Added information for parameters and improved code quality

*  Javadocs added

*  Added null check and javadocs

*  Removed extra spaces
This commit is contained in:
Tanvi Dadu 2018-07-26 13:12:51 +05:30 committed by Vivek Maskara
parent 86878fb62d
commit e9c0aa22ea
25 changed files with 2456 additions and 17 deletions

View file

@ -21,6 +21,7 @@ dependencies {
implementation 'com.jakewharton.timber:timber:4.5.1'
implementation 'info.debatty:java-string-similarity:0.24'
implementation 'com.borjabravo:readmoretextview:2.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation('com.mapbox.mapboxsdk:mapbox-android-sdk:5.5.0@aar') {
transitive = true
@ -59,6 +60,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'
implementation 'com.dinuscxj:circleprogressbar:1.1.1'
implementation 'com.caverock:androidsvg:1.2.1'
implementation 'com.github.bumptech.glide:glide:4.7.1'

View file

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.free.nrw.commons">
@ -108,8 +109,11 @@
android:parentActivityName=".contributions.ContributionsActivity"
/>
<service android:name=".upload.UploadService" />
<activity
android:name=".achievements.AchievementsActivity"
android:label="@string/Achievements" />
<service android:name=".upload.UploadService" />
<service
android:name=".auth.WikiAccountAuthenticatorService"
android:exported="true"

View file

@ -2,11 +2,13 @@ package fr.free.nrw.commons;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.customtabs.CustomTabsIntent;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.widget.Toast;
import org.apache.commons.codec.binary.Hex;
@ -150,7 +152,7 @@ public class Utils {
StringBuilder stringBuilder = new StringBuilder();
try {
String[] command = new String[] {"logcat","-d","-v","threadtime"};
String[] command = new String[]{"logcat","-d","-v","threadtime"};
Process process = Runtime.getRuntime().exec(command);
@ -199,4 +201,18 @@ public class Utils {
customTabsIntent.launchUrl(context, url);
}
/**
* To take screenshot of the screen and return it in Bitmap format
*
* @param view
* @return
*/
public static Bitmap getScreenShot(View view) {
View screenView = view.getRootView();
screenView.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(screenView.getDrawingCache());
screenView.setDrawingCacheEnabled(false);
return bitmap;
}
}

View file

@ -0,0 +1,205 @@
package fr.free.nrw.commons.achievements;
import android.util.Log;
/**
* represnts Achievements class ans stores all the parameters
*/
public class Achievements {
private int uniqueUsedImages;
private int articlesUsingImages;
private int thanksReceived;
private int imagesEditedBySomeoneElse;
private int featuredImages;
private int imagesUploaded;
private int revertCount;
public Achievements(){
}
/**
* constructor for achievements class to set its data members
* @param uniqueUsedImages
* @param articlesUsingImages
* @param thanksReceived
* @param imagesEditedBySomeoneElse
* @param featuredImages
* @param imagesUploaded
* @param revertCount
*/
public Achievements(int uniqueUsedImages,
int articlesUsingImages,
int thanksReceived,
int imagesEditedBySomeoneElse,
int featuredImages,
int imagesUploaded,
int revertCount) {
this.uniqueUsedImages = uniqueUsedImages;
this.articlesUsingImages = articlesUsingImages;
this.thanksReceived = thanksReceived;
this.imagesEditedBySomeoneElse = imagesEditedBySomeoneElse;
this.featuredImages = featuredImages;
this.imagesUploaded = imagesUploaded;
this.revertCount = revertCount;
}
/**
* Builder class for Achievements class
*/
public class AchievementsBuilder {
private int nestedUniqueUsedImages;
private int nestedArticlesUsingImages;
private int nestedThanksReceived;
private int nestedImagesEditedBySomeoneElse;
private int nestedFeaturedImages;
private int nestedImagesUploaded;
private int nestedRevertCount;
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 AchievementsBuilder setRevertCount( int revertCount){
this.nestedRevertCount = revertCount;
return this;
}
public Achievements createAchievements(){
return new Achievements(nestedUniqueUsedImages,
nestedArticlesUsingImages,
nestedThanksReceived,
nestedImagesEditedBySomeoneElse,
nestedFeaturedImages,
nestedImagesUploaded,
nestedRevertCount);
}
}
/**
* getter function to get count of images uploaded
* @return
*/
public int getImagesUploaded() {
return imagesUploaded;
}
/**
* getter function to get count of featured images
* @return
*/
public int getFeaturedImages() {
return featuredImages;
}
/**
* getter function to get count of thanks received
* @return
*/
public int getThanksReceived() {
return thanksReceived;
}
/**
* getter function to get count of unique images used by wiki
* @return
*/
public int getUniqueUsedImages() {
return uniqueUsedImages;
}
/**
* setter function to count of images uploaded
* @param imagesUploaded
*/
public void setImagesUploaded(int imagesUploaded) {
this.imagesUploaded = imagesUploaded;
}
/**
* setter function to set count of featured images
* @param featuredImages
*/
public void setFeaturedImages(int featuredImages) {
this.featuredImages = featuredImages;
}
/**
* setter function to set the count of images edited by someone
* @param imagesEditedBySomeoneElse
*/
public void setImagesEditedBySomeoneElse(int imagesEditedBySomeoneElse) {
this.imagesEditedBySomeoneElse = imagesEditedBySomeoneElse;
}
/**
* setter function to set count of thanks received
* @param thanksReceived
*/
public void setThanksReceived(int thanksReceived) {
this.thanksReceived = thanksReceived;
}
/**
* setter function to count of articles using images uploaded
* @param articlesUsingImages
*/
public void setArticlesUsingImages(int articlesUsingImages) {
this.articlesUsingImages = articlesUsingImages;
}
/**
* setter function to set count of uniques images used by wiki
* @param uniqueUsedImages
*/
public void setUniqueUsedImages(int uniqueUsedImages) {
this.uniqueUsedImages = uniqueUsedImages;
}
/**
* to set count of images reverted
* @param revertCount
*/
public void setRevertCount(int revertCount) {
this.revertCount = revertCount;
}
/**
* used to calculate the percentages of images that haven't been reverted
* @return
*/
public int getNotRevertPercentage(){
try {
return ((imagesUploaded - revertCount) * 100)/imagesUploaded;
} catch (ArithmeticException divideByZero ){
return 100;
}
}
}

View file

@ -0,0 +1,458 @@
package fr.free.nrw.commons.achievements;
import android.accounts.Account;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
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;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Optional;
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;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
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 timber.log.Timber;
/**
* activity for sharing feedback on uploaded activity
*/
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 Boolean isRevertFetched = false;
private Achievements achievements = new Achievements();
private LevelController.LevelInfo levelInfo;
@BindView(R.id.achievement_badge)
ImageView imageView;
@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_progressbar)
CircleProgressBar imagesUsedByWikiProgessbar;
@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;
@Inject
SessionManager sessionManager;
@Inject
MediaWikiApi mediaWikiApi;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
/**
* This method helps in the creation Achievement screen and
* dynamically set the size of imageView
*
* @param savedInstanceState Data bundle
*/
@Override
@SuppressLint("StringFormatInvalid")
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_achievements);
ButterKnife.bind(this);
/**
* DisplayMetrics used to fetch the size of the screen
*/
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
/**
* Used for the setting the size of imageView at runtime
*/
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)
imageView.getLayoutParams();
params.height = (int) (height * BADGE_IMAGE_HEIGHT_RATIO);
params.width = (int) (width * BADGE_IMAGE_WIDTH_RATIO);
imageView.setImageResource(R.drawable.badge);
imageView.requestLayout();
setSupportActionBar(toolbar);
progressBar.setVisibility(View.VISIBLE);
hideLayouts();
setAchievements();
setUploadCount();
setRevertCount();
initDrawer();
}
/**
* 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) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_about, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.share_app_icon) {
View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
Bitmap screenShot = Utils.getScreenShot(rootView);
showAlert(screenShot);
}
return super.onOptionsItemSelected(item);
}
/**
* 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");
FileOutputStream fOut = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
file.setReadable(true, false);
final Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
intent.setType("image/png");
startActivity(Intent.createChooser(intent, "Share image via"));
} catch (IOException e) {
//Do Nothing
}
}
/**
* To call the API to get results in form Single<JSONObject>
* which then calls parseJson when results are fetched
*/
private void setAchievements() {
if(checkAccount()) {
compositeDisposable.add(mediaWikiApi
.getAchievements(sessionManager.getCurrentAccount().name)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
jsonObject -> parseJson(jsonObject),
t -> Timber.e(t, "Fetching achievements statisticss failed")
));
}
}
/**
* To call the API to get reverts count in form of JSONObject
*
*/
private void setRevertCount(){
if(checkAccount()) {
compositeDisposable.add(mediaWikiApi
.getRevertCount(sessionManager.getCurrentAccount().name)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
object -> parseJsonRevertCount(object),
t -> Timber.e(t, "Fetching revert count failed")
));
}
}
/**
* used to set number of deleted images
* @param object
*/
private void parseJsonRevertCount(JSONObject object){
try {
achievements.setRevertCount(object.getInt("deletedUploads"));
} catch (JSONException e) {
Timber.d( e, e.getMessage());
}
isRevertFetched = true;
hideProgressBar();
}
/**
* used to the count of images uploaded by user
*/
private void setUploadCount() {
if(checkAccount()) {
compositeDisposable.add(mediaWikiApi
.getUploadCount(sessionManager.getCurrentAccount().name)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
uploadCount -> setAchievementsUploadCount(uploadCount),
t -> Timber.e(t, "Fetching upload count failed")
));
}
}
/**
* used to set achievements upload count and call hideProgressbar
* @param uploadCount
*/
private void setAchievementsUploadCount(int uploadCount){
achievements.setImagesUploaded(uploadCount);
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 set the non revert image percentage
* @param notRevertPercentage
*/
private void setImageRevertPercentage(int notRevertPercentage){
imageRevertsProgressbar.setProgress(notRevertPercentage);
String revertPercentage = Integer.toString(notRevertPercentage);
imageRevertsProgressbar.setProgressTextFormatPattern(revertPercentage + "%%");
imagesRevertLimitText.setText(getResources().getString(R.string.achievements_revert_limit_message)+ levelInfo.getMinNonRevertPercentage() + "%");
}
/**
* used to parse the JSONObject containing results
* @param object
*/
private void parseJson(JSONObject object) {
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);
}
/**
* 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_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
/**
* to hide progressbar
*/
private void hideProgressBar() {
if (progressBar != null && isUploadFetched && isStatisticsFetched && isRevertFetched) {
levelInfo = LevelController.LevelInfo.from(achievements.getImagesUploaded(),
achievements.getUniqueUsedImages(),
achievements.getNotRevertPercentage());
inflateAchievements(achievements);
setUploadProgress(achievements.getImagesUploaded());
setImageRevertPercentage(achievements.getNotRevertPercentage());
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();
}
@OnClick(R.id.images_upload_info)
public void showUploadInfo(){
launchAlert(getResources().getString(R.string.images_uploaded)
,getResources().getString(R.string.images_uploaded_explanation));
}
@OnClick(R.id.images_reverted_info)
public void showRevertedInfo(){
launchAlert(getResources().getString(R.string.image_reverts)
,getResources().getString(R.string.images_reverted_explanation));
}
@OnClick(R.id.images_used_by_wiki_info)
public void showUsedByWikiInfo(){
launchAlert(getResources().getString(R.string.images_used_by_wiki)
,getResources().getString(R.string.images_used_explanation));
}
/**
* takes title and message as input to display alerts
* @param title
* @param message
*/
private void launchAlert(String title, String message){
new AlertDialog.Builder(AchievementsActivity.this)
.setTitle(title)
.setMessage(message)
.setCancelable(true)
.setNeutralButton(android.R.string.ok, (dialog, id) -> dialog.cancel())
.create()
.show();
}
/**
* 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(this, getResources().getString(R.string.user_not_logged_in));
sessionManager.forceLogin(this);
return false;
}
return true;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,85 @@
package fr.free.nrw.commons.achievements;
import android.util.Log;
import fr.free.nrw.commons.R;
/**
* calculates the level of the user
*/
public class LevelController {
public LevelInfo level;
public enum LevelInfo{
LEVEL_1(1, R.style.LevelOne, 5, 20, 85),
LEVEL_2(2, R.style.LevelTwo, 10, 30, 86),
LEVEL_3(3, R.style.LevelThree, 15,40, 87),
LEVEL_4(4, R.style.LevelFour,20,50, 88),
LEVEL_5(5, R.style.LevelFive, 25, 60, 89),
LEVEL_6(6,R.style.LevelOne,30,70, 90),
LEVEL_7(7, R.style.LevelTwo, 40, 80, 90),
LEVEL_8(8, R.style.LevelThree, 45, 90, 90),
LEVEL_9(9, R.style.LevelFour, 50, 100, 90),
LEVEL_10(10, R.style.LevelFive, 55, 110, 90),
LEVEL_11(11,R.style.LevelOne, 60, 120, 90),
LEVEL_12(12,R.style.LevelTwo,65 , 130, 90),
LEVEL_13(13,R.style.LevelThree, 70, 140, 90),
LEVEL_14(14,R.style.LevelFour, 75 , 150, 90),
LEVEL_15(15,R.style.LevelFive, 80, 160, 90);
private int levelNumber;
private int levelStyle;
private int maxUniqueImages;
private int maxUploadCount;
private int minNonRevertPercentage;
LevelInfo(int levelNumber,
int levelStyle,
int maxUniqueImages,
int maxUploadCount,
int minNonRevertPercentage) {
this.levelNumber = levelNumber;
this.levelStyle = levelStyle;
this.maxUniqueImages = maxUniqueImages;
this.maxUploadCount = maxUploadCount;
this.minNonRevertPercentage = minNonRevertPercentage;
}
public static LevelInfo from(int imagesUploaded,
int uniqueImagesUsed,
int nonRevertRate) {
LevelInfo level = LEVEL_15;
for (LevelInfo levelInfo : LevelInfo.values()) {
if (imagesUploaded < levelInfo.maxUploadCount
|| uniqueImagesUsed < levelInfo.maxUniqueImages
|| nonRevertRate < levelInfo.minNonRevertPercentage ) {
level = levelInfo;
return level;
}
}
return level;
}
public int getLevelStyle() {
return levelStyle;
}
public int getLevelNumber() {
return levelNumber;
}
public int getMaxUniqueImages() {
return maxUniqueImages;
}
public int getMaxUploadCount() {
return maxUploadCount;
}
public int getMinNonRevertPercentage(){
return minNonRevertPercentage;
}
}
}

View file

@ -135,11 +135,6 @@ public class LoginActivity extends AccountAuthenticatorActivity {
}
}
public static void startYourself(Context context) {
Intent intent = new Intent(context, LoginActivity.class);
context.startActivity(intent);
}
private void forgotPassword() {
Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL));
}
@ -445,4 +440,9 @@ public class LoginActivity extends AccountAuthenticatorActivity {
loginButton.setEnabled(enabled);
}
}
public static void startYourself(Context context) {
Intent intent = new Intent(context, LoginActivity.class);
context.startActivity(intent);
}
}

View file

@ -4,12 +4,14 @@ import dagger.Module;
import dagger.android.ContributesAndroidInjector;
import fr.free.nrw.commons.AboutActivity;
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.SignupActivity;
import fr.free.nrw.commons.category.CategoryDetailsActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.category.CategoryImagesActivity;
import fr.free.nrw.commons.explore.SearchActivity;
import fr.free.nrw.commons.nearby.NearbyActivity;
import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.settings.SettingsActivity;
@ -58,4 +60,8 @@ public abstract class ActivityBuilderModule {
@ContributesAndroidInjector
abstract CategoryDetailsActivity bindCategoryDetailsActivity();
@ContributesAndroidInjector
abstract AchievementsActivity bindAchievementsActivity();
}

View file

@ -23,6 +23,7 @@ import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.mediawiki.api.ApiResult;
import org.mediawiki.api.MWApi;
import org.w3c.dom.Element;
@ -53,6 +54,10 @@ import fr.free.nrw.commons.notification.NotificationUtils;
import in.yuvi.http.fluent.Http;
import io.reactivex.Observable;
import io.reactivex.Single;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber;
import static fr.free.nrw.commons.utils.ContinueUtils.getQueryContinue;
@ -902,13 +907,11 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
.param("meta", "userinfo")
.param("uiprop", "blockinfo")
.get();
if(result != null) {
if (result != null) {
String blockEnd = result.getString("/api/query/userinfo/@blockexpiry");
if(blockEnd.equals("infinite"))
{
if (blockEnd.equals("infinite")) {
userBlocked = true;
}
else if (!blockEnd.isEmpty()) {
} else if (!blockEnd.isEmpty()) {
Date endDate = parseMWDate(blockEnd);
Date current = new Date();
userBlocked = endDate.after(current);
@ -922,6 +925,68 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
return userBlocked;
}
/**
* This takes userName as input, which is then used to fetch the feedback/achievements
* statistics using OkHttp and JavaRx. This function return JSONObject
* @param userName
* @return
*/
@NonNull
@Override
public Single<JSONObject> getAchievements(String userName) {
final String fetchAchievementUrlTemplate =
wikiMediaToolforgeUrl + "urbanecmbot/commonsmisc/feedback.py";
return Single.fromCallable(() -> {
String url = String.format(
Locale.ENGLISH,
fetchAchievementUrlTemplate,
new PageTitle(userName).getText());
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
urlBuilder.addQueryParameter("user", userName);
Log.i("url", urlBuilder.toString());
Request request = new Request.Builder()
.url(urlBuilder.toString())
.build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
String jsonData = response.body().string();
JSONObject jsonObject = new JSONObject(jsonData);
return jsonObject;
});
}
/**
* This takes userName as input, which is then used to fetch the no of images deleted
* using OkHttp and JavaRx. This function return JSONObject
* @param userName
* @return
*/
@NonNull
@Override
public Single<JSONObject> getRevertCount(String userName){
final String fetchRevertCountUrlTemplate =
wikiMediaToolforgeUrl + "urbanecmbot/commonsmisc/feedback.py";
return Single.fromCallable(() -> {
String url = String.format(
Locale.ENGLISH,
fetchRevertCountUrlTemplate,
new PageTitle(userName).getText());
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
urlBuilder.addQueryParameter("user", userName);
urlBuilder.addQueryParameter("fetch","deletedUploads");
Log.i("url", urlBuilder.toString());
Request request = new Request.Builder()
.url(urlBuilder.toString())
.build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
String jsonData = response.body().string();
JSONObject jsonRevertObject = new JSONObject(jsonData);
return jsonRevertObject;
});
}
private Date parseMWDate(String mwDate) {
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

View file

@ -3,6 +3,8 @@ package fr.free.nrw.commons.mwapi;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@ -97,6 +99,12 @@ public interface MediaWikiApi {
boolean isUserBlockedFromCommons();
@NonNull
Single<JSONObject> getAchievements(String userName);
@NonNull
Single<JSONObject> getRevertCount(String userName);
interface ProgressListener {
void onProgress(long transferred, long total);
}

View file

@ -15,6 +15,7 @@ import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@ -24,6 +25,7 @@ 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.AccountUtil;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity;
@ -68,12 +70,19 @@ public abstract class NavigationBaseActivity extends BaseActivity
View navHeaderView = navigationView.getHeaderView(0);
TextView username = navHeaderView.findViewById(R.id.username);
AccountManager accountManager = AccountManager.get(this);
Account[] allAccounts = accountManager.getAccountsByType(AccountUtil.ACCOUNT_TYPE);
if (allAccounts.length != 0) {
username.setText(allAccounts[0].name);
}
ImageView userIcon = navHeaderView.findViewById(R.id.user_icon);
userIcon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
drawerLayout.closeDrawer(navigationView);
AchievementsActivity.startYourself(NavigationBaseActivity.this);
}
});
}
public void initBackButton() {

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#00376d"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

View file

@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="96"
android:viewportWidth="96" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#00AF89" android:pathData="M17.36,17.959v46.007L5.56,77.188c24.029,0 72.417,0 77.54,0c7.133,0 7.471,-7.096 7.482,-7.449V17.959H17.36zM63.443,29.27c2.354,0 4.258,1.907 4.258,4.256s-1.904,4.253 -4.258,4.253c-2.348,0 -4.256,-1.904 -4.256,-4.253S61.096,29.27 63.443,29.27zM45.82,29.27c2.348,0 4.252,1.907 4.252,4.256s-1.904,4.253 -4.252,4.253c-2.351,0 -4.258,-1.904 -4.258,-4.253S43.47,29.27 45.82,29.27zM54.631,65.881c-12.434,0 -22.515,-10.082 -22.515,-22.518c0,0 11.276,4.866 22.996,4.866c11.986,0 22.035,-4.866 22.035,-4.866C77.146,55.799 67.066,65.881 54.631,65.881z"/>
</vector>

View file

@ -0,0 +1,324 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/toolbar"
android:background="@color/layout_light_grey"
android:orientation="vertical">
<TextView
style="?android:textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:text="@string/level"
android:id="@+id/achievement_level" />
<ImageView
android:id="@+id/achievement_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_margin_vertical"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_alignParentRight="true"
app:srcCompat="@drawable/ic_info_outline_black_24dp"
android:layout_marginVertical="@dimen/activity_margin_vertical" />
<ImageView
android:id="@+id/achievement_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/achievement_level"
android:layout_centerHorizontal="true"
android:layout_marginVertical="@dimen/activity_margin_vertical" />
<RelativeLayout
android:id="@+id/layout_image_uploaded"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/achievement_badge"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal">
<TextView
style="?android:textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:id="@+id/images_upload_text_param"
android:layout_marginTop="@dimen/achievements_activity_margin_vertical"
android:text="@string/images_uploaded" />
<ImageView
android:layout_width="12dp"
android:layout_height="12dp"
android:id="@+id/images_upload_info"
android:layout_marginTop="8dp"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_toRightOf="@+id/images_upload_text_param"
app:srcCompat="@drawable/ic_info_outline_blue_24dp"
android:layout_marginLeft="8dp" />
<com.dinuscxj.progressbar.CircleProgressBar
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_marginRight="32dp"
android:id="@+id/images_uploaded_progressbar"
android:progress="50"
app:progress_text_size="9dp"
app:progress_end_color="#8C8B98"
app:progress_start_color="#3A3381"
app:progress_stroke_width="3dp"
app:progress_text_format_pattern="573/110"
app:progress_text_color="@color/secondaryColor"
app:style="solid_line" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/layout_image_reverts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_below="@+id/layout_image_uploaded"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal">
<TextView
style="?android:textAppearanceMedium"
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" />
<ImageView
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginTop="8dp"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:id="@+id/images_reverted_info"
android:layout_toRightOf="@+id/images_reverted_text"
app:srcCompat="@drawable/ic_info_outline_blue_24dp"
android:layout_marginLeft="8dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/achievements_revert_limit_message"
android:textSize="10dp"
android:id="@+id/images_revert_limit_text"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_below="@+id/images_reverted_text"/>
<com.dinuscxj.progressbar.CircleProgressBar
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_marginRight="32dp"
android:progress="50"
android:id="@+id/image_reverts_progressbar"
app:progress_end_color="#8C8B98"
app:progress_start_color="#3A3381"
app:progress_text_size="9dp"
app:progress_stroke_width="3dp"
app:progress_text_format_pattern="92%%"
app:progress_text_color="@color/secondaryColor"
app:style="solid_line" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/layout_image_used_by_wiki"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_below="@+id/layout_image_reverts"
android:layout_marginBottom="@dimen/activity_margin_vertical"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal">
<TextView
style="?android:textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/images_used_by_wiki_text"
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" />
<ImageView
android:layout_width="12dp"
android:layout_height="12dp"
android:id="@+id/images_used_by_wiki_info"
android:layout_marginTop="8dp"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_toRightOf="@+id/images_used_by_wiki_text"
app:srcCompat="@drawable/ic_info_outline_blue_24dp"
android:layout_marginLeft="8dp" />
<com.dinuscxj.progressbar.CircleProgressBar
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_marginRight="32dp"
android:progress="50"
app:progress_text_size="9dp"
android:id="@+id/images_used_by_wiki_progressbar"
app:progress_end_color="#8C8B98"
app:progress_start_color="#3A3381"
app:progress_stroke_width="2.5dp"
app:progress_text_color="@color/secondaryColor"
app:progress_text_format_pattern="12/24"
app:style="solid_line" />
</RelativeLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_centerVertical="true"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/layout_statistics"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/statistics"
style="?android:textAppearanceLarge"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginTop="@dimen/activity_margin_vertical" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_marginTop="@dimen/activity_margin_horizontal">
<ImageView
android:layout_width="@dimen/overflow_icon_dimen"
android:layout_height="@dimen/overflow_icon_dimen"
android:id="@+id/featured_image_icon"
app:srcCompat="@drawable/featured" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:textAppearanceMedium"
android:layout_toRightOf="@+id/featured_image_icon"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:text="@string/statistics_featured" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:textAppearanceMedium"
android:layout_alignParentRight="true"
android:layout_marginEnd="44dp"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:text="2"
android:id="@+id/image_featured"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginRight="44dp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal">
<ImageView
android:layout_width="@dimen/overflow_icon_dimen"
android:layout_height="@dimen/overflow_icon_dimen"
app:srcCompat="@drawable/ic_thanks"
android:id="@+id/thanks_image_icon"
android:scaleType="centerCrop" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:textAppearanceMedium"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_toRightOf="@+id/thanks_image_icon"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:text="@string/statistics_thanks" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:textAppearanceMedium"
android:layout_alignParentRight="true"
android:layout_marginEnd="44dp"
android:layout_marginTop="@dimen/activity_margin_horizontal"
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" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/drawer_header"
app:menu="@menu/drawer" />
</android.support.v4.widget.DrawerLayout>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="match_parent"
android:layout_width="match_parent"
@ -27,4 +28,16 @@
android:layout_centerHorizontal="true"
android:paddingBottom="@dimen/small_gap"/>
<ImageView
android:id="@+id/user_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_person_black_24dp"
android:textColor="@color/item_white_background"
android:textSize="@dimen/subheading_text_size"
android:layout_below="@+id/pictureOfTheDay"
android:layout_marginHorizontal="@dimen/activity_margin_horizontal"
android:layout_toRightOf="@+id/username"
android:paddingBottom="@dimen/small_gap"/>
</RelativeLayout>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_margin="@dimen/activity_margin_horizontal"
android:layout_width="140dp"
android:layout_height="200dp"
android:id="@+id/alert_image" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/alert_text"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginLeft="@dimen/activity_margin_horizontal"/>
</LinearLayout>

View file

@ -23,4 +23,10 @@
<attr name="drawableEnd" format="reference"/>
<attr name="drawableBottom" format="reference"/>
</declare-styleable>
<declare-styleable name="Badge">
<attr name="boundary" format="color"/>
<attr name="camera" format="color"/>
<attr name="centerRegion" format="color"/>
</declare-styleable>
</resources>

View file

@ -50,6 +50,7 @@
<color name="bottom_bar_light">#E0E0E0</color>
<color name="bottom_bar_dark">#424242</color>
<color name="layout_light_grey">#D6DCE0</color>
<color name="opak_middle_grey">#757575</color>
<color name="white">#FFFFFF</color>

View file

@ -4,6 +4,7 @@
<!-- Standard margin / padding for the containing activity -->
<dimen name="activity_margin_horizontal">16dp</dimen>
<dimen name="activity_margin_vertical">16dp</dimen>
<dimen name="achievements_activity_margin_vertical">8dp</dimen>
<!-- Standard margins / padding -->
<dimen name="huge_gap">48dp</dimen>

View file

@ -306,5 +306,20 @@
<string name="wallpaper_set_successfully">Wallpaper set successfully!</string>
<string name="delete_recent_searches_dialog">Are you sure you want to clear your search history?</string>
<string name="search_history_deleted">Search history deleted</string>
<string name="Achievements">Achievements</string>
<string name="statistics">STATISTICS</string>
<string name="statistics_thanks">Thanks Received</string>
<string name="statistics_featured">Featured Images</string>
<string name="level">LEVEL</string>
<string name="images_uploaded">Images Uploaded</string>
<string name="image_reverts">Images Not Reverted</string>
<string name="images_used_by_wiki">Images Used</string>
<string name="achievements_share_message">Share your achievements with your friends!</string>
<string name="achievements_info_message">Your level increases as you meet these requirements. Items in the "statistics" section do not count towards your level.</string>
<string name="achievements_revert_limit_message">minimum required: </string>
<string name="images_uploaded_explanation">The number of images you have uploaded to Commons, via any upload software</string>
<string name="images_reverted_explanation">The percentage of images you have uploaded to Commons that were not deleted</string>
<string name="images_used_explanation">The number of images you have uploaded to Commons that were used in Wikimedia articles</string>
<string name="user_not_logged_in">Login session expired, please log in again.</string>
</resources>

View file

@ -57,4 +57,34 @@
<item name="android:background">@android:color/transparent</item>
</style>
<style name="LevelOne">
<item name="boundary">#ddb4aa</item>
<item name="camera">#ae403a</item>
<item name="centerRegion">#bc6853</item>
</style>
<style name="LevelTwo">
<item name="boundary">#a8a8c0</item>
<item name="camera">#604890</item>
<item name="centerRegion">#6060a8</item>
</style>
<style name="LevelThree">
<item name="boundary">#90c0a8</item>
<item name="camera">#487878</item>
<item name="centerRegion">#48a8a8</item>
</style>
<style name="LevelFour">
<item name="boundary">#ffc078</item>
<item name="camera">#d87818</item>
<item name="centerRegion">#f09018</item>
</style>
<style name="LevelFive">
<item name="boundary">#c0a8c0</item>
<item name="camera">#784878</item>
<item name="centerRegion">#906078</item>
</style>
</resources>