diff --git a/.travis.yml b/.travis.yml index 0f3376e76..8c0af5731 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,10 @@ android: components: - platform-tools - tools - - build-tools-25.0.0 + - build-tools-25.0.1 - extra-google-m2repository - extra-android-m2repository - - android-23 + - android-25 - sys-img-x86-android-18 jdk: # - openjdk8 # not yet available diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a92b105d..16e64137f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Wikimedia Commons for Android +##v2.0.2 +- Make "View in browser" direct to mobile website + ##v2.0.1 - Disabled minify again (reenabling test failed) - Hotfix for ShareAction bug diff --git a/README.md b/README.md index 6293a332e..687b53ebb 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ -# Wikimedia Commons Android app +# Wikimedia Commons Android app [![Build status](https://api.travis-ci.org/commons-app/apps-android-commons.svg)](https://travis-ci.org/commons-app/apps-android-commons) The Wikimedia Commons Android app allows users to upload pictures from their Android phone/tablet to Wikimedia Commons. Download the app [here][8], or view our [website][9]. Initially started by the Wikimedia Foundation, this app is now maintained by volunteers. Anyone is welcome to improve it, just choose among the [open issues](https://github.com/commons-app/apps-android-commons/issues) and send us a pull request :-) -[![Build status](https://api.travis-ci.org/commons-app/apps-android-commons.svg)](https://travis-ci.org/commons-app/apps-android-commons) + +Get it on F-Droid + +Get it on Google Play ## Develop with Android Studio or IntelliJ ## diff --git a/app/build.gradle b/app/build.gradle index f9fdff800..20e337c69 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,30 +12,24 @@ dependencies { compile 'ch.acra:acra:4.7.0' compile 'org.mediawiki:api:1.3' compile 'commons-codec:commons-codec:1.10' - compile 'com.android.support:support-v4:25.0.0' - compile 'com.android.support:appcompat-v7:25.0.0' - compile 'com.android.support:design:25.0.0' + compile "com.android.support:support-v4:${project.supportLibVersion}" + compile "com.android.support:appcompat-v7:${project.supportLibVersion}" + compile "com.android.support:design:${project.supportLibVersion}" + compile 'com.google.code.gson:gson:2.7' testCompile 'junit:junit:4.12' - - //noinspection GradleDependency - old version has required feature - compile 'com.google.code.gson:gson:1.4' } android { - compileSdkVersion 23 - buildToolsVersion '25' + compileSdkVersion project.compileSdkVersion + buildToolsVersion project.buildToolsVersion - useLibrary 'org.apache.http.legacy' + useLibrary 'org.apache.http.legacy' defaultConfig { - applicationId "fr.free.nrw.commons" - minSdkVersion 15 - targetSdkVersion 23 - - ndk { - moduleName "libtranscode" - } + applicationId 'fr.free.nrw.commons' + minSdkVersion project.minSdkVersion + targetSdkVersion project.targetSdkVersion } buildTypes { @@ -50,4 +44,4 @@ android { disable 'ExtraTranslation' abortOnError false } -} +} \ No newline at end of file diff --git a/app/src/debug/res/values/placeholder_strings.xml b/app/src/debug/res/values/placeholder_strings.xml new file mode 100644 index 000000000..dd7a60b20 --- /dev/null +++ b/app/src/debug/res/values/placeholder_strings.xml @@ -0,0 +1,6 @@ + + + Overlay + Name + Description + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1ae84b242..9fea24db7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,8 @@ + android:versionCode="65" + android:versionName="2.0.2" > @@ -85,7 +86,7 @@ + android:parentActivityName=".contributions.ContributionsActivity" /> 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 44bdf0336..916ea38cd 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java @@ -59,6 +59,7 @@ public class CommonsApplication extends Application { public static final String API_URL = "https://commons.wikimedia.org/w/api.php"; public static final String IMAGE_URL_BASE = "https://upload.wikimedia.org/wikipedia/commons"; public static final String HOME_URL = "https://commons.wikimedia.org/wiki/"; + public static final String MOBILE_HOME_URL = "https://commons.m.wikimedia.org/wiki/"; public static final String EVENTLOG_URL = "https://www.wikimedia.org/beacon/event"; public static final String EVENTLOG_WIKI = "commonswiki"; 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 9c791109e..0ef067496 100644 --- a/app/src/main/java/fr/free/nrw/commons/LicenseList.java +++ b/app/src/main/java/fr/free/nrw/commons/LicenseList.java @@ -7,6 +7,7 @@ import org.xmlpull.v1.XmlPullParser; import java.util.Collection; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -54,7 +55,7 @@ public class LicenseList { public 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().replace("-", "_").replace(".", "_"); + return "license_name_" + template.toLowerCase(Locale.ENGLISH).replace("-", "_").replace(".", "_"); } private int stringIdByName(String stringId) { 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 366844a27..924427cef 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.java +++ b/app/src/main/java/fr/free/nrw/commons/Media.java @@ -61,6 +61,10 @@ public class Media implements Parcelable { return CommonsApplication.HOME_URL + "File:" + Utils.urlEncode(getFilename().replace("File:", "").replace(" ", "_")); } + public String getMobileDescriptionUrl() { + return CommonsApplication.MOBILE_HOME_URL + "File:" + Utils.urlEncode(getFilename().replace("File:", "").replace(" ", "_")); + } + public Uri getLocalUri() { return localUri; } diff --git a/app/src/main/java/fr/free/nrw/commons/Utils.java b/app/src/main/java/fr/free/nrw/commons/Utils.java index f07d5c8bd..9d93a8834 100644 --- a/app/src/main/java/fr/free/nrw/commons/Utils.java +++ b/app/src/main/java/fr/free/nrw/commons/Utils.java @@ -27,6 +27,7 @@ import java.security.NoSuchAlgorithmException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.Executor; import java.util.regex.Pattern; @@ -81,7 +82,7 @@ public class Utils { } public static Date parseMWDate(String mwDate) { - SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); // Assuming MW always gives me UTC + SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC try { return isoFormat.parse(mwDate); } catch (ParseException e) { @@ -90,7 +91,7 @@ public class Utils { } public static String toMWDate(Date date) { - SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); // Assuming MW always gives me UTC + SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC isoFormat.setTimeZone(TimeZone.getTimeZone("UTC")); return isoFormat.format(date); } @@ -201,7 +202,7 @@ public class Utils { } public static String capitalize(String string) { - return string.substring(0,1).toUpperCase() + string.substring(1); + return string.substring(0,1).toUpperCase(Locale.getDefault()) + string.substring(1); } public static String licenseTemplateFor(String license) { @@ -303,13 +304,17 @@ public class Utils { Pattern jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE); // People are used to ".jpg" more than ".jpeg" which the system gives us. - if (extension != null && extension.toLowerCase().equals("jpeg")) { + if (extension != null && extension.toLowerCase(Locale.ENGLISH).equals("jpeg")) { extension = "jpg"; } title = jpegPattern.matcher(title).replaceFirst(".jpg"); - if (extension != null && !title.toLowerCase().endsWith("." + extension.toLowerCase())) { + if (extension != null && !title.toLowerCase(Locale.getDefault()).endsWith("." + extension.toLowerCase(Locale.ENGLISH))) { title += "." + extension; } return title; } + + public static boolean isNullOrWhiteSpace(String value) { + return value == null || value.trim().isEmpty(); + } } 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 2726c38b3..f5eccf6fb 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 @@ -1,12 +1,16 @@ package fr.free.nrw.commons.auth; +import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; +import android.content.pm.PackageManager; import android.os.AsyncTask; import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import java.io.IOException; @@ -20,23 +24,24 @@ public abstract class AuthenticatedActivity extends AppCompatActivity { CommonsApplication app; private String authCookie; - + public AuthenticatedActivity(String accountType) { - this.accountType = accountType; + this.accountType = accountType; } - + private class GetAuthCookieTask extends AsyncTask { private Account account; private AccountManager accountManager; + public GetAuthCookieTask(Account account, AccountManager accountManager) { this.account = account; this.accountManager = accountManager; } - + @Override protected void onPostExecute(String result) { super.onPostExecute(result); - if(result != null) { + if (result != null) { authCookie = result; onAuthCookieAcquired(result); } else { @@ -60,19 +65,19 @@ public abstract class AuthenticatedActivity extends AppCompatActivity { } } } - + private class AddAccountTask extends AsyncTask { private AccountManager accountManager; + public AddAccountTask(AccountManager accountManager) { this.accountManager = accountManager; } - + @Override protected void onPostExecute(String result) { super.onPostExecute(result); - if(result != null) { - Account[] allAccounts =accountManager.getAccountsByType(accountType); - Account curAccount = allAccounts[0]; + if (result != null) { + Account curAccount = getCurrentAccount(); GetAuthCookieTask getCookieTask = new GetAuthCookieTask(curAccount, accountManager); getCookieTask.execute(); } else { @@ -80,6 +85,18 @@ public abstract class AuthenticatedActivity extends AppCompatActivity { } } + @Nullable + private Account getCurrentAccount() { + if (ActivityCompat.checkSelfPermission(AuthenticatedActivity.this, Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED) { + return null; + } + Account[] allAccounts = accountManager.getAccountsByType(accountType); + if (allAccounts == null) { + return null; + } + return allAccounts[0]; + } + @Override protected String doInBackground(Void... params) { AccountManagerFuture resultFuture = accountManager.addAccount(accountType, null, null, null, AuthenticatedActivity.this, null, null); diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java index 3dbe7377d..9b76fb627 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java @@ -26,10 +26,12 @@ import android.widget.TextView; import android.widget.Toast; import java.io.IOException; +import java.util.Locale; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.EventLog; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.WelcomeActivity; import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.contributions.ContributionsContentProvider; @@ -220,7 +222,7 @@ public class LoginActivity extends AccountAuthenticatorActivity { private void performLogin() { String username = usernameEdit.getText().toString(); // Because Mediawiki is upercase-first-char-then-case-sensitive :) - String canonicalUsername = username.substring(0,1).toUpperCase() + username.substring(1); + String canonicalUsername = Utils.capitalize(username.substring(0,1)) + username.substring(1); String password = passwordEdit.getText().toString(); 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 3d9776129..482a4df7f 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 @@ -11,6 +11,7 @@ import android.text.TextUtils; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Locale; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.EventLog; @@ -131,7 +132,7 @@ public class Contribution extends Media { public String getPageContents() { StringBuffer buffer = new StringBuffer(); - SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd"); + SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH); buffer .append("== {{int:filedesc}} ==\n") 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 ffe93edc6..9adbdb882 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 @@ -143,7 +143,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa // View in browser Intent viewIntent = new Intent(); viewIntent.setAction(Intent.ACTION_VIEW); - viewIntent.setData(Uri.parse(m.getDescriptionUrl())); + viewIntent.setData(Uri.parse(m.getMobileDescriptionUrl())); startActivity(viewIntent); return true; case R.id.menu_download_current_image: diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java index e91b52a93..b17162705 100644 --- a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java @@ -49,13 +49,16 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter { String authCookie; try { authCookie = AccountManager.get(getContext()).blockingGetAuthToken(account, "", false); - } catch (OperationCanceledException e) { + } catch (OperationCanceledException | AuthenticatorException e) { throw new RuntimeException(e); } catch (IOException e) { Log.d("Commons", "Could not authenticate :("); return; - } catch (AuthenticatorException e) { - throw new RuntimeException(e); + } + + if(Utils.isNullOrWhiteSpace(authCookie)) { + Log.d("Commons", "Could not authenticate :("); + return; } MWApi api = CommonsApplication.createMWApi(); 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 64a969933..5f4ff14d6 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 @@ -130,7 +130,7 @@ public class SingleUploadFragment extends Fragment { setLicenseSummary(license); SharedPreferences.Editor editor = prefs.edit(); editor.putString(Prefs.DEFAULT_LICENSE, license); - editor.commit(); + editor.apply(); } @Override diff --git a/app/src/main/res/layout-land/welcome_final.xml b/app/src/main/res/layout-land/welcome_final.xml index e6277bf49..62d2a9e00 100644 --- a/app/src/main/res/layout-land/welcome_final.xml +++ b/app/src/main/res/layout-land/welcome_final.xml @@ -41,8 +41,7 @@ android:textStyle="bold" android:textAlignment="center" android:gravity="center_horizontal" - android:textColor="@android:color/white" - android:singleLine="false"/> + android:textColor="@android:color/white"/>