Codacy suggestions for improvement.

This commit is contained in:
Paul Hawke 2017-09-03 17:55:45 -05:00
parent 8cbf56fa7b
commit 15107e622b
13 changed files with 405 additions and 345 deletions

View file

@ -220,10 +220,8 @@ public class CommonsApplication extends Application {
setIndex(getIndex() + 1); setIndex(getIndex() + 1);
try { try {
if (accountManagerFuture != null) { if (accountManagerFuture != null && accountManagerFuture.getResult()) {
if (accountManagerFuture.getResult()) { Timber.d("Account removed successfully.");
Timber.d("Account removed successfully.");
}
} }
} catch (OperationCanceledException | IOException | AuthenticatorException e) { } catch (OperationCanceledException | IOException | AuthenticatorException e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -10,18 +10,16 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set;
public class LicenseList { public class LicenseList {
Map<String, License> licenses = new HashMap<>(); private Map<String, License> licenses = new HashMap<>();
Resources res; private Resources res;
private static String XMLNS_LICENSE = "https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses";
public LicenseList(Activity activity) { public LicenseList(Activity activity) {
res = activity.getResources(); res = activity.getResources();
XmlPullParser parser = res.getXml(R.xml.wikimedia_licenses); 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 id = parser.getAttributeValue(null, "id");
String template = parser.getAttributeValue(null, "template"); String template = parser.getAttributeValue(null, "template");
String url = parser.getAttributeValue(null, "url"); String url = parser.getAttributeValue(null, "url");
@ -31,10 +29,6 @@ public class LicenseList {
} }
} }
public Set<String> keySet() {
return licenses.keySet();
}
public Collection<License> values() { public Collection<License> values() {
return licenses.values(); return licenses.values();
} }
@ -44,7 +38,7 @@ public class LicenseList {
} }
@Nullable @Nullable
public License licenseForTemplate(String template) { License licenseForTemplate(String template) {
String ucTemplate = new PageTitle(template).getDisplayText(); String ucTemplate = new PageTitle(template).getDisplayText();
for (License license : values()) { for (License license : values()) {
if (ucTemplate.equals(new PageTitle(license.getTemplate()).getDisplayText())) { if (ucTemplate.equals(new PageTitle(license.getTemplate()).getDisplayText())) {
@ -54,26 +48,16 @@ public class LicenseList {
return null; return null;
} }
public String nameIdForTemplate(String template) { private String nameIdForTemplate(String template) {
// hack :D (converts dashes and periods to underscores) // hack :D (converts dashes and periods to underscores)
// cc-by-sa-3.0 -> cc_by_sa_3_0 // 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) { private String nameForTemplate(String template) {
return res.getIdentifier("fr.free.nrw.commons:string/" + stringId, null, null); int nameId = res.getIdentifier("fr.free.nrw.commons:string/"
} + nameIdForTemplate(template), null, null);
return (nameId != 0) ? res.getString(nameId) : template;
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;
} }
} }

View file

@ -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() { protected Media() {
this.categories = new ArrayList<>(); this.categories = new ArrayList<>();
this.descriptions = new HashMap<>(); 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) { public Object getTag(String key) {
return tags.get(key); return tags.get(key);
@ -44,15 +98,14 @@ public class Media implements Parcelable {
tags.put(key, value); tags.put(key, value);
} }
public static Pattern displayTitlePattern = Pattern.compile("(.*)(\\.\\w+)", Pattern.CASE_INSENSITIVE); public String getDisplayTitle() {
public String getDisplayTitle() { if (filename == null) {
if(filename == null) {
return ""; return "";
} }
// FIXME: Gross hack bercause my regex skills suck maybe or I am too lazy who knows // FIXME: Gross hack bercause my regex skills suck maybe or I am too lazy who knows
String title = getFilePageTitle().getDisplayText().replaceFirst("^File:", ""); String title = getFilePageTitle().getDisplayText().replaceFirst("^File:", "");
Matcher matcher = displayTitlePattern.matcher(title); Matcher matcher = displayTitlePattern.matcher(title);
if(matcher.matches()) { if (matcher.matches()) {
return matcher.group(1); return matcher.group(1);
} else { } else {
return title; return title;
@ -68,7 +121,7 @@ public class Media implements Parcelable {
} }
public String getImageUrl() { public String getImageUrl() {
if(imageUrl == null) { if (imageUrl == null) {
imageUrl = Utils.makeThumbBaseUrl(this.getFilename()); imageUrl = Utils.makeThumbBaseUrl(this.getFilename());
} }
return imageUrl; return imageUrl;
@ -86,6 +139,10 @@ public class Media implements Parcelable {
return description; return description;
} }
public void setDescription(String description) {
this.description = description;
}
public long getDataLength() { public long getDataLength() {
return dataLength; return dataLength;
} }
@ -102,7 +159,8 @@ public class Media implements Parcelable {
this.dateCreated = date; this.dateCreated = date;
} }
public @Nullable Date getDateUploaded() { public @Nullable
Date getDateUploaded() {
return dateUploaded; return dateUploaded;
} }
@ -138,7 +196,8 @@ public class Media implements Parcelable {
this.license = license; this.license = license;
} }
public @Nullable LatLng getCoordinates() { public @Nullable
LatLng getCoordinates() {
return coordinates; return coordinates;
} }
@ -146,24 +205,9 @@ public class Media implements Parcelable {
this.coordinates = coordinates; this.coordinates = coordinates;
} }
// Primary metadata fields @SuppressWarnings("unchecked")
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
public ArrayList<String> getCategories() { public ArrayList<String> getCategories() {
return (ArrayList<String>)categories.clone(); // feels dirty return (ArrayList<String>) categories.clone(); // feels dirty
} }
public void setCategories(List<String> categories) { public void setCategories(List<String> categories) {
@ -171,7 +215,7 @@ public class Media implements Parcelable {
this.categories.addAll(categories); this.categories.addAll(categories);
} }
public void setDescriptions(Map<String,String> descriptions) { void setDescriptions(Map<String, String> descriptions) {
for (String key : this.descriptions.keySet()) { for (String key : this.descriptions.keySet()) {
this.descriptions.remove(key); 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 @Override
public int describeContents() { public int describeContents() {
return 0; return 0;
@ -235,27 +262,4 @@ public class Media implements Parcelable {
parcel.writeStringList(categories); parcel.writeStringList(categories);
parcel.writeMap(descriptions); 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;
}
} }

View file

@ -53,13 +53,13 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
} }
protected void requestAuthToken() { protected void requestAuthToken() {
if(authCookie != null) { if (authCookie != null) {
onAuthCookieAcquired(authCookie); onAuthCookieAcquired(authCookie);
return; return;
} }
AccountManager accountManager = AccountManager.get(this); AccountManager accountManager = AccountManager.get(this);
Account curAccount = app.getCurrentAccount(); Account curAccount = app.getCurrentAccount();
if(curAccount == null) { if (curAccount == null) {
addAccount(accountManager); addAccount(accountManager);
} else { } else {
getAuthCookie(curAccount, accountManager); getAuthCookie(curAccount, accountManager);
@ -70,7 +70,7 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
app = CommonsApplication.getInstance(); app = CommonsApplication.getInstance();
if(savedInstanceState != null) { if (savedInstanceState != null) {
authCookie = savedInstanceState.getString("authCookie"); authCookie = savedInstanceState.getString("authCookie");
} }
} }
@ -82,5 +82,6 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
} }
protected abstract void onAuthCookieAcquired(String authCookie); protected abstract void onAuthCookieAcquired(String authCookie);
protected abstract void onAuthFailure(); protected abstract void onAuthFailure();
} }

View file

@ -16,21 +16,32 @@ import java.io.IOException;
import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.mwapi.MediaWikiApi; 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 { public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
private Context context; private Context context;
public WikiAccountAuthenticator(Context context) { WikiAccountAuthenticator(Context context) {
super(context); super(context);
this.context = context; this.context = context;
} }
private Bundle unsupportedOperation() { private Bundle unsupportedOperation() {
Bundle bundle = new Bundle(); 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. // 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; return bundle;
} }
@ -54,10 +65,10 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
private Bundle addAccount(AccountAuthenticatorResponse response) { private Bundle addAccount(AccountAuthenticatorResponse response) {
Intent Intent = new Intent(context, LoginActivity.class); 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 bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, Intent); bundle.putParcelable(KEY_INTENT, Intent);
return bundle; return bundle;
} }
@ -78,14 +89,16 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
MediaWikiApi api = CommonsApplication.getInstance().getMWApi(); MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
//TODO add 2fa support here //TODO add 2fa support here
String result = api.login(username, password); String result = api.login(username, password);
if(result.equals("PASS")) { if (result.equals("PASS")) {
return api.getAuthCookie(); return api.getAuthCookie();
} else { } else {
return null; return null;
} }
} }
@Override @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 // Extract the username and password from the Account Manager, and ask
// the server for an appropriate AuthToken. // the server for an appropriate AuthToken.
final AccountManager am = AccountManager.get(context); final AccountManager am = AccountManager.get(context);
@ -101,9 +114,9 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
} }
if (authCookie != null) { if (authCookie != null) {
final Bundle result = new Bundle(); final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); result.putString(KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, AccountUtil.accountType()); result.putString(KEY_ACCOUNT_TYPE, AccountUtil.accountType());
result.putString(AccountManager.KEY_AUTHTOKEN, authCookie); result.putString(KEY_AUTHTOKEN, authCookie);
return result; return result;
} }
} }
@ -112,10 +125,10 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
// need to re-prompt them for their credentials. We do that by creating // need to re-prompt them for their credentials. We do that by creating
// an intent to display our AuthenticatorActivity panel. // an intent to display our AuthenticatorActivity panel.
final Intent intent = new Intent(context, LoginActivity.class); final Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra(LoginActivity.PARAM_USERNAME, account.name); intent.putExtra(PARAM_USERNAME, account.name);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); intent.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent); bundle.putParcelable(KEY_INTENT, intent);
return bundle; return bundle;
} }
@ -133,7 +146,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
@NonNull Account account, @NonNull String[] features) @NonNull Account account, @NonNull String[] features)
throws NetworkErrorException { throws NetworkErrorException {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false); bundle.putBoolean(KEY_BOOLEAN_RESULT, false);
return bundle; return bundle;
} }
@ -141,8 +154,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
@Override @Override
public Bundle updateCredentials(@NonNull AccountAuthenticatorResponse response, public Bundle updateCredentials(@NonNull AccountAuthenticatorResponse response,
@NonNull Account account, @Nullable String authTokenType, @NonNull Account account, @Nullable String authTokenType,
@Nullable Bundle options) @Nullable Bundle options) throws NetworkErrorException {
throws NetworkErrorException {
return unsupportedOperation(); return unsupportedOperation();
} }

View file

@ -15,31 +15,39 @@ import fr.free.nrw.commons.data.Category;
import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.data.DBOpenHelper;
import timber.log.Timber; 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 class CategoryContentProvider extends ContentProvider {
public static final String AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
// For URI matcher // For URI matcher
private static final int CATEGORIES = 1; private static final int CATEGORIES = 1;
private static final int CATEGORIES_ID = 2; 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"; private static final String BASE_PATH = "categories";
public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH); 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 { static {
uriMatcher.addURI(AUTHORITY, BASE_PATH, CATEGORIES); uriMatcher.addURI(AUTHORITY, BASE_PATH, CATEGORIES);
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID); uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID);
} }
private DBOpenHelper dbOpenHelper;
public static Uri uriForId(int id) { public static Uri uriForId(int id) {
return Uri.parse(BASE_URI.toString() + "/" + id); return Uri.parse(BASE_URI.toString() + "/" + id);
} }
private DBOpenHelper dbOpenHelper; @SuppressWarnings("ConstantConditions")
@Override @Override
public boolean onCreate() { public boolean onCreate() {
dbOpenHelper = CommonsApplication.getInstance().getDBOpenHelper(); CommonsApplication app = ((CommonsApplication) getContext().getApplicationContext());
dbOpenHelper = app.getDBOpenHelper();
return false; return false;
} }
@ -48,23 +56,23 @@ public class CategoryContentProvider extends ContentProvider {
public Cursor query(@NonNull Uri uri, String[] projection, String selection, public Cursor query(@NonNull Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) { String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(Category.Table.TABLE_NAME); queryBuilder.setTables(TABLE_NAME);
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase db = dbOpenHelper.getReadableDatabase(); SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
Cursor cursor; Cursor cursor;
switch(uriType) { switch (uriType) {
case CATEGORIES: case CATEGORIES:
cursor = queryBuilder.query(db, projection, selection, selectionArgs, cursor = queryBuilder.query(db, projection, selection, selectionArgs,
null, null, sortOrder); null, null, sortOrder);
break; break;
case CATEGORIES_ID: case CATEGORIES_ID:
cursor = queryBuilder.query(db, cursor = queryBuilder.query(db,
Category.Table.ALL_FIELDS, ALL_FIELDS,
"_id = ?", "_id = ?",
new String[] { uri.getLastPathSegment() }, new String[]{uri.getLastPathSegment()},
null, null,
null, null,
sortOrder sortOrder
@ -92,7 +100,7 @@ public class CategoryContentProvider extends ContentProvider {
long id; long id;
switch (uriType) { switch (uriType) {
case CATEGORIES: case CATEGORIES:
id = sqlDB.insert(Category.Table.TABLE_NAME, null, contentValues); id = sqlDB.insert(TABLE_NAME, null, contentValues);
break; break;
default: default:
throw new IllegalArgumentException("Unknown URI: " + uri); throw new IllegalArgumentException("Unknown URI: " + uri);
@ -115,9 +123,9 @@ public class CategoryContentProvider extends ContentProvider {
sqlDB.beginTransaction(); sqlDB.beginTransaction();
switch (uriType) { switch (uriType) {
case CATEGORIES: case CATEGORIES:
for(ContentValues value: values) { for (ContentValues value : values) {
Timber.d("Inserting! %s", value); Timber.d("Inserting! %s", value);
sqlDB.insert(Category.Table.TABLE_NAME, null, value); sqlDB.insert(TABLE_NAME, null, value);
} }
break; break;
default: default:
@ -145,13 +153,12 @@ public class CategoryContentProvider extends ContentProvider {
int rowsUpdated; int rowsUpdated;
switch (uriType) { switch (uriType) {
case CATEGORIES_ID: case CATEGORIES_ID:
int id = Integer.valueOf(uri.getLastPathSegment());
if (TextUtils.isEmpty(selection)) { if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(Category.Table.TABLE_NAME, int id = Integer.valueOf(uri.getLastPathSegment());
rowsUpdated = sqlDB.update(TABLE_NAME,
contentValues, contentValues,
Category.Table.COLUMN_ID + " = ?", COLUMN_ID + " = ?",
new String[] { String.valueOf(id) } ); new String[]{String.valueOf(id)});
} else { } else {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Parameter `selection` should be empty when updating an ID"); "Parameter `selection` should be empty when updating an ID");

View file

@ -1,11 +1,12 @@
package fr.free.nrw.commons.contributions; package fr.free.nrw.commons.contributions;
import android.app.Activity; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.FileProvider; import android.support.v4.content.FileProvider;
import java.io.File; import java.io.File;
@ -15,16 +16,25 @@ import fr.free.nrw.commons.upload.ShareActivity;
import fr.free.nrw.commons.upload.UploadService; import fr.free.nrw.commons.upload.UploadService;
import timber.log.Timber; 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 android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
import static android.provider.MediaStore.EXTRA_OUTPUT;
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 Fragment fragment;
private Activity activity;
private final static int SELECT_FROM_GALLERY = 1; ContributionController(Fragment fragment) {
private final static int SELECT_FROM_CAMERA = 2;
public ContributionController(Fragment fragment) {
this.fragment = fragment; this.fragment = fragment;
this.activity = fragment.getActivity();
} }
// See http://stackoverflow.com/a/5054673/17865 for why this is done // See http://stackoverflow.com/a/5054673/17865 for why this is done
@ -34,43 +44,44 @@ public class ContributionController {
File photoFile = new File(fragment.getContext().getCacheDir() + "/images", File photoFile = new File(fragment.getContext().getCacheDir() + "/images",
new Date().getTime() + ".jpg"); new Date().getTime() + ".jpg");
photoFile.getParentFile().mkdirs(); photoFile.getParentFile().mkdirs();
Context applicationContext = fragment.getActivity().getApplicationContext();
return FileProvider.getUriForFile( return FileProvider.getUriForFile(
fragment.getContext(), fragment.getContext(),
fragment.getActivity().getApplicationContext().getPackageName() + ".provider", applicationContext.getPackageName() + ".provider",
photoFile); photoFile);
} }
public void startCameraCapture() { void startCameraCapture() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Intent pictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
lastGeneratedCaptureUri = reGenerateImageCaptureUriInCache(); lastGeneratedCaptureUri = reGenerateImageCaptureUriInCache();
takePictureIntent.setFlags( pictureIntent.setFlags(FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); pictureIntent.putExtra(EXTRA_OUTPUT, lastGeneratedCaptureUri);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, lastGeneratedCaptureUri); fragment.startActivityForResult(pictureIntent, SELECT_FROM_CAMERA);
fragment.startActivityForResult(takePictureIntent, SELECT_FROM_CAMERA);
} }
public void startGalleryPick() { void startGalleryPick() {
//FIXME: Starts gallery (opens Google Photos) //FIXME: Starts gallery (opens Google Photos)
Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT); Intent pickImageIntent = new Intent(ACTION_GET_CONTENT);
pickImageIntent.setType("image/*"); pickImageIntent.setType("image/*");
fragment.startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY); 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); Intent shareIntent = new Intent(activity, ShareActivity.class);
shareIntent.setAction(Intent.ACTION_SEND); shareIntent.setAction(ACTION_SEND);
switch(requestCode) { switch (requestCode) {
case SELECT_FROM_GALLERY: case SELECT_FROM_GALLERY:
//Handles image picked from gallery //Handles image picked from gallery
Uri imageData = data.getData(); Uri imageData = data.getData();
shareIntent.setType(activity.getContentResolver().getType(imageData)); shareIntent.setType(activity.getContentResolver().getType(imageData));
shareIntent.putExtra(Intent.EXTRA_STREAM, imageData); shareIntent.putExtra(EXTRA_STREAM, imageData);
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_GALLERY); shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY);
break; break;
case SELECT_FROM_CAMERA: case SELECT_FROM_CAMERA:
shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type
shareIntent.putExtra(Intent.EXTRA_STREAM, lastGeneratedCaptureUri); shareIntent.putExtra(EXTRA_STREAM, lastGeneratedCaptureUri);
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_CAMERA); shareIntent.putExtra(EXTRA_SOURCE, SOURCE_CAMERA);
break; break;
} }
Timber.i("Image selected"); Timber.i("Image selected");
@ -81,12 +92,14 @@ public class ContributionController {
} }
} }
public void saveState(Bundle outState) { void saveState(Bundle outState) {
outState.putParcelable("lastGeneratedCaptureURI", lastGeneratedCaptureUri); if (outState != null) {
outState.putParcelable("lastGeneratedCaptureURI", lastGeneratedCaptureUri);
}
} }
public void loadState(Bundle savedInstanceState) { void loadState(Bundle savedInstanceState) {
if(savedInstanceState != null) { if (savedInstanceState != null) {
lastGeneratedCaptureUri = savedInstanceState.getParcelable("lastGeneratedCaptureURI"); lastGeneratedCaptureUri = savedInstanceState.getParcelable("lastGeneratedCaptureURI");
} }
} }

View file

@ -1,7 +1,6 @@
package fr.free.nrw.commons.contributions; package fr.free.nrw.commons.contributions;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
@ -38,13 +37,17 @@ import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import timber.log.Timber; import timber.log.Timber;
public class ContributionsActivity import static android.content.ContentResolver.requestSync;
extends AuthenticatedActivity import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
implements LoaderManager.LoaderCallbacks<Cursor>, import static fr.free.nrw.commons.contributions.Contribution.Table.ALL_FIELDS;
AdapterView.OnItemClickListener, import static fr.free.nrw.commons.contributions.ContributionsContentProvider.AUTHORITY;
MediaDetailPagerFragment.MediaDetailProvider, import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
FragmentManager.OnBackStackChangedListener, import static fr.free.nrw.commons.settings.Prefs.UPLOADS_SHOWING;
ContributionsListFragment.SourceRefresher {
public class ContributionsActivity extends AuthenticatedActivity
implements LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener,
MediaDetailPagerFragment.MediaDetailProvider, FragmentManager.OnBackStackChangedListener,
ContributionsListFragment.SourceRefresher {
private Cursor allContributions; private Cursor allContributions;
private ContributionsListFragment contributionsList; private ContributionsListFragment contributionsList;
@ -63,14 +66,18 @@ public class ContributionsActivity
This is why Contribution.STATE_COMPLETED is -1. 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 CompositeDisposable compositeDisposable = new CompositeDisposable();
private ServiceConnection uploadServiceConnection = new ServiceConnection() { private ServiceConnection uploadServiceConnection = new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName componentName, IBinder binder) { public void onServiceConnected(ComponentName componentName, IBinder binder) {
uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder)binder).getService(); uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder) binder)
.getService();
isUploadServiceConnected = true; isUploadServiceConnected = true;
} }
@ -86,7 +93,7 @@ public class ContributionsActivity
compositeDisposable.clear(); compositeDisposable.clear();
getSupportFragmentManager().removeOnBackStackChangedListener(this); getSupportFragmentManager().removeOnBackStackChangedListener(this);
super.onDestroy(); super.onDestroy();
if(isUploadServiceConnected) { if (isUploadServiceConnected) {
unbindService(uploadServiceConnection); unbindService(uploadServiceConnection);
} }
} }
@ -96,9 +103,9 @@ public class ContributionsActivity
super.onResume(); super.onResume();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean isSettingsChanged = boolean isSettingsChanged =
sharedPreferences.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED,false); sharedPreferences.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
SharedPreferences.Editor editor = sharedPreferences.edit(); SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED,false); editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
editor.apply(); editor.apply();
if (isSettingsChanged) { if (isSettingsChanged) {
refreshSource(); refreshSource();
@ -107,14 +114,16 @@ public class ContributionsActivity
@Override @Override
protected void onAuthCookieAcquired(String authCookie) { protected void onAuthCookieAcquired(String authCookie) {
// Do a sync everytime we get here! // Do a sync every time we get here!
ContentResolver.requestSync(CommonsApplication.getInstance().getCurrentAccount(), ContributionsContentProvider.AUTHORITY, new Bundle()); CommonsApplication app = ((CommonsApplication) getApplication());
requestSync(app.getCurrentAccount(), AUTHORITY, new Bundle());
Intent uploadServiceIntent = new Intent(this, UploadService.class); Intent uploadServiceIntent = new Intent(this, UploadService.class);
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE); uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
startService(uploadServiceIntent); startService(uploadServiceIntent);
bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE); 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); getSupportLoaderManager().initLoader(0, null, this);
} }
@ -127,12 +136,13 @@ public class ContributionsActivity
// Activity can call methods in the fragment by acquiring a // Activity can call methods in the fragment by acquiring a
// reference to the Fragment from FragmentManager, using findFragmentById() // reference to the Fragment from FragmentManager, using findFragmentById()
contributionsList = (ContributionsListFragment)getSupportFragmentManager() FragmentManager supportFragmentManager = getSupportFragmentManager();
contributionsList = (ContributionsListFragment) supportFragmentManager
.findFragmentById(R.id.contributionsListFragment); .findFragmentById(R.id.contributionsListFragment);
getSupportFragmentManager().addOnBackStackChangedListener(this); supportFragmentManager.addOnBackStackChangedListener(this);
if (savedInstanceState != null) { if (savedInstanceState != null) {
mediaDetails = (MediaDetailPagerFragment)getSupportFragmentManager() mediaDetails = (MediaDetailPagerFragment) supportFragmentManager
.findFragmentById(R.id.contributionsFragmentContainer); .findFragmentById(R.id.contributionsFragmentContainer);
} }
requestAuthToken(); requestAuthToken();
@ -143,21 +153,25 @@ public class ContributionsActivity
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(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. * Replace whatever is in the current contributionsFragmentContainer view with
/ Called when user selects a contribution. */ * mediaDetailPagerFragment, and preserve previous state in back stack.
* Called when user selects a contribution.
*/
private void showDetail(int i) { private void showDetail(int i) {
if(mediaDetails == null ||!mediaDetails.isVisible()) { if (mediaDetails == null || !mediaDetails.isVisible()) {
mediaDetails = new MediaDetailPagerFragment(); mediaDetails = new MediaDetailPagerFragment();
this.getSupportFragmentManager() FragmentManager supportFragmentManager = getSupportFragmentManager();
supportFragmentManager
.beginTransaction() .beginTransaction()
.replace(R.id.contributionsFragmentContainer, mediaDetails) .replace(R.id.contributionsFragmentContainer, mediaDetails)
.addToBackStack(null) .addToBackStack(null)
.commit(); .commit();
this.getSupportFragmentManager().executePendingTransactions(); supportFragmentManager.executePendingTransactions();
} }
mediaDetails.showImage(i); mediaDetails.showImage(i);
} }
@ -165,7 +179,7 @@ public class ContributionsActivity
public void retryUpload(int i) { public void retryUpload(int i) {
allContributions.moveToPosition(i); allContributions.moveToPosition(i);
Contribution c = Contribution.fromCursor(allContributions); Contribution c = Contribution.fromCursor(allContributions);
if(c.getState() == Contribution.STATE_FAILED) { if (c.getState() == STATE_FAILED) {
uploadService.queue(UploadService.ACTION_UPLOAD_FILE, c); uploadService.queue(UploadService.ACTION_UPLOAD_FILE, c);
Timber.d("Restarting for %s", c.toContentValues()); Timber.d("Restarting for %s", c.toContentValues());
} else { } else {
@ -176,9 +190,9 @@ public class ContributionsActivity
public void deleteUpload(int i) { public void deleteUpload(int i) {
allContributions.moveToPosition(i); allContributions.moveToPosition(i);
Contribution c = Contribution.fromCursor(allContributions); Contribution c = Contribution.fromCursor(allContributions);
if(c.getState() == Contribution.STATE_FAILED) { if (c.getState() == STATE_FAILED) {
Timber.d("Deleting failed contrib %s", c.toContentValues()); Timber.d("Deleting failed contrib %s", c.toContentValues());
c.setContentProviderClient(getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY)); c.setContentProviderClient(getContentResolver().acquireContentProviderClient(AUTHORITY));
c.delete(); c.delete();
} else { } else {
Timber.d("Skipping deletion for non-failed contrib %s", c.toContentValues()); Timber.d("Skipping deletion for non-failed contrib %s", c.toContentValues());
@ -187,9 +201,9 @@ public class ContributionsActivity
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) { switch (item.getItemId()) {
case android.R.id.home: case android.R.id.home:
if(mediaDetails.isVisible()) { if (mediaDetails.isVisible()) {
getSupportFragmentManager().popBackStack(); getSupportFragmentManager().popBackStack();
} }
return true; return true;
@ -215,21 +229,20 @@ public class ContributionsActivity
@Override @Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
SharedPreferences sharedPref = SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
PreferenceManager.getDefaultSharedPreferences(this); int uploads = sharedPref.getInt(UPLOADS_SHOWING, 100);
int uploads = sharedPref.getInt(Prefs.UPLOADS_SHOWING, 100); return new CursorLoader(this, BASE_URI,
return new CursorLoader(this, ContributionsContentProvider.BASE_URI, ALL_FIELDS, CONTRIBUTION_SELECTION, null,
Contribution.Table.ALL_FIELDS, CONTRIBUTION_SELECTION, null,
CONTRIBUTION_SORT + "LIMIT " + uploads); CONTRIBUTION_SORT + "LIMIT " + uploads);
} }
@Override @Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
if(contributionsList.getAdapter() == null) { if (contributionsList.getAdapter() == null) {
contributionsList contributionsList.setAdapter(new ContributionsListAdapter(getApplicationContext(),
.setAdapter(new ContributionsListAdapter(getApplicationContext(), cursor, 0)); cursor, 0));
} else { } else {
((CursorAdapter)contributionsList.getAdapter()).swapCursor(cursor); ((CursorAdapter) contributionsList.getAdapter()).swapCursor(cursor);
} }
setUploadCount(); setUploadCount();
@ -249,34 +262,32 @@ public class ContributionsActivity
if (contributionsList.getAdapter() == null) { if (contributionsList.getAdapter() == null) {
// not yet ready to return data // not yet ready to return data
return null; return null;
} else { } else {
return Contribution.fromCursor((Cursor) contributionsList.getAdapter().getItem(i)); return Contribution.fromCursor((Cursor) contributionsList.getAdapter().getItem(i));
} }
} }
@Override @Override
public int getTotalMediaCount() { public int getTotalMediaCount() {
if(contributionsList.getAdapter() == null) { if (contributionsList.getAdapter() == null) {
return 0; return 0;
} }
return contributionsList.getAdapter().getCount(); return contributionsList.getAdapter().getCount();
} }
@SuppressWarnings("ConstantConditions")
private void setUploadCount() { private void setUploadCount() {
CommonsApplication application = CommonsApplication.getInstance(); CommonsApplication app = ((CommonsApplication) getApplication());
compositeDisposable.add( compositeDisposable.add(
CommonsApplication.getInstance().getMWApi() app.getMWApi()
.getUploadCount(application.getCurrentAccount().name) .getUploadCount(app.getCurrentAccount().name)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
uploadCount -> uploadCount -> getSupportActionBar().setSubtitle(getResources()
getSupportActionBar().setSubtitle(getResources() .getQuantityString(R.plurals.contributions_subtitle,
.getQuantityString(R.plurals.contributions_subtitle, uploadCount, uploadCount)),
uploadCount, t -> Timber.e(t, "Fetching upload count failed")
uploadCount)),
throwable -> Timber.e(throwable, "Fetching upload count failed")
) )
); );
} }
@ -332,8 +343,7 @@ public class ContributionsActivity
} }
public static void startYourself(Context context) { public static void startYourself(Context context) {
Intent contributionsIntent = new Intent(context, ContributionsActivity.class); context.startActivity(new Intent(context, ContributionsActivity.class));
context.startActivity(contributionsIntent);
} }
} }

View file

@ -13,17 +13,20 @@ import android.text.TextUtils;
import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.CommonsApplication;
import timber.log.Timber; 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 = 1;
private static final int CONTRIBUTIONS_ID = 2; 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 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); public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static { static {
uriMatcher.addURI(AUTHORITY, BASE_PATH, CONTRIBUTIONS); uriMatcher.addURI(AUTHORITY, BASE_PATH, CONTRIBUTIONS);
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID); uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID);
@ -38,25 +41,29 @@ public class ContributionsContentProvider extends ContentProvider{
return false; return false;
} }
@SuppressWarnings("ConstantConditions")
@Override @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(); SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(Contribution.Table.TABLE_NAME); queryBuilder.setTables(TABLE_NAME);
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase db = CommonsApplication.getInstance().getDBOpenHelper().getReadableDatabase(); CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
SQLiteDatabase db = app.getDBOpenHelper().getReadableDatabase();
Cursor cursor; Cursor cursor;
switch(uriType) { switch (uriType) {
case CONTRIBUTIONS: case CONTRIBUTIONS:
cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder); cursor = queryBuilder.query(db, projection, selection, selectionArgs,
null, null, sortOrder);
break; break;
case CONTRIBUTIONS_ID: case CONTRIBUTIONS_ID:
cursor = queryBuilder.query(db, cursor = queryBuilder.query(db,
Contribution.Table.ALL_FIELDS, ALL_FIELDS,
"_id = ?", "_id = ?",
new String[] { uri.getLastPathSegment() }, new String[]{uri.getLastPathSegment()},
null, null,
null, null,
sortOrder sortOrder
@ -76,14 +83,16 @@ public class ContributionsContentProvider extends ContentProvider{
return null; return null;
} }
@SuppressWarnings("ConstantConditions")
@Override @Override
public Uri insert(@NonNull Uri uri, ContentValues contentValues) { public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase(); CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
long id = 0; SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
long id;
switch (uriType) { switch (uriType) {
case CONTRIBUTIONS: case CONTRIBUTIONS:
id = sqlDB.insert(Contribution.Table.TABLE_NAME, null, contentValues); id = sqlDB.insert(TABLE_NAME, null, contentValues);
break; break;
default: default:
throw new IllegalArgumentException("Unknown URI: " + uri); throw new IllegalArgumentException("Unknown URI: " + uri);
@ -92,19 +101,21 @@ public class ContributionsContentProvider extends ContentProvider{
return Uri.parse(BASE_URI + "/" + id); return Uri.parse(BASE_URI + "/" + id);
} }
@SuppressWarnings("ConstantConditions")
@Override @Override
public int delete(@NonNull Uri uri, String s, String[] strings) { public int delete(@NonNull Uri uri, String s, String[] strings) {
int rows = 0; int rows;
int uriType = uriMatcher.match(uri); 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: case CONTRIBUTIONS_ID:
Timber.d("Deleting contribution id %s", uri.getLastPathSegment()); Timber.d("Deleting contribution id %s", uri.getLastPathSegment());
rows = db.delete(Contribution.Table.TABLE_NAME, rows = sqlDB.delete(TABLE_NAME,
"_id = ?", "_id = ?",
new String[] { uri.getLastPathSegment() } new String[]{uri.getLastPathSegment()}
); );
break; break;
default: default:
@ -114,17 +125,19 @@ public class ContributionsContentProvider extends ContentProvider{
return rows; return rows;
} }
@SuppressWarnings("ConstantConditions")
@Override @Override
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
Timber.d("Hello, bulk insert! (ContributionsContentProvider)"); Timber.d("Hello, bulk insert! (ContributionsContentProvider)");
int uriType = uriMatcher.match(uri); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase(); CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
sqlDB.beginTransaction(); sqlDB.beginTransaction();
switch (uriType) { switch (uriType) {
case CONTRIBUTIONS: case CONTRIBUTIONS:
for(ContentValues value: values) { for (ContentValues value : values) {
Timber.d("Inserting! %s", value); Timber.d("Inserting! %s", value);
sqlDB.insert(Contribution.Table.TABLE_NAME, null, value); sqlDB.insert(TABLE_NAME, null, value);
} }
break; break;
default: default:
@ -136,6 +149,7 @@ public class ContributionsContentProvider extends ContentProvider{
return values.length; return values.length;
} }
@SuppressWarnings("ConstantConditions")
@Override @Override
public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) { public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
/* /*
@ -146,25 +160,24 @@ public class ContributionsContentProvider extends ContentProvider{
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); int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase(); CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
int rowsUpdated = 0; SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
int rowsUpdated;
switch (uriType) { switch (uriType) {
case CONTRIBUTIONS: case CONTRIBUTIONS:
rowsUpdated = sqlDB.update(Contribution.Table.TABLE_NAME, rowsUpdated = sqlDB.update(TABLE_NAME, contentValues, selection, selectionArgs);
contentValues,
selection,
selectionArgs);
break; break;
case CONTRIBUTIONS_ID: case CONTRIBUTIONS_ID:
int id = Integer.valueOf(uri.getLastPathSegment()); int id = Integer.valueOf(uri.getLastPathSegment());
if (TextUtils.isEmpty(selection)) { if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(Contribution.Table.TABLE_NAME, rowsUpdated = sqlDB.update(TABLE_NAME,
contentValues, contentValues,
Contribution.Table.COLUMN_ID + " = ?", Contribution.Table.COLUMN_ID + " = ?",
new String[] { String.valueOf(id) } ); new String[]{String.valueOf(id)});
} else { } 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; break;
default: default:

View file

@ -1,9 +1,7 @@
package fr.free.nrw.commons.contributions; package fr.free.nrw.commons.contributions;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -30,17 +28,15 @@ import timber.log.Timber;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.app.Activity.RESULT_OK; 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 class ContributionsListFragment extends Fragment {
public interface SourceRefresher {
void refreshSource();
}
@BindView(R.id.contributionsList) GridView contributionsList; @BindView(R.id.contributionsList) GridView contributionsList;
@BindView(R.id.waitingMessage) TextView waitingMessage; @BindView(R.id.waitingMessage) TextView waitingMessage;
@BindView(R.id.emptyMessage) TextView emptyMessage; @BindView(R.id.emptyMessage) TextView emptyMessage;
private ContributionController controller; private ContributionController controller;
@Override @Override
@ -48,21 +44,21 @@ public class ContributionsListFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_contributions, container, false); View v = inflater.inflate(R.layout.fragment_contributions, container, false);
ButterKnife.bind(this, v); ButterKnife.bind(this, v);
contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener)getActivity()); contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity());
if(savedInstanceState != null) { if (savedInstanceState != null) {
Timber.d("Scrolling to %d", savedInstanceState.getInt("grid-position")); Timber.d("Scrolling to %d", savedInstanceState.getInt("grid-position"));
contributionsList.setSelection(savedInstanceState.getInt("grid-position")); contributionsList.setSelection(savedInstanceState.getInt("grid-position"));
} }
//TODO: Should this be in onResume? //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", ""); String lastModified = prefs.getString("lastSyncTimestamp", "");
Timber.d("Last Sync Timestamp: %s", lastModified); Timber.d("Last Sync Timestamp: %s", lastModified);
if (lastModified.equals("")) { if (lastModified.equals("")) {
waitingMessage.setVisibility(View.VISIBLE); waitingMessage.setVisibility(View.VISIBLE);
} else { } else {
waitingMessage.setVisibility(View.GONE); waitingMessage.setVisibility(GONE);
} }
return v; return v;
@ -91,7 +87,7 @@ public class ContributionsListFragment extends Fragment {
//FIXME: must get the file data for Google Photos when receive the intent answer, in the onActivityResult method //FIXME: must get the file data for Google Photos when receive the intent answer, in the onActivityResult method
super.onActivityResult(requestCode, resultCode, data); 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", Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
requestCode, resultCode, data); requestCode, resultCode, data);
controller.handleImagePicked(requestCode, data); controller.handleImagePicked(requestCode, data);
@ -103,7 +99,7 @@ public class ContributionsListFragment extends Fragment {
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_from_gallery: case R.id.menu_from_gallery:
//Gallery crashes before reach ShareActivity screen so must implement permissions check here //Gallery crashes before reach ShareActivity screen so must implement permissions check here
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@ -111,7 +107,7 @@ public class ContributionsListFragment extends Fragment {
// Here, thisActivity is the current activity // Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(getActivity(), if (ContextCompat.checkSelfPermission(getActivity(),
READ_EXTERNAL_STORAGE) READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) { != PERMISSION_GRANTED) {
// Should we show an explanation? // Should we show an explanation?
if (shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE)) { if (shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE)) {
@ -161,14 +157,15 @@ public class ContributionsListFragment extends Fragment {
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
Timber.d("onRequestPermissionsResult: req code = " + " perm = " + permissions + " grant =" + grantResults); Timber.d("onRequestPermissionsResult: req code = " + " perm = "
+ permissions + " grant =" + grantResults);
switch (requestCode) { switch (requestCode) {
// 1 = Storage allowed when gallery selected // 1 = Storage allowed when gallery selected
case 1: { case 1: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
Timber.d("Call controller.startGalleryPick()"); Timber.d("Call controller.startGalleryPick()");
controller.startGalleryPick(); controller.startGalleryPick();
} }
@ -176,7 +173,7 @@ public class ContributionsListFragment extends Fragment {
break; break;
// 2 = Location allowed when 'nearby places' selected // 2 = Location allowed when 'nearby places' selected
case 2: { case 2: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
Timber.d("Location permission granted"); Timber.d("Location permission granted");
Intent nearbyIntent = new Intent(getActivity(), NearbyActivity.class); Intent nearbyIntent = new Intent(getActivity(), NearbyActivity.class);
startActivity(nearbyIntent); startActivity(nearbyIntent);
@ -191,7 +188,8 @@ public class ContributionsListFragment extends Fragment {
menu.clear(); // See http://stackoverflow.com/a/8495697/17865 menu.clear(); // See http://stackoverflow.com/a/8495697/17865
inflater.inflate(R.menu.fragment_contributions_list, menu); 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); menu.findItem(R.id.menu_from_camera).setEnabled(false);
} }
} }
@ -215,6 +213,10 @@ public class ContributionsListFragment extends Fragment {
} }
protected void clearSyncMessage() { protected void clearSyncMessage() {
waitingMessage.setVisibility(View.GONE); waitingMessage.setVisibility(GONE);
}
public interface SourceRefresher {
void refreshSource();
} }
} }

View file

@ -23,8 +23,18 @@ import fr.free.nrw.commons.mwapi.LogEventResult;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber; 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 { 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; private static int COMMIT_THRESHOLD = 10;
public ContributionsSyncAdapter(Context context, boolean autoInitialize) { public ContributionsSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize); super(context, autoInitialize);
} }
@ -36,39 +46,38 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
return limit; // FIXME: Parameterize! 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) { private boolean fileExists(ContentProviderClient client, String filename) {
Cursor cursor = null; Cursor cursor = null;
try { try {
cursor = client.query(ContributionsContentProvider.BASE_URI, cursor = client.query(BASE_URI,
existsQuery, existsQuery,
existsSelection, existsSelection,
new String[] { filename }, new String[]{filename},
"" ""
); );
return cursor.getCount() != 0; return cursor != null && cursor.getCount() != 0;
} catch (RemoteException e) { } catch (RemoteException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally { } finally {
if ( cursor != null ) { if (cursor != null) {
cursor.close(); cursor.close();
} }
} }
} }
@Override @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! // This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
String user = account.name; String user = account.name;
MediaWikiApi api = CommonsApplication.getInstance().getMWApi(); 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", ""); String lastModified = prefs.getString("lastSyncTimestamp", "");
Date curTime = new Date(); Date curTime = new Date();
LogEventResult result; LogEventResult result;
Boolean done = false; Boolean done = false;
String queryContinue = null; String queryContinue = null;
while(!done) { while (!done) {
try { try {
result = api.logEvents(user, lastModified, queryContinue, getLimit()); result = api.logEvents(user, lastModified, queryContinue, getLimit());
@ -90,19 +99,21 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
continue; continue;
} }
String filename = image.getFilename(); String filename = image.getFilename();
if(fileExists(contentProviderClient, filename)) { if (fileExists(contentProviderClient, filename)) {
Timber.d("Skipping %s", filename); Timber.d("Skipping %s", filename);
continue; continue;
} }
String thumbUrl = Utils.makeThumbBaseUrl(filename); String thumbUrl = Utils.makeThumbBaseUrl(filename);
Date dateUpdated = image.getDateUpdated(); Date dateUpdated = image.getDateUpdated();
Contribution contrib = new Contribution(null, thumbUrl, filename, "", -1, dateUpdated, dateUpdated, user, "", ""); Contribution contrib = new Contribution(null, thumbUrl, filename,
contrib.setState(Contribution.STATE_COMPLETED); "", -1, dateUpdated, dateUpdated, user,
"", "");
contrib.setState(STATE_COMPLETED);
imageValues.add(contrib.toContentValues()); imageValues.add(contrib.toContentValues());
if(imageValues.size() % COMMIT_THRESHOLD == 0) { if (imageValues.size() % COMMIT_THRESHOLD == 0) {
try { try {
contentProviderClient.bulkInsert(ContributionsContentProvider.BASE_URI, imageValues.toArray(new ContentValues[]{})); contentProviderClient.bulkInsert(BASE_URI, imageValues.toArray(EMPTY));
} catch (RemoteException e) { } catch (RemoteException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -110,20 +121,21 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
} }
} }
if(imageValues.size() != 0) { if (imageValues.size() != 0) {
try { try {
contentProviderClient.bulkInsert(ContributionsContentProvider.BASE_URI, imageValues.toArray(new ContentValues[]{})); contentProviderClient.bulkInsert(BASE_URI, imageValues.toArray(EMPTY));
} catch (RemoteException e) { } catch (RemoteException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
queryContinue = result.getQueryContinue(); queryContinue = result.getQueryContinue();
if(TextUtils.isEmpty(queryContinue)) { if (TextUtils.isEmpty(queryContinue)) {
done = true; done = true;
} }
} }
prefs.edit().putString("lastSyncTimestamp", Utils.toMWDate(curTime)).apply(); prefs.edit().putString("lastSyncTimestamp", Utils.toMWDate(curTime)).apply();
Timber.d("Oh hai, everyone! Look, a kitty!"); Timber.d("Oh hai, everyone! Look, a kitty!");
} }
} }

View file

@ -1,11 +1,8 @@
package fr.free.nrw.commons.media; package fr.free.nrw.commons.media;
import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.DownloadManager; import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.DataSetObserver; import android.database.DataSetObserver;
import android.net.Uri; import android.net.Uri;
import android.os.Build; 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.contributions.ContributionsActivity;
import fr.free.nrw.commons.mwapi.EventLog; 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 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 ViewPager pager;
private Boolean editable; private Boolean editable;
private CommonsApplication app;
public MediaDetailPagerFragment() { public MediaDetailPagerFragment() {
this(false); this(false);
@ -61,30 +51,10 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
this.editable = editable; 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 @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); View view = inflater.inflate(R.layout.fragment_media_detail_pager, container, false);
pager = (ViewPager) view.findViewById(R.id.mediaDetailsPager); pager = (ViewPager) view.findViewById(R.id.mediaDetailsPager);
pager.addOnPageChangeListener(this); pager.addOnPageChangeListener(this);
@ -120,18 +90,18 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
if (savedInstanceState != null) { if (savedInstanceState != null) {
editable = savedInstanceState.getBoolean("editable"); editable = savedInstanceState.getBoolean("editable");
} }
app = CommonsApplication.getInstance();
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
MediaDetailProvider provider = (MediaDetailProvider)getActivity(); MediaDetailProvider provider = (MediaDetailProvider) getActivity();
Media m = provider.getMediaAtPosition(pager.getCurrentItem()); Media m = provider.getMediaAtPosition(pager.getCurrentItem());
switch(item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_share_current_image: case R.id.menu_share_current_image:
// Share - this is just logs it, intent set in onCreateOptionsMenu, around line 252 // 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("username", app.getCurrentAccount().name)
.param("filename", m.getFilename()) .param("filename", m.getFilename())
.log(); .log();
@ -139,7 +109,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
case R.id.menu_browser_current_image: case R.id.menu_browser_current_image:
// View in browser // View in browser
Intent viewIntent = new Intent(); Intent viewIntent = new Intent();
viewIntent.setAction(Intent.ACTION_VIEW); viewIntent.setAction(ACTION_VIEW);
viewIntent.setData(m.getFilePageTitle().getMobileUri()); viewIntent.setData(m.getFilePageTitle().getMobileUri());
startActivity(viewIntent); startActivity(viewIntent);
return true; return true;
@ -149,12 +119,12 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
return true; return true;
case R.id.menu_retry_current_image: case R.id.menu_retry_current_image:
// Retry // Retry
((ContributionsActivity)getActivity()).retryUpload(pager.getCurrentItem()); ((ContributionsActivity) getActivity()).retryUpload(pager.getCurrentItem());
getActivity().getSupportFragmentManager().popBackStack(); getActivity().getSupportFragmentManager().popBackStack();
return true; return true;
case R.id.menu_cancel_current_image: case R.id.menu_cancel_current_image:
// todo: delete image // todo: delete image
((ContributionsActivity)getActivity()).deleteUpload(pager.getCurrentItem()); ((ContributionsActivity) getActivity()).deleteUpload(pager.getCurrentItem());
getActivity().getSupportFragmentManager().popBackStack(); getActivity().getSupportFragmentManager().popBackStack();
return true; return true;
default: default:
@ -170,7 +140,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
*/ */
private void downloadMedia(Media m) { private void downloadMedia(Media m) {
String imageUrl = m.getImageUrl(), String imageUrl = m.getImageUrl(),
fileName = m.getFilename(); fileName = m.getFilename();
// Strip 'File:' from beginning of filename, we really shouldn't store it // Strip 'File:' from beginning of filename, we really shouldn't store it
fileName = fileName.replaceFirst("^File:", ""); fileName = fileName.replaceFirst("^File:", "");
Uri imageUri = Uri.parse(imageUrl); Uri imageUri = Uri.parse(imageUrl);
@ -185,14 +155,15 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
req.allowScanningByMediaScanner(); req.allowScanningByMediaScanner();
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); 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)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !(ContextCompat.checkSelfPermission(getContext(),
READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED)) {
Snackbar.make(getView(), R.string.storage_permission_rationale, Snackbar.make(getView(), R.string.storage_permission_rationale,
Snackbar.LENGTH_INDEFINITE) Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok,
.setAction(R.string.ok, view -> ActivityCompat.requestPermissions(getActivity(), view -> ActivityCompat.requestPermissions(getActivity(),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1)).show(); new String[]{READ_EXTERNAL_STORAGE}, 1)).show();
} else { } else {
final DownloadManager manager = (DownloadManager)getActivity().getSystemService(Context.DOWNLOAD_SERVICE); ((DownloadManager) getActivity().getSystemService(DOWNLOAD_SERVICE)).enqueue(req);
manager.enqueue(req);
} }
} }
@ -202,7 +173,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
menu.clear(); // see http://stackoverflow.com/a/8495697/17865 menu.clear(); // see http://stackoverflow.com/a/8495697/17865
inflater.inflate(R.menu.fragment_image_detail, menu); inflater.inflate(R.menu.fragment_image_detail, menu);
if (pager != null) { if (pager != null) {
MediaDetailProvider provider = (MediaDetailProvider)getActivity(); MediaDetailProvider provider = (MediaDetailProvider) getActivity();
Media m = provider.getMediaAtPosition(pager.getCurrentItem()); Media m = provider.getMediaAtPosition(pager.getCurrentItem());
if (m != null) { if (m != null) {
// Enable default set of actions, then re-enable different set of actions only if it is a failed contrib // 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) { if (m instanceof Contribution) {
Contribution c = (Contribution)m; Contribution c = (Contribution) m;
switch(c.getState()) { switch (c.getState()) {
case Contribution.STATE_FAILED: case Contribution.STATE_FAILED:
menu.findItem(R.id.menu_retry_current_image).setEnabled(true).setVisible(true); menu.findItem(R.id.menu_retry_current_image).setEnabled(true).setVisible(true);
menu.findItem(R.id.menu_cancel_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 @Override
public void onPageScrollStateChanged(int i) { 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();
}
} }
} }

View file

@ -63,10 +63,10 @@ public class NearbyInfoDialog extends OverlayDialog {
overflowButton.setVisibility(showMenu() ? View.VISIBLE : View.GONE); 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 popupMenu = new PopupMenu(getActivity(), overflowButton);
popupMenu.inflate(R.menu.nearby_info_dialog_options); popupMenu.inflate(R.menu.nearby_info_dialog_options);