diff --git a/app/build.gradle b/app/build.gradle index b5e2c2303..f43fa2820 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,6 +93,8 @@ android { buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.org/wiki/\"" buildConfigField "String", "EVENTLOG_URL", "\"https://www.wikimedia.org/beacon/event\"" buildConfigField "String", "EVENTLOG_WIKI", "\"commonswiki\"" + buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\"" + buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Main_Page&welcome=yes\"" } beta { @@ -104,6 +106,8 @@ android { buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/wiki/\"" buildConfigField "String", "EVENTLOG_URL", "\"https://commons.wikimedia.beta.wmflabs.org/beacon/event\"" buildConfigField "String", "EVENTLOG_WIKI", "\"commonswiki\"" + buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\"" + buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Main_Page&welcome=yes\"" } } diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java index 25d5c4b96..63f460ffd 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java @@ -28,7 +28,6 @@ import fr.free.nrw.commons.di.CommonsApplicationModule; import fr.free.nrw.commons.di.DaggerCommonsApplicationComponent; import fr.free.nrw.commons.modifications.ModifierSequence; import fr.free.nrw.commons.mwapi.MediaWikiApi; -import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.utils.FileUtils; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; @@ -100,7 +99,7 @@ public class CommonsApplication extends DaggerApplication { return component; } - 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()) { @@ -141,4 +140,8 @@ public class CommonsApplication extends DaggerApplication { Category.Table.onDelete(db); Contribution.Table.onDelete(db); } + + public interface LogoutListener { + void onLogoutComplete(); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/LicenseList.java b/app/src/main/java/fr/free/nrw/commons/LicenseList.java index 7c91620ff..ab32f8815 100644 --- a/app/src/main/java/fr/free/nrw/commons/LicenseList.java +++ b/app/src/main/java/fr/free/nrw/commons/LicenseList.java @@ -12,18 +12,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 licenses = new HashMap<>(); - Resources res; - - private static String XMLNS_LICENSE = "https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses"; + private Map licenses = new HashMap<>(); + private Resources res; public LicenseList(Activity activity) { res = activity.getResources(); XmlPullParser parser = res.getXml(R.xml.wikimedia_licenses); - while (xmlFastForward(parser, XMLNS_LICENSE, "license")) { + String namespace = "https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses"; + while (xmlFastForward(parser, namespace, "license")) { String id = parser.getAttributeValue(null, "id"); String template = parser.getAttributeValue(null, "template"); String url = parser.getAttributeValue(null, "url"); @@ -33,10 +31,6 @@ public class LicenseList { } } - public Set keySet() { - return licenses.keySet(); - } - public Collection values() { return licenses.values(); } @@ -46,7 +40,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())) { @@ -56,27 +50,17 @@ 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; } /** diff --git a/app/src/main/java/fr/free/nrw/commons/Media.java b/app/src/main/java/fr/free/nrw/commons/Media.java index 1d07e9c16..26a1d038b 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.java +++ b/app/src/main/java/fr/free/nrw/commons/Media.java @@ -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 categories; // as loaded at runtime? + private Map descriptions; // multilingual descriptions as loaded + private HashMap tags = new HashMap<>(); + private @Nullable LatLng coordinates; + protected Media() { this.categories = new ArrayList<>(); this.descriptions = new HashMap<>(); } - private HashMap 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) 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 categories; // as loaded at runtime? - protected Map descriptions; // multilingual descriptions as loaded - + @SuppressWarnings("unchecked") public ArrayList getCategories() { - return (ArrayList)categories.clone(); // feels dirty + return (ArrayList) categories.clone(); // feels dirty } public void setCategories(List categories) { @@ -171,7 +215,7 @@ public class Media implements Parcelable { this.categories.addAll(categories); } - public void setDescriptions(Map descriptions) { + void setDescriptions(Map 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)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; - } } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/AuthenticatedActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/AuthenticatedActivity.java index dd8c6265e..d24d00d0c 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/AuthenticatedActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/AuthenticatedActivity.java @@ -22,13 +22,14 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity { private String authCookie; + private void getAuthCookie(Account account, AccountManager accountManager) { Single.fromCallable(() -> accountManager.blockingGetAuthToken(account, "", false)) .subscribeOn(Schedulers.io()) .doOnError(Timber::e) .observeOn(AndroidSchedulers.mainThread()) .subscribe( - this::onAuthCookieAcquired, + this:: onAuthCookieAcquired, throwable -> onAuthFailure()); } @@ -55,24 +56,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 = sessionManager.getCurrentAccount(); - if(curAccount == null) { + if (curAccount == null) { addAccount(accountManager); } else { getAuthCookie(curAccount, accountManager); } } - + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if(savedInstanceState != null) { + if (savedInstanceState != null) { authCookie = savedInstanceState.getString("authCookie"); } } @@ -84,5 +85,6 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity { } protected abstract void onAuthCookieAcquired(String authCookie); + protected abstract void onAuthFailure(); } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.java index a30c6a013..1ea15ff65 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.java @@ -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.theme.BaseActivity; import timber.log.Timber; @@ -26,13 +27,13 @@ public class SignupActivity extends BaseActivity { //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); diff --git a/app/src/main/java/fr/free/nrw/commons/auth/WikiAccountAuthenticator.java b/app/src/main/java/fr/free/nrw/commons/auth/WikiAccountAuthenticator.java index b10af5589..c08e27966 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/WikiAccountAuthenticator.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/WikiAccountAuthenticator.java @@ -32,7 +32,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator { private final Context context; private MediaWikiApi mediaWikiApi; - public WikiAccountAuthenticator(Context context, MediaWikiApi mwApi) { + WikiAccountAuthenticator(Context context, MediaWikiApi mwApi) { super(context); this.context = context; this.mediaWikiApi = mwApi; @@ -98,7 +98,8 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator { } @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); @@ -154,8 +155,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(); } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoriesRenderer.java b/app/src/main/java/fr/free/nrw/commons/category/CategoriesRenderer.java index a138736cc..81cccdb72 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoriesRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoriesRenderer.java @@ -31,15 +31,12 @@ class CategoriesRenderer extends Renderer { @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); } }); } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java index ab6141392..96e6936ac 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java @@ -101,6 +101,7 @@ public class CategorizationFragment extends DaggerFragment { categoriesCache = new HashMap<>(); if (savedInstanceState != null) { items.addAll(savedInstanceState.getParcelableArrayList("currentCategories")); + //noinspection unchecked categoriesCache.putAll((HashMap>) savedInstanceState .getSerializable("categoriesCache")); } @@ -206,7 +207,7 @@ public class CategorizationFragment extends DaggerFragment { .observeOn(AndroidSchedulers.mainThread()) .subscribe( s -> categoriesAdapter.add(s), - Timber::e, + Timber::e, () -> { categoriesAdapter.notifyDataSetChanged(); categoriesSearchInProgress.setVisibility(View.GONE); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java index 4c597dcce..9c336429c 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java @@ -17,18 +17,23 @@ 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); @@ -51,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 @@ -95,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); @@ -118,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: @@ -148,13 +153,12 @@ public class CategoryContentProvider extends ContentProvider { 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"); diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java index 9d47bcc7f..e673c7d9d 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java @@ -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 diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java index 481b4cc7f..a243330c3 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java @@ -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 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"); } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java index 3ea00778b..a9384d2fa 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java @@ -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; @@ -41,12 +40,18 @@ import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; -public class ContributionsActivity extends AuthenticatedActivity - implements LoaderManager.LoaderCallbacks, - 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, + AdapterView.OnItemClickListener, + MediaDetailPagerFragment.MediaDetailProvider, + FragmentManager.OnBackStackChangedListener, + ContributionsListFragment.SourceRefresher { @Inject MediaWikiApi mediaWikiApi; @Inject SessionManager sessionManager; @@ -68,14 +73,18 @@ public class ContributionsActivity extends AuthenticatedActivity 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; } @@ -113,13 +122,14 @@ public class ContributionsActivity extends AuthenticatedActivity @Override protected void onAuthCookieAcquired(String authCookie) { // Do a sync everytime we get here! - ContentResolver.requestSync(sessionManager.getCurrentAccount(), ContributionsContentProvider.AUTHORITY, new Bundle()); + requestSync(sessionManager.getCurrentAccount(), ContributionsContentProvider.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); } @@ -132,12 +142,13 @@ public class ContributionsActivity extends AuthenticatedActivity // 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(); @@ -148,23 +159,25 @@ public class ContributionsActivity extends AuthenticatedActivity @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()) { 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); } @@ -172,7 +185,7 @@ public class ContributionsActivity extends AuthenticatedActivity 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 { @@ -183,9 +196,9 @@ public class ContributionsActivity extends AuthenticatedActivity 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()); @@ -222,19 +235,18 @@ public class ContributionsActivity extends AuthenticatedActivity @Override public Loader 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 cursorLoader, Cursor cursor) { if (contributionsList.getAdapter() == null) { - contributionsList - .setAdapter(new ContributionsListAdapter(getApplicationContext(), cursor, 0)); + contributionsList.setAdapter(new ContributionsListAdapter(getApplicationContext(), + cursor, 0)); } else { ((CursorAdapter) contributionsList.getAdapter()).swapCursor(cursor); } @@ -269,6 +281,7 @@ public class ContributionsActivity extends AuthenticatedActivity return contributionsList.getAdapter().getCount(); } + @SuppressWarnings("ConstantConditions") private void setUploadCount() { compositeDisposable.add( mediaWikiApi @@ -276,12 +289,10 @@ public class ContributionsActivity extends AuthenticatedActivity .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") ) ); } @@ -337,8 +348,7 @@ public class ContributionsActivity extends AuthenticatedActivity } public static void startYourself(Context context) { - Intent contributionsIntent = new Intent(context, ContributionsActivity.class); - context.startActivity(contributionsIntent); + context.startActivity(new Intent(context, ContributionsActivity.class)); } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java index ec88b6299..cfc25b36f 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java @@ -16,17 +16,20 @@ import dagger.android.AndroidInjection; import fr.free.nrw.commons.data.DBOpenHelper; 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); @@ -44,25 +47,28 @@ public class ContributionsContentProvider extends ContentProvider{ return true; } + @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 = dbOpenHelper.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 @@ -82,6 +88,7 @@ public class ContributionsContentProvider extends ContentProvider{ return null; } + @SuppressWarnings("ConstantConditions") @Override public Uri insert(@NonNull Uri uri, ContentValues contentValues) { int uriType = uriMatcher.match(uri); @@ -89,7 +96,7 @@ public class ContributionsContentProvider extends ContentProvider{ long id = 0; 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); @@ -98,19 +105,20 @@ 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 = dbOpenHelper.getReadableDatabase(); - switch(uriType) { + switch (uriType) { case CONTRIBUTIONS_ID: Timber.d("Deleting contribution id %s", uri.getLastPathSegment()); - rows = db.delete(Contribution.Table.TABLE_NAME, + rows = db.delete(TABLE_NAME, "_id = ?", - new String[] { uri.getLastPathSegment() } + new String[]{uri.getLastPathSegment()} ); break; default: @@ -120,6 +128,7 @@ 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)"); @@ -128,9 +137,9 @@ public class ContributionsContentProvider extends ContentProvider{ 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: @@ -142,6 +151,7 @@ public class ContributionsContentProvider extends ContentProvider{ return values.length; } + @SuppressWarnings("ConstantConditions") @Override public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) { /* @@ -156,21 +166,19 @@ public class ContributionsContentProvider extends ContentProvider{ int rowsUpdated = 0; 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: diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java index d244f970c..8570e7c6e 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java @@ -1,6 +1,5 @@ package fr.free.nrw.commons.contributions; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -29,17 +28,15 @@ import timber.log.Timber; import static android.Manifest.permission.READ_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 DaggerFragment { - public interface SourceRefresher { - void refreshSource(); - } - @BindView(R.id.contributionsList) GridView contributionsList; @BindView(R.id.waitingMessage) TextView waitingMessage; @BindView(R.id.emptyMessage) TextView emptyMessage; - private ContributionController controller; @Override @@ -47,21 +44,21 @@ public class ContributionsListFragment extends DaggerFragment { 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; @@ -90,7 +87,7 @@ public class ContributionsListFragment extends DaggerFragment { //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); @@ -102,7 +99,7 @@ public class ContributionsListFragment extends DaggerFragment { @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) { @@ -110,7 +107,7 @@ public class ContributionsListFragment extends DaggerFragment { // 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)) { @@ -160,14 +157,15 @@ public class ContributionsListFragment extends DaggerFragment { } @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(); } @@ -175,7 +173,7 @@ public class ContributionsListFragment extends DaggerFragment { 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); @@ -220,6 +218,10 @@ public class ContributionsListFragment extends DaggerFragment { } protected void clearSyncMessage() { - waitingMessage.setVisibility(View.GONE); + waitingMessage.setVisibility(GONE); + } + + public interface SourceRefresher { + void refreshSource(); } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java index bd54c8568..8b331e885 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java @@ -28,9 +28,19 @@ 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; + @SuppressWarnings("WeakerAccess") @Inject MediaWikiApi mwApi; public ContributionsSyncAdapter(Context context, boolean autoInitialize) { @@ -44,19 +54,16 @@ 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}, "" ); - return cursor.getCount() != 0; + return cursor != null && cursor.getCount() != 0; } catch (RemoteException e) { throw new RuntimeException(e); } finally { @@ -67,12 +74,12 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { } @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) { ((CommonsApplication) getContext().getApplicationContext()).injector().inject(this); - // This code is fraught with possibilities of race conditions, but lalalalala I can't hear you! String user = account.name; - SharedPreferences prefs = getContext().getSharedPreferences("prefs", Context.MODE_PRIVATE); + SharedPreferences prefs = getContext().getSharedPreferences("prefs", MODE_PRIVATE); String lastModified = prefs.getString("lastSyncTimestamp", ""); Date curTime = new Date(); LogEventResult result; @@ -106,13 +113,15 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { } 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) { try { - contentProviderClient.bulkInsert(ContributionsContentProvider.BASE_URI, imageValues.toArray(new ContentValues[]{})); + contentProviderClient.bulkInsert(BASE_URI, imageValues.toArray(EMPTY)); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -122,7 +131,7 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { 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); } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index fa621218a..dc2295f97 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -280,13 +280,13 @@ public class MediaDetailFragment extends DaggerFragment { 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); diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index 94f640240..49a40b546 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -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; @@ -39,20 +36,14 @@ import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.mwapi.EventLog; import fr.free.nrw.commons.mwapi.MediaWikiApi; +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 DaggerFragment implements ViewPager.OnPageChangeListener { - public interface MediaDetailProvider { - Media getMediaAtPosition(int i); - - int getTotalMediaCount(); - - void notifyDatasetChanged(); - - void registerDataSetObserver(DataSetObserver observer); - - void unregisterDataSetObserver(DataSetObserver observer); - } - @Inject MediaWikiApi mwApi; @Inject SessionManager sessionManager; @@ -68,30 +59,10 @@ public class MediaDetailPagerFragment extends DaggerFragment implements ViewPage 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); @@ -137,7 +108,8 @@ public class MediaDetailPagerFragment extends DaggerFragment implements ViewPage 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, getContext().getApplicationContext(), mwApi) + CommonsApplication app = (CommonsApplication) getActivity().getApplication(); + EventLog.schema(EVENT_SHARE_ATTEMPT, getContext().getApplicationContext(), mwApi) .param("username", sessionManager.getCurrentAccount().name) .param("filename", m.getFilename()) .log(); @@ -145,7 +117,7 @@ public class MediaDetailPagerFragment extends DaggerFragment implements ViewPage 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; @@ -191,17 +163,13 @@ public class MediaDetailPagerFragment extends DaggerFragment implements ViewPage 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)) { + 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.LENGTH_INDEFINITE) - .setAction(R.string.ok, view -> ActivityCompat.requestPermissions(getActivity(), - new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1)).show(); + 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); } } @@ -276,6 +244,39 @@ public class MediaDetailPagerFragment extends DaggerFragment implements ViewPage @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(); + } } } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/CategoryModifier.java b/app/src/main/java/fr/free/nrw/commons/modifications/CategoryModifier.java index 8f20c6e50..bb650513b 100644 --- a/app/src/main/java/fr/free/nrw/commons/modifications/CategoryModifier.java +++ b/app/src/main/java/fr/free/nrw/commons/modifications/CategoryModifier.java @@ -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("]]"); diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java index 67152f85b..f2accfd51 100644 --- a/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java +++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java @@ -48,7 +48,7 @@ public class ModifierSequence { } public String getEditSummary() { - StringBuffer editSummary = new StringBuffer(); + StringBuilder editSummary = new StringBuilder(); for(PageModifier modifier: modifiers) { editSummary.append(modifier.getEditSumary()).append(" "); } diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index e7efd4cba..1e059f59b 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -32,6 +32,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; @@ -236,8 +237,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } return categories; - }) - .flatMapObservable(list -> Observable.fromIterable(list)); + }).flatMapObservable(Observable::fromIterable); } @Override @@ -266,15 +266,14 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } return categories; - }) - .flatMapObservable(list -> Observable.fromIterable(list)); + }).flatMapObservable(Observable::fromIterable); } @Override @NonNull public Observable searchTitles(String title, int searchCatsLimit) { - return Single.fromCallable(() -> { - ArrayList categoryNodes = null; + return Single.fromCallable((Callable>) () -> { + ArrayList categoryNodes; try { categoryNodes = api.action("query") @@ -288,7 +287,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) { @@ -303,8 +302,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } return titleCategories; - }) - .flatMapObservable(list -> Observable.fromIterable(list)); + }).flatMapObservable(Observable::fromIterable); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyInfoDialog.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyInfoDialog.java index 1203d5a0d..2e677f33d 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyInfoDialog.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyInfoDialog.java @@ -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); diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java index d256e4c21..3537e4a1d 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java @@ -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; @@ -120,17 +118,11 @@ public class NavigationBaseActivity extends BaseActivity new AlertDialog.Builder(this) .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(); - })) + .setPositiveButton(R.string.yes, (dialog, which) -> { + BaseLogoutListener logoutListener = new BaseLogoutListener(); + CommonsApplication app = (CommonsApplication) getApplication(); + app.clearApplicationData(this, logoutListener); + }) .setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel()) .show(); return true; @@ -139,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(); + } } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java index 36ff9c1eb..d20d8c1eb 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java @@ -77,8 +77,8 @@ public class MultipleUploadListFragment extends DaggerFragment { 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 DaggerFragment { .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 DaggerFragment { 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 DaggerFragment { @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 DaggerFragment { @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 DaggerFragment { 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 DaggerFragment { @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(""); } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java index f2827e832..a742a6a1c 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java @@ -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; @@ -40,13 +39,10 @@ import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.settings.Prefs; import timber.log.Timber; -public class SingleUploadFragment extends DaggerFragment { - 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 DaggerFragment { @BindView(R.id.titleEdit) EditText titleEdit; @BindView(R.id.descEdit) EditText descEdit; @@ -54,13 +50,15 @@ public class SingleUploadFragment extends DaggerFragment { @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); } } @@ -90,7 +88,7 @@ public class SingleUploadFragment extends DaggerFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.fragment_single_upload, null); + View rootView = inflater.inflate(R.layout.fragment_single_upload, container, false); ButterKnife.bind(this, rootView); @@ -113,10 +111,10 @@ public class SingleUploadFragment extends DaggerFragment { Timber.d(license); ArrayAdapter 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); } @@ -147,26 +145,27 @@ public class SingleUploadFragment extends DaggerFragment { 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); @@ -178,10 +177,9 @@ public class SingleUploadFragment extends DaggerFragment { 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(licenseUrlFor(license))); @@ -192,7 +190,8 @@ public class SingleUploadFragment extends DaggerFragment { } } - @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", ""); @@ -206,57 +205,41 @@ public class SingleUploadFragment extends DaggerFragment { /** * 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)))); } @@ -297,16 +280,22 @@ public class SingleUploadFragment extends DaggerFragment { throw new RuntimeException("Unrecognized license value: " + license); } + 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(); } } diff --git a/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java index 4d50f32aa..1c816b9e1 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java @@ -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( diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 108eadc9b..287daaa76 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -47,6 +47,8 @@ تصنيفات البحث احفظ أنعش + تفعيل GPS + لا مرفوعات بعد لا توجد تصنيفات تطابق %1$s أضف التصانيف لتسهل اكتشاف صورك على ويكيميديا كومنز.\n\nابدأ الكتاب لتضيف التصانيف.\nانقر هذه الرسالة لتتجاوز هذه الخطوة. تصنيفات @@ -81,10 +83,22 @@ ترخيص غير معلوم تحديث موافق + الأماكن القريبة تحذير نعم لا العنوان + عنوان الوسيط الوصف + الترخيص + الإحداثيات + استخدم ويكي بيانات + الحد الأقصى + الرئيسية + رفع + بالقرب من هنا + حول الإعدادات + تسجيل الخروج + صفحة ملف كومنز diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 21a05851c..b42e75286 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -85,6 +85,7 @@ পুনঃচেষ্টা করুন বাতিল এই ছবিটি %1$s এর অধীনে লাইসেন্স করা হবে + এই চিত্র জমা দেয়ার দ্বারা, আমি ঘোষণা দিচ্ছি যে এটি আমার নিজস্ব কাজ, এতে কপিরাইটযুক্ত উপাদান নেই বা এটই সেলফি নয়, এবং <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines/bn\">উইকিমিডিয়া কমন্স নীতি</a> অনুসরণ করে। ডাউনলোড লাইসেন্স পূর্ববর্তী শিরোনাম/বিবরণ ব্যবহার করুন @@ -148,6 +149,7 @@ আপলোডের তারিখ লাইসেন্স স্থানাঙ্কসমূহ + কিছু দেয়া হয়নি বিটা টেস্টার হোন উইকিউপাত্ত ব্যবহার করুন (সতর্কতা: এটি নিষ্ক্রিয় করা অধিক পরিমাণে মোবাইল ডেটা খরচ হওয়ার কারণ হতে পারে) @@ -166,6 +168,7 @@ রংধনুর সেতু টিউলিপ কোন সেলফি নয় + মালিকানা চিত্র উইকিপিডিয়ায় স্বাগতম স্বাগতম কপিরাইট সিডনি অপেরা হাউস diff --git a/app/src/main/res/values-diq/strings.xml b/app/src/main/res/values-diq/strings.xml index 8d04e2d08..783591cdd 100644 --- a/app/src/main/res/values-diq/strings.xml +++ b/app/src/main/res/values-diq/strings.xml @@ -111,4 +111,11 @@ Tembe kem E + Sername + Lisans + Koordinati + Bıtexelne + Ake + Keye + Bar ke diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index eafc67609..d088e1970 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -14,7 +14,7 @@ Tunnistautuminen epäonnistui! Tallentaminen aloitettiin! %1$s tallennettiin! - Napauta katsoaksesi tallennuksen + Napauta katsoaksesi tallennusta Aloitetaan tiedoston %1$s tallennusta Tallennetaan %1$s %1$s tallennettu @@ -29,7 +29,7 @@ Epäonnistui %1$d%% valmis Tallennetaan - Olemassaoleva + Galleriasta Ota kuva Lähistöllä Omat tallennukset @@ -39,7 +39,7 @@ Kuvaus Kirjautuminen epäonnistui - verkkovirhe Kirjautuminen epäonnistui - tarkista käyttäjätunnus - Kirjautuminen epäonnistui - tarkista salasana + Kirjautuminen epäonnistui - tarkista salasanasi Liikaa epäonnistuneita yrityksiä. Yritä uudelleen parin minuutin kuluttua. Pahoittelut, tämä käyttäjä on estetty Commonsissa Anna kaksivaiheisen tunnistuksen koodi. @@ -54,18 +54,18 @@ GPS ei ole käytössä. Haluatko ottaa sen käyttöön? Ota GPS käyttöön Ei tallennuksia vielä - - Ei tallennuksia - 1 tallennus + + \@string/contributions_subtitle_zero + %d tallennus %d tallennusta - - 1 tallennus alkaa - %d tallennusta alkaa + + aloitetaan %d tallennus + aloitetaan %d tallennusta - - 1 lataus - %d latausta + + %d tallennus + %d tallennusta Luokkaa %1$s ei löytynyt Lisää luokkia tehdäksesi kuvistasi helpommin löydettäviä.\n\nAloita kirjoittaminen lisätäksesi luokkia.\nNapauta tätä viestiä (tai paina takaisin) ohittaaksesi tämän vaiheen. @@ -73,7 +73,7 @@ Asetukset Rekisteröidy Tietoja - Tämä on vapaan lähdekoodin ohjelmisto, joka on julkaistu <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache License v2</a> -lisenssin alaisena. Wikimedia Commons ja sen logo ovat Wikimedia Foundationin tavaramerkkejä ja niitä käytetään Wikimedia Foundationin luvalla. Emme ole hyväksyttyjä tai sidoksissa Wikimedia Foundationioniin. + Tämä on vapaan lähdekoodin ohjelmisto, joka on julkaistu <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache License v2</a> -lisenssin alaisena. Wikimedia Commons ja sen logo ovat Wikimedia Foundationin tavaramerkkejä ja niitä käytetään Wikimedia Foundationin luvalla. Emme ole hyväksyttyjä tai sidoksissa Wikimedia Foundationioniin. <a href=\"https://github.com/commons-app/apps-android-commons\">Lähde</a> ja <a href=\"https://commons-app.github.io/\">nettisivusto</a> GitHubissa. Luo uusi <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub-issue</a> bugiraporteille ja ehdotuksille. <a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">Yksityisyydensuoja</a> <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">Tekijät</a> @@ -86,6 +86,7 @@ Yritä uudelleen Peruuta Tiedosto tallennetaan lisenssin %1$s ehtojen mukaisesti. + Lisäämällä kuvan, ilmoitan tämän olevan oma työ ja että se ei sisällä tekijänoikeuden alaista materiaalia tai selfietä ja muuten noudattaa <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons policies</a>. Lataa Lisenssi Käytä edellistä otsikkoa/kuvausta @@ -115,15 +116,15 @@ CC0 Wikimedia Commonsiin on tallennettu lähes kaikki Wikipediassa käytetyt kuvat. Kuvasi auttavat ihmisiä kaikkialta maailmasta ymmärtämään maailmaa! - Tallenna kuvia, jotka sinä itse olet kuvannot tai luonut: + Tallenna kuvia, jotka sinä itse olet ottanut tai luonut: - Luontokohteet (kukat, eläimet, vuoret)\n- Hyödylliset esineet (polkupyörät, rautatieasemat)\n- Kuuluisat henkilöt (kaupunginjohtajasi, tapaamasi olympiaurheilijat) - Älä tallenna seuraavia: + ÄLÄ tallenna seuraavia: - Selfiet tai kuvat ystävistäsi\n- Netistä ladatut kuvat\n- Kuvakaappaukset kaupallisista sovelluksista Tallennusesimerkki: - Nimi: Sydneyn operatalo\n- Kuvaus: Sydneyn oopperatalo katsottuna lahden toisella puolella\n- Luokat: Sydneyn oopperatalo, Sydneyn oopperatalo lännestä, Sydneyn oopperatalo remote views Herätä Wikipedia-artikkelit eloon kuvillasi! Tuo kuvasi Wikipediaan. Wikipedian kuvat tulevat Wikimedia Commonsista. - Kuvat auttavat useita ihmisiä ympäri maailmaa artikkeleiden ymmärtämisessä. + Kuvasi auttavat useita ihmisiä ympäri maailmaa artikkeleiden ymmärtämisessä. Vältä tekijänoikeuksien alaista materiaalia, kuten julisteita, kirjan kansia ja useimpia Internetistä löydettyjä kuvia. Luuletko ymmärtäneesi tämän? Kyllä! @@ -134,8 +135,9 @@ Tuntematon lisenssi Päivitä Vaadittu oikeus: Ulkoisen tallennustilan luku. Appi ei toimi ilman tätä oikeutta. + Valinnainen lupa: Saada tämänhetkinen sijainti loukkasuosituksia varten. OK - Läheiset paikat + Lähellä olevat paikat Lähistöltä ei löytynyt paikkoja Varoitus Tämä tiedosto on jo Wikimedia Commonsissa. Haluatko varmasti jatkaa? @@ -147,14 +149,24 @@ Tallennuspäivämäärä Lisenssi Koordinaatit + ei annettu Ryhdy beetatestaajaksi Käytä Wikidataa + (Varoitus: poiskytkeminen voi aiheuttaa suuren mobiilidatankäytön) + Maksimimäärä + Ei voida näyttää enempää, kuin 500 Kaksivaiheinen tunnistus ei ole vielä tuettu. - Haluatko kirjautua ulos? + Haluatko varmasti kirjautua ulos? + Commons Logo + Taustakuva + Kuvaa ei löytynyt Lataa kuva + Zao-vuori + Sateenkaarisilta Tulppaani Ei selfieitä Tervetuloa Wikipediaan + Tervetuloa tekijänoikeus Sydneyn oopperatalo Peru Avaa @@ -166,6 +178,9 @@ Asetukset Palaute Kirjaudu ulos + Opas + Lähellä olevia paikkoja ei voida näyttää ilman sijaintilupaa Commons-tiedostosivu Wikidata-kohde + Tiedoston yksilöllinen ja kuvaava otsikko, jota käytetään tiedostonimenä. Voit käyttää tavallista kieltä välilyönnein. Älä sisällytä tiedoston päätettä. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 34d47a881..b5853509e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -114,7 +114,7 @@ CC BY-SA 4.0 CC BY 4.0 CC Zéro - Wikimédia Communs héberge la plupart des images qui sont utilisées dans Wikipédia + Wikimedia Commons héberge la plupart des images qui sont utilisées dans Wikipédia. Vos images aident à éduquer les gens dans le monde entier ! Veuillez téléverser des images qui sont prises ou créées entièrement par vous : - Objets naturels (fleurs, animaux, montagnes) \n- Objets utiles (bicyclettes, gares ferroviaires) \n- Personnes célèbres (votre maire, les athlètes olympiques que vous avez rencontrés) @@ -140,7 +140,7 @@ Endroits à proximité Rien trouvé dans le voisinage Avertissement - Ce fichier existe déjà sur Communs. Êtes-vous sûr de vouloir continuer ? + Ce fichier existe déjà sur Commons. Êtes-vous sûr de vouloir continuer ? Oui Non Titre @@ -162,7 +162,7 @@ Fixer la limite de téléversement récent L’authentification à deux facteurs n’est pas prise en charge pour le moment. Voulez-vous vraiment vous déconnecter ? - Logo de Communs + Logo de Commons Image de fond Échec sur l’image du média Aucune image trouvée @@ -189,7 +189,7 @@ Tutoriel Les endroits proches ne peuvent pas être affichés si vous ne partagez pas votre position géographique. aucune description trouvée - Page des fichiers de Communs + Page des fichiers de Commons Élément de Wikidata Erreur en mettant les images en cache Un titre descriptif unique pour le fichier, qui servira de nom de fichier. Vous pouvez utiliser un langage simple avec des espaces. N’incluez pas l’extension du fichier diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 58d8f01e5..f8ed3756c 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -42,6 +42,7 @@ Nie można zalogować - sprawdź hasło Zbyt wiele nieudanych prób zalogowania. Spróbuj ponownie za kilka minut. Przepraszamy, ten użytkownik został zablokowany na Commons + Wprowadź swój kod dla dwuetapowej autoryzacji. Logowanie nie powiodło się Prześlij Nazwij ten zestaw @@ -52,6 +53,7 @@ Odśwież GPS w twoim urządzeniu jest wyłączony. Czy chcesz go włączyć? Włącz GPS + Na razie brak przesłanych plików! Przesłano @string/contributions_subtitle_zero Przesłano %d plik @@ -151,6 +153,8 @@ Dołącz do kanału bety w Google Play i dostań wczesny dostęp do nowych funkcji i łatek Użyj Wikidanych Kod 2FA + Górna Granica + Nie można wyświetlić ponad 500 Uwierzytelnianie dwuskładnikowe obecnie nie jest obsługiwane. Czy na pewno wylogować? Logo Commons @@ -160,8 +164,10 @@ Lamy Rainbow Bridge Tulipan + Brak Selfie Witaj na Wikipedii Witaj w prawach autorskich. + Opera Sydnej Anuluj Otwórz Zamknij diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 896de23b6..45ed21a92 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -153,6 +153,7 @@ Дата загрузки Лицензия Координаты + Не предоставлены Стать бета-тестером Подпишитесь на наш канал бета-версии на Google Play и получите ранний доступ к новым функциям и исправлениям ошибок Использовать Викиданные diff --git a/app/src/main/res/values-sd/strings.xml b/app/src/main/res/values-sd/strings.xml index 36a914281..19322b77f 100644 --- a/app/src/main/res/values-sd/strings.xml +++ b/app/src/main/res/values-sd/strings.xml @@ -2,7 +2,7 @@ العام ترتيبون - يُوزرنانءُ + واپرائيندڙ-نانءُ ڳجھولفظ داخل ٿيو کاتو کوليو @@ -20,11 +20,11 @@ %1$s جو چاڙھ مڪمل ٿيندي %1$s جو چاڙھڻ ناڪام ويو ڏسڻ لاءِ ٺونگيو - - 1 فائيل چاڙھيندي + + %d فائيل چاڙھيندي %d فائيلَ چاڙھيندي - منھنجا چاڙھ + منھنجا تازا چاڙھ قطار ۾ ناڪام %1$d%% مڪمل @@ -38,10 +38,10 @@ عنوان تشريح ناقابلِ داخل ٿيڻ - باھمڄار ناڪامي - ناقابلِ داخل ٿيڻ - براءِ مھرباني پنھنجو يُوزرنانءُ چڪاسيو + ناقابلِ داخل ٿيڻ - براءِ مھرباني پنھنجو واپرائيندڙ-نانءُ چڪاسيو ناقابل داخل ٿيڻ - براءِ مھرباني پنھنجو ڳجھولفظ چڪاسيو ھيڪانديون ناڪام ڪوششون. براءِ مھرباني ڪجھ منٽن کانپوءِ ٻيھر ڪوشش ڪريو. - افسوس، ھي واھپ العام تي بندشيل آھي + افسوس، ھي واپرائيندڙ العام تي بندشيل آھي داخل ٿيڻ ناڪام چاڙھيو ھن سيٽ کي نالو ڏيو @@ -50,6 +50,8 @@ زمرا ڳوليو سانڍيو تازو ڪيو + جي پي ايس چالو ڪيو (اين ايبل جي پي ايس) + اڃان تائين ڪو به ڄاڙهه (اَپلوڊ) نه ٿيو آهي اڃان تائين ڪو چاڙھ ناھي 1 چاڙھ @@ -121,7 +123,7 @@ توھان سمجھو ٿا توھان سمجھي ويئو؟ ھا! زمرا - اتاريندي… + لاهيندي... ڪوبہ چونڊيل ناھي ڪا تشريح ناھي اڻڄاتل لائسنس @@ -137,6 +139,28 @@ عنوان ابلاغ جو عنوان تشريح + چاڙهڻ جي تاريخ (اَپلوڊ ڊيٽ) + لائسنس (اجازت نامو) آزمائشي آزمائيندڙ ٿيو وڪيڊيٽا استعماڪ ڪريو + منهنجي تازي چاڙهڻ (اَپلوڊ) جي حد (لِمٽ) + وڌ ۾ وڌ حد (ميگزيمم لِمٽ) + ڪوبہ عڪس نہ لڌو + عڪس چاڙهيو + گل لالا (ٽيولپ) + ڪي به پاڻفيون نه (نو سيلفيز) + سڃاڻپ واري تصوير (پروپرائٽري اميج) + وڪيپيڊيا تي ڀليڪار + حقن ۽ واسطن جي آجيان (ويلڪم ڪاپي رائيٽ) + سڊني اوپيرا هائوس + رد + کوليو + چاڙهيو + بابت + ترتيبون (سيٽنگس) + اوهان جي راءِ + سکيا (ٽيوٽوريل) + ڪا به وضاحت نه ملي + ڪامن فائيل جو ورق + وڪيڊيٽا جزو (وڪيڊيٽا آئيٽم) diff --git a/app/src/main/res/values-xmf/error.xml b/app/src/main/res/values-xmf/error.xml new file mode 100644 index 000000000..68ef5b21f --- /dev/null +++ b/app/src/main/res/values-xmf/error.xml @@ -0,0 +1,7 @@ + + + ვიკიოწკარუექ აკორთუ + უი. მუდგაინქ ქჷმოხვადუǃ + ქომწით მუს აკეთენდით დო ქჷმომჯღონით ელექტრონული ფოსტათ. თენა ჩქი პრობლემაშ გიშათინუა ქემეხვარებჷნა! + დიდი მარდიǃ + diff --git a/app/src/main/res/values-xmf/strings.xml b/app/src/main/res/values-xmf/strings.xml new file mode 100644 index 000000000..1cf120d60 --- /dev/null +++ b/app/src/main/res/values-xmf/strings.xml @@ -0,0 +1,138 @@ + + + ვიკიოწკარუე + პარამეტრეფი + მახვარებუშ ჯოხო + პაროლი + მიშულა + რეგისტრაცია + სისტემაშა მიშულა + ქორთხინთ ქიმიცადით … + სისტემაშა მიშულაქ წჷმოძინელო გეთუ! + სისტემაშა მიშულაქ ვემიხუჯინუ! + ფაილქ ვეგორუ. ქორთხინთ, ქოცადით შხვა ფაილი. + აუთენტიფიკაციაქ ვემიხუჯინუ! + ეხარგუაქ ქჷდიჭყუ! + %1$ ეხარგილი რე! + ქეგუწკანტეთ თქვანი ეხარგუაშ ოძირაფალო + ქჷდიჭყუ %1$ ეხარგუაქ + %1$ იხარგუ + ეხარგუაშ თება %1$ + %1$ ეხარგუაქ ვემიხუჯინუ + ოძირაფალო ქეგუწკანტეთ + + %d ფაილი იხარგუ + %d ფაილი იხარგუ + + ჩქიმი ბოლო ეხარგუეფი + რადი + ვემიხუჯინუ + %1$d%% გეთუ + იხარგუაფუ + გალერეაშე + ფოტოშ გინოღალა + გოხოლუას + ჩქიმი ეხარგუეფი + გობჟინაფა + ბრაუზერს გონწყუმა + დუდჯოხო + ეჭარუა + მიშულაქ ვემიხუჯინუ - რშვილიშ ჩილათა + მიშულაქ ვემიხუჯინუ - ქორთხინთ გეგნაჯინით ჯოხოს + მიშულაქ ვემიხუჯინუ - ქორთხინთ გეგნაჯინით პაროლს + ძალამ მიარე უმწუძინუ ცადება. ქორთხინ, მუხირენ წუთშა ხოლო ქოცადით. + მორდება, თე მახვარებუ ბლოკირი რე ვიკიოწკარუეს + თქვა გემშიონათ ოკო ჟირფაქტორიანი ავტორიზაციაშ კოდი. + მიშულაქ ვემიხუჯინუ + ეხარგუა + სერიაშ ჯოხო + მოდიფიკაციეფი + ეხარგუა + კატეგორიაშ გიშაგორუა + ჩუალა + გოახალაფა + თქვან მონწყილობას GPS თიშილი რე. თახმო გოკონანო ჩართება? + GPS-იშ ჩართება + ეხარგუეფი ვა რე + + \@string/contributions_subtitle_zero + %d ეხარგუა + %d ეხარგუა + + + იჭყაფუ %d ეხარგუა + იჭყაფუ %d ეხარგუა + + + %d ეხარგუა + %d ეხარგუა + + კატეგორია + პარამეტრეფი + რეგისტრაცია + პროგრამაშ გეშა + გომაჟირაფა + გოუქვაფა + თე სურათი გიბჟინუ %1$s ლიცენზიათ + გინოჭარუა + ლიცენზია + წჷმი სათაურიშ/ეჭარუაშ გჷმორინაფა + ასეიანი ორენიშ ავტომატური მეღება + სერიშ რეჟიმი + რუმე თემაშ გჷმორინაფა + Attribution-ShareAlike 4.0 + Attribution 4.0 + Attribution-ShareAlike 3.0 + Attribution 3.0 + CC0 + CC BY-SA 3.0 + CC BY-SA 3.0 (ავსტრია) + CC BY-SA 3.0 (გერმანია) + CC BY-SA 3.0 (ესტონეთი) + CC BY-SA 3.0 (ესპანეთი) + CC BY-SA 3.0 (ხორვატია) + CC BY-SA 3.0 (ლუქსემბურგი) + CC BY-SA 3.0 (ნიდერლანდი) + CC BY-SA 3.0 (ნორვეგია) + CC BY-SA 3.0 (პოლონეთი) + CC BY-SA 3.0 (რუმინეთი) + CC BY-SA 3.0 + CC BY-SA 4.0 + CC BY 4.0 + CC Zero + ქორთხინთ გეხარგეთ, ხვალე თქვანით ინოღალირი ვარდა აკოქიმინელი სურათეფი: + - ორთაშობური ობიექტეფი (პიოლეფი, ჩხოლარეფი, გვალეფი)\n- ორგებელი ობიექტეფი (ველოსიპედეფი, მახინწალიშ დგჷმიელფი)\n- ჩინებული ადამიერეფი (თქვანი ნოღაშ მერი, ოლიმპიარი სპორტსმენეფი, ნამუეფსჷთ შეხვალამუთჷნ) + ქორთხინთ ვეხარგათ: + - სელფი ვარდა მაჸალეეფიშ სურათეფი\n- ინტერნეტშე გჷმოხარგილი ფოტოეფი\n- ვადუდიშული აპლიკაციეფიშ სკრინშოტეფი + ეხარგუაშ მინუში: + - დუდჯოხო: სიდნეიშ ოპერაშ თეატრი\n- ეჭარუა: სიდნეიშ ოპერაშ თეატრიშ მიოჯინი ლუბაშე\n- კატეგორიეფი: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views + გეხარგეთ თქვანი ფოტოეფი. ქემეხვარით ვიკიპედიაშ სტატიეფიშ გაჭყანიერებას! + ვიკიპედიაშ ფოტოეფი ვიკიოწკარუეს იჩუალუაფუ. + თქვანი სურათეფი კათაშ გონათუას ოხვარუ ედომუშამ მოსოფელს. + ქოǃ + კატეგორიეფი + იხარგუ... + მუთუნ ვა რე გიშაგორილი + ვა რე ეჭარუა + უჩინებუ ლიცენზია + გოახალაფა + OK + გოხოლუაშ აბანეფი + გოხოლუაშ აბანეფქ ვეგორუ + გათხილება + ქო + ვარი + დუდჯოხო + მედიაფაილიშ დუდჯოხო + ეჭარუა + ეხარგუაშ თარიღი + ლიცენზია + კოორდინატეფი + ბეტა ტესტირებას კათაფი + ვიკიმუნაჩემეფიშ გჷმორინაფა + 2ფა კოდი + ჩქიმი ბოლო ეხარგუეფიშ ლიმიტი + მაქსიმალური ლიმიტი + 500-შე უმოსიშ ძირაფა ვეშილებე + ბოლო ეხარგუეფიშ ლიმიტიშ გერინაფა +