diff --git a/app/build.gradle b/app/build.gradle
index 5d37f8f54..25853684b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -21,8 +21,8 @@ dependencies {
implementation 'info.debatty:java-string-similarity:0.24'
implementation 'com.borjabravo:readmoretextview:2.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
- implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar'){
- transitive=true
+ implementation('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar') {
+ transitive = true
}
implementation "com.github.deano2390:MaterialShowcaseView:1.2.0"
@@ -68,6 +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'
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
@@ -116,7 +117,8 @@ android {
buildTypes {
release {
- minifyEnabled false // See https://stackoverflow.com/questions/40232404/google-play-apk-and-android-studio-apk-usb-debug-behaving-differently - proguard.cfg modification alone insufficient.
+ minifyEnabled false
+ // See https://stackoverflow.com/questions/40232404/google-play-apk-and-android-studio-apk-usb-debug-behaving-differently - proguard.cfg modification alone insufficient.
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 17f6770d2..430e5f83c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,3 +1,4 @@
+
@@ -14,7 +15,7 @@
-
+
@@ -23,17 +24,18 @@
android:name=".CommonsApplication"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
- android:theme="@style/LightAppTheme"
- android:supportsRtl="true" >
-
-
+ android:supportsRtl="true"
+ android:theme="@style/LightAppTheme">
+
+
@@ -46,7 +48,9 @@
android:label="@string/app_name">
+
+
@@ -58,7 +62,9 @@
android:label="@string/app_name">
+
+
@@ -96,8 +102,12 @@
android:label="@string/title_activity_featured_images"
android:parentActivityName=".contributions.ContributionsActivity" />
-
+
+
+
+
@@ -128,6 +139,7 @@
+
@@ -166,4 +178,4 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/Utils.java b/app/src/main/java/fr/free/nrw/commons/Utils.java
index 91c23ce26..a27903ad6 100644
--- a/app/src/main/java/fr/free/nrw/commons/Utils.java
+++ b/app/src/main/java/fr/free/nrw/commons/Utils.java
@@ -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;
@@ -106,7 +108,7 @@ public class Utils {
/**
* Fixing incorrect extension
- * @param title File name
+ * @param title File name
* @param extension Correct extension
* @return File with correct extension
*/
@@ -146,7 +148,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);
@@ -171,8 +173,7 @@ public class Utils {
final String appPackageName = BuildConfig.class.getPackage().getName();
try {
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName)));
- }
- catch (android.content.ActivityNotFoundException anfe) {
+ } catch (android.content.ActivityNotFoundException anfe) {
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName)));
}
}
@@ -194,4 +195,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;
+ }
+
}
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
new file mode 100644
index 000000000..ea934c19b
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/achievements/Achievements.java
@@ -0,0 +1,5 @@
+package fr.free.nrw.commons.achievements;
+
+public class Achievements {
+
+}
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
new file mode 100644
index 000000000..57187be51
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/achievements/AchievementsActivity.java
@@ -0,0 +1,171 @@
+package fr.free.nrw.commons.achievements;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.widget.Toolbar;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+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 io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * 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;
+
+ @BindView(R.id.achievement_badge)
+ ImageView imageView;
+ @BindView(R.id.achievement_level)
+ TextView textView;
+ @BindView(R.id.toolbar)
+ Toolbar toolbar;
+ @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.featured);
+ imageView.requestLayout();
+
+ setSupportActionBar(toolbar);
+ setAchievements();
+ initDrawer();
+ }
+
+ @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);
+ shareScreen(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
+ * which then calls parseJson when results are fetched
+ */
+ private void setAchievements() {
+ compositeDisposable.add(mediaWikiApi
+ .getAchievements(sessionManager.getCurrentAccount().name)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ jsonObject -> parseJson(jsonObject)
+ ));
+ }
+
+ /**
+ * used to parse the JSONObject containing results
+ *
+ * @param object
+ */
+ private void parseJson(JSONObject object) {
+ Log.i("json", object.toString());
+ }
+
+ /**
+ * 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);
+ }
+
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java
index 51aa85903..20fadf624 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java
@@ -4,10 +4,11 @@ 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.contributions.ContributionsActivity;
import fr.free.nrw.commons.category.CategoryImagesActivity;
+import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.nearby.NearbyActivity;
import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.settings.SettingsActivity;
@@ -50,4 +51,7 @@ public abstract class ActivityBuilderModule {
@ContributesAndroidInjector
abstract CategoryImagesActivity bindFeaturedImagesActivity();
+
+ @ContributesAndroidInjector
+ abstract AchievementsActivity bindAchievementsActivity();
}
diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java
index 6629d0933..5056a0e12 100644
--- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java
+++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java
@@ -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.NodeList;
@@ -49,6 +50,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;
@@ -217,7 +222,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
@Override
public boolean pageExists(String pageName) throws IOException {
- return Double.parseDouble( api.action("query")
+ return Double.parseDouble(api.action("query")
.param("titles", pageName)
.get()
.getString("/api/query/pages/page/@_idx")) != -1;
@@ -615,6 +620,37 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
});
}
+ /**
+ * 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 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;
+ });
+
+ }
+
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
try {
diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java
index c0bd2fd87..5cb5c4591 100644
--- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java
+++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java
@@ -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;
@@ -75,6 +77,9 @@ public interface MediaWikiApi {
@NonNull
Single getUploadCount(String userName);
+ @NonNull
+ Single getAchievements(String userName);
+
interface ProgressListener {
void onProgress(long transferred, long total);
}
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 4a7322b57..8a970ec52 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
@@ -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,20 @@ 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() {
@@ -181,9 +191,10 @@ public abstract class NavigationBaseActivity extends BaseActivity
public static void startActivityWithFlags(Context context, Class cls, int... flags) {
Intent intent = new Intent(context, cls);
- for (int flag: flags) {
+ for (int flag : flags) {
intent.addFlags(flag);
}
context.startActivity(intent);
}
+
}
diff --git a/app/src/main/res/drawable/featured.xml b/app/src/main/res/drawable/featured.xml
new file mode 100644
index 000000000..e971c3446
--- /dev/null
+++ b/app/src/main/res/drawable/featured.xml
@@ -0,0 +1,1069 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_person_black_24dp.xml b/app/src/main/res/drawable/ic_person_black_24dp.xml
new file mode 100644
index 000000000..d7366bda0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_person_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_thanks.xml b/app/src/main/res/drawable/ic_thanks.xml
new file mode 100644
index 000000000..480b45c1b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_thanks.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_achievements.xml b/app/src/main/res/layout/activity_achievements.xml
new file mode 100644
index 000000000..99e1d28c1
--- /dev/null
+++ b/app/src/main/res/layout/activity_achievements.xml
@@ -0,0 +1,260 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/drawer_header.xml b/app/src/main/res/layout/drawer_header.xml
index 9bd3ae3a3..53a2684a0 100644
--- a/app/src/main/res/layout/drawer_header.xml
+++ b/app/src/main/res/layout/drawer_header.xml
@@ -1,5 +1,6 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index ef5000d60..02dae825d 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -50,6 +50,7 @@
#E0E0E0
#424242
+ #D6DCE0
#757575
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6e30baa10..60080d208 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,6 +1,6 @@
- Appearance
+ AppearanceGeneral
Feedback
Location
@@ -74,8 +74,8 @@
- Starting %1$d uploads
- - %1$d upload
- - %1$d uploads
+ - %1$d upload
+ - %1$d uploads
No categories matching %1$s found
Add categories to make your images more discoverable on Wikimedia Commons.\nStart typing to add categories.
@@ -188,7 +188,7 @@
Unable to display more than 500
Set Recent Upload Limit
Two factor authentication is currently not supported.
- Do you really want to logout?
+ Do you really want to logout?
Commons Logo
Commons Website
Commons Facebook Page
@@ -284,4 +284,12 @@
Coordinates were not specified during image selection
Error fetching nearby places.
+ Achievements
+ STATISTICS
+ Thanks Received
+ Featured Images
+ LEVEL
+ Images Uploaded
+ Images Not Reverted
+ Images Used By Wiki