mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 12:53:55 +01:00
Wmhack2018 (#1536)
* Add new activity to manifest * Create review activity layout base * Add a new menu item to drawer for peer review * Add a top menu with randomizer icon to review activity * Add strings for review button * Add activity to ActivityBuilderModule for injection * Add a new drawer item to start review acitivty * Create base of the Review Activity * Add fragment pager * Add new fragment for injection * Create a fragment pager layout * Wikimedia hackathon 2018 (#1533) * First draft of fn to get random recent image * Use log entries for requests to beta, try to connect refresh button FIXME: runs http request on main thread, breaks * Tweak button connection * Add ReviewController class * Fix fragments * Wmhack2018 (#1534) * tiny fixes * Load pictures into activities * Re-use same class for all review fragments (#1537) And try to add pager indicator * [WIP] category check * [WIP] add on-click actions to ReviewActivity * [WIP] add SendThankTask * Make it beautiful * Use standalone category extraction code in MediaDataExtractor * Add categories to category review page
This commit is contained in:
parent
1520fc01f7
commit
01cb9ccd70
28 changed files with 1086 additions and 45 deletions
|
|
@ -96,6 +96,10 @@
|
|||
android:label="@string/title_activity_featured_images"
|
||||
android:parentActivityName=".contributions.ContributionsActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".review.ReviewActivity"
|
||||
android:label="@string/title_activity_review" />
|
||||
|
||||
<service android:name=".upload.UploadService" />
|
||||
|
||||
<service
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import javax.xml.parsers.ParserConfigurationException;
|
|||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.mwapi.MediaResult;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.utils.MediaDataExtractorUtil;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
|
|
@ -71,28 +72,13 @@ public class MediaDataExtractor {
|
|||
MediaResult result = mediaWikiApi.fetchMediaByFilename(filename);
|
||||
|
||||
// In-page category links are extracted from source, as XML doesn't cover [[links]]
|
||||
extractCategories(result.getWikiSource());
|
||||
categories = MediaDataExtractorUtil.extractCategories(result.getWikiSource());
|
||||
|
||||
// Description template info is extracted from preprocessor XML
|
||||
processWikiParseTree(result.getParseTreeXmlSource(), licenseList);
|
||||
fetched = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* We could fetch all category links from API, but we actually only want the ones
|
||||
* directly in the page source so they're editable. In the future this may change.
|
||||
*
|
||||
* @param source wikitext source code
|
||||
*/
|
||||
private void extractCategories(String source) {
|
||||
Pattern regex = Pattern.compile("\\[\\[\\s*Category\\s*:([^]]*)\\s*\\]\\]", Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = regex.matcher(source);
|
||||
while (matcher.find()) {
|
||||
String cat = matcher.group(1).trim();
|
||||
categories.add(cat);
|
||||
}
|
||||
}
|
||||
|
||||
private void processWikiParseTree(String source, LicenseList licenseList) throws IOException {
|
||||
Document doc;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ public class DeleteTask extends AsyncTask<Void, Integer, Boolean> {
|
|||
|
||||
String editToken;
|
||||
String authCookie;
|
||||
String summary = "Nominating " + media.getFilename() +" for deletion.";
|
||||
String summary = context.getString(R.string.nominating_file_for_deletion, media.getFilename());
|
||||
|
||||
authCookie = sessionManager.getAuthCookie();
|
||||
mwApi.setAuthCookie(authCookie);
|
||||
|
|
@ -123,29 +123,21 @@ public class DeleteTask extends AsyncTask<Void, Integer, Boolean> {
|
|||
protected void onProgressUpdate (Integer... values){
|
||||
super.onProgressUpdate(values);
|
||||
|
||||
int[] messages = new int[]{
|
||||
R.string.getting_edit_token,
|
||||
R.string.nominate_for_deletion_edit_file_page,
|
||||
R.string.nominate_for_deletion_create_deletion_request,
|
||||
R.string.nominate_for_deletion_edit_deletion_request_log,
|
||||
R.string.nominate_for_deletion_notify_user,
|
||||
R.string.nominate_for_deletion_done
|
||||
};
|
||||
|
||||
String message = "";
|
||||
switch (values[0]){
|
||||
case 0:
|
||||
message = "Getting token";
|
||||
break;
|
||||
case 1:
|
||||
message = "Adding delete message to file";
|
||||
break;
|
||||
case 2:
|
||||
message = "Creating Delete requests sub-page";
|
||||
break;
|
||||
case 3:
|
||||
message = "Adding file to Delete requests log";
|
||||
break;
|
||||
case 4:
|
||||
message = "Notifying User on Talk page";
|
||||
break;
|
||||
case 5:
|
||||
message = "Done";
|
||||
break;
|
||||
if (0 < values[0] && values[0] < messages.length) {
|
||||
message = context.getString(messages[values[0]]);
|
||||
}
|
||||
|
||||
notificationBuilder.setContentTitle("Nominating "+media.getDisplayTitle()+" for deletion")
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.nominating_file_for_deletion, media.getFilename()))
|
||||
.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(message))
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import fr.free.nrw.commons.contributions.ContributionsActivity;
|
|||
import fr.free.nrw.commons.category.CategoryImagesActivity;
|
||||
import fr.free.nrw.commons.nearby.NearbyActivity;
|
||||
import fr.free.nrw.commons.notification.NotificationActivity;
|
||||
import fr.free.nrw.commons.review.ReviewActivity;
|
||||
import fr.free.nrw.commons.settings.SettingsActivity;
|
||||
import fr.free.nrw.commons.upload.MultipleShareActivity;
|
||||
import fr.free.nrw.commons.upload.ShareActivity;
|
||||
|
|
@ -50,4 +51,7 @@ public abstract class ActivityBuilderModule {
|
|||
|
||||
@ContributesAndroidInjector
|
||||
abstract CategoryImagesActivity bindFeaturedImagesActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ReviewActivity bindReviewActivity();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ import dagger.android.support.AndroidSupportInjectionModule;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.MediaWikiImageView;
|
||||
import fr.free.nrw.commons.auth.LoginActivity;
|
||||
import fr.free.nrw.commons.contributions.Contribution;
|
||||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||
import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
|
||||
import fr.free.nrw.commons.delete.DeleteTask;
|
||||
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
|
||||
import fr.free.nrw.commons.review.CheckCategoryTask;
|
||||
import fr.free.nrw.commons.review.SendThankTask;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
import fr.free.nrw.commons.nearby.PlaceRenderer;
|
||||
|
||||
|
|
@ -40,6 +40,10 @@ public interface CommonsApplicationComponent extends AndroidInjector<Application
|
|||
|
||||
void inject(DeleteTask deleteTask);
|
||||
|
||||
void inject(CheckCategoryTask checkCategoryTask);
|
||||
|
||||
void inject(SendThankTask sendThankTask);
|
||||
|
||||
void inject(SettingsFragment fragment);
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
|||
import fr.free.nrw.commons.nearby.NearbyListFragment;
|
||||
import fr.free.nrw.commons.nearby.NearbyMapFragment;
|
||||
import fr.free.nrw.commons.nearby.NoPermissionsFragment;
|
||||
import fr.free.nrw.commons.review.ReviewImageFragment;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
import fr.free.nrw.commons.upload.MultipleUploadListFragment;
|
||||
import fr.free.nrw.commons.upload.SingleUploadFragment;
|
||||
|
|
@ -51,4 +52,7 @@ public abstract class FragmentBuilderModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract CategoryImagesListFragment bindFeaturedImagesListFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ReviewImageFragment bindReviewOutOfContextFragment();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,12 @@ import fr.free.nrw.commons.delete.DeleteTask;
|
|||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.review.CheckCategoryTask;
|
||||
import fr.free.nrw.commons.review.SendThankTask;
|
||||
import fr.free.nrw.commons.ui.widget.CompatTextView;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
|
|
@ -357,6 +362,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
String reason = input.getText().toString();
|
||||
|
||||
DeleteTask deleteTask = new DeleteTask(getActivity(), media, reason);
|
||||
deleteTask.execute();
|
||||
enableDeleteButton(false);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package fr.free.nrw.commons.media;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class RecentChangesImageUtils {
|
||||
|
||||
private static final String[] imageExtensions = new String[]
|
||||
{".jpg", ".jpeg", ".png"};
|
||||
|
||||
@Nullable
|
||||
public static String findImageInRecentChanges(NodeList childNodes) {
|
||||
String imageTitle;
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Element e = (Element)childNodes.item(i);
|
||||
if (e.getAttribute("type").equals("log") && !e.getAttribute("old_revid").equals("0")) {
|
||||
// For log entries, we only want ones where old_revid is zero, indicating a new file
|
||||
continue;
|
||||
}
|
||||
imageTitle = e.getAttribute("title");
|
||||
|
||||
for (String imageExtension : imageExtensions) {
|
||||
if (imageTitle.toLowerCase().endsWith(imageExtension)) {
|
||||
return imageTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -38,12 +38,14 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.Random;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.PageTitle;
|
||||
import fr.free.nrw.commons.category.CategoryImageUtils;
|
||||
import fr.free.nrw.commons.category.QueryContinue;
|
||||
import fr.free.nrw.commons.media.RecentChangesImageUtils;
|
||||
import fr.free.nrw.commons.notification.Notification;
|
||||
import fr.free.nrw.commons.notification.NotificationUtils;
|
||||
import in.yuvi.http.fluent.Http;
|
||||
|
|
@ -60,6 +62,14 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
private String wikiMediaToolforgeUrl = "https://tools.wmflabs.org/";
|
||||
|
||||
private static final String THUMB_SIZE = "640";
|
||||
// Give up if no random recent image found after 5 tries
|
||||
private static final int MAX_RANDOM_TRIES = 5;
|
||||
// Random image request is for some time in the past 30 days
|
||||
private static final int RANDOM_SECONDS = 60 * 60 * 24 * 30;
|
||||
// Assuming MW always gives me UTC
|
||||
private static final SimpleDateFormat isoFormat =
|
||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH);
|
||||
private static final String FILE_NAMESPACE = "6";
|
||||
private AbstractHttpClient httpClient;
|
||||
private MWApi api;
|
||||
private Context context;
|
||||
|
|
@ -223,6 +233,19 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
.getString("/api/query/pages/page/@_idx")) != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean thank(String editToken, String revision) throws IOException {
|
||||
ApiResult res = api.action("thank")
|
||||
.param("rev", revision)
|
||||
.param("token", editToken)
|
||||
.param("source", getUserAgent())
|
||||
.post();
|
||||
String r = res.getString("/api/result/@success");
|
||||
// Does this correctly check the success/failure?
|
||||
// The docs https://www.mediawiki.org/wiki/Extension:Thanks seems unclear about that.
|
||||
return r.equals("success");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String edit(String editToken, String processedPageContent, String filename, String summary) throws IOException {
|
||||
|
|
@ -435,6 +458,20 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
.getString("/api/query/pages/page/revisions/rev");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String firstRevisionOfFile(String filename) throws IOException {
|
||||
String res = api.action("query")
|
||||
.param("prop", "revisions")
|
||||
.param("rvprop", "timestamp|ids")
|
||||
.param("titles", filename)
|
||||
.param("rvdir", "newer")
|
||||
.param("rvlimit", "1")
|
||||
.get()
|
||||
.getString("/api/query/pages/page/revisions/rev/@revid");
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public List<Notification> getNotifications() {
|
||||
|
|
@ -616,11 +653,59 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
}
|
||||
|
||||
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 {
|
||||
return isoFormat.parse(mwDate);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String formatMWDate(Date date) {
|
||||
return isoFormat.format(date);
|
||||
}
|
||||
|
||||
public Media getRecentRandomImage() throws IOException {
|
||||
Media media = null;
|
||||
int tries = 0;
|
||||
Random r = new Random();
|
||||
|
||||
while (media == null && tries < MAX_RANDOM_TRIES) {
|
||||
Date now = new Date();
|
||||
Date startDate = new Date(now.getTime() - r.nextInt(RANDOM_SECONDS) * 1000L);
|
||||
ApiResult apiResult = null;
|
||||
try {
|
||||
MWApi.RequestBuilder requestBuilder = api.action("query")
|
||||
.param("list", "recentchanges")
|
||||
.param("rcstart", formatMWDate(startDate))
|
||||
.param("rcnamespace", FILE_NAMESPACE)
|
||||
.param("rcprop", "title|ids")
|
||||
.param("rctype", "new|log")
|
||||
.param("rctoponly", "1");
|
||||
|
||||
apiResult = requestBuilder.get();
|
||||
} catch (IOException e) {
|
||||
Timber.e("Failed to obtain recent random", e);
|
||||
}
|
||||
if (apiResult != null) {
|
||||
ApiResult recentChangesNode = apiResult.getNode("/api/query/recentchanges");
|
||||
if (recentChangesNode != null
|
||||
&& recentChangesNode.getDocument() != null
|
||||
&& recentChangesNode.getDocument().getChildNodes() != null
|
||||
&& recentChangesNode.getDocument().getChildNodes().getLength() > 0) {
|
||||
NodeList childNodes = recentChangesNode.getDocument().getChildNodes();
|
||||
String imageTitle = RecentChangesImageUtils.findImageInRecentChanges(childNodes);
|
||||
if (imageTitle != null) {
|
||||
boolean deletionStatus = pageExists("Commons:Deletion_requests/" + imageTitle);
|
||||
if (!deletionStatus) {
|
||||
// strip File: prefix
|
||||
imageTitle = imageTitle.replace("File:", "");
|
||||
media = new Media(imageTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tries++;
|
||||
}
|
||||
return media;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,14 @@ public interface MediaWikiApi {
|
|||
@NonNull
|
||||
Single<Integer> getUploadCount(String userName);
|
||||
|
||||
boolean thank(String editToken, String revision) throws IOException;
|
||||
|
||||
String firstRevisionOfFile(String filename) throws IOException;
|
||||
|
||||
interface ProgressListener {
|
||||
void onProgress(long transferred, long total);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Media getRecentRandomImage() throws IOException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
package fr.free.nrw.commons.review;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.view.Gravity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.support.v4.app.NotificationCompat.DEFAULT_ALL;
|
||||
import static android.support.v4.app.NotificationCompat.PRIORITY_HIGH;
|
||||
|
||||
// Example code:
|
||||
// CheckCategoryTask deleteTask = new CheckCategoryTask(getActivity(), media);
|
||||
|
||||
public class CheckCategoryTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
@Inject
|
||||
MediaWikiApi mwApi;
|
||||
@Inject
|
||||
SessionManager sessionManager;
|
||||
|
||||
public static final int NOTIFICATION_CHECK_CATEGORY = 0x101;
|
||||
|
||||
private NotificationManager notificationManager;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
private Context context;
|
||||
private Media media;
|
||||
|
||||
public CheckCategoryTask(Context context, Media media){
|
||||
this.context = context;
|
||||
this.media = media;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute(){
|
||||
ApplicationlessInjection
|
||||
.getInstance(context.getApplicationContext())
|
||||
.getCommonsApplicationComponent()
|
||||
.inject(this);
|
||||
|
||||
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationBuilder = new NotificationCompat.Builder(context);
|
||||
Toast toast = new Toast(context);
|
||||
toast.setGravity(Gravity.CENTER,0,0);
|
||||
toast = Toast.makeText(context, context.getString(R.string.check_category_toast, media.getDisplayTitle()), Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void ...voids) {
|
||||
publishProgress(0);
|
||||
|
||||
String editToken;
|
||||
String authCookie;
|
||||
String summary = context.getString(R.string.check_category_edit_summary);
|
||||
|
||||
authCookie = sessionManager.getAuthCookie();
|
||||
mwApi.setAuthCookie(authCookie);
|
||||
|
||||
try {
|
||||
editToken = mwApi.getEditToken();
|
||||
if (editToken.equals("+\\")) {
|
||||
return false;
|
||||
}
|
||||
publishProgress(1);
|
||||
|
||||
mwApi.appendEdit(editToken, "\n{{subst:chc}}\n", media.getFilename(), summary);
|
||||
publishProgress(2);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Timber.d(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate (Integer... values){
|
||||
super.onProgressUpdate(values);
|
||||
|
||||
int[] messages = new int[]{R.string.getting_edit_token, R.string.check_category_adding_template};
|
||||
String message = "";
|
||||
if (0 < values[0] && values[0] < messages.length) {
|
||||
message = context.getString(messages[values[0]]);
|
||||
}
|
||||
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.check_category_notification_title, media.getDisplayTitle()))
|
||||
.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(message))
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setProgress(messages.length, values[0], false)
|
||||
.setOngoing(true);
|
||||
notificationManager.notify(NOTIFICATION_CHECK_CATEGORY, notificationBuilder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
String message = "";
|
||||
String title = "";
|
||||
|
||||
if (result){
|
||||
title = context.getString(R.string.check_category_success_title);
|
||||
message = context.getString(R.string.check_category_success_message, media.getDisplayTitle());
|
||||
}
|
||||
else {
|
||||
title = context.getString(R.string.check_category_failure_title);
|
||||
message = context.getString(R.string.check_category_failure_message, media.getDisplayTitle());
|
||||
}
|
||||
|
||||
notificationBuilder.setDefaults(DEFAULT_ALL)
|
||||
.setContentTitle(title)
|
||||
.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(message))
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setProgress(0,0,false)
|
||||
.setOngoing(false)
|
||||
.setPriority(PRIORITY_HIGH);
|
||||
notificationManager.notify(NOTIFICATION_CHECK_CATEGORY, notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
173
app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java
Normal file
173
app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
package fr.free.nrw.commons.review;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
import com.viewpagerindicator.CirclePageIndicator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
||||
import fr.free.nrw.commons.mwapi.MediaResult;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.utils.MediaDataExtractorUtil;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by root on 18.05.2018.
|
||||
*/
|
||||
|
||||
public class ReviewActivity extends AuthenticatedActivity {
|
||||
|
||||
@BindView(R.id.toolbar)
|
||||
Toolbar toolbar;
|
||||
@BindView(R.id.navigation_view)
|
||||
NavigationView navigationView;
|
||||
@BindView(R.id.drawer_layout)
|
||||
DrawerLayout drawerLayout;
|
||||
|
||||
@BindView(R.id.reviewPager)
|
||||
ViewPager pager;
|
||||
|
||||
@Inject MediaWikiApi mwApi;
|
||||
|
||||
private ReviewPagerAdapter reviewPagerAdapter;
|
||||
|
||||
//private ReviewCallback reviewCallback;
|
||||
private ReviewController reviewController;
|
||||
|
||||
@BindView(R.id.reviewPagerIndicator)
|
||||
public CirclePageIndicator pagerIndicator;
|
||||
|
||||
@Override
|
||||
protected void onAuthCookieAcquired(String authCookie) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAuthFailure() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_review);
|
||||
ButterKnife.bind(this);
|
||||
initDrawer();
|
||||
|
||||
reviewController = new ReviewController();
|
||||
|
||||
|
||||
reviewPagerAdapter = new ReviewPagerAdapter(getSupportFragmentManager());
|
||||
pager.setAdapter(reviewPagerAdapter);
|
||||
reviewPagerAdapter.getItem(0);
|
||||
pagerIndicator.setViewPager(pager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.review_randomizer_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
if (id == R.id.action_review_randomizer) {
|
||||
Observable.fromCallable(() -> {
|
||||
Media result = null;
|
||||
try {
|
||||
result = mwApi.getRecentRandomImage();
|
||||
|
||||
//String thumBaseUrl = Utils.makeThumbBaseUrl(result.getFilename());
|
||||
//reviewPagerAdapter.currentThumbBasedUrl = thumBaseUrl;
|
||||
|
||||
//Log.d("review", result.getWikiSource());
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.d("review", e.toString());
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::updateImage);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void updateImage(Media result) {
|
||||
reviewController.onImageRefreshed(result.getFilename()); //file name is updated
|
||||
reviewPagerAdapter.updateFilename();
|
||||
pager.setCurrentItem(0);
|
||||
Observable.fromCallable(() -> {
|
||||
MediaResult media = mwApi.fetchMediaByFilename("File:" + result.getFilename());
|
||||
return MediaDataExtractorUtil.extractCategories(media.getWikiSource());
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::updateCategories);
|
||||
}
|
||||
|
||||
private void updateCategories(ArrayList<String> categories) {
|
||||
reviewController.onCategoriesRefreshed(categories);
|
||||
reviewPagerAdapter.updateCategories();
|
||||
}
|
||||
|
||||
/**
|
||||
* References ReviewPagerAdapter to null before the activity is destroyed
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
//adapter.setCallback(null);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumers should be simply using this method to use this activity.
|
||||
* @param context
|
||||
* @param title Page title
|
||||
*/
|
||||
public static void startYourself(Context context, String title) {
|
||||
Intent reviewActivity = new Intent(context, ReviewActivity.class);
|
||||
context.startActivity(reviewActivity);
|
||||
}
|
||||
|
||||
interface ReviewCallback {
|
||||
void onImageRefreshed(String itemTitle);
|
||||
void onQuestionChanged();
|
||||
void onSurveyFinished();
|
||||
void onImproperImageReported();
|
||||
void onLicenceViolationReported();
|
||||
void onWrongCategoryReported();
|
||||
void onThankSent();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package fr.free.nrw.commons.review;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by root on 19.05.2018.
|
||||
*/
|
||||
|
||||
public class ReviewController implements ReviewActivity.ReviewCallback {
|
||||
public static String fileName;
|
||||
protected static ArrayList<String> categories;
|
||||
|
||||
@Override
|
||||
public void onImageRefreshed(String fileName) {
|
||||
ReviewController.fileName = fileName;
|
||||
ReviewController.categories = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void onCategoriesRefreshed(ArrayList<String> categories) {
|
||||
ReviewController.categories = categories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuestionChanged() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurveyFinished() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImproperImageReported() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLicenceViolationReported() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWrongCategoryReported() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThankSent() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package fr.free.nrw.commons.review;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
|
||||
/**
|
||||
* Created by root on 19.05.2018.
|
||||
*/
|
||||
|
||||
public class ReviewImageFragment extends CommonsDaggerSupportFragment {
|
||||
|
||||
public static final int SPAM = 0;
|
||||
public static final int COPYRIGHT = 1;
|
||||
public static final int CATEGORY = 2;
|
||||
|
||||
private int position;
|
||||
private String fileName;
|
||||
private String catString;
|
||||
private View catsView;
|
||||
private SimpleDraweeView simpleDraweeView;
|
||||
|
||||
public void update(int position, String fileName) {
|
||||
this.position = position;
|
||||
this.fileName = fileName;
|
||||
|
||||
if (simpleDraweeView!=null) {
|
||||
simpleDraweeView.setImageURI(Utils.makeThumbBaseUrl(fileName));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateCategories(Iterable<String> categories) {
|
||||
catString = TextUtils.join(", ", categories);
|
||||
if (catsView != null) {
|
||||
((TextView) catsView).setText(catString);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
position = getArguments().getInt("position");
|
||||
View layoutView = inflater.inflate(R.layout.fragment_review_image, container,
|
||||
false);
|
||||
View textView = layoutView.findViewById(R.id.reviewQuestion);
|
||||
catsView = layoutView.findViewById(R.id.reviewCategories);
|
||||
String question;
|
||||
switch(position) {
|
||||
case COPYRIGHT:
|
||||
question = getString(R.string.review_copyright);
|
||||
break;
|
||||
case CATEGORY:
|
||||
question = getString(R.string.review_category);
|
||||
catsView.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
case SPAM:
|
||||
question = getString(R.string.review_spam);
|
||||
break;
|
||||
default:
|
||||
question = "How did we get here?";
|
||||
}
|
||||
((TextView) textView).setText(question);
|
||||
simpleDraweeView = layoutView.findViewById(R.id.imageView);
|
||||
|
||||
if (fileName != null) {
|
||||
simpleDraweeView.setImageURI(Utils.makeThumbBaseUrl(fileName));
|
||||
}
|
||||
if (catString != null) {
|
||||
((TextView) catsView).setText(catString);
|
||||
}
|
||||
return layoutView;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package fr.free.nrw.commons.review;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
|
||||
/**
|
||||
* Created by nes on 19.05.2018.
|
||||
*/
|
||||
|
||||
public class ReviewPagerAdapter extends FragmentStatePagerAdapter {
|
||||
private int currentPosition;
|
||||
ReviewImageFragment[] reviewImageFragments;
|
||||
|
||||
|
||||
public ReviewPagerAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
reviewImageFragments = new ReviewImageFragment[] {
|
||||
new ReviewImageFragment(),
|
||||
new ReviewImageFragment(),
|
||||
new ReviewImageFragment()
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
public void updateFilename() {
|
||||
for (int i = 0; i < getCount(); i++) {
|
||||
ReviewImageFragment fragment = reviewImageFragments[i];
|
||||
fragment.update(i, ReviewController.fileName);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateCategories() {
|
||||
ReviewImageFragment categoryFragment = reviewImageFragments[ReviewImageFragment.CATEGORY];
|
||||
categoryFragment.updateCategories(ReviewController.categories);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt("position", position);
|
||||
reviewImageFragments[position].setArguments(bundle);
|
||||
return reviewImageFragments[position];
|
||||
}
|
||||
|
||||
}
|
||||
140
app/src/main/java/fr/free/nrw/commons/review/SendThankTask.java
Normal file
140
app/src/main/java/fr/free/nrw/commons/review/SendThankTask.java
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package fr.free.nrw.commons.review;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.view.Gravity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.support.v4.app.NotificationCompat.DEFAULT_ALL;
|
||||
import static android.support.v4.app.NotificationCompat.PRIORITY_HIGH;
|
||||
|
||||
// example code:
|
||||
//
|
||||
// media = new Media("File:Iru.png");
|
||||
// Observable.fromCallable(() -> mwApi.firstRevisionOfFile(media.getFilename()))
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(revision -> {
|
||||
// SendThankTask task = new SendThankTask(getActivity(), media, revision);
|
||||
// task.execute();
|
||||
// });
|
||||
|
||||
public class SendThankTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
@Inject
|
||||
MediaWikiApi mwApi;
|
||||
@Inject
|
||||
SessionManager sessionManager;
|
||||
|
||||
public static final int NOTIFICATION_SEND_THANK = 0x102;
|
||||
|
||||
private NotificationManager notificationManager;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
private Context context;
|
||||
private Media media;
|
||||
private String revision;
|
||||
|
||||
public SendThankTask(Context context, Media media, String revision){
|
||||
this.context = context;
|
||||
this.media = media;
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute(){
|
||||
ApplicationlessInjection
|
||||
.getInstance(context.getApplicationContext())
|
||||
.getCommonsApplicationComponent()
|
||||
.inject(this);
|
||||
|
||||
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationBuilder = new NotificationCompat.Builder(context);
|
||||
Toast toast = new Toast(context);
|
||||
toast.setGravity(Gravity.CENTER,0,0);
|
||||
toast = Toast.makeText(context, context.getString(R.string.send_thank_toast, media.getDisplayTitle()), Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void ...voids) {
|
||||
publishProgress(0);
|
||||
|
||||
String editToken;
|
||||
String authCookie;
|
||||
|
||||
authCookie = sessionManager.getAuthCookie();
|
||||
mwApi.setAuthCookie(authCookie);
|
||||
|
||||
try {
|
||||
editToken = mwApi.getEditToken();
|
||||
if (editToken.equals("+\\")) {
|
||||
return false;
|
||||
}
|
||||
publishProgress(1);
|
||||
|
||||
mwApi.thank(editToken, revision);
|
||||
|
||||
publishProgress(2);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Timber.d(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate (Integer... values){
|
||||
super.onProgressUpdate(values);
|
||||
|
||||
int[] messages = new int[]{R.string.getting_edit_token, R.string.send_thank_send};
|
||||
String message = "";
|
||||
if (0 < values[0] && values[0] < messages.length) {
|
||||
message = context.getString(messages[values[0]]);
|
||||
}
|
||||
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.send_thank_notification_title))
|
||||
.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(message))
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setProgress(messages.length, values[0], false)
|
||||
.setOngoing(true);
|
||||
notificationManager.notify(NOTIFICATION_SEND_THANK, notificationBuilder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
String message = "";
|
||||
String title = "";
|
||||
|
||||
if (result){
|
||||
title = context.getString(R.string.send_thank_success_title);
|
||||
message = context.getString(R.string.send_thank_success_message, media.getDisplayTitle());
|
||||
}
|
||||
else {
|
||||
title = context.getString(R.string.send_thank_failure_title);
|
||||
message = context.getString(R.string.send_thank_failure_message, media.getDisplayTitle());
|
||||
}
|
||||
|
||||
notificationBuilder.setDefaults(DEFAULT_ALL)
|
||||
.setContentTitle(title)
|
||||
.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(message))
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setProgress(0,0,false)
|
||||
.setOngoing(false)
|
||||
.setPriority(PRIORITY_HIGH);
|
||||
notificationManager.notify(NOTIFICATION_SEND_THANK, notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ import fr.free.nrw.commons.contributions.ContributionsActivity;
|
|||
import fr.free.nrw.commons.category.CategoryImagesActivity;
|
||||
import fr.free.nrw.commons.nearby.NearbyActivity;
|
||||
import fr.free.nrw.commons.notification.NotificationActivity;
|
||||
import fr.free.nrw.commons.review.ReviewActivity;
|
||||
import fr.free.nrw.commons.settings.SettingsActivity;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
|
@ -160,6 +161,11 @@ public abstract class NavigationBaseActivity extends BaseActivity
|
|||
drawerLayout.closeDrawer(navigationView);
|
||||
CategoryImagesActivity.startYourself(this, getString(R.string.title_activity_featured_images), FEATURED_IMAGES_CATEGORY);
|
||||
return true;
|
||||
|
||||
case R.id.action_review:
|
||||
drawerLayout.closeDrawer(navigationView);
|
||||
ReviewActivity.startYourself(this, getString(R.string.title_activity_review));
|
||||
return true;
|
||||
default:
|
||||
Timber.e("Unknown option [%s] selected from the navigation menu", itemId);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MediaDataExtractorUtil {
|
||||
|
||||
/**
|
||||
* We could fetch all category links from API, but we actually only want the ones
|
||||
* directly in the page source so they're editable. In the future this may change.
|
||||
*
|
||||
* @param source wikitext source code
|
||||
*/
|
||||
public static ArrayList<String> extractCategories(String source) {
|
||||
ArrayList<String> categories = new ArrayList<>();
|
||||
Pattern regex = Pattern.compile("\\[\\[\\s*Category\\s*:([^]]*)\\s*\\]\\]", Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = regex.matcher(source);
|
||||
while (matcher.find()) {
|
||||
String cat = matcher.group(1).trim();
|
||||
categories.add(cat);
|
||||
}
|
||||
|
||||
return categories;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
9
app/src/main/res/drawable/ic_check_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_check_black_24dp.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_refresh_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_refresh_black_24dp.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/tab_indicator_default.xml
Normal file
12
app/src/main/res/drawable/tab_indicator_default.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape
|
||||
android:innerRadius="0dp"
|
||||
android:shape="ring"
|
||||
android:thickness="2dp"
|
||||
android:useLevel="false">
|
||||
<solid android:color="@android:color/darker_gray"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
8
app/src/main/res/drawable/tab_indicator_selected.xml
Normal file
8
app/src/main/res/drawable/tab_indicator_selected.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape android:innerRadius="0dp"
|
||||
android:shape="ring"
|
||||
android:thickness="4dp"
|
||||
android:useLevel="false"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/commons_app_blue_dark"/>
|
||||
</shape>
|
||||
8
app/src/main/res/drawable/tab_selector.xml
Normal file
8
app/src/main/res/drawable/tab_selector.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:drawable="@drawable/tab_indicator_selected"
|
||||
android:state_selected="true"/>
|
||||
|
||||
<item android:drawable="@drawable/tab_indicator_default"/>
|
||||
</selector>
|
||||
65
app/src/main/res/layout/activity_review.xml
Normal file
65
app/src/main/res/layout/activity_review.xml
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<?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">
|
||||
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/coordinator_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<include
|
||||
android:id="@+id/toolbar"
|
||||
layout="@layout/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
</include>
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/toolbar">
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
android:id="@+id/reviewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fadingEdge="none" />
|
||||
|
||||
|
||||
<com.viewpagerindicator.CirclePageIndicator
|
||||
android:id="@+id/reviewPagerIndicator"
|
||||
android:layout_height="@dimen/half_standard_height"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_below="@id/reviewPager"
|
||||
android:layout_gravity="bottom"
|
||||
android:elevation="3dp"
|
||||
android:background="?attr/colorPrimaryDark"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
||||
|
||||
<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>
|
||||
89
app/src/main/res/layout/fragment_review_image.xml
Normal file
89
app/src/main/res/layout/fragment_review_image.xml
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<?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="match_parent"
|
||||
android:weightSum="6"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
>
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="3"
|
||||
android:src="@drawable/commons_logo_large"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/reviewQuestion"
|
||||
android:layout_width="match_parent"
|
||||
android:textColor="@android:color/black"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.1"
|
||||
android:textAlignment="center"
|
||||
android:textSize="32sp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="testing1"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/reviewQuestionContext"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="0.7"
|
||||
android:textAlignment="center"
|
||||
android:textSize="24sp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="testing2"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reviewCategories"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="0.7"
|
||||
android:textSize="24sp"
|
||||
android:textAlignment="center"
|
||||
android:text="testing2"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="0.6"
|
||||
android:weightSum="2"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/yesButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@android:color/transparent"
|
||||
android:text="@string/yes"
|
||||
android:textSize="20sp"
|
||||
android:textColor="?attr/colorPrimaryDark"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/noButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/no"
|
||||
android:textSize="20sp"
|
||||
android:textColor="?attr/colorPrimaryDark"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
<View
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="0.6"
|
||||
android:background="?attr/colorPrimaryDark"
|
||||
></View>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -15,6 +15,11 @@
|
|||
android:icon="@drawable/ic_star_black_24dp"
|
||||
android:title="@string/navigation_item_featured_images"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_review"
|
||||
android:icon="@drawable/ic_check_black_24dp"
|
||||
android:title="@string/navigation_item_review"/>
|
||||
|
||||
</group>
|
||||
<group android:id="@+id/drawer_account">
|
||||
<item
|
||||
|
|
|
|||
10
app/src/main/res/menu/review_randomizer_menu.xml
Normal file
10
app/src/main/res/menu/review_randomizer_menu.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/action_review_randomizer"
|
||||
android:title="@string/menu_from_gallery"
|
||||
app:showAsAction="ifRoom|withText"
|
||||
android:icon="@drawable/ic_refresh_white_24dp"
|
||||
/>
|
||||
|
||||
</menu>
|
||||
|
|
@ -83,6 +83,7 @@
|
|||
<string name="title_activity_settings">Settings</string>
|
||||
<string name="title_activity_signup">Sign Up</string>
|
||||
<string name="title_activity_featured_images">Featured Images</string>
|
||||
<string name="title_activity_review">Peer Review</string>
|
||||
<string name="menu_about">About</string>
|
||||
<string name="about_license">The Wikimedia Commons app is an open-source app created and maintained by grantees and volunteers of the Wikimedia community. The Wikimedia Foundation is not involved in the creation, development, or maintenance of the app. </string>
|
||||
<string name="trademarked_name" translatable="false">Wikimedia Commons</string>
|
||||
|
|
@ -219,6 +220,7 @@
|
|||
<string name="navigation_item_info">Tutorial</string>
|
||||
<string name="navigation_item_notification">Notifications</string>
|
||||
<string name="navigation_item_featured_images">Featured</string>
|
||||
<string name="navigation_item_review">Review</string>
|
||||
<string name="nearby_needs_permissions">Nearby places cannot be displayed without location permissions</string>
|
||||
<string name="no_description_found">no description found</string>
|
||||
<string name="nearby_info_menu_commons_article">Commons file page</string>
|
||||
|
|
@ -239,6 +241,8 @@
|
|||
<string name="null_url">Error! URL not found</string>
|
||||
<string name="nominate_deletion">Nominate for Deletion</string>
|
||||
<string name="nominated_for_deletion">This image has been nominated for deletion.</string>
|
||||
<string name="nominating_file_for_deletion">Nominating %1$s for deletion.</string>
|
||||
<string name="nominating_for_deletion_status">Nominating file for deletion: %1$s</string>
|
||||
<string name="nominated_see_more"><u>See webpage for details</u></string>
|
||||
<string name="view_browser">View in Browser</string>
|
||||
|
||||
|
|
@ -283,5 +287,31 @@
|
|||
<string name="share_app_title">Share App</string>
|
||||
<string name="share_coordinates_not_present">Coordinates were not specified during image selection</string>
|
||||
<string name="error_fetching_nearby_places">Error fetching nearby places.</string>
|
||||
<string name="getting_edit_token">Getting token for editing</string>
|
||||
<string name="check_category_adding_template">Adding template for category check</string>
|
||||
<string name="check_category_notification_title">Requesting category check for %1$s</string>
|
||||
<string name="check_category_edit_summary">Requesting category check</string>
|
||||
<string name="check_category_success_title">Requesting category check: Success</string>
|
||||
<string name="check_category_failure_title">Requesting category check: Failed</string>
|
||||
<string name="check_category_success_message">Successfully requested category check for %1$s</string>
|
||||
<string name="check_category_failure_message">Could not request category check for %1$s</string>
|
||||
<string name="check_category_toast">Requesting category check for %1$s</string>
|
||||
<string name="nominate_for_deletion_edit_file_page">Adding delete message to file</string>
|
||||
<string name="nominate_for_deletion_done">Done</string>
|
||||
<string name="nominate_for_deletion_notify_user">Notifying User on Talk page</string>
|
||||
<string name="nominate_for_deletion_edit_deletion_request_log">Adding file to Delete requests log</string>
|
||||
<string name="nominate_for_deletion_create_deletion_request">Creating Delete requests subpage</string>
|
||||
<string name="notsure">Not sure</string>
|
||||
<string name="send_thank_success_title">Sending Thanks: Success</string>
|
||||
<string name="send_thank_success_message">Successfully sent thanks to %1$s</string>
|
||||
<string name="send_thank_failure_message">Failed to send thanks %1$s</string>
|
||||
<string name="send_thank_failure_title">Sending Thanks: Failure</string>
|
||||
<string name="send_thank_send">Sending thanks</string>
|
||||
<string name="send_thank_notification_title">Sending thanks</string>
|
||||
<string name="send_thank_toast">Sending Thanks for %1$s</string>
|
||||
|
||||
<string name="review_copyright">Is this a copyright violation?</string>
|
||||
<string name="review_category">Is this mis-categorized?</string>
|
||||
<string name="review_spam">Is this spam?</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue