mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Merge remote-tracking branch 'refs/remotes/origin/2.8-release'
This commit is contained in:
commit
18fbd47535
36 changed files with 648 additions and 161 deletions
|
|
@ -122,7 +122,6 @@ android {
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-glide.txt'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-glide.txt'
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
applicationIdSuffix ".debug"
|
|
||||||
testCoverageEnabled true
|
testCoverageEnabled true
|
||||||
versionNameSuffix "-debug-" + getBranchName() + "~" + getBuildVersion()
|
versionNameSuffix "-debug-" + getBranchName() + "~" + getBuildVersion()
|
||||||
}
|
}
|
||||||
|
|
@ -131,6 +130,9 @@ android {
|
||||||
flavorDimensions 'tier'
|
flavorDimensions 'tier'
|
||||||
productFlavors {
|
productFlavors {
|
||||||
prod {
|
prod {
|
||||||
|
|
||||||
|
applicationId 'fr.free.nrw.commons'
|
||||||
|
|
||||||
buildConfigField "String", "WIKIMEDIA_API_POTD", "\"https://commons.wikimedia.org/w/api.php?action=featuredfeed&feed=potd&feedformat=rss&language=en\""
|
buildConfigField "String", "WIKIMEDIA_API_POTD", "\"https://commons.wikimedia.org/w/api.php?action=featuredfeed&feed=potd&feedformat=rss&language=en\""
|
||||||
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.org/w/api.php\""
|
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.org/w/api.php\""
|
||||||
buildConfigField "String", "WIKIDATA_API_HOST", "\"https://www.wikidata.org/w/api.php\""
|
buildConfigField "String", "WIKIDATA_API_HOST", "\"https://www.wikidata.org/w/api.php\""
|
||||||
|
|
@ -144,10 +146,18 @@ android {
|
||||||
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_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\""
|
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Main_Page&welcome=yes\""
|
||||||
buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.org/wiki/Special:PasswordReset\""
|
buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.org/wiki/Special:PasswordReset\""
|
||||||
|
|
||||||
|
buildConfigField "String", "ACCOUNT_TYPE", "\"fr.free.nrw.commons\""
|
||||||
|
buildConfigField "String", "CONTRIBUTION_AUTHORITY", "\"fr.free.nrw.commons.contributions.contentprovider\""
|
||||||
|
buildConfigField "String", "MODIFICATION_AUTHORITY", "\"fr.free.nrw.commons.modifications.contentprovider\""
|
||||||
|
buildConfigField "String", "CATEGORY_AUTHORITY", "\"fr.free.nrw.commons.categories.contentprovider\""
|
||||||
|
|
||||||
dimension 'tier'
|
dimension 'tier'
|
||||||
}
|
}
|
||||||
|
|
||||||
beta {
|
beta {
|
||||||
|
applicationId 'fr.free.nrw.commons.beta'
|
||||||
|
|
||||||
// What values do we need to hit the BETA versions of the site / api ?
|
// What values do we need to hit the BETA versions of the site / api ?
|
||||||
buildConfigField "String", "WIKIMEDIA_API_POTD", "\"https://commons.wikimedia.org/w/api.php?action=featuredfeed&feed=potd&feedformat=rss&language=en\""
|
buildConfigField "String", "WIKIMEDIA_API_POTD", "\"https://commons.wikimedia.org/w/api.php?action=featuredfeed&feed=potd&feedformat=rss&language=en\""
|
||||||
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.beta.wmflabs.org/w/api.php\""
|
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.beta.wmflabs.org/w/api.php\""
|
||||||
|
|
@ -162,6 +172,12 @@ android {
|
||||||
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_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\""
|
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Main_Page&welcome=yes\""
|
||||||
buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/Special:PasswordReset\""
|
buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/Special:PasswordReset\""
|
||||||
|
|
||||||
|
buildConfigField "String", "ACCOUNT_TYPE", "\"fr.free.nrw.commons.beta\""
|
||||||
|
buildConfigField "String", "CONTRIBUTION_AUTHORITY", "\"fr.free.nrw.commons.beta.contributions.contentprovider\""
|
||||||
|
buildConfigField "String", "MODIFICATION_AUTHORITY", "\"fr.free.nrw.commons.beta.modifications.contentprovider\""
|
||||||
|
buildConfigField "String", "CATEGORY_AUTHORITY", "\"fr.free.nrw.commons.beta.categories.contentprovider\""
|
||||||
|
|
||||||
dimension 'tier'
|
dimension 'tier'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
7
app/src/beta/res/values/adapter.xml
Normal file
7
app/src/beta/res/values/adapter.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="account_type">fr.free.nrw.commons.beta</string>
|
||||||
|
<string name="contribution_authority">fr.free.nrw.commons.beta.contributions.contentprovider</string>
|
||||||
|
<string name="modification_authority">fr.free.nrw.commons.beta.modifications.contentprovider</string>
|
||||||
|
<string name="category_authority">fr.free.nrw.commons.beta.categories.contentprovider</string>
|
||||||
|
</resources>
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="placeholder_place_distance">Overlay</string>
|
|
||||||
<string name="placeholder_place_name">Name</string>
|
|
||||||
<string name="placeholder_place_description">Description</string>
|
|
||||||
</resources>
|
|
||||||
|
|
@ -168,21 +168,21 @@
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".contributions.ContributionsContentProvider"
|
android:name=".contributions.ContributionsContentProvider"
|
||||||
android:authorities="fr.free.nrw.commons.contributions.contentprovider"
|
android:authorities="${applicationId}.contributions.contentprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/provider_contributions"
|
android:label="@string/provider_contributions"
|
||||||
android:syncable="true" />
|
android:syncable="true" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".modifications.ModificationsContentProvider"
|
android:name=".modifications.ModificationsContentProvider"
|
||||||
android:authorities="fr.free.nrw.commons.modifications.contentprovider"
|
android:authorities="${applicationId}.modifications.contentprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/provider_modifications"
|
android:label="@string/provider_modifications"
|
||||||
android:syncable="true" />
|
android:syncable="true" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".category.CategoryContentProvider"
|
android:name=".category.CategoryContentProvider"
|
||||||
android:authorities="fr.free.nrw.commons.categories.contentprovider"
|
android:authorities="${applicationId}.categories.contentprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/provider_categories"
|
android:label="@string/provider_categories"
|
||||||
android:syncable="false" />
|
android:syncable="false" />
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||||
import fr.free.nrw.commons.modifications.ModifierSequenceDao;
|
import fr.free.nrw.commons.modifications.ModifierSequenceDao;
|
||||||
import fr.free.nrw.commons.upload.FileUtils;
|
import fr.free.nrw.commons.upload.FileUtils;
|
||||||
|
import fr.free.nrw.commons.utils.ContributionUtils;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
@ -68,7 +69,6 @@ public class CommonsApplication extends MultiDexApplication {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
ApplicationlessInjection
|
ApplicationlessInjection
|
||||||
.getInstance(this)
|
.getInstance(this)
|
||||||
.getCommonsApplicationComponent()
|
.getCommonsApplicationComponent()
|
||||||
|
|
@ -81,6 +81,8 @@ public class CommonsApplication extends MultiDexApplication {
|
||||||
if (setupLeakCanary() == RefWatcher.DISABLED) {
|
if (setupLeakCanary() == RefWatcher.DISABLED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Empty temp directory in case some temp files are created and never removed.
|
||||||
|
ContributionUtils.emptyTemporaryDirectory();
|
||||||
|
|
||||||
Timber.plant(new Timber.DebugTree());
|
Timber.plant(new Timber.DebugTree());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,16 +51,10 @@ public class MediaWikiImageView extends SimpleDraweeView {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(media.getFilename() != null) {
|
if (media.getFilename() != null && thumbnailUrlCache.get(media.getFilename()) != null) {
|
||||||
if (thumbnailUrlCache.get(media.getFilename()) != null) {
|
setImageUrl(thumbnailUrlCache.get(media.getFilename()));
|
||||||
setImageUrl(thumbnailUrlCache.get(media.getFilename()));
|
} else {
|
||||||
} else {
|
setImageUrl(null);
|
||||||
setImageUrl(null);
|
|
||||||
currentThumbnailTask = new ThumbnailFetchTask(media, mwApi);
|
|
||||||
currentThumbnailTask.execute(media.getFilename());
|
|
||||||
}
|
|
||||||
} else { // local image
|
|
||||||
setImageUrl(media.getLocalUri().toString());
|
|
||||||
currentThumbnailTask = new ThumbnailFetchTask(media, mwApi);
|
currentThumbnailTask = new ThumbnailFetchTask(media, mwApi);
|
||||||
currentThumbnailTask.execute(media.getFilename());
|
currentThumbnailTask.execute(media.getFilename());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,6 @@
|
||||||
package fr.free.nrw.commons.auth;
|
package fr.free.nrw.commons.auth;
|
||||||
|
|
||||||
import android.accounts.Account;
|
|
||||||
import android.accounts.AccountAuthenticatorResponse;
|
|
||||||
import android.accounts.AccountManager;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import static android.accounts.AccountManager.ERROR_CODE_REMOTE_EXCEPTION;
|
|
||||||
import static android.accounts.AccountManager.KEY_ACCOUNT_NAME;
|
|
||||||
import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE;
|
|
||||||
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY;
|
|
||||||
import static fr.free.nrw.commons.modifications.ModificationsContentProvider.MODIFICATIONS_AUTHORITY;
|
|
||||||
|
|
||||||
public class AccountUtil {
|
public class AccountUtil {
|
||||||
|
|
||||||
|
|
@ -27,38 +13,4 @@ public class AccountUtil {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createAccount(@Nullable AccountAuthenticatorResponse response,
|
|
||||||
String username, String password) {
|
|
||||||
|
|
||||||
Account account = new Account(username, ACCOUNT_TYPE);
|
|
||||||
boolean created = accountManager().addAccountExplicitly(account, password, null);
|
|
||||||
|
|
||||||
Timber.d("account creation " + (created ? "successful" : "failure"));
|
|
||||||
|
|
||||||
if (created) {
|
|
||||||
if (response != null) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putString(KEY_ACCOUNT_NAME, username);
|
|
||||||
bundle.putString(KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);
|
|
||||||
|
|
||||||
|
|
||||||
response.onResult(bundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (response != null) {
|
|
||||||
response.onError(ERROR_CODE_REMOTE_EXCEPTION, "");
|
|
||||||
}
|
|
||||||
Timber.d("account creation failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: If the user turns it off, it shouldn't be auto turned back on
|
|
||||||
ContentResolver.setSyncAutomatically(account, CONTRIBUTION_AUTHORITY, true); // Enable sync by default!
|
|
||||||
ContentResolver.setSyncAutomatically(account, MODIFICATIONS_AUTHORITY, true); // Enable sync by default!
|
|
||||||
}
|
|
||||||
|
|
||||||
private AccountManager accountManager() {
|
|
||||||
return AccountManager.get(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
public static final String PARAM_USERNAME = "fr.free.nrw.commons.login.username";
|
public static final String PARAM_USERNAME = "fr.free.nrw.commons.login.username";
|
||||||
|
|
||||||
@Inject MediaWikiApi mwApi;
|
@Inject MediaWikiApi mwApi;
|
||||||
@Inject AccountUtil accountUtil;
|
|
||||||
@Inject SessionManager sessionManager;
|
@Inject SessionManager sessionManager;
|
||||||
@Inject @Named("application_preferences") SharedPreferences prefs;
|
@Inject @Named("application_preferences") SharedPreferences prefs;
|
||||||
@Inject @Named("default_preferences") SharedPreferences defaultPrefs;
|
@Inject @Named("default_preferences") SharedPreferences defaultPrefs;
|
||||||
|
|
@ -248,7 +247,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accountUtil.createAccount(response, username, password);
|
sessionManager.createAccount(response, username, password);
|
||||||
startMainActivity();
|
startMainActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,24 @@
|
||||||
package fr.free.nrw.commons.auth;
|
package fr.free.nrw.commons.auth;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountAuthenticatorResponse;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import io.reactivex.Completable;
|
import io.reactivex.Completable;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static fr.free.nrw.commons.auth.AccountUtil.ACCOUNT_TYPE;
|
import static android.accounts.AccountManager.ERROR_CODE_REMOTE_EXCEPTION;
|
||||||
|
import static android.accounts.AccountManager.KEY_ACCOUNT_NAME;
|
||||||
|
import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage the current logged in user session.
|
* Manage the current logged in user session.
|
||||||
|
|
@ -23,13 +29,57 @@ public class SessionManager {
|
||||||
private Account currentAccount; // Unlike a savings account... ;-)
|
private Account currentAccount; // Unlike a savings account... ;-)
|
||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
public SessionManager(Context context, MediaWikiApi mediaWikiApi, SharedPreferences sharedPreferences) {
|
|
||||||
|
public SessionManager(Context context,
|
||||||
|
MediaWikiApi mediaWikiApi,
|
||||||
|
SharedPreferences sharedPreferences) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.mediaWikiApi = mediaWikiApi;
|
this.mediaWikiApi = mediaWikiApi;
|
||||||
this.currentAccount = null;
|
this.currentAccount = null;
|
||||||
this.sharedPreferences = sharedPreferences;
|
this.sharedPreferences = sharedPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creata a new account
|
||||||
|
*
|
||||||
|
* @param response
|
||||||
|
* @param username
|
||||||
|
* @param password
|
||||||
|
*/
|
||||||
|
public void createAccount(@Nullable AccountAuthenticatorResponse response,
|
||||||
|
String username, String password) {
|
||||||
|
|
||||||
|
Account account = new Account(username, BuildConfig.ACCOUNT_TYPE);
|
||||||
|
boolean created = accountManager().addAccountExplicitly(account, password, null);
|
||||||
|
|
||||||
|
Timber.d("account creation " + (created ? "successful" : "failure"));
|
||||||
|
|
||||||
|
if (created) {
|
||||||
|
if (response != null) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putString(KEY_ACCOUNT_NAME, username);
|
||||||
|
bundle.putString(KEY_ACCOUNT_TYPE, BuildConfig.ACCOUNT_TYPE);
|
||||||
|
|
||||||
|
|
||||||
|
response.onResult(bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (response != null) {
|
||||||
|
response.onError(ERROR_CODE_REMOTE_EXCEPTION, "");
|
||||||
|
}
|
||||||
|
Timber.d("account creation failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: If the user turns it off, it shouldn't be auto turned back on
|
||||||
|
ContentResolver.setSyncAutomatically(account, BuildConfig.CONTRIBUTION_AUTHORITY, true); // Enable sync by default!
|
||||||
|
ContentResolver.setSyncAutomatically(account, BuildConfig.MODIFICATION_AUTHORITY, true); // Enable sync by default!
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccountManager accountManager() {
|
||||||
|
return AccountManager.get(context);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Account|null
|
* @return Account|null
|
||||||
*/
|
*/
|
||||||
|
|
@ -37,7 +87,7 @@ public class SessionManager {
|
||||||
public Account getCurrentAccount() {
|
public Account getCurrentAccount() {
|
||||||
if (currentAccount == null) {
|
if (currentAccount == null) {
|
||||||
AccountManager accountManager = AccountManager.get(context);
|
AccountManager accountManager = AccountManager.get(context);
|
||||||
Account[] allAccounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
|
Account[] allAccounts = accountManager.getAccountsByType(BuildConfig.ACCOUNT_TYPE);
|
||||||
if (allAccounts.length != 0) {
|
if (allAccounts.length != 0) {
|
||||||
currentAccount = allAccounts[0];
|
currentAccount = allAccounts[0];
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +103,7 @@ public class SessionManager {
|
||||||
return false; // This should never happen
|
return false; // This should never happen
|
||||||
}
|
}
|
||||||
|
|
||||||
accountManager.invalidateAuthToken(ACCOUNT_TYPE, mediaWikiApi.getAuthCookie());
|
accountManager.invalidateAuthToken(BuildConfig.ACCOUNT_TYPE, mediaWikiApi.getAuthCookie());
|
||||||
String authCookie = getAuthCookie();
|
String authCookie = getAuthCookie();
|
||||||
|
|
||||||
if (authCookie == null) {
|
if (authCookie == null) {
|
||||||
|
|
@ -92,7 +142,7 @@ public class SessionManager {
|
||||||
|
|
||||||
public Completable clearAllAccounts() {
|
public Completable clearAllAccounts() {
|
||||||
AccountManager accountManager = AccountManager.get(context);
|
AccountManager accountManager = AccountManager.get(context);
|
||||||
Account[] allAccounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
|
Account[] allAccounts = accountManager.getAccountsByType(BuildConfig.ACCOUNT_TYPE);
|
||||||
return Completable.fromObservable(Observable.fromArray(allAccounts)
|
return Completable.fromObservable(Observable.fromArray(allAccounts)
|
||||||
.map(a -> accountManager.removeAccount(a, null, null).getResult()))
|
.map(a -> accountManager.removeAccount(a, null, null).getResult()))
|
||||||
.doOnComplete(() -> currentAccount = null);
|
.doOnComplete(() -> currentAccount = null);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
||||||
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
|
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
|
||||||
|
|
||||||
|
|
@ -19,7 +20,7 @@ import static fr.free.nrw.commons.auth.AccountUtil.ACCOUNT_TYPE;
|
||||||
import static fr.free.nrw.commons.auth.AccountUtil.AUTH_TOKEN_TYPE;
|
import static fr.free.nrw.commons.auth.AccountUtil.AUTH_TOKEN_TYPE;
|
||||||
|
|
||||||
public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
||||||
private static final String[] SYNC_AUTHORITIES = {ContributionsContentProvider.CONTRIBUTION_AUTHORITY, ModificationsContentProvider.MODIFICATIONS_AUTHORITY};
|
private static final String[] SYNC_AUTHORITIES = {BuildConfig.CONTRIBUTION_AUTHORITY, BuildConfig.MODIFICATION_AUTHORITY};
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import android.text.TextUtils;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
@ -22,19 +23,18 @@ import static fr.free.nrw.commons.category.CategoryDao.Table.TABLE_NAME;
|
||||||
|
|
||||||
public class CategoryContentProvider extends CommonsDaggerContentProvider {
|
public class CategoryContentProvider extends CommonsDaggerContentProvider {
|
||||||
|
|
||||||
public static final String AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
|
|
||||||
// For URI matcher
|
// For URI matcher
|
||||||
private static final int CATEGORIES = 1;
|
private static final int CATEGORIES = 1;
|
||||||
private static final int CATEGORIES_ID = 2;
|
private static final int CATEGORIES_ID = 2;
|
||||||
private static final String BASE_PATH = "categories";
|
private static final String BASE_PATH = "categories";
|
||||||
|
|
||||||
public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
|
public static final Uri BASE_URI = Uri.parse("content://" + BuildConfig.CATEGORY_AUTHORITY + "/" + BASE_PATH);
|
||||||
|
|
||||||
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
|
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
uriMatcher.addURI(AUTHORITY, BASE_PATH, CATEGORIES);
|
uriMatcher.addURI(BuildConfig.CATEGORY_AUTHORITY, BASE_PATH, CATEGORIES);
|
||||||
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID);
|
uriMatcher.addURI(BuildConfig.CATEGORY_AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Uri uriForId(int id) {
|
public static Uri uriForId(int id) {
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ public class Contribution extends Media {
|
||||||
private String decimalCoords;
|
private String decimalCoords;
|
||||||
private boolean isMultiple;
|
private boolean isMultiple;
|
||||||
private String wikiDataEntityId;
|
private String wikiDataEntityId;
|
||||||
|
private Uri contentProviderUri;
|
||||||
|
|
||||||
public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date timestamp,
|
public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date timestamp,
|
||||||
int state, long dataLength, Date dateUploaded, long transferred,
|
int state, long dataLength, Date dateUploaded, long transferred,
|
||||||
|
|
@ -236,4 +237,12 @@ public class Contribution extends Media {
|
||||||
public void setWikiDataEntityId(String wikiDataEntityId) {
|
public void setWikiDataEntityId(String wikiDataEntityId) {
|
||||||
this.wikiDataEntityId = wikiDataEntityId;
|
this.wikiDataEntityId = wikiDataEntityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setContentProviderUri(Uri contentProviderUri) {
|
||||||
|
this.contentProviderUri = contentProviderUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uri getContentProviderUri() {
|
||||||
|
return contentProviderUri;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,11 @@ import android.content.pm.ResolveInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentActivity;
|
import android.support.v4.app.FragmentActivity;
|
||||||
import android.support.v4.content.FileProvider;
|
import android.support.v4.content.FileProvider;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
@ -28,8 +30,8 @@ import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ENTITY_ID_
|
||||||
|
|
||||||
public class ContributionController {
|
public class ContributionController {
|
||||||
|
|
||||||
private static final int SELECT_FROM_GALLERY = 1;
|
public static final int SELECT_FROM_GALLERY = 1;
|
||||||
private static final int SELECT_FROM_CAMERA = 2;
|
public static final int SELECT_FROM_CAMERA = 2;
|
||||||
|
|
||||||
private Fragment fragment;
|
private Fragment fragment;
|
||||||
|
|
||||||
|
|
@ -91,8 +93,7 @@ public class ContributionController {
|
||||||
fragment.startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY);
|
fragment.startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleImagePicked(int requestCode, Intent data, boolean isDirectUpload, String wikiDataEntityId) {
|
public void handleImagePicked(int requestCode, @Nullable Uri uri, boolean isDirectUpload, String wikiDataEntityId) {
|
||||||
Timber.d("Is direct upload %s and the Wikidata entity ID is %s", isDirectUpload, wikiDataEntityId);
|
|
||||||
FragmentActivity activity = fragment.getActivity();
|
FragmentActivity activity = fragment.getActivity();
|
||||||
Timber.d("handleImagePicked() called with onActivityResult()");
|
Timber.d("handleImagePicked() called with onActivityResult()");
|
||||||
Intent shareIntent = new Intent(activity, ShareActivity.class);
|
Intent shareIntent = new Intent(activity, ShareActivity.class);
|
||||||
|
|
@ -100,7 +101,7 @@ public class ContributionController {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case SELECT_FROM_GALLERY:
|
case SELECT_FROM_GALLERY:
|
||||||
//Handles image picked from gallery
|
//Handles image picked from gallery
|
||||||
Uri imageData = data.getData();
|
Uri imageData = uri;
|
||||||
shareIntent.setType(activity.getContentResolver().getType(imageData));
|
shareIntent.setType(activity.getContentResolver().getType(imageData));
|
||||||
shareIntent.putExtra(EXTRA_STREAM, imageData);
|
shareIntent.putExtra(EXTRA_STREAM, imageData);
|
||||||
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY);
|
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY);
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import fr.free.nrw.commons.quiz.QuizChecker;
|
import fr.free.nrw.commons.quiz.QuizChecker;
|
||||||
import fr.free.nrw.commons.settings.Prefs;
|
import fr.free.nrw.commons.settings.Prefs;
|
||||||
import fr.free.nrw.commons.upload.UploadService;
|
import fr.free.nrw.commons.upload.UploadService;
|
||||||
|
import fr.free.nrw.commons.utils.ContributionUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
|
@ -110,7 +111,7 @@ public class ContributionsActivity
|
||||||
@Override
|
@Override
|
||||||
protected void onAuthCookieAcquired(String authCookie) {
|
protected void onAuthCookieAcquired(String authCookie) {
|
||||||
// Do a sync everytime we get here!
|
// Do a sync everytime we get here!
|
||||||
requestSync(sessionManager.getCurrentAccount(), ContributionsContentProvider.CONTRIBUTION_AUTHORITY, new Bundle());
|
requestSync(sessionManager.getCurrentAccount(), BuildConfig.CONTRIBUTION_AUTHORITY, new Bundle());
|
||||||
Intent uploadServiceIntent = new Intent(this, UploadService.class);
|
Intent uploadServiceIntent = new Intent(this, UploadService.class);
|
||||||
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
|
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
|
||||||
startService(uploadServiceIntent);
|
startService(uploadServiceIntent);
|
||||||
|
|
@ -199,6 +200,9 @@ public class ContributionsActivity
|
||||||
Contribution c = contributionDao.fromCursor(allContributions);
|
Contribution c = contributionDao.fromCursor(allContributions);
|
||||||
if (c.getState() == STATE_FAILED) {
|
if (c.getState() == STATE_FAILED) {
|
||||||
Timber.d("Deleting failed contrib %s", c.toString());
|
Timber.d("Deleting failed contrib %s", c.toString());
|
||||||
|
// If upload fails and then user decides to cancel upload at all, which means contribution
|
||||||
|
// object will be deleted. So we have to delete temp file for that contribution.
|
||||||
|
ContributionUtils.removeTemporaryFile(c.getLocalUri());
|
||||||
contributionDao.delete(c);
|
contributionDao.delete(c);
|
||||||
} else {
|
} else {
|
||||||
Timber.d("Skipping deletion for non-failed contrib %s", c.toString());
|
Timber.d("Skipping deletion for non-failed contrib %s", c.toString());
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import android.text.TextUtils;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
@ -25,13 +26,12 @@ public class ContributionsContentProvider extends CommonsDaggerContentProvider {
|
||||||
private static final int CONTRIBUTIONS_ID = 2;
|
private static final int CONTRIBUTIONS_ID = 2;
|
||||||
private static final String BASE_PATH = "contributions";
|
private static final String BASE_PATH = "contributions";
|
||||||
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
|
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
|
||||||
public static final String CONTRIBUTION_AUTHORITY = "fr.free.nrw.commons.contributions.contentprovider";
|
|
||||||
|
|
||||||
public static final Uri BASE_URI = Uri.parse("content://" + CONTRIBUTION_AUTHORITY + "/" + BASE_PATH);
|
public static final Uri BASE_URI = Uri.parse("content://" + BuildConfig.CONTRIBUTION_AUTHORITY + "/" + BASE_PATH);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
uriMatcher.addURI(CONTRIBUTION_AUTHORITY, BASE_PATH, CONTRIBUTIONS);
|
uriMatcher.addURI(BuildConfig.CONTRIBUTION_AUTHORITY, BASE_PATH, CONTRIBUTIONS);
|
||||||
uriMatcher.addURI(CONTRIBUTION_AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID);
|
uriMatcher.addURI(BuildConfig.CONTRIBUTION_AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Uri uriForId(int id) {
|
public static Uri uriForId(int id) {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,13 @@ package fr.free.nrw.commons.contributions;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
|
@ -31,6 +33,7 @@ import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||||
import fr.free.nrw.commons.nearby.NearbyActivity;
|
import fr.free.nrw.commons.nearby.NearbyActivity;
|
||||||
|
import fr.free.nrw.commons.utils.ContributionUtils;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
||||||
|
|
@ -117,7 +120,13 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment {
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||||
requestCode, resultCode, data);
|
requestCode, resultCode, data);
|
||||||
controller.handleImagePicked(requestCode, data, false, null);
|
if (requestCode == ContributionController.SELECT_FROM_CAMERA) {
|
||||||
|
// If coming from camera, pass null as uri. Because camera photos get saved to a
|
||||||
|
// fixed directory
|
||||||
|
controller.handleImagePicked(requestCode, null, false, null);
|
||||||
|
} else {
|
||||||
|
controller.handleImagePicked(requestCode, data.getData(), false, null);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||||
requestCode, resultCode, data);
|
requestCode, resultCode, data);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import javax.inject.Singleton;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.auth.AccountUtil;
|
import fr.free.nrw.commons.auth.AccountUtil;
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
|
|
@ -23,15 +24,11 @@ import fr.free.nrw.commons.wikidata.WikidataEditListener;
|
||||||
import fr.free.nrw.commons.wikidata.WikidataEditListenerImpl;
|
import fr.free.nrw.commons.wikidata.WikidataEditListenerImpl;
|
||||||
|
|
||||||
import static android.content.Context.MODE_PRIVATE;
|
import static android.content.Context.MODE_PRIVATE;
|
||||||
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY;
|
|
||||||
import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.RECENT_SEARCH_AUTHORITY;
|
import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.RECENT_SEARCH_AUTHORITY;
|
||||||
import static fr.free.nrw.commons.modifications.ModificationsContentProvider.MODIFICATIONS_AUTHORITY;
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
public class CommonsApplicationModule {
|
public class CommonsApplicationModule {
|
||||||
public static final String CATEGORY_AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
|
|
||||||
|
|
||||||
private Context applicationContext;
|
private Context applicationContext;
|
||||||
|
|
||||||
public CommonsApplicationModule(Context applicationContext) {
|
public CommonsApplicationModule(Context applicationContext) {
|
||||||
|
|
@ -51,7 +48,7 @@ public class CommonsApplicationModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Named("category")
|
@Named("category")
|
||||||
public ContentProviderClient provideCategoryContentProviderClient(Context context) {
|
public ContentProviderClient provideCategoryContentProviderClient(Context context) {
|
||||||
return context.getContentResolver().acquireContentProviderClient(CATEGORY_AUTHORITY);
|
return context.getContentResolver().acquireContentProviderClient(BuildConfig.CATEGORY_AUTHORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -69,13 +66,13 @@ public class CommonsApplicationModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Named("contribution")
|
@Named("contribution")
|
||||||
public ContentProviderClient provideContributionContentProviderClient(Context context) {
|
public ContentProviderClient provideContributionContentProviderClient(Context context) {
|
||||||
return context.getContentResolver().acquireContentProviderClient(CONTRIBUTION_AUTHORITY);
|
return context.getContentResolver().acquireContentProviderClient(BuildConfig.CONTRIBUTION_AUTHORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Named("modification")
|
@Named("modification")
|
||||||
public ContentProviderClient provideModificationContentProviderClient(Context context) {
|
public ContentProviderClient provideModificationContentProviderClient(Context context) {
|
||||||
return context.getContentResolver().acquireContentProviderClient(MODIFICATIONS_AUTHORITY);
|
return context.getContentResolver().acquireContentProviderClient(BuildConfig.MODIFICATION_AUTHORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import android.text.TextUtils;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
@ -22,15 +23,14 @@ public class ModificationsContentProvider extends CommonsDaggerContentProvider {
|
||||||
private static final int MODIFICATIONS = 1;
|
private static final int MODIFICATIONS = 1;
|
||||||
private static final int MODIFICATIONS_ID = 2;
|
private static final int MODIFICATIONS_ID = 2;
|
||||||
|
|
||||||
public static final String MODIFICATIONS_AUTHORITY = "fr.free.nrw.commons.modifications.contentprovider";
|
|
||||||
public static final String BASE_PATH = "modifications";
|
public static final String BASE_PATH = "modifications";
|
||||||
|
|
||||||
public static final Uri BASE_URI = Uri.parse("content://" + MODIFICATIONS_AUTHORITY + "/" + BASE_PATH);
|
public static final Uri BASE_URI = Uri.parse("content://" + BuildConfig.MODIFICATION_AUTHORITY + "/" + BASE_PATH);
|
||||||
|
|
||||||
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
static {
|
static {
|
||||||
uriMatcher.addURI(MODIFICATIONS_AUTHORITY, BASE_PATH, MODIFICATIONS);
|
uriMatcher.addURI(BuildConfig.MODIFICATION_AUTHORITY, BASE_PATH, MODIFICATIONS);
|
||||||
uriMatcher.addURI(MODIFICATIONS_AUTHORITY, BASE_PATH + "/#", MODIFICATIONS_ID);
|
uriMatcher.addURI(BuildConfig.MODIFICATION_AUTHORITY, BASE_PATH + "/#", MODIFICATIONS_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Uri uriForId(int id) {
|
public static Uri uriForId(int id) {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import java.io.IOException;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
import fr.free.nrw.commons.contributions.ContributionDao;
|
import fr.free.nrw.commons.contributions.ContributionDao;
|
||||||
|
|
@ -77,7 +78,7 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
|
|
||||||
ContentProviderClient contributionsClient = null;
|
ContentProviderClient contributionsClient = null;
|
||||||
try {
|
try {
|
||||||
contributionsClient = getContext().getContentResolver().acquireContentProviderClient(ContributionsContentProvider.CONTRIBUTION_AUTHORITY);
|
contributionsClient = getContext().getContentResolver().acquireContentProviderClient(BuildConfig.CONTRIBUTION_AUTHORITY);
|
||||||
|
|
||||||
while (!allModifications.isAfterLast()) {
|
while (!allModifications.isAfterLast()) {
|
||||||
ModifierSequence sequence = modifierSequenceDao.fromCursor(allModifications);
|
ModifierSequence sequence = modifierSequenceDao.fromCursor(allModifications);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package fr.free.nrw.commons.mwapi;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
@ -51,6 +52,7 @@ import fr.free.nrw.commons.category.CategoryImageUtils;
|
||||||
import fr.free.nrw.commons.category.QueryContinue;
|
import fr.free.nrw.commons.category.QueryContinue;
|
||||||
import fr.free.nrw.commons.notification.Notification;
|
import fr.free.nrw.commons.notification.Notification;
|
||||||
import fr.free.nrw.commons.notification.NotificationUtils;
|
import fr.free.nrw.commons.notification.NotificationUtils;
|
||||||
|
import fr.free.nrw.commons.utils.ContributionUtils;
|
||||||
import in.yuvi.http.fluent.Http;
|
import in.yuvi.http.fluent.Http;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
|
@ -92,6 +94,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
|
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
|
||||||
params.setParameter(CoreProtocolPNames.USER_AGENT, getUserAgent());
|
params.setParameter(CoreProtocolPNames.USER_AGENT, getUserAgent());
|
||||||
httpClient = new DefaultHttpClient(cm, params);
|
httpClient = new DefaultHttpClient(cm, params);
|
||||||
|
httpClient.addRequestInterceptor(NetworkInterceptors.getHttpRequestInterceptor());
|
||||||
api = new MWApi(apiURL, httpClient);
|
api = new MWApi(apiURL, httpClient);
|
||||||
wikidataApi = new MWApi(wikidatApiURL, httpClient);
|
wikidataApi = new MWApi(wikidatApiURL, httpClient);
|
||||||
this.defaultPreferences = defaultPreferences;
|
this.defaultPreferences = defaultPreferences;
|
||||||
|
|
@ -855,17 +858,23 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
long dataLength,
|
long dataLength,
|
||||||
String pageContents,
|
String pageContents,
|
||||||
String editSummary,
|
String editSummary,
|
||||||
final ProgressListener progressListener) throws IOException {
|
final ProgressListener progressListener,
|
||||||
|
Uri fileUri,
|
||||||
|
Uri contentProviderUri) throws IOException {
|
||||||
|
|
||||||
ApiResult result = api.upload(filename, file, dataLength, pageContents, editSummary, progressListener::onProgress);
|
ApiResult result = api.upload(filename, file, dataLength, pageContents, editSummary, progressListener::onProgress);
|
||||||
|
|
||||||
Log.e("WTF", "Result: " + result.toString());
|
Log.e("WTF", "Result: " + result.toString());
|
||||||
|
|
||||||
String resultStatus = result.getString("/api/upload/@result");
|
String resultStatus = result.getString("/api/upload/@result");
|
||||||
|
|
||||||
if (!resultStatus.equals("Success")) {
|
if (!resultStatus.equals("Success")) {
|
||||||
String errorCode = result.getString("/api/error/@code");
|
String errorCode = result.getString("/api/error/@code");
|
||||||
Timber.e(errorCode);
|
Timber.e(errorCode);
|
||||||
return new UploadResult(resultStatus, errorCode);
|
return new UploadResult(resultStatus, errorCode);
|
||||||
} else {
|
} else {
|
||||||
|
// If success we have to remove file from temp directory
|
||||||
|
ContributionUtils.removeTemporaryFile(fileUri);
|
||||||
Date dateUploaded = parseMWDate(result.getString("/api/upload/imageinfo/@timestamp"));
|
Date dateUploaded = parseMWDate(result.getString("/api/upload/imageinfo/@timestamp"));
|
||||||
String canonicalFilename = "File:" + result.getString("/api/upload/@filename").replace("_", " "); // Title vs Filename
|
String canonicalFilename = "File:" + result.getString("/api/upload/@filename").replace("_", " "); // Title vs Filename
|
||||||
String imageUrl = result.getString("/api/upload/imageinfo/@url");
|
String imageUrl = result.getString("/api/upload/imageinfo/@url");
|
||||||
|
|
@ -873,7 +882,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Single<Integer> getUploadCount(String userName) {
|
public Single<Integer> getUploadCount(String userName) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package fr.free.nrw.commons.mwapi;
|
package fr.free.nrw.commons.mwapi;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
|
@ -54,7 +55,7 @@ public interface MediaWikiApi {
|
||||||
List<String> searchCategory(String title, int offset);
|
List<String> searchCategory(String title, int offset);
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, ProgressListener progressListener) throws IOException;
|
UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, ProgressListener progressListener, Uri fileUri, Uri contentProviderUri) throws IOException;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
String edit(String editToken, String processedPageContent, String filename, String summary) throws IOException;
|
String edit(String editToken, String processedPageContent, String filename, String summary) throws IOException;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
package fr.free.nrw.commons.mwapi;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpRequest;
|
||||||
|
import org.apache.http.HttpRequestInterceptor;
|
||||||
|
import org.apache.http.impl.client.ClientParamsStack;
|
||||||
|
import org.apache.http.params.HttpParamsNames;
|
||||||
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class NetworkInterceptors {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interceptor to log the HTTP request
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static HttpRequestInterceptor getHttpRequestInterceptor() {
|
||||||
|
return (HttpRequest request, HttpContext httpContext) -> {
|
||||||
|
Timber.v("<<<<<<<<<<<<<< START OF REQUEST LOGGING [%s] >>>>>>>>>>>>", request.getRequestLine().getUri());
|
||||||
|
|
||||||
|
Timber.v("Request line:\n %s", request.getRequestLine().toString());
|
||||||
|
logRequestParams(request);
|
||||||
|
logRequestHeaders(request);
|
||||||
|
Timber.v("Protocol version:\n %s", request.getProtocolVersion());
|
||||||
|
|
||||||
|
Timber.v("<<<<<<<<<<<<<< END OF REQUEST LOGGING [%s] >>>>>>>>>>>>", request.getRequestLine().getUri());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log all request params from a HTTPRequest
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
private static void logRequestParams(HttpRequest request) {
|
||||||
|
Set<String> names = new HashSet<>();
|
||||||
|
if (request.getParams() instanceof ClientParamsStack) {
|
||||||
|
ClientParamsStack cps = (ClientParamsStack) request.getParams();
|
||||||
|
if (cps.getApplicationParams() != null
|
||||||
|
&& cps.getRequestParams() instanceof HttpParamsNames) {
|
||||||
|
names.addAll(((HttpParamsNames) cps.getApplicationParams()).getNames());
|
||||||
|
}
|
||||||
|
if (cps.getClientParams() != null
|
||||||
|
&& cps.getClientParams() instanceof HttpParamsNames) {
|
||||||
|
names.addAll(((HttpParamsNames) cps.getClientParams()).getNames());
|
||||||
|
}
|
||||||
|
if (cps.getRequestParams() != null
|
||||||
|
&& cps.getRequestParams() instanceof HttpParamsNames) {
|
||||||
|
names.addAll(((HttpParamsNames) cps.getRequestParams()).getNames());
|
||||||
|
}
|
||||||
|
if (cps.getOverrideParams() != null
|
||||||
|
&& cps.getRequestParams() instanceof HttpParamsNames) {
|
||||||
|
names.addAll(((HttpParamsNames) cps.getOverrideParams()).getNames());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HttpParamsNames params = (HttpParamsNames) request.getParams();
|
||||||
|
names = params.getNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.v("<<<<<<<<<<<<<< REQUEST PARAMS >>>>>>>>>>>>");
|
||||||
|
for (String name : names) {
|
||||||
|
Timber.v("Param >> %s: %s", name, request.getParams().getParameter(name));
|
||||||
|
}
|
||||||
|
Timber.v("<<<<<<<<<<<<<< REQUEST PARAMS >>>>>>>>>>>>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log all headers from a HTTPRequest
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
private static void logRequestHeaders(HttpRequest request) {
|
||||||
|
Header[] headerFields = request.getAllHeaders();
|
||||||
|
|
||||||
|
Timber.v("<<<<<<<<<<<<<< HEADERS >>>>>>>>>>>>");
|
||||||
|
for (int e = 0; e < request.getAllHeaders().length; e++) {
|
||||||
|
Timber.v("Header >> %s: %s", headerFields[e].getName(), headerFields[e].getValue());
|
||||||
|
}
|
||||||
|
Timber.v("<<<<<<<<<<<<<< HEADERS >>>>>>>>>>>>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,7 @@ import dagger.android.support.DaggerFragment;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.contributions.ContributionController;
|
import fr.free.nrw.commons.contributions.ContributionController;
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
|
import fr.free.nrw.commons.utils.ContributionUtils;
|
||||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
@ -147,7 +148,13 @@ public class NearbyListFragment extends DaggerFragment {
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||||
requestCode, resultCode, data);
|
requestCode, resultCode, data);
|
||||||
controller.handleImagePicked(requestCode, data, true, directPrefs.getString(WIKIDATA_ENTITY_ID_PREF, null));
|
if (requestCode == ContributionController.SELECT_FROM_CAMERA) {
|
||||||
|
// If coming from camera, pass null as uri. Because camera photos get saved to a
|
||||||
|
// fixed directory
|
||||||
|
controller.handleImagePicked(requestCode, null, true, null);
|
||||||
|
} else {
|
||||||
|
controller.handleImagePicked(requestCode, data.getData(), true, null);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||||
requestCode, resultCode, data);
|
requestCode, resultCode, data);
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ import dagger.android.support.DaggerFragment;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.Utils;
|
import fr.free.nrw.commons.Utils;
|
||||||
import fr.free.nrw.commons.contributions.ContributionController;
|
import fr.free.nrw.commons.contributions.ContributionController;
|
||||||
|
import fr.free.nrw.commons.utils.ContributionUtils;
|
||||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
@ -765,10 +766,17 @@ public class NearbyMapFragment extends DaggerFragment {
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||||
requestCode, resultCode, data);
|
requestCode, resultCode, data);
|
||||||
controller.handleImagePicked(requestCode, data, true, directPrefs.getString(WIKIDATA_ENTITY_ID_PREF, null));
|
if (requestCode == ContributionController.SELECT_FROM_CAMERA) {
|
||||||
|
// If coming from camera, pass null as uri. Because camera photos get saved to a
|
||||||
|
// fixed directory
|
||||||
|
controller.handleImagePicked(requestCode, null, true, null);
|
||||||
|
} else {
|
||||||
|
controller.handleImagePicked(requestCode, data.getData(), true, null);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||||
requestCode, resultCode, data);
|
requestCode, resultCode, data);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import android.provider.DocumentsContract;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.Media;
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
||||||
|
|
@ -45,6 +46,8 @@ import fr.free.nrw.commons.modifications.ModifierSequence;
|
||||||
import fr.free.nrw.commons.modifications.ModifierSequenceDao;
|
import fr.free.nrw.commons.modifications.ModifierSequenceDao;
|
||||||
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
|
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
import fr.free.nrw.commons.utils.ContributionUtils;
|
||||||
|
import fr.free.nrw.commons.utils.ExternalStorageUtils;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
//TODO: We should use this class to see how multiple uploads are handled, and then REMOVE it.
|
//TODO: We should use this class to see how multiple uploads are handled, and then REMOVE it.
|
||||||
|
|
@ -54,7 +57,8 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
||||||
AdapterView.OnItemClickListener,
|
AdapterView.OnItemClickListener,
|
||||||
FragmentManager.OnBackStackChangedListener,
|
FragmentManager.OnBackStackChangedListener,
|
||||||
MultipleUploadListFragment.OnMultipleUploadInitiatedHandler,
|
MultipleUploadListFragment.OnMultipleUploadInitiatedHandler,
|
||||||
OnCategoriesSaveHandler {
|
OnCategoriesSaveHandler,
|
||||||
|
ActivityCompat.OnRequestPermissionsResultCallback{
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MediaWikiApi mwApi;
|
MediaWikiApi mwApi;
|
||||||
|
|
@ -75,6 +79,8 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
||||||
private CategorizationFragment categorizationFragment;
|
private CategorizationFragment categorizationFragment;
|
||||||
|
|
||||||
private boolean locationPermitted = false;
|
private boolean locationPermitted = false;
|
||||||
|
private boolean isMultipleUploadsPrepared = false;
|
||||||
|
private boolean isMultipleUploadsFinalised = false; // Checks is user clicked to upload button or regret before this phase
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Media getMediaAtPosition(int i) {
|
public Media getMediaAtPosition(int i) {
|
||||||
|
|
@ -113,30 +119,25 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void OnMultipleUploadInitiated() {
|
public void OnMultipleUploadInitiated() {
|
||||||
|
// No need to request external permission here, because if user can reach this point, then she permission granted
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
Timber.d("OnMultipleUploadInitiated");
|
||||||
//Check for Storage permission that is required for upload. Do not allow user to proceed without permission, otherwise will crash
|
multipleUploadBegins();
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
|
|
||||||
} else {
|
|
||||||
multipleUploadBegins();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
multipleUploadBegins();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
if (requestCode == 1 && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (requestCode == 1 && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
multipleUploadBegins();
|
Timber.d("onRequestPermissionsResult external storage permission granted");
|
||||||
|
prepareMultipleUpoadList();
|
||||||
|
} else {
|
||||||
|
// Permission is not granted, close activity
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void multipleUploadBegins() {
|
private void multipleUploadBegins() {
|
||||||
|
|
||||||
Timber.d("Multiple upload begins");
|
Timber.d("Multiple upload begins");
|
||||||
|
|
||||||
final ProgressDialog dialog = new ProgressDialog(this);
|
final ProgressDialog dialog = new ProgressDialog(this);
|
||||||
dialog.setIndeterminate(false);
|
dialog.setIndeterminate(false);
|
||||||
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||||
|
|
@ -174,6 +175,7 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.add(R.id.uploadsFragmentContainer, categorizationFragment, "categorization")
|
.add(R.id.uploadsFragmentContainer, categorizationFragment, "categorization")
|
||||||
.commitAllowingStateLoss();
|
.commitAllowingStateLoss();
|
||||||
|
isMultipleUploadsFinalised = true;
|
||||||
//See http://stackoverflow.com/questions/7469082/getting-exception-illegalstateexception-can-not-perform-this-action-after-onsa
|
//See http://stackoverflow.com/questions/7469082/getting-exception-illegalstateexception-can-not-perform-this-action-after-onsa
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,7 +193,7 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
||||||
}
|
}
|
||||||
// FIXME: Make sure that the content provider is up
|
// FIXME: Make sure that the content provider is up
|
||||||
// This is the wrong place for it, but bleh - better than not having it turned on by default for people who don't go throughl ogin
|
// This is the wrong place for it, but bleh - better than not having it turned on by default for people who don't go throughl ogin
|
||||||
ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(), ModificationsContentProvider.MODIFICATIONS_AUTHORITY, true); // Enable sync by default!
|
ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(), BuildConfig.MODIFICATION_AUTHORITY, true); // Enable sync by default!
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -253,13 +255,40 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
/* This will be true if permission request is granted before we request. Otherwise we will
|
||||||
outState.putParcelableArrayList("uploadsList", photosList);
|
* explicitly call operations under this method again.
|
||||||
|
*/
|
||||||
|
if (isMultipleUploadsPrepared) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
Timber.d("onSaveInstanceState multiple uploads is prepared, permission granted");
|
||||||
|
outState.putParcelableArrayList("uploadsList", photosList);
|
||||||
|
} else {
|
||||||
|
Timber.d("onSaveInstanceState multiple uploads is not prepared, permission not granted");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onAuthCookieAcquired(String authCookie) {
|
protected void onAuthCookieAcquired(String authCookie) {
|
||||||
|
// Multiple uploads prepared boolean is used to decide when to call multipleUploadsBegin()
|
||||||
|
isMultipleUploadsFinalised = false;
|
||||||
|
isMultipleUploadsPrepared = false;
|
||||||
mwApi.setAuthCookie(authCookie);
|
mwApi.setAuthCookie(authCookie);
|
||||||
|
if (!ExternalStorageUtils.isStoragePermissionGranted(this)) {
|
||||||
|
ExternalStorageUtils.requestExternalStoragePermission(this);
|
||||||
|
isMultipleUploadsPrepared = false;
|
||||||
|
return; // Postpone operation to do after gettion permission
|
||||||
|
} else {
|
||||||
|
isMultipleUploadsPrepared = true;
|
||||||
|
prepareMultipleUpoadList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares a list from files will be uploaded. Saves these files temporarily to external
|
||||||
|
* storage. Adds them to uploads list
|
||||||
|
*/
|
||||||
|
private void prepareMultipleUpoadList() {
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|
||||||
if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) {
|
if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) {
|
||||||
|
|
@ -269,6 +298,8 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
||||||
for (int i = 0; i < urisList.size(); i++) {
|
for (int i = 0; i < urisList.size(); i++) {
|
||||||
Contribution up = new Contribution();
|
Contribution up = new Contribution();
|
||||||
Uri uri = urisList.get(i);
|
Uri uri = urisList.get(i);
|
||||||
|
// Use temporarily saved file Uri instead
|
||||||
|
uri = ContributionUtils.saveFileBeingUploadedTemporarily(this, uri);
|
||||||
up.setLocalUri(uri);
|
up.setLocalUri(uri);
|
||||||
up.setTag("mimeType", intent.getType());
|
up.setTag("mimeType", intent.getType());
|
||||||
up.setTag("sequence", i);
|
up.setTag("sequence", i);
|
||||||
|
|
@ -350,4 +381,24 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If on back pressed before sharing
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
// Remove saved files if activity is stopped before upload operation, ie user changed mind
|
||||||
|
if (!isMultipleUploadsFinalised) {
|
||||||
|
if (photosList != null) {
|
||||||
|
for (Contribution contribution : photosList) {
|
||||||
|
Timber.d("User changed mind, didn't click to upload button, deleted file: "+contribution.getLocalUri());
|
||||||
|
ContributionUtils.removeTemporaryFile(contribution.getLocalUri());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,9 @@ import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.RequiresApi;
|
import android.support.annotation.RequiresApi;
|
||||||
import android.support.design.widget.FloatingActionButton;
|
import android.support.design.widget.FloatingActionButton;
|
||||||
import android.support.graphics.drawable.VectorDrawableCompat;
|
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
@ -34,6 +36,8 @@ import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||||
import com.facebook.drawee.view.SimpleDraweeView;
|
import com.facebook.drawee.view.SimpleDraweeView;
|
||||||
import com.github.chrisbanes.photoview.PhotoView;
|
import com.github.chrisbanes.photoview.PhotoView;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
@ -46,6 +50,7 @@ import javax.inject.Named;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import butterknife.OnClick;
|
import butterknife.OnClick;
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
import fr.free.nrw.commons.auth.LoginActivity;
|
||||||
|
|
@ -61,6 +66,8 @@ import fr.free.nrw.commons.modifications.ModifierSequenceDao;
|
||||||
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
|
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
|
||||||
import fr.free.nrw.commons.mwapi.CategoryApi;
|
import fr.free.nrw.commons.mwapi.CategoryApi;
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
import fr.free.nrw.commons.utils.ContributionUtils;
|
||||||
|
import fr.free.nrw.commons.utils.ExternalStorageUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
@ -76,7 +83,9 @@ import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ENTITY_ID_
|
||||||
public class ShareActivity
|
public class ShareActivity
|
||||||
extends AuthenticatedActivity
|
extends AuthenticatedActivity
|
||||||
implements SingleUploadFragment.OnUploadActionInitiated,
|
implements SingleUploadFragment.OnUploadActionInitiated,
|
||||||
OnCategoriesSaveHandler {
|
OnCategoriesSaveHandler,
|
||||||
|
ActivityCompat.OnRequestPermissionsResultCallback {
|
||||||
|
|
||||||
private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4;
|
private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4;
|
||||||
//Had to make them class variables, to extract out the click listeners, also I see no harm in this
|
//Had to make them class variables, to extract out the click listeners, also I see no harm in this
|
||||||
final Rect startBounds = new Rect();
|
final Rect startBounds = new Rect();
|
||||||
|
|
@ -119,6 +128,7 @@ public class ShareActivity
|
||||||
private String mimeType;
|
private String mimeType;
|
||||||
private CategorizationFragment categorizationFragment;
|
private CategorizationFragment categorizationFragment;
|
||||||
private Uri mediaUri;
|
private Uri mediaUri;
|
||||||
|
private Uri contentProviderUri;
|
||||||
private Contribution contribution;
|
private Contribution contribution;
|
||||||
private GPSExtractor gpsObj;
|
private GPSExtractor gpsObj;
|
||||||
private String decimalCoords;
|
private String decimalCoords;
|
||||||
|
|
@ -135,9 +145,12 @@ public class ShareActivity
|
||||||
private long ShortAnimationDuration;
|
private long ShortAnimationDuration;
|
||||||
private boolean isFABOpen = false;
|
private boolean isFABOpen = false;
|
||||||
private float startScaleFinal;
|
private float startScaleFinal;
|
||||||
|
private Bundle savedInstanceState;
|
||||||
|
private boolean isUploadFinalised = false; // Checks is user clicked to upload button or regret before this phase
|
||||||
private boolean isZoom = false;
|
private boolean isZoom = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when user taps the submit button.
|
* Called when user taps the submit button.
|
||||||
* Requests Storage permission, if needed.
|
* Requests Storage permission, if needed.
|
||||||
|
|
@ -183,6 +196,7 @@ public class ShareActivity
|
||||||
return !FileUtils.isSelfOwned(getApplicationContext(), mediaUri)
|
return !FileUtils.isSelfOwned(getApplicationContext(), mediaUri)
|
||||||
&& (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
&& (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
!= PackageManager.PERMISSION_GRANTED);
|
!= PackageManager.PERMISSION_GRANTED);
|
||||||
|
//return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -203,13 +217,12 @@ public class ShareActivity
|
||||||
Timber.d("Cache the categories found");
|
Timber.d("Cache the categories found");
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadController.startUpload(title,mediaUri,description,mimeType,source,decimalCoords,wikiDataEntityId,c ->
|
uploadController.startUpload(title, contentProviderUri, mediaUri, description, mimeType, source, decimalCoords, wikiDataEntityId, c -> {
|
||||||
|
ShareActivity.this.contribution = c;
|
||||||
{
|
showPostUpload();
|
||||||
ShareActivity.this.contribution = c;
|
});
|
||||||
showPostUpload();
|
isUploadFinalised = true;
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts CategorizationFragment after uploadBegins.
|
* Starts CategorizationFragment after uploadBegins.
|
||||||
|
|
@ -241,7 +254,7 @@ public class ShareActivity
|
||||||
|
|
||||||
// FIXME: Make sure that the content provider is up
|
// FIXME: Make sure that the content provider is up
|
||||||
// This is the wrong place for it, but bleh - better than not having it turned on by default for people who don't go throughl ogin
|
// This is the wrong place for it, but bleh - better than not having it turned on by default for people who don't go throughl ogin
|
||||||
ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(), ModificationsContentProvider.MODIFICATIONS_AUTHORITY, true); // Enable sync by default!
|
ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(), BuildConfig.MODIFICATION_AUTHORITY, true); // Enable sync by default!
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
@ -270,7 +283,7 @@ public class ShareActivity
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
isUploadFinalised = false;
|
||||||
setContentView(R.layout.activity_share);
|
setContentView(R.layout.activity_share);
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
initBack();
|
initBack();
|
||||||
|
|
@ -281,9 +294,29 @@ public class ShareActivity
|
||||||
.setFailureImage(VectorDrawableCompat.create(getResources(),
|
.setFailureImage(VectorDrawableCompat.create(getResources(),
|
||||||
R.drawable.ic_error_outline_black_24dp, getTheme()))
|
R.drawable.ic_error_outline_black_24dp, getTheme()))
|
||||||
.build());
|
.build());
|
||||||
|
if (!ExternalStorageUtils.isStoragePermissionGranted(this)) {
|
||||||
|
this.savedInstanceState = savedInstanceState;
|
||||||
|
ExternalStorageUtils.requestExternalStoragePermission(this);
|
||||||
|
return; // Postpone operation to do after getting permission
|
||||||
|
} else {
|
||||||
|
receiveImageIntent();
|
||||||
|
createContributionWithReceivedIntent(savedInstanceState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
receiveImageIntent();
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
// If upload is not finalised with failure or success, but contribution is created,
|
||||||
|
// we have to remove temp file, to prevent using unnecessary memory
|
||||||
|
if (!isUploadFinalised) {
|
||||||
|
if (mediaUri != null) {
|
||||||
|
ContributionUtils.removeTemporaryFile(mediaUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createContributionWithReceivedIntent(Bundle savedInstanceState) {
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
contribution = savedInstanceState.getParcelable("contribution");
|
contribution = savedInstanceState.getParcelable("contribution");
|
||||||
}
|
}
|
||||||
|
|
@ -318,6 +351,11 @@ public class ShareActivity
|
||||||
|
|
||||||
if (Intent.ACTION_SEND.equals(intent.getAction())) {
|
if (Intent.ACTION_SEND.equals(intent.getAction())) {
|
||||||
mediaUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
mediaUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||||
|
|
||||||
|
contentProviderUri = mediaUri;
|
||||||
|
|
||||||
|
mediaUri = ContributionUtils.saveFileBeingUploadedTemporarily(this, mediaUri);
|
||||||
|
|
||||||
if (intent.hasExtra(UploadService.EXTRA_SOURCE)) {
|
if (intent.hasExtra(UploadService.EXTRA_SOURCE)) {
|
||||||
source = intent.getStringExtra(UploadService.EXTRA_SOURCE);
|
source = intent.getStringExtra(UploadService.EXTRA_SOURCE);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -400,17 +438,20 @@ public class ShareActivity
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
switch (requestCode) {
|
if (requestCode == 1 && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
// Storage (from submit button) - this needs to be separate from (1) because only the
|
Timber.d("onRequestPermissionsResult external storage permission granted");
|
||||||
// submit button should bring user to next screen
|
// You can receive image intent and save image to a temp file only if ext storage permission is granted
|
||||||
case REQUEST_PERM_ON_SUBMIT_STORAGE: {
|
receiveImageIntent();
|
||||||
if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
createContributionWithReceivedIntent(savedInstanceState);
|
||||||
checkIfFileExists();
|
|
||||||
|
|
||||||
//Uploading only begins if storage permission granted from arrow icon
|
if (requestCode == REQUEST_PERM_ON_SUBMIT_STORAGE) {
|
||||||
uploadBegins();
|
checkIfFileExists();
|
||||||
}
|
//Uploading only begins if storage permission granted from arrow icon
|
||||||
|
uploadBegins();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,10 @@ import android.os.AsyncTask;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.widget.Toast;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
@ -100,7 +101,7 @@ public class UploadController {
|
||||||
* @param wikiDataEntityId
|
* @param wikiDataEntityId
|
||||||
* @param onComplete the progress tracker
|
* @param onComplete the progress tracker
|
||||||
*/
|
*/
|
||||||
public void startUpload(String title, Uri mediaUri, String description, String mimeType, String source, String decimalCoords, String wikiDataEntityId, ContributionUploadProgress onComplete) {
|
public void startUpload(String title, Uri contentProviderUri, Uri mediaUri, String description, String mimeType, String source, String decimalCoords, String wikiDataEntityId, ContributionUploadProgress onComplete) {
|
||||||
Contribution contribution;
|
Contribution contribution;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -133,6 +134,7 @@ public class UploadController {
|
||||||
contribution.setTag("mimeType", mimeType);
|
contribution.setTag("mimeType", mimeType);
|
||||||
contribution.setSource(source);
|
contribution.setSource(source);
|
||||||
contribution.setWikiDataEntityId(wikiDataEntityId);
|
contribution.setWikiDataEntityId(wikiDataEntityId);
|
||||||
|
contribution.setContentProviderUri(contentProviderUri);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -168,9 +170,12 @@ public class UploadController {
|
||||||
long length;
|
long length;
|
||||||
ContentResolver contentResolver = context.getContentResolver();
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
//TODO: understand do we really need this code
|
||||||
if (contribution.getDataLength() <= 0) {
|
if (contribution.getDataLength() <= 0) {
|
||||||
|
Log.d("deneme","UploadController/doInBackground, contribution.getLocalUri():"+contribution.getLocalUri());
|
||||||
AssetFileDescriptor assetFileDescriptor = contentResolver
|
AssetFileDescriptor assetFileDescriptor = contentResolver
|
||||||
.openAssetFileDescriptor(contribution.getLocalUri(), "r");
|
.openAssetFileDescriptor(Uri.fromFile(new File(contribution.getLocalUri().getPath())), "r");
|
||||||
if (assetFileDescriptor != null) {
|
if (assetFileDescriptor != null) {
|
||||||
length = assetFileDescriptor.getLength();
|
length = assetFileDescriptor.getLength();
|
||||||
if (length == -1) {
|
if (length == -1) {
|
||||||
|
|
@ -220,7 +225,7 @@ public class UploadController {
|
||||||
contribution.setDateCreated(new Date());
|
contribution.setDateCreated(new Date());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contribution;
|
return contribution;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,12 @@ import android.content.Intent;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.util.Log;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
@ -23,6 +26,7 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.HandlerService;
|
import fr.free.nrw.commons.HandlerService;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.Utils;
|
import fr.free.nrw.commons.Utils;
|
||||||
|
|
@ -179,13 +183,15 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
|
|
||||||
@SuppressLint("StringFormatInvalid")
|
@SuppressLint("StringFormatInvalid")
|
||||||
private void uploadContribution(Contribution contribution) {
|
private void uploadContribution(Contribution contribution) {
|
||||||
InputStream file;
|
InputStream fileInputStream;
|
||||||
|
|
||||||
String notificationTag = contribution.getLocalUri().toString();
|
String notificationTag = contribution.getLocalUri().toString();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//FIXME: Google Photos bug
|
//FIXME: Google Photos bug
|
||||||
file = this.getContentResolver().openInputStream(contribution.getLocalUri());
|
File file1 = new File(contribution.getLocalUri().getPath());
|
||||||
|
fileInputStream = new FileInputStream(file1);
|
||||||
|
//fileInputStream = this.getContentResolver().openInputStream(contribution.getLocalUri());
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Timber.d("File not found");
|
Timber.d("File not found");
|
||||||
Toast fileNotFound = Toast.makeText(this, R.string.upload_failed, Toast.LENGTH_LONG);
|
Toast fileNotFound = Toast.makeText(this, R.string.upload_failed, Toast.LENGTH_LONG);
|
||||||
|
|
@ -193,9 +199,9 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//As the file is null there's no point in continuing the upload process
|
//As the fileInputStream is null there's no point in continuing the upload process
|
||||||
//mwapi.upload accepts a NonNull input stream
|
//mwapi.upload accepts a NonNull input stream
|
||||||
if(file == null) {
|
if(fileInputStream == null) {
|
||||||
Timber.d("File not found");
|
Timber.d("File not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -243,7 +249,7 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
getString(R.string.upload_progress_notification_title_finishing, contribution.getDisplayTitle()),
|
getString(R.string.upload_progress_notification_title_finishing, contribution.getDisplayTitle()),
|
||||||
contribution
|
contribution
|
||||||
);
|
);
|
||||||
UploadResult uploadResult = mwApi.uploadFile(filename, file, contribution.getDataLength(), contribution.getPageContents(), contribution.getEditSummary(), notificationUpdater);
|
UploadResult uploadResult = mwApi.uploadFile(filename, fileInputStream, contribution.getDataLength(), contribution.getPageContents(), contribution.getEditSummary(), notificationUpdater, contribution.getLocalUri(), contribution.getContentProviderUri());
|
||||||
|
|
||||||
Timber.d("Response is %s", uploadResult.toString());
|
Timber.d("Response is %s", uploadResult.toString());
|
||||||
|
|
||||||
|
|
@ -272,7 +278,7 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
toUpload--;
|
toUpload--;
|
||||||
if (toUpload == 0) {
|
if (toUpload == 0) {
|
||||||
// Sync modifications right after all uplaods are processed
|
// Sync modifications right after all uplaods are processed
|
||||||
ContentResolver.requestSync(sessionManager.getCurrentAccount(), ModificationsContentProvider.MODIFICATIONS_AUTHORITY, new Bundle());
|
ContentResolver.requestSync(sessionManager.getCurrentAccount(), BuildConfig.MODIFICATION_AUTHORITY, new Bundle());
|
||||||
stopForeground(true);
|
stopForeground(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
package fr.free.nrw.commons.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class includes utility methods for uploading process of images.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ContributionUtils {
|
||||||
|
|
||||||
|
private static String TEMP_EXTERNAL_DIRECTORY =
|
||||||
|
android.os.Environment.getExternalStorageDirectory().getPath()+
|
||||||
|
File.separatorChar+"UploadingByCommonsApp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves images temporarily to a fixed folder and use Uri of that file during upload process.
|
||||||
|
* Otherwise, temporary Uri provided by content provider sometimes points to a null space and
|
||||||
|
* consequently upload fails. See: issue #1400A and E.
|
||||||
|
* Not: Saved image will be deleted, our directory will be empty after upload process.
|
||||||
|
* @return URI of saved image
|
||||||
|
*/
|
||||||
|
public static Uri saveFileBeingUploadedTemporarily(Context context, Uri URIfromContentProvider) {
|
||||||
|
// TODO add exceptions for Google Drive URİ is needed
|
||||||
|
Uri result = null;
|
||||||
|
|
||||||
|
if (FileUtils.checkIfDirectoryExists(TEMP_EXTERNAL_DIRECTORY)) {
|
||||||
|
String destinationFilename = decideTempDestinationFileName();
|
||||||
|
result = FileUtils.saveFileFromURI(context, URIfromContentProvider, destinationFilename);
|
||||||
|
} else { // If directory doesn't exist, create it and recursive call current method to check again
|
||||||
|
|
||||||
|
File file = new File(TEMP_EXTERNAL_DIRECTORY);
|
||||||
|
if (file.mkdirs()) {
|
||||||
|
Timber.d("saveFileBeingUploadedTemporarily() parameters: URI from Content Provider %s", URIfromContentProvider);
|
||||||
|
result = saveFileBeingUploadedTemporarily(context, URIfromContentProvider); // If directory is created
|
||||||
|
} else { //An error occurred to create directory
|
||||||
|
Timber.e("saveFileBeingUploadedTemporarily() parameters: URI from Content Provider %s", URIfromContentProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes temp file created during upload
|
||||||
|
* @param tempFileUri
|
||||||
|
*/
|
||||||
|
public static void removeTemporaryFile(Uri tempFileUri) {
|
||||||
|
//TODO: do I have to notify file system about deletion?
|
||||||
|
File tempFile = new File(tempFileUri.getPath());
|
||||||
|
if (tempFile.exists()) {
|
||||||
|
boolean isDeleted= tempFile.delete();
|
||||||
|
Timber.e("removeTemporaryFile() parameters: URI tempFileUri %s, deleted status %b", tempFileUri, isDeleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String decideTempDestinationFileName() {
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
if (new File(TEMP_EXTERNAL_DIRECTORY +File.separatorChar+i+"_tmp").exists()) {
|
||||||
|
// This file is in use, try enother file
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
// Use time stamp for file name, so that two temporary file never has same file name
|
||||||
|
// to prevent previous file reference bug
|
||||||
|
Long tsLong = System.currentTimeMillis()/1000;
|
||||||
|
String ts = tsLong.toString();
|
||||||
|
|
||||||
|
// For multiple uploads, time randomisation should be combined with another random
|
||||||
|
// parameter, since they created at same time
|
||||||
|
int multipleUploadRandomParameter = new Random().nextInt(100);
|
||||||
|
return TEMP_EXTERNAL_DIRECTORY +File.separatorChar+ts+multipleUploadRandomParameter+"_tmp";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void emptyTemporaryDirectory() {
|
||||||
|
File dir = new File(TEMP_EXTERNAL_DIRECTORY);
|
||||||
|
if (dir.isDirectory())
|
||||||
|
{
|
||||||
|
String[] children = dir.list();
|
||||||
|
if (children != null && children.length >0) {
|
||||||
|
for (int i = 0; i < children.length; i++)
|
||||||
|
{
|
||||||
|
new File(dir, children[i]).delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package fr.free.nrw.commons.utils;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by root on 23.07.2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ExternalStorageUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if external storage permission is granted
|
||||||
|
* @param context activity we are on
|
||||||
|
* @return true if permission is granted, false if not
|
||||||
|
*/
|
||||||
|
public static boolean isStoragePermissionGranted(Context context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
|
if (context.checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Timber.d("External storage permission granted, API >= 23");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Timber.d("External storage permission not granted, API >= 23");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else { //permission is automatically granted on sdk<23 upon installation
|
||||||
|
Timber.d("External storage permission granted before, API < 23");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests external storage permission
|
||||||
|
* @param context activity we are on
|
||||||
|
*/
|
||||||
|
public static void requestExternalStoragePermission(Context context) {
|
||||||
|
Timber.d("External storage permission requested");
|
||||||
|
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
89
app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java
Normal file
89
app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package fr.free.nrw.commons.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created for file operations
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class FileUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves file from source URI to destination.
|
||||||
|
* @param sourceUri Uri which points to file to be saved
|
||||||
|
* @param destinationFilename where file will be located at
|
||||||
|
* @return Uri points to file saved
|
||||||
|
*/
|
||||||
|
public static Uri saveFileFromURI(Context context, Uri sourceUri, String destinationFilename) {
|
||||||
|
File file = new File(destinationFilename);
|
||||||
|
if (file.exists()) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream in = null;
|
||||||
|
OutputStream out = null;
|
||||||
|
try {
|
||||||
|
in = context.getContentResolver().openInputStream(sourceUri);
|
||||||
|
out = new FileOutputStream(new File(destinationFilename));
|
||||||
|
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while((len=in.read(buf))>0){
|
||||||
|
out.write(buf,0,len);
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Uri.parse("file://" + destinationFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if directory exists
|
||||||
|
* @param pathToCheck path of directory to check
|
||||||
|
* @return true if directory exists, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean checkIfDirectoryExists(String pathToCheck) {
|
||||||
|
File director = new File(pathToCheck);
|
||||||
|
if(director.exists() && director.isDirectory()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new directory.
|
||||||
|
* @param pathToCreateAt where directory will be created at
|
||||||
|
* @return true if directory is created, false if an error occured, or already exists.
|
||||||
|
*/
|
||||||
|
public static boolean createDirectory(String pathToCreateAt) {
|
||||||
|
File directory = new File(pathToCreateAt);
|
||||||
|
if (!directory.exists()) {
|
||||||
|
return directory.mkdirs(); //true if directory is created
|
||||||
|
} else {
|
||||||
|
return false; //false if file already exists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:accountType="fr.free.nrw.commons"
|
android:accountType="@string/account_type"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:smallIcon="@drawable/ic_launcher" />
|
android:smallIcon="@drawable/ic_launcher" />
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:contentAuthority="fr.free.nrw.commons.contributions.contentprovider"
|
android:contentAuthority="@string/contribution_authority"
|
||||||
android:accountType="fr.free.nrw.commons"
|
android:accountType="@string/account_type"
|
||||||
android:supportsUploading="false"
|
android:supportsUploading="false"
|
||||||
android:userVisible="true"
|
android:userVisible="true"
|
||||||
android:isAlwaysSyncable="true"
|
android:isAlwaysSyncable="true"
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:contentAuthority="fr.free.nrw.commons.modifications.contentprovider"
|
android:contentAuthority="@string/modification_authority"
|
||||||
android:accountType="fr.free.nrw.commons"
|
android:accountType="@string/account_type"
|
||||||
android:supportsUploading="true"
|
android:supportsUploading="true"
|
||||||
android:userVisible="true"
|
android:userVisible="true"
|
||||||
android:isAlwaysSyncable="true"
|
android:isAlwaysSyncable="true"
|
||||||
|
|
|
||||||
7
app/src/prod/res/values/adapter.xml
Normal file
7
app/src/prod/res/values/adapter.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="account_type">fr.free.nrw.commons</string>
|
||||||
|
<string name="contribution_authority">fr.free.nrw.commons.contributions.contentprovider</string>
|
||||||
|
<string name="modification_authority">fr.free.nrw.commons.modifications.contentprovider</string>
|
||||||
|
<string name="category_authority">fr.free.nrw.commons.categories.contentprovider</string>
|
||||||
|
</resources>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue