mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 21:03:54 +01:00
Synced branch to master
This commit is contained in:
commit
6b96b65203
123 changed files with 1667 additions and 930 deletions
|
|
@ -13,7 +13,6 @@ import android.content.pm.PackageManager;
|
|||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.util.LruCache;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.stetho.Stetho;
|
||||
|
|
@ -35,7 +34,6 @@ import fr.free.nrw.commons.modifications.ModifierSequence;
|
|||
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.nearby.NearbyPlaces;
|
||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||
import fr.free.nrw.commons.utils.FileUtils;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
|
@ -51,12 +49,6 @@ import timber.log.Timber;
|
|||
public class CommonsApplication extends Application {
|
||||
|
||||
private Account currentAccount = null; // Unlike a savings account...
|
||||
public static final String API_URL = "https://commons.wikimedia.org/w/api.php";
|
||||
public static final String IMAGE_URL_BASE = "https://upload.wikimedia.org/wikipedia/commons";
|
||||
public static final String HOME_URL = "https://commons.wikimedia.org/wiki/";
|
||||
public static final String MOBILE_HOME_URL = "https://commons.m.wikimedia.org/wiki/";
|
||||
public static final String EVENTLOG_URL = "https://www.wikimedia.org/beacon/event";
|
||||
public static final String EVENTLOG_WIKI = "commonswiki";
|
||||
|
||||
public static final Object[] EVENT_UPLOAD_ATTEMPT = {"MobileAppUploadAttempts", 5334329L};
|
||||
public static final Object[] EVENT_LOGIN_ATTEMPT = {"MobileAppLoginAttempts", 5257721L};
|
||||
|
|
@ -92,7 +84,7 @@ public class CommonsApplication extends Application {
|
|||
|
||||
public MediaWikiApi getMWApi() {
|
||||
if (api == null) {
|
||||
api = new ApacheHttpClientMediaWikiApi(API_URL);
|
||||
api = new ApacheHttpClientMediaWikiApi(BuildConfig.WIKIMEDIA_API_HOST);
|
||||
}
|
||||
return api;
|
||||
}
|
||||
|
|
@ -155,10 +147,10 @@ public class CommonsApplication extends Application {
|
|||
* @return Account|null
|
||||
*/
|
||||
public Account getCurrentAccount() {
|
||||
if(currentAccount == null) {
|
||||
if (currentAccount == null) {
|
||||
AccountManager accountManager = AccountManager.get(this);
|
||||
Account[] allAccounts = accountManager.getAccountsByType(AccountUtil.accountType());
|
||||
if(allAccounts.length != 0) {
|
||||
if (allAccounts.length != 0) {
|
||||
currentAccount = allAccounts[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -169,7 +161,7 @@ public class CommonsApplication extends Application {
|
|||
AccountManager accountManager = AccountManager.get(this);
|
||||
Account curAccount = getCurrentAccount();
|
||||
|
||||
if(curAccount == null) {
|
||||
if (curAccount == null) {
|
||||
return false; // This should never happen
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +182,7 @@ public class CommonsApplication extends Application {
|
|||
pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
|
||||
}
|
||||
|
||||
public void clearApplicationData(Context context, NavigationBaseActivity.LogoutListener logoutListener) {
|
||||
public void clearApplicationData(Context context, LogoutListener logoutListener) {
|
||||
File cacheDirectory = context.getCacheDir();
|
||||
File applicationDirectory = new File(cacheDirectory.getParent());
|
||||
if (applicationDirectory.exists()) {
|
||||
|
|
@ -222,10 +214,8 @@ public class CommonsApplication extends Application {
|
|||
setIndex(getIndex() + 1);
|
||||
|
||||
try {
|
||||
if (accountManagerFuture != null) {
|
||||
if (accountManagerFuture.getResult()) {
|
||||
Timber.d("Account removed successfully.");
|
||||
}
|
||||
if (accountManagerFuture != null && accountManagerFuture.getResult()) {
|
||||
Timber.d("Account removed successfully.");
|
||||
}
|
||||
} catch (OperationCanceledException | IOException | AuthenticatorException e) {
|
||||
e.printStackTrace();
|
||||
|
|
@ -234,11 +224,13 @@ public class CommonsApplication extends Application {
|
|||
if (getIndex() == allAccounts.length) {
|
||||
Timber.d("All accounts have been removed");
|
||||
//TODO: fix preference manager
|
||||
PreferenceManager.getDefaultSharedPreferences(getInstance()).edit().clear().commit();
|
||||
PreferenceManager.getDefaultSharedPreferences(getInstance())
|
||||
.edit().clear().commit();
|
||||
SharedPreferences preferences = context
|
||||
.getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE);
|
||||
preferences.edit().clear().commit();
|
||||
context.getSharedPreferences("prefs", Context.MODE_PRIVATE).edit().clear().commit();
|
||||
context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
|
||||
.edit().clear().commit();
|
||||
preferences.edit().putBoolean("firstrun", false).apply();
|
||||
updateAllDatabases();
|
||||
currentAccount = null;
|
||||
|
|
@ -265,4 +257,8 @@ public class CommonsApplication extends Application {
|
|||
Category.Table.onDelete(db);
|
||||
Contribution.Table.onDelete(db);
|
||||
}
|
||||
|
||||
public interface LogoutListener {
|
||||
void onLogoutComplete();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ package fr.free.nrw.commons;
|
|||
import android.support.annotation.Nullable;
|
||||
|
||||
public class License {
|
||||
String key;
|
||||
String template;
|
||||
String url;
|
||||
String name;
|
||||
private String key;
|
||||
private String template;
|
||||
private String url;
|
||||
private String name;
|
||||
|
||||
public License(String key, String template, String url, String name) {
|
||||
if (key == null) {
|
||||
|
|
|
|||
|
|
@ -10,18 +10,16 @@ import java.util.Collection;
|
|||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class LicenseList {
|
||||
Map<String, License> licenses = new HashMap<>();
|
||||
Resources res;
|
||||
|
||||
private static String XMLNS_LICENSE = "https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses";
|
||||
private Map<String, License> licenses = new HashMap<>();
|
||||
private Resources res;
|
||||
|
||||
public LicenseList(Activity activity) {
|
||||
res = activity.getResources();
|
||||
XmlPullParser parser = res.getXml(R.xml.wikimedia_licenses);
|
||||
while (Utils.xmlFastForward(parser, XMLNS_LICENSE, "license")) {
|
||||
String namespace = "https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses";
|
||||
while (Utils.xmlFastForward(parser, namespace, "license")) {
|
||||
String id = parser.getAttributeValue(null, "id");
|
||||
String template = parser.getAttributeValue(null, "template");
|
||||
String url = parser.getAttributeValue(null, "url");
|
||||
|
|
@ -31,10 +29,6 @@ public class LicenseList {
|
|||
}
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return licenses.keySet();
|
||||
}
|
||||
|
||||
public Collection<License> values() {
|
||||
return licenses.values();
|
||||
}
|
||||
|
|
@ -44,7 +38,7 @@ public class LicenseList {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
public License licenseForTemplate(String template) {
|
||||
License licenseForTemplate(String template) {
|
||||
String ucTemplate = new PageTitle(template).getDisplayText();
|
||||
for (License license : values()) {
|
||||
if (ucTemplate.equals(new PageTitle(license.getTemplate()).getDisplayText())) {
|
||||
|
|
@ -54,26 +48,16 @@ public class LicenseList {
|
|||
return null;
|
||||
}
|
||||
|
||||
public String nameIdForTemplate(String template) {
|
||||
private String nameIdForTemplate(String template) {
|
||||
// hack :D (converts dashes and periods to underscores)
|
||||
// cc-by-sa-3.0 -> cc_by_sa_3_0
|
||||
return "license_name_" + template.toLowerCase(Locale.ENGLISH).replace("-", "_").replace(".", "_");
|
||||
return "license_name_" + template.toLowerCase(Locale.ENGLISH).replace("-",
|
||||
"_").replace(".", "_");
|
||||
}
|
||||
|
||||
private int stringIdByName(String stringId) {
|
||||
return res.getIdentifier("fr.free.nrw.commons:string/" + stringId, null, null);
|
||||
}
|
||||
|
||||
public String nameForTemplate(String template) {
|
||||
//Log.d("Commons", "LicenseList.nameForTemplate: template: " + template);
|
||||
String stringId = nameIdForTemplate(template);
|
||||
//Log.d("Commons", "LicenseList.nameForTemplate: stringId: " + stringId);
|
||||
int nameId = stringIdByName(stringId);
|
||||
//Log.d("Commons", "LicenseList.nameForTemplate: nameId: " + nameId);
|
||||
if(nameId != 0) {
|
||||
//Log.d("Commons", "LicenseList.nameForTemplate: name: " + name);
|
||||
return res.getString(nameId);
|
||||
}
|
||||
return template;
|
||||
private String nameForTemplate(String template) {
|
||||
int nameId = res.getIdentifier("fr.free.nrw.commons:string/"
|
||||
+ nameIdForTemplate(template), null, null);
|
||||
return (nameId != 0) ? res.getString(nameId) : template;
|
||||
}
|
||||
}
|
||||
|
|
@ -29,12 +29,66 @@ public class Media implements Parcelable {
|
|||
}
|
||||
};
|
||||
|
||||
private static Pattern displayTitlePattern = Pattern.compile("(.*)(\\.\\w+)", Pattern.CASE_INSENSITIVE);
|
||||
// Primary metadata fields
|
||||
protected Uri localUri;
|
||||
protected String imageUrl;
|
||||
protected String filename;
|
||||
protected String description; // monolingual description on input...
|
||||
protected long dataLength;
|
||||
protected Date dateCreated;
|
||||
protected @Nullable Date dateUploaded;
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected String license;
|
||||
protected String creator;
|
||||
protected ArrayList<String> categories; // as loaded at runtime?
|
||||
private Map<String, String> descriptions; // multilingual descriptions as loaded
|
||||
private HashMap<String, Object> tags = new HashMap<>();
|
||||
private @Nullable LatLng coordinates;
|
||||
|
||||
protected Media() {
|
||||
this.categories = new ArrayList<>();
|
||||
this.descriptions = new HashMap<>();
|
||||
}
|
||||
|
||||
private HashMap<String, Object> tags = new HashMap<>();
|
||||
public Media(String filename) {
|
||||
this();
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public Media(Uri localUri, String imageUrl, String filename, String description,
|
||||
long dataLength, Date dateCreated, @Nullable Date dateUploaded, String creator) {
|
||||
this();
|
||||
this.localUri = localUri;
|
||||
this.imageUrl = imageUrl;
|
||||
this.filename = filename;
|
||||
this.description = description;
|
||||
this.dataLength = dataLength;
|
||||
this.dateCreated = dateCreated;
|
||||
this.dateUploaded = dateUploaded;
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Media(Parcel in) {
|
||||
localUri = in.readParcelable(Uri.class.getClassLoader());
|
||||
imageUrl = in.readString();
|
||||
filename = in.readString();
|
||||
description = in.readString();
|
||||
dataLength = in.readLong();
|
||||
dateCreated = (Date) in.readSerializable();
|
||||
dateUploaded = (Date) in.readSerializable();
|
||||
creator = in.readString();
|
||||
tags = (HashMap<String, Object>) in.readSerializable();
|
||||
width = in.readInt();
|
||||
height = in.readInt();
|
||||
license = in.readString();
|
||||
if (categories != null) {
|
||||
in.readStringList(categories);
|
||||
}
|
||||
descriptions = in.readHashMap(ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
|
||||
public Object getTag(String key) {
|
||||
return tags.get(key);
|
||||
|
|
@ -44,15 +98,14 @@ public class Media implements Parcelable {
|
|||
tags.put(key, value);
|
||||
}
|
||||
|
||||
public static Pattern displayTitlePattern = Pattern.compile("(.*)(\\.\\w+)", Pattern.CASE_INSENSITIVE);
|
||||
public String getDisplayTitle() {
|
||||
if(filename == null) {
|
||||
public String getDisplayTitle() {
|
||||
if (filename == null) {
|
||||
return "";
|
||||
}
|
||||
// FIXME: Gross hack bercause my regex skills suck maybe or I am too lazy who knows
|
||||
String title = getFilePageTitle().getDisplayText().replaceFirst("^File:", "");
|
||||
Matcher matcher = displayTitlePattern.matcher(title);
|
||||
if(matcher.matches()) {
|
||||
if (matcher.matches()) {
|
||||
return matcher.group(1);
|
||||
} else {
|
||||
return title;
|
||||
|
|
@ -68,7 +121,7 @@ public class Media implements Parcelable {
|
|||
}
|
||||
|
||||
public String getImageUrl() {
|
||||
if(imageUrl == null) {
|
||||
if (imageUrl == null) {
|
||||
imageUrl = Utils.makeThumbBaseUrl(this.getFilename());
|
||||
}
|
||||
return imageUrl;
|
||||
|
|
@ -86,6 +139,10 @@ public class Media implements Parcelable {
|
|||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public long getDataLength() {
|
||||
return dataLength;
|
||||
}
|
||||
|
|
@ -102,7 +159,8 @@ public class Media implements Parcelable {
|
|||
this.dateCreated = date;
|
||||
}
|
||||
|
||||
public @Nullable Date getDateUploaded() {
|
||||
public @Nullable
|
||||
Date getDateUploaded() {
|
||||
return dateUploaded;
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +196,8 @@ public class Media implements Parcelable {
|
|||
this.license = license;
|
||||
}
|
||||
|
||||
public @Nullable LatLng getCoordinates() {
|
||||
public @Nullable
|
||||
LatLng getCoordinates() {
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
|
|
@ -146,24 +205,9 @@ public class Media implements Parcelable {
|
|||
this.coordinates = coordinates;
|
||||
}
|
||||
|
||||
// Primary metadata fields
|
||||
protected Uri localUri;
|
||||
protected String imageUrl;
|
||||
protected String filename;
|
||||
protected String description; // monolingual description on input...
|
||||
protected long dataLength;
|
||||
protected Date dateCreated;
|
||||
protected @Nullable Date dateUploaded;
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected String license;
|
||||
private @Nullable LatLng coordinates;
|
||||
protected String creator;
|
||||
protected ArrayList<String> categories; // as loaded at runtime?
|
||||
protected Map<String, String> descriptions; // multilingual descriptions as loaded
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ArrayList<String> getCategories() {
|
||||
return (ArrayList<String>)categories.clone(); // feels dirty
|
||||
return (ArrayList<String>) categories.clone(); // feels dirty
|
||||
}
|
||||
|
||||
public void setCategories(List<String> categories) {
|
||||
|
|
@ -171,7 +215,7 @@ public class Media implements Parcelable {
|
|||
this.categories.addAll(categories);
|
||||
}
|
||||
|
||||
public void setDescriptions(Map<String,String> descriptions) {
|
||||
void setDescriptions(Map<String, String> descriptions) {
|
||||
for (String key : this.descriptions.keySet()) {
|
||||
this.descriptions.remove(key);
|
||||
}
|
||||
|
|
@ -196,23 +240,6 @@ public class Media implements Parcelable {
|
|||
}
|
||||
}
|
||||
|
||||
public Media(String filename) {
|
||||
this();
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public Media(Uri localUri, String imageUrl, String filename, String description, long dataLength, Date dateCreated, @Nullable Date dateUploaded, String creator) {
|
||||
this();
|
||||
this.localUri = localUri;
|
||||
this.imageUrl = imageUrl;
|
||||
this.filename = filename;
|
||||
this.description = description;
|
||||
this.dataLength = dataLength;
|
||||
this.dateCreated = dateCreated;
|
||||
this.dateUploaded = dateUploaded;
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
|
|
@ -235,27 +262,4 @@ public class Media implements Parcelable {
|
|||
parcel.writeStringList(categories);
|
||||
parcel.writeMap(descriptions);
|
||||
}
|
||||
|
||||
public Media(Parcel in) {
|
||||
localUri = in.readParcelable(Uri.class.getClassLoader());
|
||||
imageUrl = in.readString();
|
||||
filename = in.readString();
|
||||
description = in.readString();
|
||||
dataLength = in.readLong();
|
||||
dateCreated = (Date) in.readSerializable();
|
||||
dateUploaded = (Date) in.readSerializable();
|
||||
creator = in.readString();
|
||||
tags = (HashMap<String, Object>)in.readSerializable();
|
||||
width = in.readInt();
|
||||
height = in.readInt();
|
||||
license = in.readString();
|
||||
if (categories != null) {
|
||||
in.readStringList(categories);
|
||||
}
|
||||
descriptions = in.readHashMap(ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package fr.free.nrw.commons;
|
|||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.mediawiki.api.ApiResult;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ public class PageTitle {
|
|||
*/
|
||||
@NonNull
|
||||
public Uri getCanonicalUri() {
|
||||
String uriStr = CommonsApplication.HOME_URL + Uri.encode(getPrefixedText(), ":/");
|
||||
String uriStr = BuildConfig.HOME_URL + Uri.encode(getPrefixedText(), ":/");
|
||||
return Uri.parse(uriStr);
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ public class PageTitle {
|
|||
*/
|
||||
@NonNull
|
||||
public Uri getMobileUri() {
|
||||
String uriStr = CommonsApplication.MOBILE_HOME_URL + Uri.encode(getPrefixedText(), ":/");
|
||||
String uriStr = BuildConfig.MOBILE_HOME_URL + Uri.encode(getPrefixedText(), ":/");
|
||||
return Uri.parse(uriStr);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ import javax.xml.transform.stream.StreamResult;
|
|||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
public class Utils {
|
||||
|
||||
// Get SHA1 of file from input stream
|
||||
|
|
@ -80,10 +79,12 @@ public class Utils {
|
|||
}
|
||||
}
|
||||
|
||||
/** Fix Html.fromHtml is deprecated problem
|
||||
/**
|
||||
* Fix Html.fromHtml is deprecated problem
|
||||
*
|
||||
* @param source provided Html string
|
||||
* @return returned Spanned of appropriate method according to version check
|
||||
* */
|
||||
*/
|
||||
public static Spanned fromHtml(String source) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
return Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY);
|
||||
|
|
@ -127,7 +128,7 @@ public class Utils {
|
|||
public static String makeThumbBaseUrl(String filename) {
|
||||
String name = new PageTitle(filename).getPrefixedText();
|
||||
String sha = new String(Hex.encodeHex(DigestUtils.md5(name)));
|
||||
return String.format("%s/%s/%s/%s", CommonsApplication.IMAGE_URL_BASE, sha.substring(0, 1), sha.substring(0, 2), urlEncode(name));
|
||||
return String.format("%s/%s/%s/%s", BuildConfig.IMAGE_URL_BASE, sha.substring(0, 1), sha.substring(0, 2), urlEncode(name));
|
||||
}
|
||||
|
||||
public static String getStringFromDOM(Node dom) {
|
||||
|
|
@ -241,9 +242,9 @@ public class Utils {
|
|||
public static boolean xmlFastForward(XmlPullParser parser, String namespace, String element) {
|
||||
try {
|
||||
while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
||||
if (parser.getEventType() == XmlPullParser.START_TAG &&
|
||||
parser.getNamespace().equals(namespace) &&
|
||||
parser.getName().equals(element)) {
|
||||
if (parser.getEventType() == XmlPullParser.START_TAG
|
||||
&& parser.getNamespace().equals(namespace)
|
||||
&& parser.getName().equals(element)) {
|
||||
// We found it!
|
||||
return true;
|
||||
}
|
||||
|
|
@ -266,7 +267,8 @@ public class Utils {
|
|||
extension = "jpg";
|
||||
}
|
||||
title = jpegPattern.matcher(title).replaceFirst(".jpg");
|
||||
if (extension != null && !title.toLowerCase(Locale.getDefault()).endsWith("." + extension.toLowerCase(Locale.ENGLISH))) {
|
||||
if (extension != null && !title.toLowerCase(Locale.getDefault())
|
||||
.endsWith("." + extension.toLowerCase(Locale.ENGLISH))) {
|
||||
title += "." + extension;
|
||||
}
|
||||
return title;
|
||||
|
|
@ -277,6 +279,6 @@ public class Utils {
|
|||
}
|
||||
|
||||
public static boolean isDarkTheme(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("theme",false);
|
||||
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("theme", false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ import timber.log.Timber;
|
|||
|
||||
public abstract class AuthenticatedActivity extends NavigationBaseActivity {
|
||||
|
||||
String accountType;
|
||||
private String accountType;
|
||||
CommonsApplication app;
|
||||
|
||||
private String authCookie;
|
||||
|
||||
|
||||
public AuthenticatedActivity() {
|
||||
this.accountType = AccountUtil.accountType();
|
||||
}
|
||||
|
|
@ -28,9 +28,7 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
|
|||
.subscribeOn(Schedulers.io())
|
||||
.doOnError(Timber::e)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
cookie -> onAuthCookieAcquired(cookie),
|
||||
throwable -> onAuthFailure());
|
||||
.subscribe(this::onAuthCookieAcquired, throwable -> onAuthFailure());
|
||||
}
|
||||
|
||||
private void addAccount(AccountManager accountManager) {
|
||||
|
|
@ -55,24 +53,24 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
|
|||
}
|
||||
|
||||
protected void requestAuthToken() {
|
||||
if(authCookie != null) {
|
||||
if (authCookie != null) {
|
||||
onAuthCookieAcquired(authCookie);
|
||||
return;
|
||||
}
|
||||
AccountManager accountManager = AccountManager.get(this);
|
||||
Account curAccount = app.getCurrentAccount();
|
||||
if(curAccount == null) {
|
||||
if (curAccount == null) {
|
||||
addAccount(accountManager);
|
||||
} else {
|
||||
getAuthCookie(curAccount, accountManager);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
app = CommonsApplication.getInstance();
|
||||
if(savedInstanceState != null) {
|
||||
if (savedInstanceState != null) {
|
||||
authCookie = savedInstanceState.getString("authCookie");
|
||||
}
|
||||
}
|
||||
|
|
@ -84,5 +82,6 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
|
|||
}
|
||||
|
||||
protected abstract void onAuthCookieAcquired(String authCookie);
|
||||
|
||||
protected abstract void onAuthFailure();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,8 @@ import android.os.Bundle;
|
|||
import android.support.v4.app.NavUtils;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import android.webkit.WebView;
|
|||
import android.webkit.WebViewClient;
|
||||
import android.widget.Toast;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import timber.log.Timber;
|
||||
|
|
@ -24,16 +25,17 @@ public class SignupActivity extends BaseActivity {
|
|||
|
||||
webView.setWebViewClient(new MyWebViewClient());
|
||||
WebSettings webSettings = webView.getSettings();
|
||||
//Needed to refresh Captcha. Might introduce XSS vulnerabilities, but we can trust Wikimedia's site... right?
|
||||
/*Needed to refresh Captcha. Might introduce XSS vulnerabilities, but we can
|
||||
trust Wikimedia's site... right?*/
|
||||
webSettings.setJavaScriptEnabled(true);
|
||||
|
||||
webView.loadUrl("https://commons.m.wikimedia.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes");
|
||||
webView.loadUrl(BuildConfig.SIGNUP_LANDING_URL);
|
||||
}
|
||||
|
||||
private class MyWebViewClient extends WebViewClient {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
if (url.equals("https://commons.m.wikimedia.org/w/index.php?title=Main_Page&welcome=yes")) {
|
||||
if (url.equals(BuildConfig.SIGNUP_SUCCESS_REDIRECTION_URL)) {
|
||||
//Signup success, so clear cookies, notify user, and load LoginActivity again
|
||||
Timber.d("Overriding URL %s", url);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,21 +16,32 @@ import java.io.IOException;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
|
||||
import static android.accounts.AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION;
|
||||
import static android.accounts.AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE;
|
||||
import static android.accounts.AccountManager.KEY_ACCOUNT_NAME;
|
||||
import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE;
|
||||
import static android.accounts.AccountManager.KEY_AUTHTOKEN;
|
||||
import static android.accounts.AccountManager.KEY_BOOLEAN_RESULT;
|
||||
import static android.accounts.AccountManager.KEY_ERROR_CODE;
|
||||
import static android.accounts.AccountManager.KEY_ERROR_MESSAGE;
|
||||
import static android.accounts.AccountManager.KEY_INTENT;
|
||||
import static fr.free.nrw.commons.auth.LoginActivity.PARAM_USERNAME;
|
||||
|
||||
public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
||||
|
||||
private Context context;
|
||||
|
||||
public WikiAccountAuthenticator(Context context) {
|
||||
WikiAccountAuthenticator(Context context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
private Bundle unsupportedOperation() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
|
||||
bundle.putInt(KEY_ERROR_CODE, ERROR_CODE_UNSUPPORTED_OPERATION);
|
||||
|
||||
// HACK: the docs indicate that this is a required key bit it's not displayed to the user.
|
||||
bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "");
|
||||
bundle.putString(KEY_ERROR_MESSAGE, "");
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
|
@ -54,10 +65,10 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
|||
|
||||
private Bundle addAccount(AccountAuthenticatorResponse response) {
|
||||
Intent Intent = new Intent(context, LoginActivity.class);
|
||||
Intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
|
||||
Intent.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(AccountManager.KEY_INTENT, Intent);
|
||||
bundle.putParcelable(KEY_INTENT, Intent);
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
|
@ -78,14 +89,16 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
|||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
||||
//TODO add 2fa support here
|
||||
String result = api.login(username, password);
|
||||
if(result.equals("PASS")) {
|
||||
if (result.equals("PASS")) {
|
||||
return api.getAuthCookie();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
|
||||
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
|
||||
String authTokenType, Bundle options) throws NetworkErrorException {
|
||||
// Extract the username and password from the Account Manager, and ask
|
||||
// the server for an appropriate AuthToken.
|
||||
final AccountManager am = AccountManager.get(context);
|
||||
|
|
@ -101,9 +114,9 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
|||
}
|
||||
if (authCookie != null) {
|
||||
final Bundle result = new Bundle();
|
||||
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
|
||||
result.putString(AccountManager.KEY_ACCOUNT_TYPE, AccountUtil.accountType());
|
||||
result.putString(AccountManager.KEY_AUTHTOKEN, authCookie);
|
||||
result.putString(KEY_ACCOUNT_NAME, account.name);
|
||||
result.putString(KEY_ACCOUNT_TYPE, AccountUtil.accountType());
|
||||
result.putString(KEY_AUTHTOKEN, authCookie);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -112,10 +125,10 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
|||
// need to re-prompt them for their credentials. We do that by creating
|
||||
// an intent to display our AuthenticatorActivity panel.
|
||||
final Intent intent = new Intent(context, LoginActivity.class);
|
||||
intent.putExtra(LoginActivity.PARAM_USERNAME, account.name);
|
||||
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
|
||||
intent.putExtra(PARAM_USERNAME, account.name);
|
||||
intent.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
|
||||
bundle.putParcelable(KEY_INTENT, intent);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +146,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
|||
@NonNull Account account, @NonNull String[] features)
|
||||
throws NetworkErrorException {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
|
||||
bundle.putBoolean(KEY_BOOLEAN_RESULT, false);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
|
|
@ -141,8 +154,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
|||
@Override
|
||||
public Bundle updateCredentials(@NonNull AccountAuthenticatorResponse response,
|
||||
@NonNull Account account, @Nullable String authTokenType,
|
||||
@Nullable Bundle options)
|
||||
throws NetworkErrorException {
|
||||
@Nullable Bundle options) throws NetworkErrorException {
|
||||
return unsupportedOperation();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public class CacheController {
|
|||
public void cacheCategory() {
|
||||
List<String> pointCatList = new ArrayList<>();
|
||||
if (MwVolleyApi.GpsCatExists.getGpsCatExists()) {
|
||||
pointCatList.addAll(MwVolleyApi.getGpsCat());
|
||||
pointCatList.addAll(MwVolleyApi.getGpsCat());
|
||||
Timber.d("Categories being cached: %s", pointCatList);
|
||||
} else {
|
||||
Timber.d("No categories found, so no categories cached");
|
||||
|
|
|
|||
|
|
@ -31,15 +31,12 @@ class CategoriesRenderer extends Renderer<CategoryItem> {
|
|||
|
||||
@Override
|
||||
protected void hookListeners(View view) {
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
CategoryItem item = getContent();
|
||||
item.setSelected(!item.isSelected());
|
||||
checkedView.setChecked(item.isSelected());
|
||||
if (listener != null) {
|
||||
listener.categoryClicked(item);
|
||||
}
|
||||
view.setOnClickListener(v -> {
|
||||
CategoryItem item = getContent();
|
||||
item.setSelected(!item.isSelected());
|
||||
checkedView.setChecked(item.isSelected());
|
||||
if (listener != null) {
|
||||
listener.categoryClicked(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.pedrogomez.renderers.RVRendererAdapter;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
|
@ -36,6 +37,7 @@ import fr.free.nrw.commons.CommonsApplication;
|
|||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.data.Category;
|
||||
import fr.free.nrw.commons.upload.MwVolleyApi;
|
||||
import fr.free.nrw.commons.utils.StringSortingUtils;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
|
@ -97,6 +99,7 @@ public class CategorizationFragment extends Fragment {
|
|||
categoriesCache = new HashMap<>();
|
||||
if (savedInstanceState != null) {
|
||||
items.addAll(savedInstanceState.getParcelableArrayList("currentCategories"));
|
||||
//noinspection unchecked
|
||||
categoriesCache.putAll((HashMap<String, ArrayList<String>>) savedInstanceState
|
||||
.getSerializable("categoriesCache"));
|
||||
}
|
||||
|
|
@ -106,7 +109,7 @@ public class CategorizationFragment extends Fragment {
|
|||
|
||||
RxTextView.textChanges(categoriesFilter)
|
||||
.takeUntil(RxView.detaches(categoriesFilter))
|
||||
.debounce(300, TimeUnit.MILLISECONDS)
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(filter -> updateCategoryList(filter.toString()));
|
||||
return rootView;
|
||||
|
|
@ -194,16 +197,15 @@ public class CategorizationFragment extends Fragment {
|
|||
.concatWith(
|
||||
searchAll(filter)
|
||||
.mergeWith(searchCategories(filter))
|
||||
.concatWith( TextUtils.isEmpty(filter)
|
||||
.concatWith(TextUtils.isEmpty(filter)
|
||||
? defaultCategories() : Observable.empty())
|
||||
)
|
||||
.filter(categoryItem -> !containsYear(categoryItem.getName()))
|
||||
.distinct()
|
||||
.sorted(sortBySimilarity(filter))
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
s -> categoriesAdapter.add(s),
|
||||
throwable -> Timber.e(throwable),
|
||||
() -> {
|
||||
s -> categoriesAdapter.add(s), Timber::e, () -> {
|
||||
categoriesAdapter.notifyDataSetChanged();
|
||||
categoriesSearchInProgress.setVisibility(View.GONE);
|
||||
|
||||
|
|
@ -222,6 +224,12 @@ public class CategorizationFragment extends Fragment {
|
|||
);
|
||||
}
|
||||
|
||||
private Comparator<CategoryItem> sortBySimilarity(final String filter) {
|
||||
Comparator<String> stringSimilarityComparator = StringSortingUtils.sortBySimilarity(filter);
|
||||
return (firstItem, secondItem) -> stringSimilarityComparator
|
||||
.compare(firstItem.getName(), secondItem.getName());
|
||||
}
|
||||
|
||||
private List<String> getStringList(List<CategoryItem> input) {
|
||||
List<String> output = new ArrayList<>();
|
||||
for (CategoryItem item : input) {
|
||||
|
|
|
|||
|
|
@ -15,31 +15,39 @@ import fr.free.nrw.commons.data.Category;
|
|||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.content.UriMatcher.NO_MATCH;
|
||||
import static fr.free.nrw.commons.data.Category.Table.ALL_FIELDS;
|
||||
import static fr.free.nrw.commons.data.Category.Table.COLUMN_ID;
|
||||
import static fr.free.nrw.commons.data.Category.Table.TABLE_NAME;
|
||||
|
||||
public class CategoryContentProvider extends ContentProvider {
|
||||
|
||||
public static final String AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
|
||||
// For URI matcher
|
||||
private static final int CATEGORIES = 1;
|
||||
private static final int CATEGORIES_ID = 2;
|
||||
|
||||
public static final String AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
|
||||
private static final String BASE_PATH = "categories";
|
||||
|
||||
public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
|
||||
|
||||
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
|
||||
|
||||
static {
|
||||
uriMatcher.addURI(AUTHORITY, BASE_PATH, CATEGORIES);
|
||||
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID);
|
||||
}
|
||||
|
||||
private DBOpenHelper dbOpenHelper;
|
||||
|
||||
public static Uri uriForId(int id) {
|
||||
return Uri.parse(BASE_URI.toString() + "/" + id);
|
||||
}
|
||||
|
||||
private DBOpenHelper dbOpenHelper;
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
dbOpenHelper = CommonsApplication.getInstance().getDBOpenHelper();
|
||||
CommonsApplication app = ((CommonsApplication) getContext().getApplicationContext());
|
||||
dbOpenHelper = app.getDBOpenHelper();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -48,23 +56,23 @@ public class CategoryContentProvider extends ContentProvider {
|
|||
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(Category.Table.TABLE_NAME);
|
||||
queryBuilder.setTables(TABLE_NAME);
|
||||
|
||||
int uriType = uriMatcher.match(uri);
|
||||
|
||||
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
Cursor cursor;
|
||||
|
||||
switch(uriType) {
|
||||
switch (uriType) {
|
||||
case CATEGORIES:
|
||||
cursor = queryBuilder.query(db, projection, selection, selectionArgs,
|
||||
null, null, sortOrder);
|
||||
break;
|
||||
case CATEGORIES_ID:
|
||||
cursor = queryBuilder.query(db,
|
||||
Category.Table.ALL_FIELDS,
|
||||
ALL_FIELDS,
|
||||
"_id = ?",
|
||||
new String[] { uri.getLastPathSegment() },
|
||||
new String[]{uri.getLastPathSegment()},
|
||||
null,
|
||||
null,
|
||||
sortOrder
|
||||
|
|
@ -92,7 +100,7 @@ public class CategoryContentProvider extends ContentProvider {
|
|||
long id;
|
||||
switch (uriType) {
|
||||
case CATEGORIES:
|
||||
id = sqlDB.insert(Category.Table.TABLE_NAME, null, contentValues);
|
||||
id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI: " + uri);
|
||||
|
|
@ -115,9 +123,9 @@ public class CategoryContentProvider extends ContentProvider {
|
|||
sqlDB.beginTransaction();
|
||||
switch (uriType) {
|
||||
case CATEGORIES:
|
||||
for(ContentValues value: values) {
|
||||
for (ContentValues value : values) {
|
||||
Timber.d("Inserting! %s", value);
|
||||
sqlDB.insert(Category.Table.TABLE_NAME, null, value);
|
||||
sqlDB.insert(TABLE_NAME, null, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
@ -134,24 +142,25 @@ public class CategoryContentProvider extends ContentProvider {
|
|||
public int update(@NonNull Uri uri, ContentValues contentValues, String selection,
|
||||
String[] selectionArgs) {
|
||||
/*
|
||||
SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
|
||||
Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues
|
||||
SQL Injection warnings: First, note that we're not exposing this to the
|
||||
outside world (exported="false"). Even then, we should make sure to sanitize
|
||||
all user input appropriately. Input that passes through ContentValues
|
||||
should be fine. So only issues are those that pass in via concating.
|
||||
|
||||
In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise.
|
||||
In here, the only concat created argument is for id. It is cast to an int,
|
||||
and will error out otherwise.
|
||||
*/
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
int rowsUpdated;
|
||||
switch (uriType) {
|
||||
case CATEGORIES_ID:
|
||||
int id = Integer.valueOf(uri.getLastPathSegment());
|
||||
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
rowsUpdated = sqlDB.update(Category.Table.TABLE_NAME,
|
||||
int id = Integer.valueOf(uri.getLastPathSegment());
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
contentValues,
|
||||
Category.Table.COLUMN_ID + " = ?",
|
||||
new String[] { String.valueOf(id) } );
|
||||
COLUMN_ID + " = ?",
|
||||
new String[]{String.valueOf(id)});
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID");
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ public class Contribution extends Media {
|
|||
}
|
||||
|
||||
public String getPageContents() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
|
||||
|
||||
buffer
|
||||
|
|
|
|||
|
|
@ -1,30 +1,39 @@
|
|||
package fr.free.nrw.commons.contributions;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import fr.free.nrw.commons.upload.ShareActivity;
|
||||
import fr.free.nrw.commons.upload.UploadService;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class ContributionController {
|
||||
import static android.content.Intent.ACTION_GET_CONTENT;
|
||||
import static android.content.Intent.ACTION_SEND;
|
||||
import static android.content.Intent.EXTRA_STREAM;
|
||||
import static fr.free.nrw.commons.contributions.Contribution.SOURCE_CAMERA;
|
||||
import static fr.free.nrw.commons.contributions.Contribution.SOURCE_GALLERY;
|
||||
import static fr.free.nrw.commons.upload.UploadService.EXTRA_SOURCE;
|
||||
|
||||
class ContributionController {
|
||||
|
||||
private static final int SELECT_FROM_GALLERY = 1;
|
||||
private static final int SELECT_FROM_CAMERA = 2;
|
||||
|
||||
private Fragment fragment;
|
||||
private Activity activity;
|
||||
|
||||
private final static int SELECT_FROM_GALLERY = 1;
|
||||
private final static int SELECT_FROM_CAMERA = 2;
|
||||
|
||||
public ContributionController(Fragment fragment) {
|
||||
ContributionController(Fragment fragment) {
|
||||
this.fragment = fragment;
|
||||
this.activity = fragment.getActivity();
|
||||
}
|
||||
|
||||
// See http://stackoverflow.com/a/5054673/17865 for why this is done
|
||||
|
|
@ -34,43 +43,59 @@ public class ContributionController {
|
|||
File photoFile = new File(fragment.getContext().getCacheDir() + "/images",
|
||||
new Date().getTime() + ".jpg");
|
||||
photoFile.getParentFile().mkdirs();
|
||||
Context applicationContext = fragment.getActivity().getApplicationContext();
|
||||
return FileProvider.getUriForFile(
|
||||
fragment.getContext(),
|
||||
fragment.getActivity().getApplicationContext().getPackageName() + ".provider",
|
||||
applicationContext.getPackageName() + ".provider",
|
||||
photoFile);
|
||||
}
|
||||
|
||||
public void startCameraCapture() {
|
||||
private static void requestWritePermission(Context context, Intent intent, Uri uri) {
|
||||
|
||||
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent,
|
||||
PackageManager.MATCH_DEFAULT_ONLY);
|
||||
for (ResolveInfo resolveInfo : resInfoList) {
|
||||
String packageName = resolveInfo.activityInfo.packageName;
|
||||
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
void startCameraCapture() {
|
||||
|
||||
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
lastGeneratedCaptureUri = reGenerateImageCaptureUriInCache();
|
||||
takePictureIntent.setFlags(
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
|
||||
// Intent.setFlags doesn't work for API level <20
|
||||
requestWritePermission(fragment.getContext(), takePictureIntent, lastGeneratedCaptureUri);
|
||||
|
||||
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, lastGeneratedCaptureUri);
|
||||
fragment.startActivityForResult(takePictureIntent, SELECT_FROM_CAMERA);
|
||||
}
|
||||
|
||||
public void startGalleryPick() {
|
||||
//FIXME: Starts gallery (opens Google Photos)
|
||||
Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
Intent pickImageIntent = new Intent(ACTION_GET_CONTENT);
|
||||
pickImageIntent.setType("image/*");
|
||||
fragment.startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY);
|
||||
}
|
||||
|
||||
public void handleImagePicked(int requestCode, Intent data) {
|
||||
void handleImagePicked(int requestCode, Intent data) {
|
||||
FragmentActivity activity = fragment.getActivity();
|
||||
Intent shareIntent = new Intent(activity, ShareActivity.class);
|
||||
shareIntent.setAction(Intent.ACTION_SEND);
|
||||
switch(requestCode) {
|
||||
shareIntent.setAction(ACTION_SEND);
|
||||
switch (requestCode) {
|
||||
case SELECT_FROM_GALLERY:
|
||||
//Handles image picked from gallery
|
||||
Uri imageData = data.getData();
|
||||
shareIntent.setType(activity.getContentResolver().getType(imageData));
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, imageData);
|
||||
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_GALLERY);
|
||||
shareIntent.putExtra(EXTRA_STREAM, imageData);
|
||||
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY);
|
||||
break;
|
||||
case SELECT_FROM_CAMERA:
|
||||
shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, lastGeneratedCaptureUri);
|
||||
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_CAMERA);
|
||||
shareIntent.putExtra(EXTRA_STREAM, lastGeneratedCaptureUri);
|
||||
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_CAMERA);
|
||||
break;
|
||||
}
|
||||
Timber.i("Image selected");
|
||||
|
|
@ -81,12 +106,14 @@ public class ContributionController {
|
|||
}
|
||||
}
|
||||
|
||||
public void saveState(Bundle outState) {
|
||||
outState.putParcelable("lastGeneratedCaptureURI", lastGeneratedCaptureUri);
|
||||
void saveState(Bundle outState) {
|
||||
if (outState != null) {
|
||||
outState.putParcelable("lastGeneratedCaptureURI", lastGeneratedCaptureUri);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadState(Bundle savedInstanceState) {
|
||||
if(savedInstanceState != null) {
|
||||
void loadState(Bundle savedInstanceState) {
|
||||
if (savedInstanceState != null) {
|
||||
lastGeneratedCaptureUri = savedInstanceState.getParcelable("lastGeneratedCaptureURI");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package fr.free.nrw.commons.contributions;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
|
|
@ -38,13 +37,17 @@ import io.reactivex.disposables.CompositeDisposable;
|
|||
import io.reactivex.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class ContributionsActivity
|
||||
extends AuthenticatedActivity
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>,
|
||||
AdapterView.OnItemClickListener,
|
||||
MediaDetailPagerFragment.MediaDetailProvider,
|
||||
FragmentManager.OnBackStackChangedListener,
|
||||
ContributionsListFragment.SourceRefresher {
|
||||
import static android.content.ContentResolver.requestSync;
|
||||
import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
|
||||
import static fr.free.nrw.commons.contributions.Contribution.Table.ALL_FIELDS;
|
||||
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.AUTHORITY;
|
||||
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
|
||||
import static fr.free.nrw.commons.settings.Prefs.UPLOADS_SHOWING;
|
||||
|
||||
public class ContributionsActivity extends AuthenticatedActivity
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener,
|
||||
MediaDetailPagerFragment.MediaDetailProvider, FragmentManager.OnBackStackChangedListener,
|
||||
ContributionsListFragment.SourceRefresher {
|
||||
|
||||
private Cursor allContributions;
|
||||
private ContributionsListFragment contributionsList;
|
||||
|
|
@ -63,14 +66,18 @@ public class ContributionsActivity
|
|||
|
||||
This is why Contribution.STATE_COMPLETED is -1.
|
||||
*/
|
||||
private String CONTRIBUTION_SORT = Contribution.Table.COLUMN_STATE + " DESC, " + Contribution.Table.COLUMN_UPLOADED + " DESC , (" + Contribution.Table.COLUMN_TIMESTAMP + " * " + Contribution.Table.COLUMN_STATE + ")";
|
||||
private String CONTRIBUTION_SORT = Contribution.Table.COLUMN_STATE + " DESC, "
|
||||
+ Contribution.Table.COLUMN_UPLOADED + " DESC , ("
|
||||
+ Contribution.Table.COLUMN_TIMESTAMP + " * "
|
||||
+ Contribution.Table.COLUMN_STATE + ")";
|
||||
|
||||
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
||||
private ServiceConnection uploadServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName componentName, IBinder binder) {
|
||||
uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder)binder).getService();
|
||||
uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder) binder)
|
||||
.getService();
|
||||
isUploadServiceConnected = true;
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +93,7 @@ public class ContributionsActivity
|
|||
compositeDisposable.clear();
|
||||
getSupportFragmentManager().removeOnBackStackChangedListener(this);
|
||||
super.onDestroy();
|
||||
if(isUploadServiceConnected) {
|
||||
if (isUploadServiceConnected) {
|
||||
unbindService(uploadServiceConnection);
|
||||
}
|
||||
}
|
||||
|
|
@ -96,9 +103,9 @@ public class ContributionsActivity
|
|||
super.onResume();
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
boolean isSettingsChanged =
|
||||
sharedPreferences.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED,false);
|
||||
sharedPreferences.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED,false);
|
||||
editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
|
||||
editor.apply();
|
||||
if (isSettingsChanged) {
|
||||
refreshSource();
|
||||
|
|
@ -107,14 +114,16 @@ public class ContributionsActivity
|
|||
|
||||
@Override
|
||||
protected void onAuthCookieAcquired(String authCookie) {
|
||||
// Do a sync everytime we get here!
|
||||
ContentResolver.requestSync(CommonsApplication.getInstance().getCurrentAccount(), ContributionsContentProvider.AUTHORITY, new Bundle());
|
||||
// Do a sync every time we get here!
|
||||
CommonsApplication app = ((CommonsApplication) getApplication());
|
||||
requestSync(app.getCurrentAccount(), AUTHORITY, new Bundle());
|
||||
Intent uploadServiceIntent = new Intent(this, UploadService.class);
|
||||
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
|
||||
startService(uploadServiceIntent);
|
||||
bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
|
||||
allContributions = getContentResolver().query(ContributionsContentProvider.BASE_URI, Contribution.Table.ALL_FIELDS, CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT);
|
||||
allContributions = getContentResolver().query(BASE_URI, ALL_FIELDS,
|
||||
CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT);
|
||||
|
||||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
|
@ -127,12 +136,13 @@ public class ContributionsActivity
|
|||
|
||||
// Activity can call methods in the fragment by acquiring a
|
||||
// reference to the Fragment from FragmentManager, using findFragmentById()
|
||||
contributionsList = (ContributionsListFragment)getSupportFragmentManager()
|
||||
FragmentManager supportFragmentManager = getSupportFragmentManager();
|
||||
contributionsList = (ContributionsListFragment) supportFragmentManager
|
||||
.findFragmentById(R.id.contributionsListFragment);
|
||||
|
||||
getSupportFragmentManager().addOnBackStackChangedListener(this);
|
||||
supportFragmentManager.addOnBackStackChangedListener(this);
|
||||
if (savedInstanceState != null) {
|
||||
mediaDetails = (MediaDetailPagerFragment)getSupportFragmentManager()
|
||||
mediaDetails = (MediaDetailPagerFragment) supportFragmentManager
|
||||
.findFragmentById(R.id.contributionsFragmentContainer);
|
||||
}
|
||||
requestAuthToken();
|
||||
|
|
@ -143,21 +153,25 @@ public class ContributionsActivity
|
|||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putBoolean("mediaDetailsVisible", (mediaDetails != null && mediaDetails.isVisible()));
|
||||
boolean mediaDetailsVisible = mediaDetails != null && mediaDetails.isVisible();
|
||||
outState.putBoolean("mediaDetailsVisible", mediaDetailsVisible);
|
||||
}
|
||||
|
||||
/** Replace whatever is in the current contributionsFragmentContainer view with mediaDetailPagerFragment,
|
||||
/ and preserve previous state in back stack.
|
||||
/ Called when user selects a contribution. */
|
||||
/**
|
||||
* Replace whatever is in the current contributionsFragmentContainer view with
|
||||
* mediaDetailPagerFragment, and preserve previous state in back stack.
|
||||
* Called when user selects a contribution.
|
||||
*/
|
||||
private void showDetail(int i) {
|
||||
if(mediaDetails == null ||!mediaDetails.isVisible()) {
|
||||
if (mediaDetails == null || !mediaDetails.isVisible()) {
|
||||
mediaDetails = new MediaDetailPagerFragment();
|
||||
this.getSupportFragmentManager()
|
||||
FragmentManager supportFragmentManager = getSupportFragmentManager();
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.contributionsFragmentContainer, mediaDetails)
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
this.getSupportFragmentManager().executePendingTransactions();
|
||||
supportFragmentManager.executePendingTransactions();
|
||||
}
|
||||
mediaDetails.showImage(i);
|
||||
}
|
||||
|
|
@ -165,7 +179,7 @@ public class ContributionsActivity
|
|||
public void retryUpload(int i) {
|
||||
allContributions.moveToPosition(i);
|
||||
Contribution c = Contribution.fromCursor(allContributions);
|
||||
if(c.getState() == Contribution.STATE_FAILED) {
|
||||
if (c.getState() == STATE_FAILED) {
|
||||
uploadService.queue(UploadService.ACTION_UPLOAD_FILE, c);
|
||||
Timber.d("Restarting for %s", c.toContentValues());
|
||||
} else {
|
||||
|
|
@ -176,9 +190,9 @@ public class ContributionsActivity
|
|||
public void deleteUpload(int i) {
|
||||
allContributions.moveToPosition(i);
|
||||
Contribution c = Contribution.fromCursor(allContributions);
|
||||
if(c.getState() == Contribution.STATE_FAILED) {
|
||||
if (c.getState() == STATE_FAILED) {
|
||||
Timber.d("Deleting failed contrib %s", c.toContentValues());
|
||||
c.setContentProviderClient(getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY));
|
||||
c.setContentProviderClient(getContentResolver().acquireContentProviderClient(AUTHORITY));
|
||||
c.delete();
|
||||
} else {
|
||||
Timber.d("Skipping deletion for non-failed contrib %s", c.toContentValues());
|
||||
|
|
@ -187,9 +201,9 @@ public class ContributionsActivity
|
|||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
if(mediaDetails.isVisible()) {
|
||||
if (mediaDetails.isVisible()) {
|
||||
getSupportFragmentManager().popBackStack();
|
||||
}
|
||||
return true;
|
||||
|
|
@ -215,21 +229,20 @@ public class ContributionsActivity
|
|||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||
SharedPreferences sharedPref =
|
||||
PreferenceManager.getDefaultSharedPreferences(this);
|
||||
int uploads = sharedPref.getInt(Prefs.UPLOADS_SHOWING, 100);
|
||||
return new CursorLoader(this, ContributionsContentProvider.BASE_URI,
|
||||
Contribution.Table.ALL_FIELDS, CONTRIBUTION_SELECTION, null,
|
||||
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
int uploads = sharedPref.getInt(UPLOADS_SHOWING, 100);
|
||||
return new CursorLoader(this, BASE_URI,
|
||||
ALL_FIELDS, CONTRIBUTION_SELECTION, null,
|
||||
CONTRIBUTION_SORT + "LIMIT " + uploads);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||
if(contributionsList.getAdapter() == null) {
|
||||
contributionsList
|
||||
.setAdapter(new ContributionsListAdapter(getApplicationContext(), cursor, 0));
|
||||
if (contributionsList.getAdapter() == null) {
|
||||
contributionsList.setAdapter(new ContributionsListAdapter(getApplicationContext(),
|
||||
cursor, 0));
|
||||
} else {
|
||||
((CursorAdapter)contributionsList.getAdapter()).swapCursor(cursor);
|
||||
((CursorAdapter) contributionsList.getAdapter()).swapCursor(cursor);
|
||||
}
|
||||
|
||||
setUploadCount();
|
||||
|
|
@ -249,34 +262,32 @@ public class ContributionsActivity
|
|||
if (contributionsList.getAdapter() == null) {
|
||||
// not yet ready to return data
|
||||
return null;
|
||||
} else {
|
||||
} else {
|
||||
return Contribution.fromCursor((Cursor) contributionsList.getAdapter().getItem(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalMediaCount() {
|
||||
if(contributionsList.getAdapter() == null) {
|
||||
if (contributionsList.getAdapter() == null) {
|
||||
return 0;
|
||||
}
|
||||
return contributionsList.getAdapter().getCount();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void setUploadCount() {
|
||||
CommonsApplication application = CommonsApplication.getInstance();
|
||||
|
||||
CommonsApplication app = ((CommonsApplication) getApplication());
|
||||
compositeDisposable.add(
|
||||
CommonsApplication.getInstance().getMWApi()
|
||||
.getUploadCount(application.getCurrentAccount().name)
|
||||
app.getMWApi()
|
||||
.getUploadCount(app.getCurrentAccount().name)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
uploadCount ->
|
||||
getSupportActionBar().setSubtitle(getResources()
|
||||
.getQuantityString(R.plurals.contributions_subtitle,
|
||||
uploadCount,
|
||||
uploadCount)),
|
||||
throwable -> Timber.e(throwable, "Fetching upload count failed")
|
||||
uploadCount -> getSupportActionBar().setSubtitle(getResources()
|
||||
.getQuantityString(R.plurals.contributions_subtitle,
|
||||
uploadCount, uploadCount)),
|
||||
t -> Timber.e(t, "Fetching upload count failed")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -332,8 +343,7 @@ public class ContributionsActivity
|
|||
}
|
||||
|
||||
public static void startYourself(Context context) {
|
||||
Intent contributionsIntent = new Intent(context, ContributionsActivity.class);
|
||||
context.startActivity(contributionsIntent);
|
||||
context.startActivity(new Intent(context, ContributionsActivity.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,17 +13,20 @@ import android.text.TextUtils;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class ContributionsContentProvider extends ContentProvider{
|
||||
import static android.content.UriMatcher.NO_MATCH;
|
||||
import static fr.free.nrw.commons.contributions.Contribution.Table.ALL_FIELDS;
|
||||
import static fr.free.nrw.commons.contributions.Contribution.Table.TABLE_NAME;
|
||||
|
||||
public class ContributionsContentProvider extends ContentProvider {
|
||||
|
||||
private static final int CONTRIBUTIONS = 1;
|
||||
private static final int CONTRIBUTIONS_ID = 2;
|
||||
|
||||
public static final String AUTHORITY = "fr.free.nrw.commons.contributions.contentprovider";
|
||||
private static final String BASE_PATH = "contributions";
|
||||
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
|
||||
public static final String AUTHORITY = "fr.free.nrw.commons.contributions.contentprovider";
|
||||
|
||||
public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
|
||||
|
||||
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
static {
|
||||
uriMatcher.addURI(AUTHORITY, BASE_PATH, CONTRIBUTIONS);
|
||||
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID);
|
||||
|
|
@ -38,25 +41,29 @@ public class ContributionsContentProvider extends ContentProvider{
|
|||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(Contribution.Table.TABLE_NAME);
|
||||
queryBuilder.setTables(TABLE_NAME);
|
||||
|
||||
int uriType = uriMatcher.match(uri);
|
||||
|
||||
SQLiteDatabase db = CommonsApplication.getInstance().getDBOpenHelper().getReadableDatabase();
|
||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
||||
SQLiteDatabase db = app.getDBOpenHelper().getReadableDatabase();
|
||||
Cursor cursor;
|
||||
|
||||
switch(uriType) {
|
||||
switch (uriType) {
|
||||
case CONTRIBUTIONS:
|
||||
cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
||||
cursor = queryBuilder.query(db, projection, selection, selectionArgs,
|
||||
null, null, sortOrder);
|
||||
break;
|
||||
case CONTRIBUTIONS_ID:
|
||||
cursor = queryBuilder.query(db,
|
||||
Contribution.Table.ALL_FIELDS,
|
||||
ALL_FIELDS,
|
||||
"_id = ?",
|
||||
new String[] { uri.getLastPathSegment() },
|
||||
new String[]{uri.getLastPathSegment()},
|
||||
null,
|
||||
null,
|
||||
sortOrder
|
||||
|
|
@ -76,14 +83,16 @@ public class ContributionsContentProvider extends ContentProvider{
|
|||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
|
||||
long id = 0;
|
||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
||||
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
|
||||
long id;
|
||||
switch (uriType) {
|
||||
case CONTRIBUTIONS:
|
||||
id = sqlDB.insert(Contribution.Table.TABLE_NAME, null, contentValues);
|
||||
id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI: " + uri);
|
||||
|
|
@ -92,19 +101,21 @@ public class ContributionsContentProvider extends ContentProvider{
|
|||
return Uri.parse(BASE_URI + "/" + id);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, String s, String[] strings) {
|
||||
int rows = 0;
|
||||
int rows;
|
||||
int uriType = uriMatcher.match(uri);
|
||||
|
||||
SQLiteDatabase db = CommonsApplication.getInstance().getDBOpenHelper().getReadableDatabase();
|
||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
||||
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
|
||||
|
||||
switch(uriType) {
|
||||
switch (uriType) {
|
||||
case CONTRIBUTIONS_ID:
|
||||
Timber.d("Deleting contribution id %s", uri.getLastPathSegment());
|
||||
rows = db.delete(Contribution.Table.TABLE_NAME,
|
||||
rows = sqlDB.delete(TABLE_NAME,
|
||||
"_id = ?",
|
||||
new String[] { uri.getLastPathSegment() }
|
||||
new String[]{uri.getLastPathSegment()}
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -114,17 +125,19 @@ public class ContributionsContentProvider extends ContentProvider{
|
|||
return rows;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
|
||||
Timber.d("Hello, bulk insert! (ContributionsContentProvider)");
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
|
||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
||||
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
|
||||
sqlDB.beginTransaction();
|
||||
switch (uriType) {
|
||||
case CONTRIBUTIONS:
|
||||
for(ContentValues value: values) {
|
||||
for (ContentValues value : values) {
|
||||
Timber.d("Inserting! %s", value);
|
||||
sqlDB.insert(Contribution.Table.TABLE_NAME, null, value);
|
||||
sqlDB.insert(TABLE_NAME, null, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
@ -136,35 +149,37 @@ public class ContributionsContentProvider extends ContentProvider{
|
|||
return values.length;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
|
||||
/*
|
||||
SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
|
||||
Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues
|
||||
should be fine. So only issues are those that pass in via concating.
|
||||
Even then, we should make sure to sanitize all user input appropriately.
|
||||
Input that passes through ContentValuesshould be fine. So only issues are those that pass
|
||||
in via concating.
|
||||
|
||||
In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise.
|
||||
In here, the only concat created argument is for id. It is cast to an int, and will
|
||||
error out otherwise.
|
||||
*/
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
|
||||
int rowsUpdated = 0;
|
||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
||||
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
|
||||
int rowsUpdated;
|
||||
switch (uriType) {
|
||||
case CONTRIBUTIONS:
|
||||
rowsUpdated = sqlDB.update(Contribution.Table.TABLE_NAME,
|
||||
contentValues,
|
||||
selection,
|
||||
selectionArgs);
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME, contentValues, selection, selectionArgs);
|
||||
break;
|
||||
case CONTRIBUTIONS_ID:
|
||||
int id = Integer.valueOf(uri.getLastPathSegment());
|
||||
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
rowsUpdated = sqlDB.update(Contribution.Table.TABLE_NAME,
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
contentValues,
|
||||
Contribution.Table.COLUMN_ID + " = ?",
|
||||
new String[] { String.valueOf(id) } );
|
||||
new String[]{String.valueOf(id)});
|
||||
} else {
|
||||
throw new IllegalArgumentException("Parameter `selection` should be empty when updating an ID");
|
||||
throw new IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
package fr.free.nrw.commons.contributions;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
|
@ -31,18 +29,20 @@ import fr.free.nrw.commons.nearby.NearbyActivity;
|
|||
import timber.log.Timber;
|
||||
|
||||
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
||||
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.view.View.GONE;
|
||||
|
||||
public class ContributionsListFragment extends Fragment {
|
||||
|
||||
public interface SourceRefresher {
|
||||
void refreshSource();
|
||||
}
|
||||
|
||||
@BindView(R.id.contributionsList) GridView contributionsList;
|
||||
@BindView(R.id.waitingMessage) TextView waitingMessage;
|
||||
@BindView(R.id.emptyMessage) TextView emptyMessage;
|
||||
|
||||
@BindView(R.id.contributionsList)
|
||||
GridView contributionsList;
|
||||
@BindView(R.id.waitingMessage)
|
||||
TextView waitingMessage;
|
||||
@BindView(R.id.emptyMessage)
|
||||
TextView emptyMessage;
|
||||
private ContributionController controller;
|
||||
|
||||
@Override
|
||||
|
|
@ -50,21 +50,21 @@ public class ContributionsListFragment extends Fragment {
|
|||
View v = inflater.inflate(R.layout.fragment_contributions, container, false);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener)getActivity());
|
||||
if(savedInstanceState != null) {
|
||||
contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity());
|
||||
if (savedInstanceState != null) {
|
||||
Timber.d("Scrolling to %d", savedInstanceState.getInt("grid-position"));
|
||||
contributionsList.setSelection(savedInstanceState.getInt("grid-position"));
|
||||
}
|
||||
|
||||
//TODO: Should this be in onResume?
|
||||
SharedPreferences prefs = this.getActivity().getSharedPreferences("prefs", Context.MODE_PRIVATE);
|
||||
SharedPreferences prefs = getActivity().getSharedPreferences("prefs", MODE_PRIVATE);
|
||||
String lastModified = prefs.getString("lastSyncTimestamp", "");
|
||||
Timber.d("Last Sync Timestamp: %s", lastModified);
|
||||
|
||||
if (lastModified.equals("")) {
|
||||
waitingMessage.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
waitingMessage.setVisibility(View.GONE);
|
||||
waitingMessage.setVisibility(GONE);
|
||||
}
|
||||
|
||||
return v;
|
||||
|
|
@ -93,7 +93,7 @@ public class ContributionsListFragment extends Fragment {
|
|||
//FIXME: must get the file data for Google Photos when receive the intent answer, in the onActivityResult method
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if ( resultCode == RESULT_OK ) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
controller.handleImagePicked(requestCode, data);
|
||||
|
|
@ -105,7 +105,7 @@ public class ContributionsListFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_from_gallery:
|
||||
//Gallery crashes before reach ShareActivity screen so must implement permissions check here
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
|
|
@ -113,7 +113,7 @@ public class ContributionsListFragment extends Fragment {
|
|||
// Here, thisActivity is the current activity
|
||||
if (ContextCompat.checkSelfPermission(getActivity(),
|
||||
READ_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
!= PERMISSION_GRANTED) {
|
||||
|
||||
// Should we show an explanation?
|
||||
if (shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE)) {
|
||||
|
|
@ -123,7 +123,7 @@ public class ContributionsListFragment extends Fragment {
|
|||
// sees the explanation, try again to request the permission.
|
||||
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setMessage(getString(R.string.storage_permission_rationale))
|
||||
.setMessage(getString(R.string.read_storage_permission_rationale))
|
||||
.setPositiveButton("OK", (dialog, which) -> {
|
||||
requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, 1);
|
||||
dialog.dismiss();
|
||||
|
|
@ -155,7 +155,42 @@ public class ContributionsListFragment extends Fragment {
|
|||
|
||||
return true;
|
||||
case R.id.menu_from_camera:
|
||||
controller.startCameraCapture();
|
||||
SharedPreferences sharedPref = PreferenceManager
|
||||
.getDefaultSharedPreferences(CommonsApplication.getInstance());
|
||||
boolean useExtStorage = sharedPref.getBoolean("useExternalStorage", true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && useExtStorage) {
|
||||
// Here, thisActivity is the current activity
|
||||
if (ContextCompat.checkSelfPermission(getActivity(), WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
if (shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE)) {
|
||||
// Show an explanation to the user *asynchronously* -- don't block
|
||||
// this thread waiting for the user's response! After the user
|
||||
// sees the explanation, try again to request the permission.
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setMessage(getString(R.string.write_storage_permission_rationale))
|
||||
.setPositiveButton("OK", (dialog, which) -> {
|
||||
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, 3);
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.create()
|
||||
.show();
|
||||
} else {
|
||||
// No explanation needed, we can request the permission.
|
||||
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE},
|
||||
3);
|
||||
// MY_PERMISSIONS_WRITE_EXTERNAL_STORAGE is an
|
||||
// app-defined int constant. The callback method gets the
|
||||
// result of the request.
|
||||
}
|
||||
} else {
|
||||
controller.startCameraCapture();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
controller.startCameraCapture();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
|
@ -163,14 +198,15 @@ public class ContributionsListFragment extends Fragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
|
||||
Timber.d("onRequestPermissionsResult: req code = " + " perm = " + permissions + " grant =" + grantResults);
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
Timber.d("onRequestPermissionsResult: req code = " + " perm = "
|
||||
+ permissions + " grant =" + grantResults);
|
||||
|
||||
switch (requestCode) {
|
||||
// 1 = Storage allowed when gallery selected
|
||||
case 1: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
|
||||
Timber.d("Call controller.startGalleryPick()");
|
||||
controller.startGalleryPick();
|
||||
}
|
||||
|
|
@ -178,13 +214,19 @@ public class ContributionsListFragment extends Fragment {
|
|||
break;
|
||||
// 2 = Location allowed when 'nearby places' selected
|
||||
case 2: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
|
||||
Timber.d("Location permission granted");
|
||||
Intent nearbyIntent = new Intent(getActivity(), NearbyActivity.class);
|
||||
startActivity(nearbyIntent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
Timber.d("Call controller.startCameraCapture()");
|
||||
controller.startCameraCapture();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +235,8 @@ public class ContributionsListFragment extends Fragment {
|
|||
menu.clear(); // See http://stackoverflow.com/a/8495697/17865
|
||||
inflater.inflate(R.menu.fragment_contributions_list, menu);
|
||||
|
||||
if (!CommonsApplication.getInstance().deviceHasCamera()) {
|
||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
||||
if (!app.deviceHasCamera()) {
|
||||
menu.findItem(R.id.menu_from_camera).setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -217,6 +260,10 @@ public class ContributionsListFragment extends Fragment {
|
|||
}
|
||||
|
||||
protected void clearSyncMessage() {
|
||||
waitingMessage.setVisibility(View.GONE);
|
||||
waitingMessage.setVisibility(GONE);
|
||||
}
|
||||
|
||||
public interface SourceRefresher {
|
||||
void refreshSource();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,18 @@ import fr.free.nrw.commons.mwapi.LogEventResult;
|
|||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED;
|
||||
import static fr.free.nrw.commons.contributions.Contribution.Table.COLUMN_FILENAME;
|
||||
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
|
||||
|
||||
public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
|
||||
private static final String[] existsQuery = {COLUMN_FILENAME};
|
||||
private static final String existsSelection = COLUMN_FILENAME + " = ?";
|
||||
private static final ContentValues[] EMPTY = {};
|
||||
private static int COMMIT_THRESHOLD = 10;
|
||||
|
||||
public ContributionsSyncAdapter(Context context, boolean autoInitialize) {
|
||||
super(context, autoInitialize);
|
||||
}
|
||||
|
|
@ -36,39 +46,38 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||
return limit; // FIXME: Parameterize!
|
||||
}
|
||||
|
||||
private static final String[] existsQuery = { Contribution.Table.COLUMN_FILENAME };
|
||||
private static final String existsSelection = Contribution.Table.COLUMN_FILENAME + " = ?";
|
||||
private boolean fileExists(ContentProviderClient client, String filename) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = client.query(ContributionsContentProvider.BASE_URI,
|
||||
cursor = client.query(BASE_URI,
|
||||
existsQuery,
|
||||
existsSelection,
|
||||
new String[] { filename },
|
||||
new String[]{filename},
|
||||
""
|
||||
);
|
||||
return cursor.getCount() != 0;
|
||||
return cursor != null && cursor.getCount() != 0;
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if ( cursor != null ) {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
|
||||
public void onPerformSync(Account account, Bundle bundle, String authority,
|
||||
ContentProviderClient contentProviderClient, SyncResult syncResult) {
|
||||
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
|
||||
String user = account.name;
|
||||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
||||
SharedPreferences prefs = this.getContext().getSharedPreferences("prefs", Context.MODE_PRIVATE);
|
||||
SharedPreferences prefs = getContext().getSharedPreferences("prefs", MODE_PRIVATE);
|
||||
String lastModified = prefs.getString("lastSyncTimestamp", "");
|
||||
Date curTime = new Date();
|
||||
LogEventResult result;
|
||||
Boolean done = false;
|
||||
String queryContinue = null;
|
||||
while(!done) {
|
||||
while (!done) {
|
||||
|
||||
try {
|
||||
result = api.logEvents(user, lastModified, queryContinue, getLimit());
|
||||
|
|
@ -90,19 +99,21 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||
continue;
|
||||
}
|
||||
String filename = image.getFilename();
|
||||
if(fileExists(contentProviderClient, filename)) {
|
||||
if (fileExists(contentProviderClient, filename)) {
|
||||
Timber.d("Skipping %s", filename);
|
||||
continue;
|
||||
}
|
||||
String thumbUrl = Utils.makeThumbBaseUrl(filename);
|
||||
Date dateUpdated = image.getDateUpdated();
|
||||
Contribution contrib = new Contribution(null, thumbUrl, filename, "", -1, dateUpdated, dateUpdated, user, "", "");
|
||||
contrib.setState(Contribution.STATE_COMPLETED);
|
||||
Contribution contrib = new Contribution(null, thumbUrl, filename,
|
||||
"", -1, dateUpdated, dateUpdated, user,
|
||||
"", "");
|
||||
contrib.setState(STATE_COMPLETED);
|
||||
imageValues.add(contrib.toContentValues());
|
||||
|
||||
if(imageValues.size() % COMMIT_THRESHOLD == 0) {
|
||||
if (imageValues.size() % COMMIT_THRESHOLD == 0) {
|
||||
try {
|
||||
contentProviderClient.bulkInsert(ContributionsContentProvider.BASE_URI, imageValues.toArray(new ContentValues[]{}));
|
||||
contentProviderClient.bulkInsert(BASE_URI, imageValues.toArray(EMPTY));
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
@ -110,20 +121,21 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
if(imageValues.size() != 0) {
|
||||
if (imageValues.size() != 0) {
|
||||
try {
|
||||
contentProviderClient.bulkInsert(ContributionsContentProvider.BASE_URI, imageValues.toArray(new ContentValues[]{}));
|
||||
contentProviderClient.bulkInsert(BASE_URI, imageValues.toArray(EMPTY));
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
queryContinue = result.getQueryContinue();
|
||||
if(TextUtils.isEmpty(queryContinue)) {
|
||||
if (TextUtils.isEmpty(queryContinue)) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
prefs.edit().putString("lastSyncTimestamp", Utils.toMWDate(curTime)).apply();
|
||||
Timber.d("Oh hai, everyone! Look, a kitty!");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,13 +276,13 @@ public class MediaDetailFragment extends Fragment {
|
|||
categoryContainer.removeAllViews();
|
||||
// @fixme add the category items
|
||||
for (String cat : categoryNames) {
|
||||
View catLabel = buildCatLabel(cat);
|
||||
View catLabel = buildCatLabel(cat, categoryContainer);
|
||||
categoryContainer.addView(catLabel);
|
||||
}
|
||||
}
|
||||
|
||||
private View buildCatLabel(final String catName) {
|
||||
final View item = getLayoutInflater(null).inflate(R.layout.detail_category_item, null, false);
|
||||
private View buildCatLabel(final String catName, ViewGroup categoryContainer) {
|
||||
final View item = LayoutInflater.from(getContext()).inflate(R.layout.detail_category_item, categoryContainer, false);
|
||||
final CompatTextView textView = (CompatTextView)item.findViewById(R.id.mediaDetailCategoryItemText);
|
||||
|
||||
textView.setText(catName);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
package fr.free.nrw.commons.media;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.DataSetObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
|
@ -34,23 +31,16 @@ import fr.free.nrw.commons.contributions.Contribution;
|
|||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||
import fr.free.nrw.commons.mwapi.EventLog;
|
||||
|
||||
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
||||
import static android.content.Context.DOWNLOAD_SERVICE;
|
||||
import static android.content.Intent.ACTION_VIEW;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static fr.free.nrw.commons.CommonsApplication.EVENT_SHARE_ATTEMPT;
|
||||
|
||||
public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPageChangeListener {
|
||||
|
||||
public interface MediaDetailProvider {
|
||||
Media getMediaAtPosition(int i);
|
||||
|
||||
int getTotalMediaCount();
|
||||
|
||||
void notifyDatasetChanged();
|
||||
|
||||
void registerDataSetObserver(DataSetObserver observer);
|
||||
|
||||
void unregisterDataSetObserver(DataSetObserver observer);
|
||||
}
|
||||
|
||||
private ViewPager pager;
|
||||
private Boolean editable;
|
||||
private CommonsApplication app;
|
||||
|
||||
public MediaDetailPagerFragment() {
|
||||
this(false);
|
||||
|
|
@ -61,30 +51,10 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
|||
this.editable = editable;
|
||||
}
|
||||
|
||||
//FragmentStatePagerAdapter allows user to swipe across collection of images (no. of images undetermined)
|
||||
private class MediaDetailAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
public MediaDetailAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int i) {
|
||||
if (i == 0) {
|
||||
// See bug https://code.google.com/p/android/issues/detail?id=27526
|
||||
pager.postDelayed(() -> getActivity().supportInvalidateOptionsMenu(), 5);
|
||||
}
|
||||
return MediaDetailFragment.forMedia(i, editable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return ((MediaDetailProvider)getActivity()).getTotalMediaCount();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_media_detail_pager, container, false);
|
||||
pager = (ViewPager) view.findViewById(R.id.mediaDetailsPager);
|
||||
pager.addOnPageChangeListener(this);
|
||||
|
|
@ -120,18 +90,18 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
|||
if (savedInstanceState != null) {
|
||||
editable = savedInstanceState.getBoolean("editable");
|
||||
}
|
||||
app = CommonsApplication.getInstance();
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
MediaDetailProvider provider = (MediaDetailProvider)getActivity();
|
||||
MediaDetailProvider provider = (MediaDetailProvider) getActivity();
|
||||
Media m = provider.getMediaAtPosition(pager.getCurrentItem());
|
||||
switch(item.getItemId()) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_share_current_image:
|
||||
// Share - this is just logs it, intent set in onCreateOptionsMenu, around line 252
|
||||
EventLog.schema(CommonsApplication.EVENT_SHARE_ATTEMPT)
|
||||
CommonsApplication app = (CommonsApplication) getActivity().getApplication();
|
||||
EventLog.schema(EVENT_SHARE_ATTEMPT)
|
||||
.param("username", app.getCurrentAccount().name)
|
||||
.param("filename", m.getFilename())
|
||||
.log();
|
||||
|
|
@ -139,7 +109,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
|||
case R.id.menu_browser_current_image:
|
||||
// View in browser
|
||||
Intent viewIntent = new Intent();
|
||||
viewIntent.setAction(Intent.ACTION_VIEW);
|
||||
viewIntent.setAction(ACTION_VIEW);
|
||||
viewIntent.setData(m.getFilePageTitle().getMobileUri());
|
||||
startActivity(viewIntent);
|
||||
return true;
|
||||
|
|
@ -149,12 +119,12 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
|||
return true;
|
||||
case R.id.menu_retry_current_image:
|
||||
// Retry
|
||||
((ContributionsActivity)getActivity()).retryUpload(pager.getCurrentItem());
|
||||
((ContributionsActivity) getActivity()).retryUpload(pager.getCurrentItem());
|
||||
getActivity().getSupportFragmentManager().popBackStack();
|
||||
return true;
|
||||
case R.id.menu_cancel_current_image:
|
||||
// todo: delete image
|
||||
((ContributionsActivity)getActivity()).deleteUpload(pager.getCurrentItem());
|
||||
((ContributionsActivity) getActivity()).deleteUpload(pager.getCurrentItem());
|
||||
getActivity().getSupportFragmentManager().popBackStack();
|
||||
return true;
|
||||
default:
|
||||
|
|
@ -170,7 +140,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
|||
*/
|
||||
private void downloadMedia(Media m) {
|
||||
String imageUrl = m.getImageUrl(),
|
||||
fileName = m.getFilename();
|
||||
fileName = m.getFilename();
|
||||
// Strip 'File:' from beginning of filename, we really shouldn't store it
|
||||
fileName = fileName.replaceFirst("^File:", "");
|
||||
Uri imageUri = Uri.parse(imageUrl);
|
||||
|
|
@ -185,14 +155,15 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
|||
req.allowScanningByMediaScanner();
|
||||
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !(ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {
|
||||
Snackbar.make(getView(), R.string.storage_permission_rationale,
|
||||
Snackbar.LENGTH_INDEFINITE)
|
||||
.setAction(R.string.ok, view -> ActivityCompat.requestPermissions(getActivity(),
|
||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1)).show();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& !(ContextCompat.checkSelfPermission(getContext(),
|
||||
READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED)) {
|
||||
Snackbar.make(getView(), R.string.read_storage_permission_rationale,
|
||||
Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok,
|
||||
view -> ActivityCompat.requestPermissions(getActivity(),
|
||||
new String[]{READ_EXTERNAL_STORAGE}, 1)).show();
|
||||
} else {
|
||||
final DownloadManager manager = (DownloadManager)getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
manager.enqueue(req);
|
||||
((DownloadManager) getActivity().getSystemService(DOWNLOAD_SERVICE)).enqueue(req);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +173,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
|||
menu.clear(); // see http://stackoverflow.com/a/8495697/17865
|
||||
inflater.inflate(R.menu.fragment_image_detail, menu);
|
||||
if (pager != null) {
|
||||
MediaDetailProvider provider = (MediaDetailProvider)getActivity();
|
||||
MediaDetailProvider provider = (MediaDetailProvider) getActivity();
|
||||
Media m = provider.getMediaAtPosition(pager.getCurrentItem());
|
||||
if (m != null) {
|
||||
// Enable default set of actions, then re-enable different set of actions only if it is a failed contrib
|
||||
|
|
@ -225,8 +196,8 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
|||
}
|
||||
|
||||
if (m instanceof Contribution) {
|
||||
Contribution c = (Contribution)m;
|
||||
switch(c.getState()) {
|
||||
Contribution c = (Contribution) m;
|
||||
switch (c.getState()) {
|
||||
case Contribution.STATE_FAILED:
|
||||
menu.findItem(R.id.menu_retry_current_image).setEnabled(true).setVisible(true);
|
||||
menu.findItem(R.id.menu_cancel_current_image).setEnabled(true).setVisible(true);
|
||||
|
|
@ -267,6 +238,39 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
|||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int i) {
|
||||
}
|
||||
|
||||
public interface MediaDetailProvider {
|
||||
Media getMediaAtPosition(int i);
|
||||
|
||||
int getTotalMediaCount();
|
||||
|
||||
void notifyDatasetChanged();
|
||||
|
||||
void registerDataSetObserver(DataSetObserver observer);
|
||||
|
||||
void unregisterDataSetObserver(DataSetObserver observer);
|
||||
}
|
||||
|
||||
//FragmentStatePagerAdapter allows user to swipe across collection of images (no. of images undetermined)
|
||||
private class MediaDetailAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
public MediaDetailAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int i) {
|
||||
if (i == 0) {
|
||||
// See bug https://code.google.com/p/android/issues/detail?id=27526
|
||||
pager.postDelayed(() -> getActivity().supportInvalidateOptionsMenu(), 5);
|
||||
}
|
||||
return MediaDetailFragment.forMedia(i, editable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return ((MediaDetailProvider) getActivity()).getTotalMediaCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ public class CategoryModifier extends PageModifier {
|
|||
JSONArray categories;
|
||||
categories = params.optJSONArray(PARAM_CATEGORIES);
|
||||
|
||||
StringBuffer categoriesString = new StringBuffer();
|
||||
StringBuilder categoriesString = new StringBuilder();
|
||||
for(int i=0; i < categories.length(); i++) {
|
||||
String category = categories.optString(i);
|
||||
categoriesString.append("\n[[Category:").append(category).append("]]");
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public class ModifierSequence {
|
|||
public ModifierSequence(Uri mediaUri, JSONObject data) {
|
||||
this(mediaUri);
|
||||
JSONArray modifiersJSON = data.optJSONArray("modifiers");
|
||||
for(int i=0; i< modifiersJSON.length(); i++) {
|
||||
for (int i=0; i< modifiersJSON.length(); i++) {
|
||||
modifiers.add(PageModifier.fromJSON(modifiersJSON.optJSONObject(i)));
|
||||
}
|
||||
}
|
||||
|
|
@ -41,14 +41,14 @@ public class ModifierSequence {
|
|||
}
|
||||
|
||||
public String executeModifications(String pageName, String pageContents) {
|
||||
for(PageModifier modifier: modifiers) {
|
||||
for (PageModifier modifier: modifiers) {
|
||||
pageContents = modifier.doModification(pageName, pageContents);
|
||||
}
|
||||
return pageContents;
|
||||
}
|
||||
|
||||
public String getEditSummary() {
|
||||
StringBuffer editSummary = new StringBuffer();
|
||||
StringBuilder editSummary = new StringBuilder();
|
||||
for(PageModifier modifier: modifiers) {
|
||||
editSummary.append(modifier.getEditSumary()).append(" ");
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ public class ModifierSequence {
|
|||
JSONObject data = new JSONObject();
|
||||
try {
|
||||
JSONArray modifiersJSON = new JSONArray();
|
||||
for(PageModifier modifier: modifiers) {
|
||||
for (PageModifier modifier: modifiers) {
|
||||
modifiersJSON.put(modifier.toJSON());
|
||||
}
|
||||
data.put("modifiers", modifiersJSON);
|
||||
|
|
@ -81,7 +81,8 @@ public class ModifierSequence {
|
|||
// Hardcoding column positions!
|
||||
ModifierSequence ms = null;
|
||||
try {
|
||||
ms = new ModifierSequence(Uri.parse(cursor.getString(1)), new JSONObject(cursor.getString(2)));
|
||||
ms = new ModifierSequence(Uri.parse(cursor.getString(1)),
|
||||
new JSONObject(cursor.getString(2)));
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import java.util.Collections;
|
|||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.PageTitle;
|
||||
|
|
@ -235,8 +236,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
}
|
||||
|
||||
return categories;
|
||||
})
|
||||
.flatMapObservable(list -> Observable.fromIterable(list));
|
||||
}).flatMapObservable(Observable::fromIterable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -265,15 +265,14 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
}
|
||||
|
||||
return categories;
|
||||
})
|
||||
.flatMapObservable(list -> Observable.fromIterable(list));
|
||||
}).flatMapObservable(Observable::fromIterable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Observable<String> searchTitles(String title, int searchCatsLimit) {
|
||||
return Single.fromCallable(() -> {
|
||||
ArrayList<ApiResult> categoryNodes = null;
|
||||
return Single.fromCallable((Callable<List<String>>) () -> {
|
||||
ArrayList<ApiResult> categoryNodes;
|
||||
|
||||
try {
|
||||
categoryNodes = api.action("query")
|
||||
|
|
@ -287,7 +286,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
.getNodes("/api/query/search/p/@title");
|
||||
} catch (IOException e) {
|
||||
Timber.e("Failed to obtain searchTitles", e);
|
||||
return new ArrayList();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (categoryNodes == null) {
|
||||
|
|
@ -302,8 +301,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
}
|
||||
|
||||
return titleCategories;
|
||||
})
|
||||
.flatMapObservable(list -> Observable.fromIterable(list));
|
||||
}).flatMapObservable(Observable::fromIterable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -392,7 +390,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
public UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, final ProgressListener progressListener) throws IOException {
|
||||
ApiResult result = api.upload(filename, file, dataLength, pageContents, editSummary, progressListener::onProgress);
|
||||
|
||||
Log.e("WTF", "Result: "+result.toString());
|
||||
Log.e("WTF", "Result: " +result.toString());
|
||||
|
||||
String resultStatus = result.getString("/api/upload/@result");
|
||||
if (!resultStatus.equals("Success")) {
|
||||
|
|
|
|||
|
|
@ -41,12 +41,12 @@ public class LogBuilder {
|
|||
try {
|
||||
fullData.put("schema", schema);
|
||||
fullData.put("revision", rev);
|
||||
fullData.put("wiki", CommonsApplication.EVENTLOG_WIKI);
|
||||
fullData.put("wiki", BuildConfig.EVENTLOG_WIKI);
|
||||
data.put("device", EventLog.DEVICE);
|
||||
data.put("platform", "Android/" + Build.VERSION.RELEASE);
|
||||
data.put("appversion", "Android/" + BuildConfig.VERSION_NAME);
|
||||
fullData.put("event", data);
|
||||
return new URL(CommonsApplication.EVENTLOG_URL + "?" + Utils.urlEncode(fullData.toString()) + ";");
|
||||
return new URL(BuildConfig.EVENTLOG_URL + "?" + Utils.urlEncode(fullData.toString()) + ";");
|
||||
} catch (MalformedURLException | JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import android.support.annotation.Nullable;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import com.mapbox.mapboxsdk.annotations.IconFactory;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
|
|
|||
|
|
@ -63,10 +63,10 @@ public class NearbyInfoDialog extends OverlayDialog {
|
|||
|
||||
overflowButton.setVisibility(showMenu() ? View.VISIBLE : View.GONE);
|
||||
|
||||
overflowButton.setOnClickListener(this::popupMenuListener);
|
||||
overflowButton.setOnClickListener(v -> popupMenuListener());
|
||||
}
|
||||
|
||||
private void popupMenuListener(View v) {
|
||||
private void popupMenuListener() {
|
||||
PopupMenu popupMenu = new PopupMenu(getActivity(), overflowButton);
|
||||
popupMenu.inflate(R.menu.nearby_info_dialog_options);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import android.graphics.Color;
|
|||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -13,7 +12,6 @@ import com.google.gson.Gson;
|
|||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
import com.mapbox.mapboxsdk.annotations.Marker;
|
||||
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
|
||||
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
|
|
@ -22,7 +20,6 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
|
|||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
|
||||
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
|
||||
import com.mapbox.services.android.telemetry.MapboxTelemetry;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
package fr.free.nrw.commons.settings;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,15 +24,13 @@ import fr.free.nrw.commons.nearby.NearbyActivity;
|
|||
import fr.free.nrw.commons.settings.SettingsActivity;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class NavigationBaseActivity extends BaseActivity
|
||||
public abstract class NavigationBaseActivity extends BaseActivity
|
||||
implements NavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
@BindView(R.id.toolbar)
|
||||
Toolbar toolbar;
|
||||
|
||||
@BindView(R.id.navigation_view)
|
||||
NavigationView navigationView;
|
||||
|
||||
@BindView(R.id.drawer_layout)
|
||||
DrawerLayout drawerLayout;
|
||||
|
||||
|
|
@ -121,16 +119,9 @@ public class NavigationBaseActivity extends BaseActivity
|
|||
.setMessage(R.string.logout_verification)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
((CommonsApplication) getApplicationContext())
|
||||
.clearApplicationData(NavigationBaseActivity.this, () -> {
|
||||
Timber.d("Logout complete callback received.");
|
||||
Intent nearbyIntent = new Intent(
|
||||
NavigationBaseActivity.this, LoginActivity.class);
|
||||
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(nearbyIntent);
|
||||
finish();
|
||||
});
|
||||
BaseLogoutListener logoutListener = new BaseLogoutListener();
|
||||
CommonsApplication app = (CommonsApplication) getApplication();
|
||||
app.clearApplicationData(this, logoutListener);
|
||||
})
|
||||
.setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel())
|
||||
.show();
|
||||
|
|
@ -140,7 +131,16 @@ public class NavigationBaseActivity extends BaseActivity
|
|||
}
|
||||
}
|
||||
|
||||
public interface LogoutListener {
|
||||
void onLogoutComplete();
|
||||
private class BaseLogoutListener implements CommonsApplication.LogoutListener {
|
||||
@Override
|
||||
public void onLogoutComplete() {
|
||||
Timber.d("Logout complete callback received.");
|
||||
Intent nearbyIntent = new Intent(
|
||||
NavigationBaseActivity.this, LoginActivity.class);
|
||||
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(nearbyIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public class MultipleShareActivity
|
|||
|
||||
@Override
|
||||
public int getTotalMediaCount() {
|
||||
if(photosList == null) {
|
||||
if (photosList == null) {
|
||||
return 0;
|
||||
}
|
||||
return photosList.size();
|
||||
|
|
@ -71,7 +71,7 @@ public class MultipleShareActivity
|
|||
|
||||
@Override
|
||||
public void notifyDatasetChanged() {
|
||||
if(uploadsList != null) {
|
||||
if (uploadsList != null) {
|
||||
uploadsList.notifyDatasetChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -145,7 +145,7 @@ public class MultipleShareActivity
|
|||
uploadsList.setImageOnlyMode(true);
|
||||
|
||||
categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization");
|
||||
if(categorizationFragment == null) {
|
||||
if (categorizationFragment == null) {
|
||||
categorizationFragment = new CategorizationFragment();
|
||||
}
|
||||
// FIXME: Stops the keyboard from being shown 'stale' while moving out of this fragment into the next
|
||||
|
|
@ -162,7 +162,7 @@ public class MultipleShareActivity
|
|||
|
||||
@Override
|
||||
public void onCategoriesSave(List<String> categories) {
|
||||
if(categories.size() > 0) {
|
||||
if (categories.size() > 0) {
|
||||
ContentProviderClient client = getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY);
|
||||
for(Contribution contribution: photosList) {
|
||||
ModifierSequence categoriesSequence = new ModifierSequence(contribution.getContentUri());
|
||||
|
|
@ -191,7 +191,7 @@ public class MultipleShareActivity
|
|||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
if(mediaDetails.isVisible()) {
|
||||
if (mediaDetails.isVisible()) {
|
||||
getSupportFragmentManager().popBackStack();
|
||||
}
|
||||
return true;
|
||||
|
|
@ -209,7 +209,7 @@ public class MultipleShareActivity
|
|||
ButterKnife.bind(this);
|
||||
initDrawer();
|
||||
|
||||
if(savedInstanceState != null) {
|
||||
if (savedInstanceState != null) {
|
||||
photosList = savedInstanceState.getParcelableArrayList("uploadsList");
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ public class MultipleShareActivity
|
|||
}
|
||||
|
||||
private void showDetail(int i) {
|
||||
if(mediaDetails == null ||!mediaDetails.isVisible()) {
|
||||
if (mediaDetails == null ||!mediaDetails.isVisible()) {
|
||||
mediaDetails = new MediaDetailPagerFragment(true);
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
|
|
@ -248,8 +248,8 @@ public class MultipleShareActivity
|
|||
app.getMWApi().setAuthCookie(authCookie);
|
||||
Intent intent = getIntent();
|
||||
|
||||
if(intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
|
||||
if(photosList == null) {
|
||||
if (intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
|
||||
if (photosList == null) {
|
||||
photosList = new ArrayList<>();
|
||||
ArrayList<Uri> urisList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
for(int i=0; i < urisList.size(); i++) {
|
||||
|
|
@ -265,7 +265,7 @@ public class MultipleShareActivity
|
|||
}
|
||||
|
||||
uploadsList = (MultipleUploadListFragment) getSupportFragmentManager().findFragmentByTag("uploadsList");
|
||||
if(uploadsList == null) {
|
||||
if (uploadsList == null) {
|
||||
uploadsList = new MultipleUploadListFragment();
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
|
|
@ -287,7 +287,7 @@ public class MultipleShareActivity
|
|||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
if(categorizationFragment != null && categorizationFragment.isVisible()) {
|
||||
if (categorizationFragment != null && categorizationFragment.isVisible()) {
|
||||
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT)
|
||||
.param("username", app.getCurrentAccount().name)
|
||||
.param("categories-count", categorizationFragment.getCurrentSelectedCount())
|
||||
|
|
@ -307,7 +307,7 @@ public class MultipleShareActivity
|
|||
|
||||
@Override
|
||||
public void onBackStackChanged() {
|
||||
if(mediaDetails != null && mediaDetails.isVisible()) {
|
||||
if (mediaDetails != null && mediaDetails.isVisible()) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
} else {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
|
|
|
|||
|
|
@ -77,8 +77,8 @@ public class MultipleUploadListFragment extends Fragment {
|
|||
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||
UploadHolderView holder;
|
||||
|
||||
if(view == null) {
|
||||
view = getLayoutInflater(null).inflate(R.layout.layout_upload_item, null);
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(getContext()).inflate(R.layout.layout_upload_item, viewGroup, false);
|
||||
holder = new UploadHolderView();
|
||||
holder.image = (SimpleDraweeView) view.findViewById(R.id.uploadImage);
|
||||
holder.title = (TextView) view.findViewById(R.id.uploadTitle);
|
||||
|
|
@ -94,17 +94,17 @@ public class MultipleUploadListFragment extends Fragment {
|
|||
.build());
|
||||
view.setTag(holder);
|
||||
} else {
|
||||
holder = (UploadHolderView)view.getTag();
|
||||
holder = (UploadHolderView) view.getTag();
|
||||
}
|
||||
|
||||
Contribution up = (Contribution)this.getItem(i);
|
||||
Contribution up = (Contribution) this.getItem(i);
|
||||
|
||||
if(holder.imageUri == null || !holder.imageUri.equals(up.getLocalUri())) {
|
||||
if (holder.imageUri == null || !holder.imageUri.equals(up.getLocalUri())) {
|
||||
holder.image.setImageURI(up.getLocalUri().toString());
|
||||
holder.imageUri = up.getLocalUri();
|
||||
}
|
||||
|
||||
if(!imageOnlyMode) {
|
||||
if (!imageOnlyMode) {
|
||||
holder.overlay.setVisibility(View.VISIBLE);
|
||||
holder.title.setText(up.getFilename());
|
||||
} else {
|
||||
|
|
@ -134,21 +134,21 @@ public class MultipleUploadListFragment extends Fragment {
|
|||
int screenHeight = screenMetrics.heightPixels;
|
||||
|
||||
int picWidth = Math.min((int) Math.sqrt(screenWidth * screenHeight / count), screenWidth);
|
||||
picWidth = Math.min((int)(192 * screenMetrics.density), Math.max((int) (120 * screenMetrics.density), picWidth / 48 * 48));
|
||||
int picHeight = Math.min(picWidth, (int)(192 * screenMetrics.density)); // Max Height is same as Contributions list
|
||||
picWidth = Math.min((int) (192 * screenMetrics.density), Math.max((int) (120 * screenMetrics.density), picWidth / 48 * 48));
|
||||
int picHeight = Math.min(picWidth, (int) (192 * screenMetrics.density)); // Max Height is same as Contributions list
|
||||
|
||||
return new Point(picWidth, picHeight);
|
||||
}
|
||||
|
||||
public void notifyDatasetChanged() {
|
||||
if(photosAdapter != null) {
|
||||
if (photosAdapter != null) {
|
||||
photosAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void setImageOnlyMode(boolean mode) {
|
||||
imageOnlyMode = mode;
|
||||
if(imageOnlyMode) {
|
||||
if (imageOnlyMode) {
|
||||
baseTitle.setVisibility(View.GONE);
|
||||
} else {
|
||||
baseTitle.setVisibility(View.VISIBLE);
|
||||
|
|
@ -159,13 +159,13 @@ public class MultipleUploadListFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_multiple_uploads_list, null);
|
||||
photosGrid = (GridView)view.findViewById(R.id.multipleShareBackground);
|
||||
baseTitle = (EditText)view.findViewById(R.id.multipleBaseTitle);
|
||||
View view = inflater.inflate(R.layout.fragment_multiple_uploads_list, container, false);
|
||||
photosGrid = (GridView) view.findViewById(R.id.multipleShareBackground);
|
||||
baseTitle = (EditText) view.findViewById(R.id.multipleBaseTitle);
|
||||
|
||||
photosAdapter = new PhotoDisplayAdapter();
|
||||
photosGrid.setAdapter(photosAdapter);
|
||||
photosGrid.setOnItemClickListener((AdapterView.OnItemClickListener)getActivity());
|
||||
photosGrid.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity());
|
||||
photoSize = calculatePicDimension(detailProvider.getTotalMediaCount());
|
||||
photosGrid.setColumnWidth(photoSize.x);
|
||||
|
||||
|
|
@ -188,7 +188,7 @@ public class MultipleUploadListFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_upload_multiple:
|
||||
multipleUploadInitiatedHandler.OnMultipleUploadInitiated();
|
||||
return true;
|
||||
|
|
@ -200,7 +200,7 @@ public class MultipleUploadListFragment extends Fragment {
|
|||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
detailProvider = (MediaDetailPagerFragment.MediaDetailProvider)getActivity();
|
||||
detailProvider = (MediaDetailPagerFragment.MediaDetailProvider) getActivity();
|
||||
multipleUploadInitiatedHandler = (OnMultipleUploadInitiatedHandler) getActivity();
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
|
|
@ -213,12 +213,12 @@ public class MultipleUploadListFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i1, int i2, int i3) {
|
||||
for(int i = 0; i < detailProvider.getTotalMediaCount(); i++) {
|
||||
for (int i = 0; i < detailProvider.getTotalMediaCount(); i++) {
|
||||
Contribution up = (Contribution) detailProvider.getMediaAtPosition(i);
|
||||
Boolean isDirty = (Boolean)up.getTag("isDirty");
|
||||
if(isDirty == null || !isDirty) {
|
||||
if(!TextUtils.isEmpty(charSequence)) {
|
||||
up.setFilename(charSequence.toString() + " - " + ((Integer)up.getTag("sequence") + 1));
|
||||
Boolean isDirty = (Boolean) up.getTag("isDirty");
|
||||
if (isDirty == null || !isDirty) {
|
||||
if (!TextUtils.isEmpty(charSequence)) {
|
||||
up.setFilename(charSequence.toString() + " - " + ((Integer) up.getTag("sequence") + 1));
|
||||
} else {
|
||||
up.setFilename("");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ package fr.free.nrw.commons.upload;
|
|||
import android.Manifest;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
|
@ -24,10 +26,10 @@ import android.widget.Toast;
|
|||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -53,9 +55,9 @@ import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE;
|
|||
* Activity for the title/desc screen after image is selected. Also starts processing image
|
||||
* GPS coordinates or user location (if enabled in Settings) for category suggestions.
|
||||
*/
|
||||
public class ShareActivity
|
||||
extends AuthenticatedActivity
|
||||
implements SingleUploadFragment.OnUploadActionInitiated,
|
||||
public class ShareActivity
|
||||
extends AuthenticatedActivity
|
||||
implements SingleUploadFragment.OnUploadActionInitiated,
|
||||
OnCategoriesSaveHandler {
|
||||
|
||||
private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1;
|
||||
|
|
@ -119,7 +121,7 @@ public class ShareActivity
|
|||
// and permission is not obtained.
|
||||
return !FileUtils.isSelfOwned(getApplicationContext(), mediaUri)
|
||||
&& (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED);
|
||||
!= PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
|
||||
private void uploadBegins() {
|
||||
|
|
@ -145,7 +147,7 @@ public class ShareActivity
|
|||
}
|
||||
|
||||
private void showPostUpload() {
|
||||
if(categorizationFragment == null) {
|
||||
if (categorizationFragment == null) {
|
||||
categorizationFragment = new CategorizationFragment();
|
||||
}
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
|
|
@ -155,7 +157,7 @@ public class ShareActivity
|
|||
|
||||
@Override
|
||||
public void onCategoriesSave(List<String> categories) {
|
||||
if(categories.size() > 0) {
|
||||
if (categories.size() > 0) {
|
||||
ModifierSequence categoriesSequence = new ModifierSequence(contribution.getContentUri());
|
||||
|
||||
categoriesSequence.queueModifier(new CategoryModifier(categories.toArray(new String[]{})));
|
||||
|
|
@ -181,7 +183,7 @@ public class ShareActivity
|
|||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if(contribution != null) {
|
||||
if (contribution != null) {
|
||||
outState.putParcelable("contribution", contribution);
|
||||
}
|
||||
}
|
||||
|
|
@ -189,7 +191,7 @@ public class ShareActivity
|
|||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
if(categorizationFragment != null && categorizationFragment.isVisible()) {
|
||||
if (categorizationFragment != null && categorizationFragment.isVisible()) {
|
||||
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT)
|
||||
.param("username", app.getCurrentAccount().name)
|
||||
.param("categories-count", categorizationFragment.getCurrentSelectedCount())
|
||||
|
|
@ -228,7 +230,7 @@ public class ShareActivity
|
|||
ButterKnife.bind(this);
|
||||
initBack();
|
||||
app = CommonsApplication.getInstance();
|
||||
backgroundImageView = (SimpleDraweeView)findViewById(R.id.backgroundImage);
|
||||
backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage);
|
||||
backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder
|
||||
.newInstance(getResources())
|
||||
.setPlaceholderImage(VectorDrawableCompat.create(getResources(),
|
||||
|
|
@ -254,7 +256,7 @@ public class ShareActivity
|
|||
backgroundImageView.setImageURI(mediaUri);
|
||||
}
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
if (savedInstanceState != null) {
|
||||
contribution = savedInstanceState.getParcelable("contribution");
|
||||
}
|
||||
|
||||
|
|
@ -279,7 +281,7 @@ public class ShareActivity
|
|||
if (useNewPermissions && (!storagePermitted || !locationPermitted)) {
|
||||
if (!storagePermitted && !locationPermitted) {
|
||||
String permissionRationales =
|
||||
getResources().getString(R.string.storage_permission_rationale) + "\n"
|
||||
getResources().getString(R.string.read_storage_permission_rationale) + "\n"
|
||||
+ getResources().getString(R.string.location_permission_rationale);
|
||||
snackbar = requestPermissionUsingSnackBar(
|
||||
permissionRationales,
|
||||
|
|
@ -292,7 +294,7 @@ public class ShareActivity
|
|||
textView.setMaxLines(3);
|
||||
} else if (!storagePermitted) {
|
||||
requestPermissionUsingSnackBar(
|
||||
getString(R.string.storage_permission_rationale),
|
||||
getString(R.string.read_storage_permission_rationale),
|
||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||
REQUEST_PERM_ON_CREATE_STORAGE);
|
||||
} else if (!locationPermitted) {
|
||||
|
|
@ -307,7 +309,7 @@ public class ShareActivity
|
|||
|
||||
SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView");
|
||||
categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization");
|
||||
if(shareView == null && categorizationFragment == null) {
|
||||
if (shareView == null && categorizationFragment == null) {
|
||||
shareView = new SingleUploadFragment();
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
|
|
@ -417,12 +419,27 @@ public class ShareActivity
|
|||
// in older devices getPath() may fail depending on the source URI
|
||||
// creating and using a copy of the file seems to work instead.
|
||||
// TODO: there might be a more proper solution than this
|
||||
String copyPath = getApplicationContext().getCacheDir().getAbsolutePath()
|
||||
+ "/" + new Date().getTime() + ".jpg";
|
||||
String copyPath = null;
|
||||
try {
|
||||
ParcelFileDescriptor descriptor
|
||||
= getContentResolver().openFileDescriptor(mediaUri, "r");
|
||||
if (descriptor != null) {
|
||||
SharedPreferences sharedPref = PreferenceManager
|
||||
.getDefaultSharedPreferences(CommonsApplication.getInstance());
|
||||
boolean useExtStorage = sharedPref.getBoolean("useExternalStorage", true);
|
||||
if (useExtStorage) {
|
||||
copyPath = Environment.getExternalStorageDirectory().toString()
|
||||
+ "/CommonsApp/" + new Date().getTime() + ".jpg";
|
||||
File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp");
|
||||
newFile.mkdir();
|
||||
FileUtils.copy(
|
||||
descriptor.getFileDescriptor(),
|
||||
copyPath);
|
||||
Timber.d("Filepath (copied): %s", copyPath);
|
||||
return copyPath;
|
||||
}
|
||||
copyPath = getApplicationContext().getCacheDir().getAbsolutePath()
|
||||
+ "/" + new Date().getTime() + ".jpg";
|
||||
FileUtils.copy(
|
||||
descriptor.getFileDescriptor(),
|
||||
copyPath);
|
||||
|
|
@ -439,6 +456,7 @@ public class ShareActivity
|
|||
|
||||
/**
|
||||
* Gets coordinates for category suggestions, either from EXIF data or user location
|
||||
*
|
||||
* @param gpsEnabled if true use GPS
|
||||
*/
|
||||
private void getFileMetadata(boolean gpsEnabled) {
|
||||
|
|
@ -474,7 +492,7 @@ public class ShareActivity
|
|||
* Then initiates the calls to MediaWiki API through an instance of MwVolleyApi.
|
||||
*/
|
||||
public void useImageCoords() {
|
||||
if(decimalCoords != null) {
|
||||
if (decimalCoords != null) {
|
||||
Timber.d("Decimal coords of image: %s", decimalCoords);
|
||||
|
||||
// Only set cache for this point if image has coords
|
||||
|
|
@ -508,8 +526,7 @@ public class ShareActivity
|
|||
try {
|
||||
imageObj.unregisterLocationManager();
|
||||
Timber.d("Unregistered locationManager");
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
} catch (NullPointerException e) {
|
||||
Timber.d("locationManager does not exist, not unregistered");
|
||||
}
|
||||
}
|
||||
|
|
@ -524,7 +541,7 @@ public class ShareActivity
|
|||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
if(categorizationFragment!=null && categorizationFragment.isVisible()) {
|
||||
if (categorizationFragment != null && categorizationFragment.isVisible()) {
|
||||
categorizationFragment.showBackButtonDialog();
|
||||
} else {
|
||||
onBackPressed();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
|
|
@ -39,13 +38,10 @@ import fr.free.nrw.commons.Utils;
|
|||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SingleUploadFragment extends Fragment {
|
||||
private SharedPreferences prefs;
|
||||
private String license;
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
public interface OnUploadActionInitiated {
|
||||
void uploadActionInitiated(String title, String description);
|
||||
}
|
||||
public class SingleUploadFragment extends Fragment {
|
||||
|
||||
@BindView(R.id.titleEdit) EditText titleEdit;
|
||||
@BindView(R.id.descEdit) EditText descEdit;
|
||||
|
|
@ -53,13 +49,15 @@ public class SingleUploadFragment extends Fragment {
|
|||
@BindView(R.id.share_license_summary) TextView licenseSummaryView;
|
||||
@BindView(R.id.licenseSpinner) Spinner licenseSpinner;
|
||||
|
||||
private SharedPreferences prefs;
|
||||
private String license;
|
||||
private OnUploadActionInitiated uploadActionInitiatedHandler;
|
||||
private TitleTextWatcher textWatcher = new TitleTextWatcher();
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.activity_share, menu);
|
||||
if(titleEdit != null) {
|
||||
if (titleEdit != null) {
|
||||
menu.findItem(R.id.menu_upload_single).setEnabled(titleEdit.getText().length() != 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -88,8 +86,9 @@ public class SingleUploadFragment extends Fragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_single_upload, null);
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_single_upload, container, false);
|
||||
ButterKnife.bind(this, rootView);
|
||||
|
||||
|
||||
|
|
@ -112,10 +111,10 @@ public class SingleUploadFragment extends Fragment {
|
|||
Timber.d(license);
|
||||
|
||||
ArrayAdapter<String> adapter;
|
||||
if (PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("theme",false)) {
|
||||
if (PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("theme", false)) {
|
||||
// dark theme
|
||||
adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_dropdown_item, licenseItems);
|
||||
}else {
|
||||
} else {
|
||||
// light theme
|
||||
adapter = new ArrayAdapter<>(getActivity(), R.layout.light_simple_spinner_dropdown_item, licenseItems);
|
||||
}
|
||||
|
|
@ -146,26 +145,27 @@ public class SingleUploadFragment extends Fragment {
|
|||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@OnItemSelected(R.id.licenseSpinner) void onLicenseSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
@OnItemSelected(R.id.licenseSpinner)
|
||||
void onLicenseSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
String licenseName = parent.getItemAtPosition(position).toString();
|
||||
|
||||
// Set selected color to white because it should be readable on random images.
|
||||
TextView selectedText = (TextView) licenseSpinner.getChildAt(0);
|
||||
if (selectedText != null ) {
|
||||
if (selectedText != null) {
|
||||
selectedText.setTextColor(Color.WHITE);
|
||||
selectedText.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
String license;
|
||||
if(getString(R.string.license_name_cc0).equals(licenseName)) {
|
||||
if (getString(R.string.license_name_cc0).equals(licenseName)) {
|
||||
license = Prefs.Licenses.CC0;
|
||||
} else if(getString(R.string.license_name_cc_by).equals(licenseName)) {
|
||||
} else if (getString(R.string.license_name_cc_by).equals(licenseName)) {
|
||||
license = Prefs.Licenses.CC_BY_3;
|
||||
} else if(getString(R.string.license_name_cc_by_sa).equals(licenseName)) {
|
||||
} else if (getString(R.string.license_name_cc_by_sa).equals(licenseName)) {
|
||||
license = Prefs.Licenses.CC_BY_SA_3;
|
||||
} else if(getString(R.string.license_name_cc_by_four).equals(licenseName)) {
|
||||
} else if (getString(R.string.license_name_cc_by_four).equals(licenseName)) {
|
||||
license = Prefs.Licenses.CC_BY_4;
|
||||
} else if(getString(R.string.license_name_cc_by_sa_four).equals(licenseName)) {
|
||||
} else if (getString(R.string.license_name_cc_by_sa_four).equals(licenseName)) {
|
||||
license = Prefs.Licenses.CC_BY_SA_4;
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown licenseName: " + licenseName);
|
||||
|
|
@ -177,10 +177,9 @@ public class SingleUploadFragment extends Fragment {
|
|||
editor.commit();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@OnTouch(R.id.share_license_summary) boolean showLicence(View view, MotionEvent motionEvent) {
|
||||
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
@OnTouch(R.id.share_license_summary)
|
||||
boolean showLicence(View view, MotionEvent motionEvent) {
|
||||
if (motionEvent.getActionMasked() == ACTION_DOWN) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(Utils.licenseUrlFor(license)));
|
||||
|
|
@ -191,7 +190,8 @@ public class SingleUploadFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.titleDescButton) void setTitleDescButton() {
|
||||
@OnClick(R.id.titleDescButton)
|
||||
void setTitleDescButton() {
|
||||
//Retrieve last title and desc entered
|
||||
SharedPreferences titleDesc = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
String title = titleDesc.getString("Title", "");
|
||||
|
|
@ -205,57 +205,41 @@ public class SingleUploadFragment extends Fragment {
|
|||
/**
|
||||
* Copied from https://stackoverflow.com/a/26269435/8065933
|
||||
*/
|
||||
@OnTouch
|
||||
(R.id.titleEdit) boolean titleInfo(View view, MotionEvent motionEvent) {
|
||||
@OnTouch(R.id.titleEdit)
|
||||
boolean titleInfo(View view, MotionEvent motionEvent) {
|
||||
//Should replace right with end to support different right-to-left languages as well
|
||||
final int value = titleEdit.getRight() - titleEdit.getCompoundDrawables()[2].getBounds().width();
|
||||
|
||||
if (motionEvent.getAction() == motionEvent.ACTION_UP && motionEvent.getRawX() >= value) {
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||
builder.setTitle(R.string.media_detail_title);
|
||||
builder.setMessage(R.string.title_info);
|
||||
builder.setCancelable(true);
|
||||
builder.setNeutralButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
AlertDialog alert = builder.create();
|
||||
alert.show();
|
||||
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() >= value) {
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.media_detail_title)
|
||||
.setMessage(R.string.title_info)
|
||||
.setCancelable(true)
|
||||
.setNeutralButton(android.R.string.ok, (dialog, id) -> dialog.cancel())
|
||||
.create()
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@OnTouch
|
||||
(R.id.descEdit) boolean descriptionInfo(View view, MotionEvent motionEvent) {
|
||||
@OnTouch(R.id.descEdit)
|
||||
boolean descriptionInfo(View view, MotionEvent motionEvent) {
|
||||
final int value = descEdit.getRight() - descEdit.getCompoundDrawables()[2].getBounds().width();
|
||||
|
||||
if (motionEvent.getAction() == motionEvent.ACTION_UP && motionEvent.getRawX() >= value) {
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||
builder.setTitle(R.string.media_detail_description);
|
||||
builder.setMessage(R.string.description_info);
|
||||
builder.setCancelable(true);
|
||||
builder.setNeutralButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
AlertDialog alert = builder.create();
|
||||
alert.show();
|
||||
return true;
|
||||
|
||||
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() >= value) {
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.media_detail_description)
|
||||
.setMessage(R.string.description_info)
|
||||
.setCancelable(true)
|
||||
.setNeutralButton(android.R.string.ok, (dialog, id) -> dialog.cancel())
|
||||
.create()
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void setLicenseSummary(String license) {
|
||||
licenseSummaryView.setText(getString(R.string.share_license_summary, getString(Utils.licenseNameFor(license))));
|
||||
}
|
||||
|
|
@ -279,16 +263,22 @@ public class SingleUploadFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
public interface OnUploadActionInitiated {
|
||||
void uploadActionInitiated(String title, String description);
|
||||
}
|
||||
|
||||
private class TitleTextWatcher implements TextWatcher {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
if(getActivity() != null) {
|
||||
if (getActivity() != null) {
|
||||
getActivity().invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public class FileUtils {
|
|||
* @return the content of the file
|
||||
*/
|
||||
public static String readFromResource(String fileName) throws IOException {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import info.debatty.java.stringsimilarity.Levenshtein;
|
||||
|
||||
public class StringSortingUtils {
|
||||
|
||||
private StringSortingUtils() {
|
||||
//no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Comparator for sorting strings by its similarity with Levenshtein
|
||||
* algorithm. By using this Comparator we get results from the highest to
|
||||
* the lowest match.
|
||||
*
|
||||
* @param filter pattern to compare similarity
|
||||
* @return Comparator with string similarity
|
||||
*/
|
||||
|
||||
public static Comparator<String> sortBySimilarity(final String filter) {
|
||||
return (firstItem, secondItem) -> {
|
||||
double firstItemSimilarity = calculateSimilarity(firstItem, filter);
|
||||
double secondItemSimilarity = calculateSimilarity(secondItem, filter);
|
||||
return (int) Math.signum(secondItemSimilarity - firstItemSimilarity);
|
||||
};
|
||||
}
|
||||
|
||||
private static double calculateSimilarity(String firstString, String secondString) {
|
||||
String longer = firstString.toLowerCase();
|
||||
String shorter = secondString.toLowerCase();
|
||||
|
||||
if (firstString.length() < secondString.length()) {
|
||||
longer = secondString;
|
||||
shorter = firstString;
|
||||
}
|
||||
int longerLength = longer.length();
|
||||
if (longerLength == 0) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
double distanceBetweenStrings = new Levenshtein().distance(longer, shorter);
|
||||
return (longerLength - distanceBetweenStrings) / (double) longerLength;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue