mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 04:43:54 +01:00
Merge pull request #956 from psh/dependency-injection
Dependency injection with Dagger
This commit is contained in:
commit
ffc0e0779d
69 changed files with 1320 additions and 966 deletions
|
|
@ -68,6 +68,11 @@ dependencies {
|
||||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.1'
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.1'
|
||||||
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
|
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
|
||||||
testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
|
testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
|
||||||
|
|
||||||
|
implementation 'com.google.dagger:dagger:2.11'
|
||||||
|
implementation 'com.google.dagger:dagger-android-support:2.11'
|
||||||
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
|
||||||
|
annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,18 @@
|
||||||
package="fr.free.nrw.commons">
|
package="fr.free.nrw.commons">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
|
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||||
<uses-permission android:name="android.permission.READ_SYNC_STATS"/>
|
<uses-permission android:name="android.permission.READ_SYNC_STATS" />
|
||||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
|
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
|
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||||
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
|
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||||
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
|
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
|
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>
|
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
|
||||||
<uses-permission android:name="com.google.android.apps.photos.permission.GOOGLE_PHOTOS"/>
|
<uses-permission android:name="com.google.android.apps.photos.permission.GOOGLE_PHOTOS" />
|
||||||
<uses-permission android:name="android.permission.READ_LOGS"/>
|
<uses-permission android:name="android.permission.READ_LOGS"/>
|
||||||
|
|
||||||
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
|
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
|
||||||
|
|
@ -32,22 +32,25 @@
|
||||||
android:finishOnTaskLaunch="true" />
|
android:finishOnTaskLaunch="true" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".auth.LoginActivity"
|
android:name="org.acra.CrashReportDialog"
|
||||||
>
|
android:excludeFromRecents="true"
|
||||||
|
android:finishOnTaskLaunch="true"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:theme="@android:style/Theme.Dialog" />
|
||||||
|
|
||||||
|
<activity android:name=".auth.LoginActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
|
||||||
android:name=".WelcomeActivity"
|
<activity android:name=".WelcomeActivity" />
|
||||||
>
|
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".upload.ShareActivity"
|
android:name=".upload.ShareActivity"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name">
|
||||||
>
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
@ -55,11 +58,11 @@
|
||||||
<data android:mimeType="audio/ogg" />
|
<data android:mimeType="audio/ogg" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".upload.MultipleShareActivity"
|
android:name=".upload.MultipleShareActivity"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name">
|
||||||
>
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
@ -69,33 +72,34 @@
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".contributions.ContributionsActivity"
|
android:name=".contributions.ContributionsActivity"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name" />
|
||||||
>
|
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".settings.SettingsActivity"
|
android:name=".settings.SettingsActivity"
|
||||||
android:label="@string/title_activity_settings"
|
android:label="@string/title_activity_settings" />
|
||||||
/>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".AboutActivity"
|
android:name=".AboutActivity"
|
||||||
android:label="@string/title_activity_about"
|
android:label="@string/title_activity_about"
|
||||||
android:parentActivityName=".contributions.ContributionsActivity" />
|
android:parentActivityName=".contributions.ContributionsActivity" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".auth.SignupActivity"
|
android:name=".auth.SignupActivity"
|
||||||
android:label="@string/title_activity_signup"/>
|
android:label="@string/title_activity_signup" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".nearby.NearbyActivity"
|
android:name=".nearby.NearbyActivity"
|
||||||
android:label="@string/title_activity_nearby"
|
android:label="@string/title_activity_nearby"
|
||||||
android:parentActivityName=".contributions.ContributionsActivity" />
|
android:parentActivityName=".contributions.ContributionsActivity" />
|
||||||
|
|
||||||
<service android:name=".upload.UploadService" >
|
<service android:name=".upload.UploadService" />
|
||||||
</service>
|
|
||||||
<service
|
<service
|
||||||
android:name=".auth.WikiAccountAuthenticatorService"
|
android:name=".auth.WikiAccountAuthenticatorService"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:process=":auth" >
|
android:process=":auth">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.accounts.AccountAuthenticator" />
|
<action android:name="android.accounts.AccountAuthenticator" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
@ -106,27 +110,25 @@
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".contributions.ContributionsSyncService"
|
android:name=".contributions.ContributionsSyncService"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action
|
<action android:name="android.content.SyncAdapter" />
|
||||||
android:name="android.content.SyncAdapter" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.content.SyncAdapter"
|
android:name="android.content.SyncAdapter"
|
||||||
android:resource="@xml/contributions_sync_adapter" />
|
android:resource="@xml/contributions_sync_adapter" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".modifications.ModificationsSyncService"
|
android:name=".modifications.ModificationsSyncService"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action
|
<action android:name="android.content.SyncAdapter" />
|
||||||
android:name="android.content.SyncAdapter" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.content.SyncAdapter"
|
android:name="android.content.SyncAdapter"
|
||||||
android:resource="@xml/modifications_sync_adapter" />
|
android:resource="@xml/modifications_sync_adapter" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
|
|
@ -136,31 +138,29 @@
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/provider_paths"/>
|
android:resource="@xml/provider_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".contributions.ContributionsContentProvider"
|
android:name=".contributions.ContributionsContentProvider"
|
||||||
android:label="@string/provider_contributions"
|
android:authorities="fr.free.nrw.commons.contributions.contentprovider"
|
||||||
android:syncable="true"
|
android:exported="false"
|
||||||
android:authorities="fr.free.nrw.commons.contributions.contentprovider"
|
android:label="@string/provider_contributions"
|
||||||
android:exported="false">
|
android:syncable="true" />
|
||||||
</provider>
|
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".modifications.ModificationsContentProvider"
|
android:name=".modifications.ModificationsContentProvider"
|
||||||
android:label="@string/provider_modifications"
|
android:authorities="fr.free.nrw.commons.modifications.contentprovider"
|
||||||
android:syncable="true"
|
android:exported="false"
|
||||||
android:authorities="fr.free.nrw.commons.modifications.contentprovider"
|
android:label="@string/provider_modifications"
|
||||||
android:exported="false">
|
android:syncable="true" />
|
||||||
</provider>
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".category.CategoryContentProvider"
|
android:name=".category.CategoryContentProvider"
|
||||||
android:label="@string/provider_categories"
|
android:authorities="fr.free.nrw.commons.categories.contentprovider"
|
||||||
android:syncable="false"
|
android:exported="false"
|
||||||
android:authorities="fr.free.nrw.commons.categories.contentprovider"
|
android:label="@string/provider_categories"
|
||||||
android:exported="false">
|
android:syncable="false" />
|
||||||
</provider>
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package fr.free.nrw.commons;
|
package fr.free.nrw.commons;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,8 @@
|
||||||
package fr.free.nrw.commons;
|
package fr.free.nrw.commons;
|
||||||
|
|
||||||
import android.accounts.Account;
|
|
||||||
import android.accounts.AccountManager;
|
|
||||||
import android.accounts.AccountManagerCallback;
|
|
||||||
import android.accounts.AccountManagerFuture;
|
|
||||||
import android.accounts.AuthenticatorException;
|
|
||||||
import android.accounts.OperationCanceledException;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v4.util.LruCache;
|
|
||||||
|
|
||||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
import com.facebook.stetho.Stetho;
|
import com.facebook.stetho.Stetho;
|
||||||
|
|
@ -25,24 +14,23 @@ import org.acra.ReportingInteractionMode;
|
||||||
import org.acra.annotation.ReportsCrashes;
|
import org.acra.annotation.ReportsCrashes;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import dagger.android.AndroidInjector;
|
import dagger.android.AndroidInjector;
|
||||||
import dagger.android.DispatchingAndroidInjector;
|
import dagger.android.DaggerApplication;
|
||||||
import dagger.android.HasActivityInjector;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.auth.AccountUtil;
|
|
||||||
import fr.free.nrw.commons.caching.CacheController;
|
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
import fr.free.nrw.commons.data.Category;
|
import fr.free.nrw.commons.data.Category;
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import fr.free.nrw.commons.di.DaggerAppComponent;
|
import fr.free.nrw.commons.di.CommonsApplicationComponent;
|
||||||
|
import fr.free.nrw.commons.di.CommonsApplicationModule;
|
||||||
|
import fr.free.nrw.commons.di.DaggerCommonsApplicationComponent;
|
||||||
import fr.free.nrw.commons.modifications.ModifierSequence;
|
import fr.free.nrw.commons.modifications.ModifierSequence;
|
||||||
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
|
||||||
import fr.free.nrw.commons.nearby.NearbyPlaces;
|
|
||||||
import fr.free.nrw.commons.utils.FileUtils;
|
import fr.free.nrw.commons.utils.FileUtils;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
// TODO: Use ProGuard to rip out reporting when publishing
|
// TODO: Use ProGuard to rip out reporting when publishing
|
||||||
|
|
@ -54,9 +42,13 @@ import timber.log.Timber;
|
||||||
resDialogCommentPrompt = R.string.crash_dialog_comment_prompt,
|
resDialogCommentPrompt = R.string.crash_dialog_comment_prompt,
|
||||||
resDialogOkToast = R.string.crash_dialog_ok_toast
|
resDialogOkToast = R.string.crash_dialog_ok_toast
|
||||||
)
|
)
|
||||||
public class CommonsApplication extends Application implements HasActivityInjector {
|
public class CommonsApplication extends DaggerApplication {
|
||||||
|
|
||||||
private Account currentAccount = null; // Unlike a savings account...
|
@Inject SessionManager sessionManager;
|
||||||
|
@Inject DBOpenHelper dbOpenHelper;
|
||||||
|
@Inject @Named("default_preferences") SharedPreferences defaultPrefs;
|
||||||
|
@Inject @Named("application_preferences") SharedPreferences applicationPrefs;
|
||||||
|
@Inject @Named("prefs") SharedPreferences otherPrefs;
|
||||||
|
|
||||||
public static final Object[] EVENT_UPLOAD_ATTEMPT = {"MobileAppUploadAttempts", 5334329L};
|
public static final Object[] EVENT_UPLOAD_ATTEMPT = {"MobileAppUploadAttempts", 5334329L};
|
||||||
public static final Object[] EVENT_LOGIN_ATTEMPT = {"MobileAppLoginAttempts", 5257721L};
|
public static final Object[] EVENT_LOGIN_ATTEMPT = {"MobileAppLoginAttempts", 5257721L};
|
||||||
|
|
@ -65,68 +57,12 @@ public class CommonsApplication extends Application implements HasActivityInject
|
||||||
|
|
||||||
public static final String DEFAULT_EDIT_SUMMARY = "Uploaded using Android Commons app";
|
public static final String DEFAULT_EDIT_SUMMARY = "Uploaded using Android Commons app";
|
||||||
|
|
||||||
public static final String FEEDBACK_EMAIL = "commons-app-android-private@googlegroups.com";
|
public static final String FEEDBACK_EMAIL = "commons-app-android@googlegroups.com";
|
||||||
public static final String FEEDBACK_EMAIL_SUBJECT = "Commons Android App (%s) Feedback";
|
public static final String FEEDBACK_EMAIL_SUBJECT = "Commons Android App (%s) Feedback";
|
||||||
|
|
||||||
@Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
|
private CommonsApplicationComponent component;
|
||||||
@Inject MediaWikiApi mediaWikiApi;
|
|
||||||
|
|
||||||
private static CommonsApplication instance = null;
|
|
||||||
private MediaWikiApi api = null;
|
|
||||||
private LruCache<String, String> thumbnailUrlCache = new LruCache<>(1024);
|
|
||||||
private CacheController cacheData = null;
|
|
||||||
private DBOpenHelper dbOpenHelper = null;
|
|
||||||
private NearbyPlaces nearbyPlaces = null;
|
|
||||||
|
|
||||||
private RefWatcher refWatcher;
|
private RefWatcher refWatcher;
|
||||||
|
|
||||||
/**
|
|
||||||
* This should not be called by ANY application code (other than the magic Android glue)
|
|
||||||
* Use CommonsApplication.getInstance() instead to get the singleton.
|
|
||||||
*/
|
|
||||||
public CommonsApplication() {
|
|
||||||
CommonsApplication.instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CommonsApplication getInstance() {
|
|
||||||
if (instance == null) {
|
|
||||||
instance = new CommonsApplication();
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MediaWikiApi getMWApi() {
|
|
||||||
if (api == null) {
|
|
||||||
api = new ApacheHttpClientMediaWikiApi(BuildConfig.WIKIMEDIA_API_HOST);
|
|
||||||
}
|
|
||||||
return api;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CacheController getCacheData() {
|
|
||||||
if (cacheData == null) {
|
|
||||||
cacheData = new CacheController();
|
|
||||||
}
|
|
||||||
return cacheData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LruCache<String, String> getThumbnailUrlCache() {
|
|
||||||
return thumbnailUrlCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized DBOpenHelper getDBOpenHelper() {
|
|
||||||
if (dbOpenHelper == null) {
|
|
||||||
dbOpenHelper = new DBOpenHelper(this);
|
|
||||||
}
|
|
||||||
return dbOpenHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized NearbyPlaces getNearbyPlaces() {
|
|
||||||
if (nearbyPlaces == null) {
|
|
||||||
nearbyPlaces = new NearbyPlaces();
|
|
||||||
}
|
|
||||||
return nearbyPlaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
@ -137,12 +73,6 @@ public class CommonsApplication extends Application implements HasActivityInject
|
||||||
|
|
||||||
Timber.plant(new Timber.DebugTree());
|
Timber.plant(new Timber.DebugTree());
|
||||||
|
|
||||||
DaggerAppComponent
|
|
||||||
.builder()
|
|
||||||
.application(this)
|
|
||||||
.build()
|
|
||||||
.inject(this);
|
|
||||||
|
|
||||||
if (!BuildConfig.DEBUG) {
|
if (!BuildConfig.DEBUG) {
|
||||||
ACRA.init(this);
|
ACRA.init(this);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -153,9 +83,6 @@ public class CommonsApplication extends Application implements HasActivityInject
|
||||||
System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0");
|
System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0");
|
||||||
|
|
||||||
Fresco.initialize(this);
|
Fresco.initialize(this);
|
||||||
|
|
||||||
//For caching area -> categories
|
|
||||||
cacheData = new CacheController();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected RefWatcher setupLeakCanary() {
|
protected RefWatcher setupLeakCanary() {
|
||||||
|
|
@ -170,43 +97,18 @@ public class CommonsApplication extends Application implements HasActivityInject
|
||||||
return application.refWatcher;
|
return application.refWatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @return Account|null
|
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
|
||||||
*/
|
return injector();
|
||||||
public Account getCurrentAccount() {
|
|
||||||
if (currentAccount == null) {
|
|
||||||
AccountManager accountManager = AccountManager.get(this);
|
|
||||||
Account[] allAccounts = accountManager.getAccountsByType(AccountUtil.accountType());
|
|
||||||
if (allAccounts.length != 0) {
|
|
||||||
currentAccount = allAccounts[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currentAccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean revalidateAuthToken() {
|
|
||||||
AccountManager accountManager = AccountManager.get(this);
|
|
||||||
Account curAccount = getCurrentAccount();
|
|
||||||
|
|
||||||
if (curAccount == null) {
|
|
||||||
return false; // This should never happen
|
|
||||||
}
|
|
||||||
|
|
||||||
accountManager.invalidateAuthToken(AccountUtil.accountType(), mediaWikiApi.getAuthCookie());
|
|
||||||
try {
|
|
||||||
String authCookie = accountManager.blockingGetAuthToken(curAccount, "", false);
|
|
||||||
mediaWikiApi.setAuthCookie(authCookie);
|
|
||||||
return true;
|
|
||||||
} catch (OperationCanceledException | NullPointerException | IOException | AuthenticatorException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean deviceHasCamera() {
|
public CommonsApplicationComponent injector() {
|
||||||
PackageManager pm = getPackageManager();
|
if (component == null) {
|
||||||
return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|
component = DaggerCommonsApplicationComponent.builder()
|
||||||
|| pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
|
.appModule(new CommonsApplicationModule(this))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return component;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearApplicationData(Context context, LogoutListener logoutListener) {
|
public void clearApplicationData(Context context, LogoutListener logoutListener) {
|
||||||
|
|
@ -221,67 +123,25 @@ public class CommonsApplication extends Application implements HasActivityInject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountManager accountManager = AccountManager.get(this);
|
sessionManager.clearAllAccounts()
|
||||||
Account[] allAccounts = accountManager.getAccountsByType(AccountUtil.accountType());
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
AccountManagerCallback<Boolean> amCallback = new AccountManagerCallback<Boolean>() {
|
.subscribe(() -> {
|
||||||
|
|
||||||
private int index = 0;
|
|
||||||
|
|
||||||
void setIndex(int index) {
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getIndex() {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run(AccountManagerFuture<Boolean> accountManagerFuture) {
|
|
||||||
setIndex(getIndex() + 1);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (accountManagerFuture != null && accountManagerFuture.getResult()) {
|
|
||||||
Timber.d("Account removed successfully.");
|
|
||||||
}
|
|
||||||
} catch (OperationCanceledException | IOException | AuthenticatorException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getIndex() == allAccounts.length) {
|
|
||||||
Timber.d("All accounts have been removed");
|
Timber.d("All accounts have been removed");
|
||||||
//TODO: fix preference manager
|
//TODO: fix preference manager
|
||||||
PreferenceManager.getDefaultSharedPreferences(getInstance())
|
defaultPrefs.edit().clear().commit();
|
||||||
.edit().clear().commit();
|
applicationPrefs.edit().clear().commit();
|
||||||
SharedPreferences preferences = context
|
applicationPrefs.edit().putBoolean("firstrun", false).apply();otherPrefs.edit().clear().commit();
|
||||||
.getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE);
|
|
||||||
preferences.edit().clear().commit();
|
|
||||||
context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
|
|
||||||
.edit().clear().commit();
|
|
||||||
preferences.edit().putBoolean("firstrun", false).apply();
|
|
||||||
updateAllDatabases();
|
updateAllDatabases();
|
||||||
currentAccount = null;
|
|
||||||
|
|
||||||
logoutListener.onLogoutComplete();
|
logoutListener.onLogoutComplete();
|
||||||
}
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (Account account : allAccounts) {
|
|
||||||
accountManager.removeAccount(account, amCallback, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AndroidInjector<Activity> activityInjector() {
|
|
||||||
return dispatchingActivityInjector;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes all tables and re-creates them.
|
* Deletes all tables and re-creates them.
|
||||||
*/
|
*/
|
||||||
public void updateAllDatabases() {
|
private void updateAllDatabases() {
|
||||||
DBOpenHelper dbOpenHelper = CommonsApplication.getInstance().getDBOpenHelper();
|
|
||||||
dbOpenHelper.getReadableDatabase().close();
|
dbOpenHelper.getReadableDatabase().close();
|
||||||
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
|
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package fr.free.nrw.commons;
|
package fr.free.nrw.commons;
|
||||||
|
|
||||||
import android.app.Service;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
|
@ -9,7 +8,9 @@ import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
|
||||||
public abstract class HandlerService<T> extends Service {
|
import dagger.android.DaggerService;
|
||||||
|
|
||||||
|
public abstract class HandlerService<T> extends DaggerService {
|
||||||
private volatile Looper threadLooper;
|
private volatile Looper threadLooper;
|
||||||
private volatile ServiceHandler threadHandler;
|
private volatile ServiceHandler threadHandler;
|
||||||
private String serviceName;
|
private String serviceName;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ import android.content.res.Resources;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
@ -19,7 +21,7 @@ public class LicenseList {
|
||||||
res = activity.getResources();
|
res = activity.getResources();
|
||||||
XmlPullParser parser = res.getXml(R.xml.wikimedia_licenses);
|
XmlPullParser parser = res.getXml(R.xml.wikimedia_licenses);
|
||||||
String namespace = "https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses";
|
String namespace = "https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses";
|
||||||
while (Utils.xmlFastForward(parser, namespace, "license")) {
|
while (xmlFastForward(parser, namespace, "license")) {
|
||||||
String id = parser.getAttributeValue(null, "id");
|
String id = parser.getAttributeValue(null, "id");
|
||||||
String template = parser.getAttributeValue(null, "template");
|
String template = parser.getAttributeValue(null, "template");
|
||||||
String url = parser.getAttributeValue(null, "url");
|
String url = parser.getAttributeValue(null, "url");
|
||||||
|
|
@ -60,4 +62,34 @@ public class LicenseList {
|
||||||
+ nameIdForTemplate(template), null, null);
|
+ nameIdForTemplate(template), null, null);
|
||||||
return (nameId != 0) ? res.getString(nameId) : template;
|
return (nameId != 0) ? res.getString(nameId) : template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fast-forward an XmlPullParser to the next instance of the given element
|
||||||
|
* in the input stream (namespaced).
|
||||||
|
*
|
||||||
|
* @param parser
|
||||||
|
* @param namespace
|
||||||
|
* @param element
|
||||||
|
* @return true on match, false on failure
|
||||||
|
*/
|
||||||
|
private boolean xmlFastForward(XmlPullParser parser, String namespace, String element) {
|
||||||
|
try {
|
||||||
|
while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
||||||
|
if (parser.getEventType() == XmlPullParser.START_TAG &&
|
||||||
|
parser.getNamespace().equals(namespace) &&
|
||||||
|
parser.getName().equals(element)) {
|
||||||
|
// We found it!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -11,12 +11,12 @@ import org.xml.sax.SAXException;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
@ -33,45 +33,39 @@ import timber.log.Timber;
|
||||||
* which are not intrinsic to the media and may change due to editing.
|
* which are not intrinsic to the media and may change due to editing.
|
||||||
*/
|
*/
|
||||||
public class MediaDataExtractor {
|
public class MediaDataExtractor {
|
||||||
|
private final MediaWikiApi mediaWikiApi;
|
||||||
private boolean fetched;
|
private boolean fetched;
|
||||||
|
|
||||||
private String filename;
|
|
||||||
private ArrayList<String> categories;
|
private ArrayList<String> categories;
|
||||||
private Map<String, String> descriptions;
|
private Map<String, String> descriptions;
|
||||||
private String license;
|
private String license;
|
||||||
private @Nullable LatLng coordinates;
|
private @Nullable LatLng coordinates;
|
||||||
private LicenseList licenseList;
|
|
||||||
|
|
||||||
/**
|
@Inject
|
||||||
* @param filename of the target media object, should include 'File:' prefix
|
public MediaDataExtractor(MediaWikiApi mwApi) {
|
||||||
*/
|
this.categories = new ArrayList<>();
|
||||||
public MediaDataExtractor(String filename, LicenseList licenseList) {
|
this.descriptions = new HashMap<>();
|
||||||
this.filename = filename;
|
this.fetched = false;
|
||||||
categories = new ArrayList<>();
|
this.mediaWikiApi = mwApi;
|
||||||
descriptions = new HashMap<>();
|
|
||||||
fetched = false;
|
|
||||||
this.licenseList = licenseList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Actually fetch the data over the network.
|
* Actually fetch the data over the network.
|
||||||
* todo: use local caching?
|
* todo: use local caching?
|
||||||
*
|
*
|
||||||
* Warning: synchronous i/o, call on a background thread
|
* Warning: synchronous i/o, call on a background thread
|
||||||
*/
|
*/
|
||||||
public void fetch() throws IOException {
|
public void fetch(String filename, LicenseList licenseList) throws IOException {
|
||||||
if (fetched) {
|
if (fetched) {
|
||||||
throw new IllegalStateException("Tried to call MediaDataExtractor.fetch() again.");
|
throw new IllegalStateException("Tried to call MediaDataExtractor.fetch() again.");
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
MediaResult result = mediaWikiApi.fetchMediaByFilename(filename);
|
||||||
MediaResult result = api.fetchMediaByFilename(filename);
|
|
||||||
|
|
||||||
// In-page category links are extracted from source, as XML doesn't cover [[links]]
|
// In-page category links are extracted from source, as XML doesn't cover [[links]]
|
||||||
extractCategories(result.getWikiSource());
|
extractCategories(result.getWikiSource());
|
||||||
|
|
||||||
// Description template info is extracted from preprocessor XML
|
// Description template info is extracted from preprocessor XML
|
||||||
processWikiParseTree(result.getParseTreeXmlSource());
|
processWikiParseTree(result.getParseTreeXmlSource(), licenseList);
|
||||||
fetched = true;
|
fetched = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +84,7 @@ public class MediaDataExtractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processWikiParseTree(String source) throws IOException {
|
private void processWikiParseTree(String source, LicenseList licenseList) throws IOException {
|
||||||
Document doc;
|
Document doc;
|
||||||
try {
|
try {
|
||||||
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,17 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
|
||||||
class MediaThumbnailFetchTask extends AsyncTask<String, String, String> {
|
class MediaThumbnailFetchTask extends AsyncTask<String, String, String> {
|
||||||
protected final Media media;
|
protected final Media media;
|
||||||
|
private MediaWikiApi mediaWikiApi;
|
||||||
|
|
||||||
public MediaThumbnailFetchTask(@NonNull Media media) {
|
public MediaThumbnailFetchTask(@NonNull Media media, MediaWikiApi mwApi) {
|
||||||
this.media = media;
|
this.media = media;
|
||||||
|
this.mediaWikiApi = mwApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String doInBackground(String... params) {
|
protected String doInBackground(String... params) {
|
||||||
try {
|
try {
|
||||||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
return mediaWikiApi.findThumbnailByFilename(params[0]);
|
||||||
return api.findThumbnailByFilename(params[0]);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Do something better!
|
// Do something better!
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.graphics.drawable.VectorDrawableCompat;
|
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||||
|
import android.support.v4.util.LruCache;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
@ -11,9 +12,15 @@ import android.widget.Toast;
|
||||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||||
import com.facebook.drawee.view.SimpleDraweeView;
|
import com.facebook.drawee.view.SimpleDraweeView;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class MediaWikiImageView extends SimpleDraweeView {
|
public class MediaWikiImageView extends SimpleDraweeView {
|
||||||
|
@Inject MediaWikiApi mwApi;
|
||||||
|
@Inject LruCache<String, String> thumbnailUrlCache;
|
||||||
|
|
||||||
private ThumbnailFetchTask currentThumbnailTask;
|
private ThumbnailFetchTask currentThumbnailTask;
|
||||||
|
|
||||||
public MediaWikiImageView(Context context) {
|
public MediaWikiImageView(Context context) {
|
||||||
|
|
@ -39,11 +46,11 @@ public class MediaWikiImageView extends SimpleDraweeView {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CommonsApplication.getInstance().getThumbnailUrlCache().get(media.getFilename()) != null) {
|
if (thumbnailUrlCache.get(media.getFilename()) != null) {
|
||||||
setImageUrl(CommonsApplication.getInstance().getThumbnailUrlCache().get(media.getFilename()));
|
setImageUrl(thumbnailUrlCache.get(media.getFilename()));
|
||||||
} else {
|
} else {
|
||||||
setImageUrl(null);
|
setImageUrl(null);
|
||||||
currentThumbnailTask = new ThumbnailFetchTask(media);
|
currentThumbnailTask = new ThumbnailFetchTask(media, mwApi);
|
||||||
currentThumbnailTask.execute(media.getFilename());
|
currentThumbnailTask.execute(media.getFilename());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,6 +64,7 @@ public class MediaWikiImageView extends SimpleDraweeView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
|
((CommonsApplication) getContext().getApplicationContext()).injector().inject(this);
|
||||||
setHierarchy(GenericDraweeHierarchyBuilder
|
setHierarchy(GenericDraweeHierarchyBuilder
|
||||||
.newInstance(getResources())
|
.newInstance(getResources())
|
||||||
.setPlaceholderImage(VectorDrawableCompat.create(getResources(),
|
.setPlaceholderImage(VectorDrawableCompat.create(getResources(),
|
||||||
|
|
@ -71,8 +79,8 @@ public class MediaWikiImageView extends SimpleDraweeView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ThumbnailFetchTask extends MediaThumbnailFetchTask {
|
private class ThumbnailFetchTask extends MediaThumbnailFetchTask {
|
||||||
ThumbnailFetchTask(@NonNull Media media) {
|
ThumbnailFetchTask(@NonNull Media media, @NonNull MediaWikiApi mwApi) {
|
||||||
super(media);
|
super(media, mwApi);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -85,7 +93,7 @@ public class MediaWikiImageView extends SimpleDraweeView {
|
||||||
} else {
|
} else {
|
||||||
// only cache meaningful thumbnails received from network.
|
// only cache meaningful thumbnails received from network.
|
||||||
try {
|
try {
|
||||||
CommonsApplication.getInstance().getThumbnailUrlCache().put(media.getFilename(), result);
|
thumbnailUrlCache.put(media.getFilename(), result);
|
||||||
} catch (NullPointerException npe) {
|
} catch (NullPointerException npe) {
|
||||||
Timber.e("error when adding pic to cache " + npe);
|
Timber.e("error when adding pic to cache " + npe);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,102 +1,26 @@
|
||||||
package fr.free.nrw.commons;
|
package fr.free.nrw.commons;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.Html;
|
|
||||||
import android.text.Spanned;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Hex;
|
import org.apache.commons.codec.binary.Hex;
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
import org.w3c.dom.Node;
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.TimeZone;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.xml.transform.Transformer;
|
|
||||||
import javax.xml.transform.TransformerConfigurationException;
|
|
||||||
import javax.xml.transform.TransformerException;
|
|
||||||
import javax.xml.transform.TransformerFactory;
|
|
||||||
import javax.xml.transform.TransformerFactoryConfigurationError;
|
|
||||||
import javax.xml.transform.dom.DOMSource;
|
|
||||||
import javax.xml.transform.stream.StreamResult;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.settings.Prefs;
|
import fr.free.nrw.commons.settings.Prefs;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
|
||||||
// Get SHA1 of file from input stream
|
|
||||||
public static String getSHA1(InputStream is) {
|
|
||||||
|
|
||||||
MessageDigest digest;
|
|
||||||
try {
|
|
||||||
digest = MessageDigest.getInstance("SHA1");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
Timber.e(e, "Exception while getting Digest");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] buffer = new byte[8192];
|
|
||||||
int read;
|
|
||||||
try {
|
|
||||||
while ((read = is.read(buffer)) > 0) {
|
|
||||||
digest.update(buffer, 0, read);
|
|
||||||
}
|
|
||||||
byte[] md5sum = digest.digest();
|
|
||||||
BigInteger bigInt = new BigInteger(1, md5sum);
|
|
||||||
String output = bigInt.toString(16);
|
|
||||||
// Fill to 40 chars
|
|
||||||
output = String.format("%40s", output).replace(' ', '0');
|
|
||||||
Timber.i("File SHA1: %s", output);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
} catch (IOException e) {
|
|
||||||
Timber.e(e, "IO Exception");
|
|
||||||
return "";
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
is.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Timber.e(e, "Exception on closing MD5 input stream");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fix Html.fromHtml is deprecated problem
|
|
||||||
*
|
|
||||||
* @param source provided Html string
|
|
||||||
* @return returned Spanned of appropriate method according to version check
|
|
||||||
*/
|
|
||||||
public static Spanned fromHtml(String source) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
return Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY);
|
|
||||||
} else {
|
|
||||||
//noinspection deprecation
|
|
||||||
return Html.fromHtml(source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strips localization symbols from a string.
|
* Strips localization symbols from a string.
|
||||||
* Removes the suffix after "@" and quotes.
|
* Removes the suffix after "@" and quotes.
|
||||||
|
|
@ -113,49 +37,12 @@ public class Utils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Date parseMWDate(String mwDate) {
|
|
||||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC
|
|
||||||
try {
|
|
||||||
return isoFormat.parse(mwDate);
|
|
||||||
} catch (ParseException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toMWDate(Date date) {
|
|
||||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC
|
|
||||||
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
||||||
return isoFormat.format(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String makeThumbBaseUrl(@NonNull String filename) {
|
public static String makeThumbBaseUrl(@NonNull String filename) {
|
||||||
String name = new PageTitle(filename).getPrefixedText();
|
String name = new PageTitle(filename).getPrefixedText();
|
||||||
String sha = new String(Hex.encodeHex(DigestUtils.md5(name)));
|
String sha = new String(Hex.encodeHex(DigestUtils.md5(name)));
|
||||||
return String.format("%s/%s/%s/%s", BuildConfig.IMAGE_URL_BASE, sha.substring(0, 1), sha.substring(0, 2), urlEncode(name));
|
return String.format("%s/%s/%s/%s", BuildConfig.IMAGE_URL_BASE, sha.substring(0, 1), sha.substring(0, 2), urlEncode(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getStringFromDOM(Node dom) {
|
|
||||||
Transformer transformer = null;
|
|
||||||
try {
|
|
||||||
transformer = TransformerFactory.newInstance().newTransformer();
|
|
||||||
} catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
StringWriter outputStream = new StringWriter();
|
|
||||||
DOMSource domSource = new DOMSource(dom);
|
|
||||||
StreamResult strResult = new StreamResult(outputStream);
|
|
||||||
|
|
||||||
try {
|
|
||||||
transformer.transform(domSource, strResult);
|
|
||||||
} catch (TransformerException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return outputStream.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String urlEncode(String url) {
|
public static String urlEncode(String url) {
|
||||||
try {
|
try {
|
||||||
return URLEncoder.encode(url, "utf-8");
|
return URLEncoder.encode(url, "utf-8");
|
||||||
|
|
@ -164,39 +51,10 @@ public class Utils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long countBytes(InputStream stream) throws IOException {
|
|
||||||
long count = 0;
|
|
||||||
BufferedInputStream bis = new BufferedInputStream(stream);
|
|
||||||
while (bis.read() != -1) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String capitalize(String string) {
|
public static String capitalize(String string) {
|
||||||
return string.substring(0, 1).toUpperCase(Locale.getDefault()) + string.substring(1);
|
return string.substring(0, 1).toUpperCase(Locale.getDefault()) + string.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String licenseTemplateFor(String license) {
|
|
||||||
switch (license) {
|
|
||||||
case Prefs.Licenses.CC_BY_3:
|
|
||||||
return "{{self|cc-by-3.0}}";
|
|
||||||
case Prefs.Licenses.CC_BY_4:
|
|
||||||
return "{{self|cc-by-4.0}}";
|
|
||||||
case Prefs.Licenses.CC_BY_SA_3:
|
|
||||||
return "{{self|cc-by-sa-3.0}}";
|
|
||||||
case Prefs.Licenses.CC_BY_SA_4:
|
|
||||||
return "{{self|cc-by-sa-4.0}}";
|
|
||||||
case Prefs.Licenses.CC0:
|
|
||||||
return "{{self|cc-zero}}";
|
|
||||||
case Prefs.Licenses.CC_BY:
|
|
||||||
return "{{self|cc-by-3.0}}";
|
|
||||||
case Prefs.Licenses.CC_BY_SA:
|
|
||||||
return "{{self|cc-by-sa-3.0}}";
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Unrecognized license value: " + license);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int licenseNameFor(String license) {
|
public static int licenseNameFor(String license) {
|
||||||
switch (license) {
|
switch (license) {
|
||||||
case Prefs.Licenses.CC_BY_3:
|
case Prefs.Licenses.CC_BY_3:
|
||||||
|
|
@ -217,51 +75,6 @@ public class Utils {
|
||||||
throw new RuntimeException("Unrecognized license value: " + license);
|
throw new RuntimeException("Unrecognized license value: " + license);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String licenseUrlFor(String license) {
|
|
||||||
switch (license) {
|
|
||||||
case Prefs.Licenses.CC_BY_3:
|
|
||||||
return "https://creativecommons.org/licenses/by/3.0/";
|
|
||||||
case Prefs.Licenses.CC_BY_4:
|
|
||||||
return "https://creativecommons.org/licenses/by/4.0/";
|
|
||||||
case Prefs.Licenses.CC_BY_SA_3:
|
|
||||||
return "https://creativecommons.org/licenses/by-sa/3.0/";
|
|
||||||
case Prefs.Licenses.CC_BY_SA_4:
|
|
||||||
return "https://creativecommons.org/licenses/by-sa/4.0/";
|
|
||||||
case Prefs.Licenses.CC0:
|
|
||||||
return "https://creativecommons.org/publicdomain/zero/1.0/";
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Unrecognized license value: " + license);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fast-forward an XmlPullParser to the next instance of the given element
|
|
||||||
* in the input stream (namespaced).
|
|
||||||
*
|
|
||||||
* @param parser
|
|
||||||
* @param namespace
|
|
||||||
* @param element
|
|
||||||
* @return true on match, false on failure
|
|
||||||
*/
|
|
||||||
public static boolean xmlFastForward(XmlPullParser parser, String namespace, String element) {
|
|
||||||
try {
|
|
||||||
while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
|
||||||
if (parser.getEventType() == XmlPullParser.START_TAG
|
|
||||||
&& parser.getNamespace().equals(namespace)
|
|
||||||
&& parser.getName().equals(element)) {
|
|
||||||
// We found it!
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch (XmlPullParserException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String fixExtension(String title, String extension) {
|
public static String fixExtension(String title, String extension) {
|
||||||
Pattern jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE);
|
Pattern jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
|
@ -277,10 +90,6 @@ public class Utils {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isNullOrWhiteSpace(String value) {
|
|
||||||
return value == null || value.trim().isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isDarkTheme(Context context) {
|
public static boolean isDarkTheme(Context context) {
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("theme", false);
|
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("theme", false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,31 @@ import android.accounts.Account;
|
||||||
import android.accounts.AccountAuthenticatorResponse;
|
import android.accounts.AccountAuthenticatorResponse;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
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;
|
||||||
import timber.log.Timber;
|
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;
|
||||||
|
|
||||||
public class AccountUtil {
|
public class AccountUtil {
|
||||||
|
|
||||||
public static void createAccount(@Nullable AccountAuthenticatorResponse response,
|
public static final String ACCOUNT_TYPE = "fr.free.nrw.commons";
|
||||||
String username, String password) {
|
private final Context context;
|
||||||
|
|
||||||
Account account = new Account(username, accountType());
|
public AccountUtil(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);
|
boolean created = accountManager().addAccountExplicitly(account, password, null);
|
||||||
|
|
||||||
Timber.d("account creation " + (created ? "successful" : "failure"));
|
Timber.d("account creation " + (created ? "successful" : "failure"));
|
||||||
|
|
@ -26,8 +36,8 @@ public class AccountUtil {
|
||||||
if (created) {
|
if (created) {
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString(AccountManager.KEY_ACCOUNT_NAME, username);
|
bundle.putString(KEY_ACCOUNT_NAME, username);
|
||||||
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType());
|
bundle.putString(KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);
|
||||||
|
|
||||||
|
|
||||||
response.onResult(bundle);
|
response.onResult(bundle);
|
||||||
|
|
@ -35,7 +45,7 @@ public class AccountUtil {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "");
|
response.onError(ERROR_CODE_REMOTE_EXCEPTION, "");
|
||||||
}
|
}
|
||||||
Timber.d("account creation failure");
|
Timber.d("account creation failure");
|
||||||
}
|
}
|
||||||
|
|
@ -45,18 +55,8 @@ public class AccountUtil {
|
||||||
ContentResolver.setSyncAutomatically(account, ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
|
ContentResolver.setSyncAutomatically(account, ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
private AccountManager accountManager() {
|
||||||
public static String accountType() {
|
return AccountManager.get(context);
|
||||||
return "fr.free.nrw.commons";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AccountManager accountManager() {
|
|
||||||
return AccountManager.get(app());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static CommonsApplication app() {
|
|
||||||
return CommonsApplication.getInstance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,47 +5,50 @@ import android.accounts.AccountManager;
|
||||||
import android.accounts.AccountManagerFuture;
|
import android.accounts.AccountManagerFuture;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
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;
|
||||||
|
|
||||||
|
import static android.accounts.AccountManager.KEY_ACCOUNT_NAME;
|
||||||
|
import static fr.free.nrw.commons.auth.AccountUtil.ACCOUNT_TYPE;
|
||||||
|
|
||||||
public abstract class AuthenticatedActivity extends NavigationBaseActivity {
|
public abstract class AuthenticatedActivity extends NavigationBaseActivity {
|
||||||
|
|
||||||
private String accountType;
|
@Inject SessionManager sessionManager;
|
||||||
CommonsApplication app;
|
|
||||||
|
|
||||||
private String authCookie;
|
private String authCookie;
|
||||||
|
|
||||||
public AuthenticatedActivity() {
|
|
||||||
this.accountType = AccountUtil.accountType();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getAuthCookie(Account account, AccountManager accountManager) {
|
private void getAuthCookie(Account account, AccountManager accountManager) {
|
||||||
Single.fromCallable(() -> accountManager.blockingGetAuthToken(account, "", false))
|
Single.fromCallable(() -> accountManager.blockingGetAuthToken(account, "", false))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.doOnError(Timber::e)
|
.doOnError(Timber::e)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(this::onAuthCookieAcquired, throwable -> onAuthFailure());
|
.subscribe(
|
||||||
|
this:: onAuthCookieAcquired,
|
||||||
|
throwable -> onAuthFailure());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAccount(AccountManager accountManager) {
|
private void addAccount(AccountManager accountManager) {
|
||||||
Single.just(accountManager.addAccount(accountType, null, null, null, AuthenticatedActivity.this, null, null))
|
Single.just(accountManager.addAccount(ACCOUNT_TYPE, null, null,
|
||||||
|
null, AuthenticatedActivity.this, null, null))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.map(AccountManagerFuture::getResult)
|
.map(AccountManagerFuture::getResult)
|
||||||
.doOnEvent((bundle, throwable) -> {
|
.doOnEvent((bundle, throwable) -> {
|
||||||
if (!bundle.containsKey(AccountManager.KEY_ACCOUNT_NAME)) {
|
if (!bundle.containsKey(KEY_ACCOUNT_NAME)) {
|
||||||
throw new RuntimeException("Bundle doesn't contain account-name key: "
|
throw new RuntimeException("Bundle doesn't contain account-name key: "
|
||||||
+ AccountManager.KEY_ACCOUNT_NAME);
|
+ KEY_ACCOUNT_NAME);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(bundle -> bundle.getString(AccountManager.KEY_ACCOUNT_NAME))
|
.map(bundle -> bundle.getString(KEY_ACCOUNT_NAME))
|
||||||
.doOnError(Timber::e)
|
.doOnError(Timber::e)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(s -> {
|
.subscribe(s -> {
|
||||||
Account[] allAccounts = accountManager.getAccountsByType(accountType);
|
Account[] allAccounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
|
||||||
Account curAccount = allAccounts[0];
|
Account curAccount = allAccounts[0];
|
||||||
getAuthCookie(curAccount, accountManager);
|
getAuthCookie(curAccount, accountManager);
|
||||||
},
|
},
|
||||||
|
|
@ -58,7 +61,7 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AccountManager accountManager = AccountManager.get(this);
|
AccountManager accountManager = AccountManager.get(this);
|
||||||
Account curAccount = app.getCurrentAccount();
|
Account curAccount = sessionManager.getCurrentAccount();
|
||||||
if (curAccount == null) {
|
if (curAccount == null) {
|
||||||
addAccount(accountManager);
|
addAccount(accountManager);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -69,7 +72,7 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
app = CommonsApplication.getInstance();
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
authCookie = savedInstanceState.getString("authCookie");
|
authCookie = savedInstanceState.getString("authCookie");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,15 +21,19 @@ import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
import dagger.android.AndroidInjection;
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.PageTitle;
|
import fr.free.nrw.commons.PageTitle;
|
||||||
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.WelcomeActivity;
|
import fr.free.nrw.commons.WelcomeActivity;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
@ -40,6 +44,12 @@ 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 AccountUtil accountUtil;
|
||||||
|
@Inject SessionManager sessionManager;
|
||||||
|
@Inject @Named("application_preferences") SharedPreferences prefs;
|
||||||
|
@Inject @Named("default_preferences") SharedPreferences defaultPrefs;
|
||||||
|
|
||||||
@BindView(R.id.loginButton) Button loginButton;
|
@BindView(R.id.loginButton) Button loginButton;
|
||||||
@BindView(R.id.signupButton) Button signupButton;
|
@BindView(R.id.signupButton) Button signupButton;
|
||||||
@BindView(R.id.loginUsername) EditText usernameEdit;
|
@BindView(R.id.loginUsername) EditText usernameEdit;
|
||||||
|
|
@ -47,11 +57,8 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
@BindView(R.id.loginTwoFactor) EditText twoFactorEdit;
|
@BindView(R.id.loginTwoFactor) EditText twoFactorEdit;
|
||||||
@BindView(R.id.error_message_container) ViewGroup errorMessageContainer;
|
@BindView(R.id.error_message_container) ViewGroup errorMessageContainer;
|
||||||
@BindView(R.id.error_message) TextView errorMessage;
|
@BindView(R.id.error_message) TextView errorMessage;
|
||||||
|
|
||||||
private CommonsApplication app;
|
|
||||||
ProgressDialog progressDialog;
|
ProgressDialog progressDialog;
|
||||||
private AppCompatDelegate delegate;
|
private AppCompatDelegate delegate;
|
||||||
private SharedPreferences prefs = null;
|
|
||||||
private LoginTextWatcher textWatcher = new LoginTextWatcher();
|
private LoginTextWatcher textWatcher = new LoginTextWatcher();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -59,16 +66,13 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
setTheme(Utils.isDarkTheme(this) ? R.style.DarkAppTheme : R.style.LightAppTheme);
|
setTheme(Utils.isDarkTheme(this) ? R.style.DarkAppTheme : R.style.LightAppTheme);
|
||||||
getDelegate().installViewFactory();
|
getDelegate().installViewFactory();
|
||||||
getDelegate().onCreate(savedInstanceState);
|
getDelegate().onCreate(savedInstanceState);
|
||||||
|
AndroidInjection.inject(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
app = CommonsApplication.getInstance();
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_login);
|
setContentView(R.layout.activity_login);
|
||||||
|
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
prefs = getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE);
|
|
||||||
|
|
||||||
usernameEdit.addTextChangedListener(textWatcher);
|
usernameEdit.addTextChangedListener(textWatcher);
|
||||||
passwordEdit.addTextChangedListener(textWatcher);
|
passwordEdit.addTextChangedListener(textWatcher);
|
||||||
twoFactorEdit.addTextChangedListener(textWatcher);
|
twoFactorEdit.addTextChangedListener(textWatcher);
|
||||||
|
|
@ -91,7 +95,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
WelcomeActivity.startYourself(this);
|
WelcomeActivity.startYourself(this);
|
||||||
prefs.edit().putBoolean("firstrun", false).apply();
|
prefs.edit().putBoolean("firstrun", false).apply();
|
||||||
}
|
}
|
||||||
if (app.getCurrentAccount() != null) {
|
if (sessionManager.getCurrentAccount() != null) {
|
||||||
startMainActivity();
|
startMainActivity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -113,6 +117,25 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LoginTask getLoginTask() {
|
||||||
|
return new LoginTask(
|
||||||
|
this,
|
||||||
|
canonicializeUsername(usernameEdit.getText().toString()),
|
||||||
|
passwordEdit.getText().toString(),
|
||||||
|
twoFactorEdit.getText().toString(),
|
||||||
|
accountUtil, mwApi, defaultPrefs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because Mediawiki is upercase-first-char-then-case-sensitive :)
|
||||||
|
* @param username String
|
||||||
|
* @return String canonicial username
|
||||||
|
*/
|
||||||
|
private String canonicializeUsername(String username) {
|
||||||
|
return new PageTitle(username).getText();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
|
|
@ -207,24 +230,6 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoginTask getLoginTask() {
|
|
||||||
return new LoginTask(
|
|
||||||
this,
|
|
||||||
canonicializeUsername(usernameEdit.getText().toString()),
|
|
||||||
passwordEdit.getText().toString(),
|
|
||||||
twoFactorEdit.getText().toString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Because Mediawiki is upercase-first-char-then-case-sensitive :)
|
|
||||||
* @param username String
|
|
||||||
* @return String canonicial username
|
|
||||||
*/
|
|
||||||
private String canonicializeUsername(String username) {
|
|
||||||
return new PageTitle(username).getText();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showMessage(@StringRes int resId, @ColorRes int colorResId) {
|
private void showMessage(@StringRes int resId, @ColorRes int colorResId) {
|
||||||
errorMessage.setText(getString(resId));
|
errorMessage.setText(getString(resId));
|
||||||
errorMessage.setTextColor(ContextCompat.getColor(this, colorResId));
|
errorMessage.setTextColor(ContextCompat.getColor(this, colorResId));
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package fr.free.nrw.commons.auth;
|
package fr.free.nrw.commons.auth;
|
||||||
|
|
||||||
import android.accounts.AccountAuthenticatorResponse;
|
import android.accounts.AccountAuthenticatorResponse;
|
||||||
import android.accounts.AccountManager;
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
|
@ -11,22 +11,34 @@ import java.io.IOException;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.mwapi.EventLog;
|
import fr.free.nrw.commons.mwapi.EventLog;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
import static android.accounts.AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE;
|
||||||
|
import static android.accounts.AccountManager.KEY_ACCOUNT_NAME;
|
||||||
|
import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE;
|
||||||
|
import static fr.free.nrw.commons.auth.AccountUtil.ACCOUNT_TYPE;
|
||||||
|
|
||||||
class LoginTask extends AsyncTask<String, String, String> {
|
class LoginTask extends AsyncTask<String, String, String> {
|
||||||
|
|
||||||
private LoginActivity loginActivity;
|
private LoginActivity loginActivity;
|
||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
private String twoFactorCode = "";
|
private String twoFactorCode = "";
|
||||||
private CommonsApplication app;
|
private AccountUtil accountUtil;
|
||||||
|
private MediaWikiApi mwApi;
|
||||||
|
private SharedPreferences prefs;
|
||||||
|
|
||||||
public LoginTask(LoginActivity loginActivity, String username, String password, String twoFactorCode) {
|
public LoginTask(LoginActivity loginActivity, String username, String password,
|
||||||
|
String twoFactorCode, AccountUtil accountUtil,
|
||||||
|
MediaWikiApi mwApi, SharedPreferences prefs) {
|
||||||
this.loginActivity = loginActivity;
|
this.loginActivity = loginActivity;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.twoFactorCode = twoFactorCode;
|
this.twoFactorCode = twoFactorCode;
|
||||||
app = CommonsApplication.getInstance();
|
this.accountUtil = accountUtil;
|
||||||
|
this.mwApi = mwApi;
|
||||||
|
this.prefs = prefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -44,9 +56,9 @@ class LoginTask extends AsyncTask<String, String, String> {
|
||||||
protected String doInBackground(String... params) {
|
protected String doInBackground(String... params) {
|
||||||
try {
|
try {
|
||||||
if (twoFactorCode.isEmpty()) {
|
if (twoFactorCode.isEmpty()) {
|
||||||
return app.getMWApi().login(username, password);
|
return mwApi.login(username, password);
|
||||||
} else {
|
} else {
|
||||||
return app.getMWApi().login(username, password, twoFactorCode);
|
return mwApi.login(username, password, twoFactorCode);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Do something better!
|
// Do something better!
|
||||||
|
|
@ -59,7 +71,7 @@ class LoginTask extends AsyncTask<String, String, String> {
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
Timber.d("Login done!");
|
Timber.d("Login done!");
|
||||||
|
|
||||||
EventLog.schema(CommonsApplication.EVENT_LOGIN_ATTEMPT)
|
EventLog.schema(CommonsApplication.EVENT_LOGIN_ATTEMPT, mwApi, prefs)
|
||||||
.param("username", username)
|
.param("username", username)
|
||||||
.param("result", result)
|
.param("result", result)
|
||||||
.log();
|
.log();
|
||||||
|
|
@ -79,16 +91,16 @@ class LoginTask extends AsyncTask<String, String, String> {
|
||||||
Bundle extras = loginActivity.getIntent().getExtras();
|
Bundle extras = loginActivity.getIntent().getExtras();
|
||||||
if (extras != null) {
|
if (extras != null) {
|
||||||
Timber.d("Bundle of extras: %s", extras);
|
Timber.d("Bundle of extras: %s", extras);
|
||||||
response = extras.getParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
|
response = extras.getParcelable(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
Bundle authResult = new Bundle();
|
Bundle authResult = new Bundle();
|
||||||
authResult.putString(AccountManager.KEY_ACCOUNT_NAME, username);
|
authResult.putString(KEY_ACCOUNT_NAME, username);
|
||||||
authResult.putString(AccountManager.KEY_ACCOUNT_TYPE, AccountUtil.accountType());
|
authResult.putString(KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);
|
||||||
response.onResult(authResult);
|
response.onResult(authResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountUtil.createAccount(response, username, password);
|
accountUtil.createAccount(response, username, password);
|
||||||
loginActivity.startMainActivity();
|
loginActivity.startMainActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package fr.free.nrw.commons.auth;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.accounts.AuthenticatorException;
|
||||||
|
import android.accounts.OperationCanceledException;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
import io.reactivex.Completable;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
|
||||||
|
import static fr.free.nrw.commons.auth.AccountUtil.ACCOUNT_TYPE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage the current logged in user session.
|
||||||
|
*/
|
||||||
|
public class SessionManager {
|
||||||
|
private final Context context;
|
||||||
|
private final MediaWikiApi mediaWikiApi;
|
||||||
|
private Account currentAccount; // Unlike a savings account... ;-)
|
||||||
|
|
||||||
|
public SessionManager(Context context, MediaWikiApi mediaWikiApi) {
|
||||||
|
this.context = context;
|
||||||
|
this.mediaWikiApi = mediaWikiApi;
|
||||||
|
this.currentAccount = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Account|null
|
||||||
|
*/
|
||||||
|
public Account getCurrentAccount() {
|
||||||
|
if (currentAccount == null) {
|
||||||
|
AccountManager accountManager = AccountManager.get(context);
|
||||||
|
Account[] allAccounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
|
||||||
|
if (allAccounts.length != 0) {
|
||||||
|
currentAccount = allAccounts[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean revalidateAuthToken() {
|
||||||
|
AccountManager accountManager = AccountManager.get(context);
|
||||||
|
Account curAccount = getCurrentAccount();
|
||||||
|
|
||||||
|
if (curAccount == null) {
|
||||||
|
return false; // This should never happen
|
||||||
|
}
|
||||||
|
|
||||||
|
accountManager.invalidateAuthToken(ACCOUNT_TYPE, mediaWikiApi.getAuthCookie());
|
||||||
|
try {
|
||||||
|
String authCookie = accountManager.blockingGetAuthToken(curAccount, "", false);
|
||||||
|
mediaWikiApi.setAuthCookie(authCookie);
|
||||||
|
return true;
|
||||||
|
} catch (OperationCanceledException | NullPointerException | IOException | AuthenticatorException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Completable clearAllAccounts() {
|
||||||
|
AccountManager accountManager = AccountManager.get(context);
|
||||||
|
Account[] allAccounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
|
||||||
|
return Completable.fromObservable(Observable.fromArray(allAccounts)
|
||||||
|
.map(a -> accountManager.removeAccount(a, null, null).getResult()))
|
||||||
|
.doOnComplete(() -> currentAccount = null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,6 @@ import android.webkit.WebViewClient;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.theme.BaseActivity;
|
import fr.free.nrw.commons.theme.BaseActivity;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
@ -39,11 +38,8 @@ public class SignupActivity extends BaseActivity {
|
||||||
//Signup success, so clear cookies, notify user, and load LoginActivity again
|
//Signup success, so clear cookies, notify user, and load LoginActivity again
|
||||||
Timber.d("Overriding URL %s", url);
|
Timber.d("Overriding URL %s", url);
|
||||||
|
|
||||||
Toast toast = Toast.makeText(
|
Toast toast = Toast.makeText(SignupActivity.this,
|
||||||
CommonsApplication.getInstance(),
|
"Account created!", Toast.LENGTH_LONG);
|
||||||
"Account created!",
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
);
|
|
||||||
toast.show();
|
toast.show();
|
||||||
// terminate on task completion.
|
// terminate on task completion.
|
||||||
finish();
|
finish();
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
|
||||||
import static android.accounts.AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION;
|
import static android.accounts.AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION;
|
||||||
|
|
@ -25,15 +24,18 @@ import static android.accounts.AccountManager.KEY_BOOLEAN_RESULT;
|
||||||
import static android.accounts.AccountManager.KEY_ERROR_CODE;
|
import static android.accounts.AccountManager.KEY_ERROR_CODE;
|
||||||
import static android.accounts.AccountManager.KEY_ERROR_MESSAGE;
|
import static android.accounts.AccountManager.KEY_ERROR_MESSAGE;
|
||||||
import static android.accounts.AccountManager.KEY_INTENT;
|
import static android.accounts.AccountManager.KEY_INTENT;
|
||||||
|
import static fr.free.nrw.commons.auth.AccountUtil.ACCOUNT_TYPE;
|
||||||
import static fr.free.nrw.commons.auth.LoginActivity.PARAM_USERNAME;
|
import static fr.free.nrw.commons.auth.LoginActivity.PARAM_USERNAME;
|
||||||
|
|
||||||
public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
||||||
|
|
||||||
private Context context;
|
private final Context context;
|
||||||
|
private MediaWikiApi mediaWikiApi;
|
||||||
|
|
||||||
WikiAccountAuthenticator(Context context) {
|
WikiAccountAuthenticator(Context context, MediaWikiApi mwApi) {
|
||||||
super(context);
|
super(context);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.mediaWikiApi = mwApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Bundle unsupportedOperation() {
|
private Bundle unsupportedOperation() {
|
||||||
|
|
@ -47,7 +49,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean supportedAccountType(@Nullable String type) {
|
private boolean supportedAccountType(@Nullable String type) {
|
||||||
return AccountUtil.accountType().equals(type);
|
return ACCOUNT_TYPE.equals(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -86,11 +88,10 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getAuthCookie(String username, String password) throws IOException {
|
private String getAuthCookie(String username, String password) throws IOException {
|
||||||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
|
||||||
//TODO add 2fa support here
|
//TODO add 2fa support here
|
||||||
String result = api.login(username, password);
|
String result = mediaWikiApi.login(username, password);
|
||||||
if (result.equals("PASS")) {
|
if (result.equals("PASS")) {
|
||||||
return api.getAuthCookie();
|
return mediaWikiApi.getAuthCookie();
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +116,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
||||||
if (authCookie != null) {
|
if (authCookie != null) {
|
||||||
final Bundle result = new Bundle();
|
final Bundle result = new Bundle();
|
||||||
result.putString(KEY_ACCOUNT_NAME, account.name);
|
result.putString(KEY_ACCOUNT_NAME, account.name);
|
||||||
result.putString(KEY_ACCOUNT_TYPE, AccountUtil.accountType());
|
result.putString(KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);
|
||||||
result.putString(KEY_AUTHTOKEN, authCookie);
|
result.putString(KEY_AUTHTOKEN, authCookie);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,28 @@
|
||||||
package fr.free.nrw.commons.auth;
|
package fr.free.nrw.commons.auth;
|
||||||
|
|
||||||
import android.accounts.AccountManager;
|
|
||||||
import android.app.Service;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
|
||||||
public class WikiAccountAuthenticatorService extends Service {
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.DaggerService;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
|
||||||
|
import static android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT;
|
||||||
|
|
||||||
|
public class WikiAccountAuthenticatorService extends DaggerService {
|
||||||
|
|
||||||
|
@Inject MediaWikiApi mwApi;
|
||||||
|
private WikiAccountAuthenticator wikiAccountAuthenticator = null;
|
||||||
|
|
||||||
private static WikiAccountAuthenticator wikiAccountAuthenticator = null;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
if (!intent.getAction().equals(AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
|
if (!intent.getAction().equals(ACTION_AUTHENTICATOR_INTENT)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wikiAccountAuthenticator == null) {
|
if (wikiAccountAuthenticator == null) {
|
||||||
wikiAccountAuthenticator = new WikiAccountAuthenticator(this);
|
wikiAccountAuthenticator = new WikiAccountAuthenticator(this, mwApi);
|
||||||
}
|
}
|
||||||
return wikiAccountAuthenticator.getIBinder();
|
return wikiAccountAuthenticator.getIBinder();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ package fr.free.nrw.commons.category;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
|
@ -31,11 +29,15 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import dagger.android.support.DaggerFragment;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.data.Category;
|
import fr.free.nrw.commons.data.Category;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import fr.free.nrw.commons.upload.MwVolleyApi;
|
import fr.free.nrw.commons.upload.MwVolleyApi;
|
||||||
import fr.free.nrw.commons.utils.StringSortingUtils;
|
import fr.free.nrw.commons.utils.StringSortingUtils;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
|
|
@ -50,7 +52,7 @@ import static fr.free.nrw.commons.category.CategoryContentProvider.AUTHORITY;
|
||||||
/**
|
/**
|
||||||
* Displays the category suggestion and selection screen. Category search is initiated here.
|
* Displays the category suggestion and selection screen. Category search is initiated here.
|
||||||
*/
|
*/
|
||||||
public class CategorizationFragment extends Fragment {
|
public class CategorizationFragment extends DaggerFragment {
|
||||||
|
|
||||||
public static final int SEARCH_CATS_LIMIT = 25;
|
public static final int SEARCH_CATS_LIMIT = 25;
|
||||||
|
|
||||||
|
|
@ -65,6 +67,9 @@ public class CategorizationFragment extends Fragment {
|
||||||
@BindView(R.id.categoriesExplanation)
|
@BindView(R.id.categoriesExplanation)
|
||||||
TextView categoriesSkip;
|
TextView categoriesSkip;
|
||||||
|
|
||||||
|
@Inject MediaWikiApi mwApi;
|
||||||
|
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||||
|
|
||||||
private RVRendererAdapter<CategoryItem> categoriesAdapter;
|
private RVRendererAdapter<CategoryItem> categoriesAdapter;
|
||||||
private OnCategoriesSaveHandler onCategoriesSaveHandler;
|
private OnCategoriesSaveHandler onCategoriesSaveHandler;
|
||||||
private HashMap<String, ArrayList<String>> categoriesCache;
|
private HashMap<String, ArrayList<String>> categoriesCache;
|
||||||
|
|
@ -205,7 +210,9 @@ public class CategorizationFragment extends Fragment {
|
||||||
.sorted(sortBySimilarity(filter))
|
.sorted(sortBySimilarity(filter))
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
s -> categoriesAdapter.add(s), Timber::e, () -> {
|
s -> categoriesAdapter.add(s),
|
||||||
|
Timber::e,
|
||||||
|
() -> {
|
||||||
categoriesAdapter.notifyDataSetChanged();
|
categoriesAdapter.notifyDataSetChanged();
|
||||||
categoriesSearchInProgress.setVisibility(View.GONE);
|
categoriesSearchInProgress.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
|
@ -253,10 +260,9 @@ public class CategorizationFragment extends Fragment {
|
||||||
|
|
||||||
private Observable<CategoryItem> titleCategories() {
|
private Observable<CategoryItem> titleCategories() {
|
||||||
//Retrieve the title that was saved when user tapped submit icon
|
//Retrieve the title that was saved when user tapped submit icon
|
||||||
SharedPreferences titleDesc = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
String title = prefs.getString("Title", "");
|
||||||
String title = titleDesc.getString("Title", "");
|
|
||||||
|
|
||||||
return CommonsApplication.getInstance().getMWApi()
|
return mwApi
|
||||||
.searchTitles(title, SEARCH_CATS_LIMIT)
|
.searchTitles(title, SEARCH_CATS_LIMIT)
|
||||||
.map(name -> new CategoryItem(name, false));
|
.map(name -> new CategoryItem(name, false));
|
||||||
}
|
}
|
||||||
|
|
@ -279,7 +285,7 @@ public class CategorizationFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
//otherwise, search API for matching categories
|
//otherwise, search API for matching categories
|
||||||
return CommonsApplication.getInstance().getMWApi()
|
return mwApi
|
||||||
.allCategories(term, SEARCH_CATS_LIMIT)
|
.allCategories(term, SEARCH_CATS_LIMIT)
|
||||||
.map(name -> new CategoryItem(name, false));
|
.map(name -> new CategoryItem(name, false));
|
||||||
}
|
}
|
||||||
|
|
@ -290,7 +296,7 @@ public class CategorizationFragment extends Fragment {
|
||||||
return Observable.empty();
|
return Observable.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommonsApplication.getInstance().getMWApi()
|
return mwApi
|
||||||
.searchCategories(term, SEARCH_CATS_LIMIT)
|
.searchCategories(term, SEARCH_CATS_LIMIT)
|
||||||
.map(s -> new CategoryItem(s, false));
|
.map(s -> new CategoryItem(s, false));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ import android.net.Uri;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjection;
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
@ -36,17 +38,15 @@ public class CategoryContentProvider extends ContentProvider {
|
||||||
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID);
|
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DBOpenHelper dbOpenHelper;
|
|
||||||
|
|
||||||
public static Uri uriForId(int id) {
|
public static Uri uriForId(int id) {
|
||||||
return Uri.parse(BASE_URI.toString() + "/" + id);
|
return Uri.parse(BASE_URI.toString() + "/" + id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@Inject DBOpenHelper dbOpenHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
CommonsApplication app = ((CommonsApplication) getContext().getApplicationContext());
|
AndroidInjection.inject(this);
|
||||||
dbOpenHelper = app.getDBOpenHelper();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
|
@ -16,7 +17,6 @@ import java.util.Locale;
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.Media;
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.Utils;
|
|
||||||
import fr.free.nrw.commons.settings.Prefs;
|
import fr.free.nrw.commons.settings.Prefs;
|
||||||
|
|
||||||
public class Contribution extends Media {
|
public class Contribution extends Media {
|
||||||
|
|
@ -149,7 +149,7 @@ public class Contribution extends Media {
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.append("== {{int:license-header}} ==\n")
|
buffer.append("== {{int:license-header}} ==\n")
|
||||||
.append(Utils.licenseTemplateFor(getLicense())).append("\n\n")
|
.append(licenseTemplateFor(getLicense())).append("\n\n")
|
||||||
.append("{{Uploaded from Mobile|platform=Android|version=").append(BuildConfig.VERSION_NAME).append("}}\n")
|
.append("{{Uploaded from Mobile|platform=Android|version=").append(BuildConfig.VERSION_NAME).append("}}\n")
|
||||||
.append(getTrackingTemplates());
|
.append(getTrackingTemplates());
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
|
|
@ -377,4 +377,25 @@ public class Contribution extends Media {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String licenseTemplateFor(String license) {
|
||||||
|
switch (license) {
|
||||||
|
case Prefs.Licenses.CC_BY_3:
|
||||||
|
return "{{self|cc-by-3.0}}";
|
||||||
|
case Prefs.Licenses.CC_BY_4:
|
||||||
|
return "{{self|cc-by-4.0}}";
|
||||||
|
case Prefs.Licenses.CC_BY_SA_3:
|
||||||
|
return "{{self|cc-by-sa-3.0}}";
|
||||||
|
case Prefs.Licenses.CC_BY_SA_4:
|
||||||
|
return "{{self|cc-by-sa-4.0}}";
|
||||||
|
case Prefs.Licenses.CC0:
|
||||||
|
return "{{self|cc-zero}}";
|
||||||
|
case Prefs.Licenses.CC_BY:
|
||||||
|
return "{{self|cc-by-3.0}}";
|
||||||
|
case Prefs.Licenses.CC_BY_SA:
|
||||||
|
return "{{self|cc-by-sa-3.0}}";
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unrecognized license value: " + license);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import android.database.Cursor;
|
||||||
import android.database.DataSetObserver;
|
import android.database.DataSetObserver;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
|
|
@ -24,21 +23,20 @@ import android.widget.AdapterView;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import dagger.android.AndroidInjection;
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.HandlerService;
|
import fr.free.nrw.commons.HandlerService;
|
||||||
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;
|
||||||
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
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 io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
@ -49,10 +47,17 @@ import static fr.free.nrw.commons.contributions.ContributionsContentProvider.AUT
|
||||||
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
|
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
|
||||||
import static fr.free.nrw.commons.settings.Prefs.UPLOADS_SHOWING;
|
import static fr.free.nrw.commons.settings.Prefs.UPLOADS_SHOWING;
|
||||||
|
|
||||||
public class ContributionsActivity extends AuthenticatedActivity
|
public class ContributionsActivity
|
||||||
implements LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener,
|
extends AuthenticatedActivity
|
||||||
MediaDetailPagerFragment.MediaDetailProvider, FragmentManager.OnBackStackChangedListener,
|
implements LoaderManager.LoaderCallbacks<Cursor>,
|
||||||
ContributionsListFragment.SourceRefresher {
|
AdapterView.OnItemClickListener,
|
||||||
|
MediaDetailPagerFragment.MediaDetailProvider,
|
||||||
|
FragmentManager.OnBackStackChangedListener,
|
||||||
|
ContributionsListFragment.SourceRefresher {
|
||||||
|
|
||||||
|
@Inject MediaWikiApi mediaWikiApi;
|
||||||
|
@Inject SessionManager sessionManager;
|
||||||
|
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||||
|
|
||||||
private Cursor allContributions;
|
private Cursor allContributions;
|
||||||
private ContributionsListFragment contributionsList;
|
private ContributionsListFragment contributionsList;
|
||||||
|
|
@ -62,9 +67,6 @@ public class ContributionsActivity extends AuthenticatedActivity
|
||||||
private ArrayList<DataSetObserver> observersWaitingForLoad = new ArrayList<>();
|
private ArrayList<DataSetObserver> observersWaitingForLoad = new ArrayList<>();
|
||||||
private String CONTRIBUTION_SELECTION = "";
|
private String CONTRIBUTION_SELECTION = "";
|
||||||
|
|
||||||
@Inject
|
|
||||||
MediaWikiApi mediaWikiApi;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This sorts in the following order:
|
This sorts in the following order:
|
||||||
Currently Uploading
|
Currently Uploading
|
||||||
|
|
@ -109,12 +111,8 @@ public class ContributionsActivity extends AuthenticatedActivity
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
boolean isSettingsChanged = prefs.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
|
||||||
boolean isSettingsChanged =
|
prefs.edit().putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false).apply();
|
||||||
sharedPreferences.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
|
|
||||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
|
||||||
editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
|
|
||||||
editor.apply();
|
|
||||||
if (isSettingsChanged) {
|
if (isSettingsChanged) {
|
||||||
refreshSource();
|
refreshSource();
|
||||||
}
|
}
|
||||||
|
|
@ -122,9 +120,8 @@ public class ContributionsActivity extends AuthenticatedActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onAuthCookieAcquired(String authCookie) {
|
protected void onAuthCookieAcquired(String authCookie) {
|
||||||
// Do a sync every time we get here!
|
// Do a sync everytime we get here!
|
||||||
CommonsApplication app = ((CommonsApplication) getApplication());
|
requestSync(sessionManager.getCurrentAccount(), ContributionsContentProvider.AUTHORITY, new Bundle());
|
||||||
requestSync(app.getCurrentAccount(), AUTHORITY, new Bundle());
|
|
||||||
Intent uploadServiceIntent = new Intent(this, UploadService.class);
|
Intent uploadServiceIntent = new Intent(this, UploadService.class);
|
||||||
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
|
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
|
||||||
startService(uploadServiceIntent);
|
startService(uploadServiceIntent);
|
||||||
|
|
@ -139,19 +136,18 @@ public class ContributionsActivity extends AuthenticatedActivity
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
AndroidInjection.inject(this);
|
|
||||||
setContentView(R.layout.activity_contributions);
|
setContentView(R.layout.activity_contributions);
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
// Activity can call methods in the fragment by acquiring a
|
// Activity can call methods in the fragment by acquiring a
|
||||||
// reference to the Fragment from FragmentManager, using findFragmentById()
|
// reference to the Fragment from FragmentManager, using findFragmentById()
|
||||||
FragmentManager supportFragmentManager = getSupportFragmentManager();
|
FragmentManager supportFragmentManager = getSupportFragmentManager();
|
||||||
contributionsList = (ContributionsListFragment) supportFragmentManager
|
contributionsList = (ContributionsListFragment)supportFragmentManager
|
||||||
.findFragmentById(R.id.contributionsListFragment);
|
.findFragmentById(R.id.contributionsListFragment);
|
||||||
|
|
||||||
supportFragmentManager.addOnBackStackChangedListener(this);
|
supportFragmentManager.addOnBackStackChangedListener(this);
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
mediaDetails = (MediaDetailPagerFragment) supportFragmentManager
|
mediaDetails = (MediaDetailPagerFragment)supportFragmentManager
|
||||||
.findFragmentById(R.id.contributionsFragmentContainer);
|
.findFragmentById(R.id.contributionsFragmentContainer);
|
||||||
|
|
||||||
getSupportLoaderManager().initLoader(0, null, this);
|
getSupportLoaderManager().initLoader(0, null, this);
|
||||||
|
|
@ -241,8 +237,7 @@ public class ContributionsActivity extends AuthenticatedActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||||
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
|
int uploads = prefs.getInt(UPLOADS_SHOWING, 100);
|
||||||
int uploads = sharedPref.getInt(UPLOADS_SHOWING, 100);
|
|
||||||
return new CursorLoader(this, BASE_URI,
|
return new CursorLoader(this, BASE_URI,
|
||||||
ALL_FIELDS, CONTRIBUTION_SELECTION, null,
|
ALL_FIELDS, CONTRIBUTION_SELECTION, null,
|
||||||
CONTRIBUTION_SORT + "LIMIT " + uploads);
|
CONTRIBUTION_SORT + "LIMIT " + uploads);
|
||||||
|
|
@ -289,9 +284,8 @@ public class ContributionsActivity extends AuthenticatedActivity
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
private void setUploadCount() {
|
private void setUploadCount() {
|
||||||
CommonsApplication app = ((CommonsApplication) getApplication());
|
compositeDisposable.add(mediaWikiApi
|
||||||
Disposable uploadCountDisposable = mediaWikiApi
|
.getUploadCount(sessionManager.getCurrentAccount().name)
|
||||||
.getUploadCount(app.getCurrentAccount().name)
|
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
|
|
@ -299,8 +293,7 @@ public class ContributionsActivity extends AuthenticatedActivity
|
||||||
.getQuantityString(R.plurals.contributions_subtitle,
|
.getQuantityString(R.plurals.contributions_subtitle,
|
||||||
uploadCount, uploadCount)),
|
uploadCount, uploadCount)),
|
||||||
t -> Timber.e(t, "Fetching upload count failed")
|
t -> Timber.e(t, "Fetching upload count failed")
|
||||||
);
|
));
|
||||||
compositeDisposable.add(uploadCountDisposable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,10 @@ import android.net.Uri;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjection;
|
||||||
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static android.content.UriMatcher.NO_MATCH;
|
import static android.content.UriMatcher.NO_MATCH;
|
||||||
|
|
@ -36,9 +39,12 @@ public class ContributionsContentProvider extends ContentProvider {
|
||||||
return Uri.parse(BASE_URI.toString() + "/" + id);
|
return Uri.parse(BASE_URI.toString() + "/" + id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject DBOpenHelper dbOpenHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
return false;
|
AndroidInjection.inject(this);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
|
@ -50,8 +56,7 @@ public class ContributionsContentProvider extends ContentProvider {
|
||||||
|
|
||||||
int uriType = uriMatcher.match(uri);
|
int uriType = uriMatcher.match(uri);
|
||||||
|
|
||||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||||
SQLiteDatabase db = app.getDBOpenHelper().getReadableDatabase();
|
|
||||||
Cursor cursor;
|
Cursor cursor;
|
||||||
|
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
|
|
@ -87,9 +92,8 @@ public class ContributionsContentProvider extends ContentProvider {
|
||||||
@Override
|
@Override
|
||||||
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
||||||
int uriType = uriMatcher.match(uri);
|
int uriType = uriMatcher.match(uri);
|
||||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||||
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
|
long id = 0;
|
||||||
long id;
|
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
case CONTRIBUTIONS:
|
case CONTRIBUTIONS:
|
||||||
id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
||||||
|
|
@ -107,13 +111,12 @@ public class ContributionsContentProvider extends ContentProvider {
|
||||||
int rows;
|
int rows;
|
||||||
int uriType = uriMatcher.match(uri);
|
int uriType = uriMatcher.match(uri);
|
||||||
|
|
||||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||||
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
|
|
||||||
|
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
case CONTRIBUTIONS_ID:
|
case CONTRIBUTIONS_ID:
|
||||||
Timber.d("Deleting contribution id %s", uri.getLastPathSegment());
|
Timber.d("Deleting contribution id %s", uri.getLastPathSegment());
|
||||||
rows = sqlDB.delete(TABLE_NAME,
|
rows = db.delete(TABLE_NAME,
|
||||||
"_id = ?",
|
"_id = ?",
|
||||||
new String[]{uri.getLastPathSegment()}
|
new String[]{uri.getLastPathSegment()}
|
||||||
);
|
);
|
||||||
|
|
@ -130,8 +133,7 @@ public class ContributionsContentProvider extends ContentProvider {
|
||||||
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
|
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
|
||||||
Timber.d("Hello, bulk insert! (ContributionsContentProvider)");
|
Timber.d("Hello, bulk insert! (ContributionsContentProvider)");
|
||||||
int uriType = uriMatcher.match(uri);
|
int uriType = uriMatcher.match(uri);
|
||||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||||
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
|
|
||||||
sqlDB.beginTransaction();
|
sqlDB.beginTransaction();
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
case CONTRIBUTIONS:
|
case CONTRIBUTIONS:
|
||||||
|
|
@ -162,9 +164,8 @@ public class ContributionsContentProvider extends ContentProvider {
|
||||||
error out otherwise.
|
error out otherwise.
|
||||||
*/
|
*/
|
||||||
int uriType = uriMatcher.match(uri);
|
int uriType = uriMatcher.match(uri);
|
||||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||||
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
|
int rowsUpdated = 0;
|
||||||
int rowsUpdated;
|
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
case CONTRIBUTIONS:
|
case CONTRIBUTIONS:
|
||||||
rowsUpdated = sqlDB.update(TABLE_NAME, contentValues, selection, selectionArgs);
|
rowsUpdated = sqlDB.update(TABLE_NAME, contentValues, selection, selectionArgs);
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,7 @@ import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
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.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
@ -22,9 +20,12 @@ import android.widget.ListAdapter;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import dagger.android.support.DaggerFragment;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.nearby.NearbyActivity;
|
import fr.free.nrw.commons.nearby.NearbyActivity;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
@ -32,11 +33,10 @@ import timber.log.Timber;
|
||||||
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
||||||
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
||||||
import static android.app.Activity.RESULT_OK;
|
import static android.app.Activity.RESULT_OK;
|
||||||
import static android.content.Context.MODE_PRIVATE;
|
|
||||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
|
|
||||||
public class ContributionsListFragment extends Fragment {
|
public class ContributionsListFragment extends DaggerFragment {
|
||||||
|
|
||||||
@BindView(R.id.contributionsList)
|
@BindView(R.id.contributionsList)
|
||||||
GridView contributionsList;
|
GridView contributionsList;
|
||||||
|
|
@ -45,6 +45,9 @@ public class ContributionsListFragment extends Fragment {
|
||||||
@BindView(R.id.loadingContributionsProgressBar)
|
@BindView(R.id.loadingContributionsProgressBar)
|
||||||
ProgressBar progressBar;
|
ProgressBar progressBar;
|
||||||
|
|
||||||
|
@Inject @Named("prefs") SharedPreferences prefs;
|
||||||
|
@Inject @Named("default_preferences") SharedPreferences defaultPrefs;
|
||||||
|
|
||||||
private ContributionController controller;
|
private ContributionController controller;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -59,7 +62,6 @@ public class ContributionsListFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Should this be in onResume?
|
//TODO: Should this be in onResume?
|
||||||
SharedPreferences prefs = getActivity().getSharedPreferences("prefs", MODE_PRIVATE);
|
|
||||||
String lastModified = prefs.getString("lastSyncTimestamp", "");
|
String lastModified = prefs.getString("lastSyncTimestamp", "");
|
||||||
Timber.d("Last Sync Timestamp: %s", lastModified);
|
Timber.d("Last Sync Timestamp: %s", lastModified);
|
||||||
|
|
||||||
|
|
@ -162,9 +164,7 @@ public class ContributionsListFragment extends Fragment {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_from_camera:
|
case R.id.menu_from_camera:
|
||||||
SharedPreferences sharedPref = PreferenceManager
|
boolean useExtStorage = defaultPrefs.getBoolean("useExternalStorage", true);
|
||||||
.getDefaultSharedPreferences(CommonsApplication.getInstance());
|
|
||||||
boolean useExtStorage = sharedPref.getBoolean("useExternalStorage", true);
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && useExtStorage) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && useExtStorage) {
|
||||||
// Here, thisActivity is the current activity
|
// Here, thisActivity is the current activity
|
||||||
if (ContextCompat.checkSelfPermission(getActivity(), WRITE_EXTERNAL_STORAGE)
|
if (ContextCompat.checkSelfPermission(getActivity(), WRITE_EXTERNAL_STORAGE)
|
||||||
|
|
@ -242,12 +242,17 @@ public class ContributionsListFragment extends Fragment {
|
||||||
menu.clear(); // See http://stackoverflow.com/a/8495697/17865
|
menu.clear(); // See http://stackoverflow.com/a/8495697/17865
|
||||||
inflater.inflate(R.menu.fragment_contributions_list, menu);
|
inflater.inflate(R.menu.fragment_contributions_list, menu);
|
||||||
|
|
||||||
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
|
if (!deviceHasCamera()) {
|
||||||
if (!app.deviceHasCamera()) {
|
|
||||||
menu.findItem(R.id.menu_from_camera).setEnabled(false);
|
menu.findItem(R.id.menu_from_camera).setEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean deviceHasCamera() {
|
||||||
|
PackageManager pm = getContext().getPackageManager();
|
||||||
|
return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) ||
|
||||||
|
pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,15 @@ import android.os.RemoteException;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.Utils;
|
import fr.free.nrw.commons.Utils;
|
||||||
|
|
@ -23,11 +29,11 @@ import fr.free.nrw.commons.mwapi.LogEventResult;
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static android.content.Context.MODE_PRIVATE;
|
|
||||||
import static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED;
|
import static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED;
|
||||||
import static fr.free.nrw.commons.contributions.Contribution.Table.COLUMN_FILENAME;
|
import static fr.free.nrw.commons.contributions.Contribution.Table.COLUMN_FILENAME;
|
||||||
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
|
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
|
|
||||||
private static final String[] existsQuery = {COLUMN_FILENAME};
|
private static final String[] existsQuery = {COLUMN_FILENAME};
|
||||||
|
|
@ -35,6 +41,10 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
private static final ContentValues[] EMPTY = {};
|
private static final ContentValues[] EMPTY = {};
|
||||||
private static int COMMIT_THRESHOLD = 10;
|
private static int COMMIT_THRESHOLD = 10;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
@Inject MediaWikiApi mwApi;
|
||||||
|
@Inject @Named("prefs") SharedPreferences prefs;
|
||||||
|
|
||||||
public ContributionsSyncAdapter(Context context, boolean autoInitialize) {
|
public ContributionsSyncAdapter(Context context, boolean autoInitialize) {
|
||||||
super(context, autoInitialize);
|
super(context, autoInitialize);
|
||||||
}
|
}
|
||||||
|
|
@ -71,10 +81,9 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
@Override
|
@Override
|
||||||
public void onPerformSync(Account account, Bundle bundle, String authority,
|
public void onPerformSync(Account account, Bundle bundle, String authority,
|
||||||
ContentProviderClient contentProviderClient, SyncResult syncResult) {
|
ContentProviderClient contentProviderClient, SyncResult syncResult) {
|
||||||
|
((CommonsApplication) getContext().getApplicationContext()).injector().inject(this);
|
||||||
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
|
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
|
||||||
String user = account.name;
|
String user = account.name;
|
||||||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
|
||||||
SharedPreferences prefs = getContext().getSharedPreferences("prefs", MODE_PRIVATE);
|
|
||||||
String lastModified = prefs.getString("lastSyncTimestamp", "");
|
String lastModified = prefs.getString("lastSyncTimestamp", "");
|
||||||
Date curTime = new Date();
|
Date curTime = new Date();
|
||||||
LogEventResult result;
|
LogEventResult result;
|
||||||
|
|
@ -83,7 +92,7 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
while (!done) {
|
while (!done) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = api.logEvents(user, lastModified, queryContinue, getLimit());
|
result = mwApi.logEvents(user, lastModified, queryContinue, getLimit());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// There isn't really much we can do, eh?
|
// There isn't really much we can do, eh?
|
||||||
// FIXME: Perhaps add EventLogging?
|
// FIXME: Perhaps add EventLogging?
|
||||||
|
|
@ -137,8 +146,13 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prefs.edit().putString("lastSyncTimestamp", Utils.toMWDate(curTime)).apply();
|
prefs.edit().putString("lastSyncTimestamp", toMWDate(curTime)).apply();
|
||||||
Timber.d("Oh hai, everyone! Look, a kitty!");
|
Timber.d("Oh hai, everyone! Look, a kitty!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String toMWDate(Date date) {
|
||||||
|
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC
|
||||||
|
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
return isoFormat.format(date);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package fr.free.nrw.commons.di;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.android.ContributesAndroidInjector;
|
|
||||||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
|
||||||
import fr.free.nrw.commons.nearby.NearbyActivity;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public abstract class ActivityBuilder {
|
|
||||||
|
|
||||||
@ContributesAndroidInjector()
|
|
||||||
abstract ContributionsActivity bindSplashScreenActivity();
|
|
||||||
|
|
||||||
@ContributesAndroidInjector()
|
|
||||||
abstract NearbyActivity bindNearbyActivity();
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package fr.free.nrw.commons.di;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.android.ContributesAndroidInjector;
|
||||||
|
import fr.free.nrw.commons.AboutActivity;
|
||||||
|
import fr.free.nrw.commons.WelcomeActivity;
|
||||||
|
import fr.free.nrw.commons.auth.LoginActivity;
|
||||||
|
import fr.free.nrw.commons.auth.SignupActivity;
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||||
|
import fr.free.nrw.commons.nearby.NearbyActivity;
|
||||||
|
import fr.free.nrw.commons.settings.SettingsActivity;
|
||||||
|
import fr.free.nrw.commons.upload.MultipleShareActivity;
|
||||||
|
import fr.free.nrw.commons.upload.ShareActivity;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
|
public abstract class ActivityBuilderModule {
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract LoginActivity bindLoginActivity();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract WelcomeActivity bindWelcomeActivity();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract ShareActivity bindShareActivity();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract MultipleShareActivity bindMultipleShareActivity();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract ContributionsActivity bindContributionsActivity();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract SettingsActivity bindSettingsActivity();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract AboutActivity bindAboutActivity();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract SignupActivity bindSignupActivity();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract NearbyActivity bindNearbyActivity();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package fr.free.nrw.commons.di;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.BindsInstance;
|
|
||||||
import dagger.Component;
|
|
||||||
import dagger.android.support.AndroidSupportInjectionModule;
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Component(modules = {
|
|
||||||
AndroidSupportInjectionModule.class,
|
|
||||||
AppModule.class,
|
|
||||||
ActivityBuilder.class
|
|
||||||
})
|
|
||||||
public interface AppComponent {
|
|
||||||
|
|
||||||
@Component.Builder
|
|
||||||
interface Builder {
|
|
||||||
@BindsInstance
|
|
||||||
Builder application(Application application);
|
|
||||||
|
|
||||||
AppComponent build();
|
|
||||||
}
|
|
||||||
|
|
||||||
void inject(CommonsApplication application);
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package fr.free.nrw.commons.di;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
|
||||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
|
||||||
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class AppModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
Context provideContext(Application application) {
|
|
||||||
return application;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
public MediaWikiApi getMWApi() {
|
|
||||||
return new ApacheHttpClientMediaWikiApi(BuildConfig.WIKIMEDIA_API_HOST);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package fr.free.nrw.commons.di;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Component;
|
||||||
|
import dagger.android.AndroidInjectionModule;
|
||||||
|
import dagger.android.AndroidInjector;
|
||||||
|
import dagger.android.support.AndroidSupportInjectionModule;
|
||||||
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
|
import fr.free.nrw.commons.MediaWikiImageView;
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
|
||||||
|
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Component(modules = {
|
||||||
|
CommonsApplicationModule.class,
|
||||||
|
AndroidInjectionModule.class,
|
||||||
|
AndroidSupportInjectionModule.class,
|
||||||
|
ActivityBuilderModule.class,
|
||||||
|
FragmentBuilderModule.class,
|
||||||
|
ServiceBuilderModule.class,
|
||||||
|
ContentProviderBuilderModule.class
|
||||||
|
})
|
||||||
|
public interface CommonsApplicationComponent extends AndroidInjector<CommonsApplication> {
|
||||||
|
void inject(CommonsApplication application);
|
||||||
|
|
||||||
|
void inject(ContributionsSyncAdapter syncAdapter);
|
||||||
|
|
||||||
|
void inject(ModificationsSyncAdapter syncAdapter);
|
||||||
|
|
||||||
|
void inject(MediaWikiImageView mediaWikiImageView);
|
||||||
|
|
||||||
|
@Component.Builder
|
||||||
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
|
interface Builder {
|
||||||
|
Builder appModule(CommonsApplicationModule applicationModule);
|
||||||
|
|
||||||
|
CommonsApplicationComponent build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
package fr.free.nrw.commons.di;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.v4.util.LruCache;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
|
import fr.free.nrw.commons.auth.AccountUtil;
|
||||||
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
|
import fr.free.nrw.commons.caching.CacheController;
|
||||||
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
|
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||||
|
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
import fr.free.nrw.commons.nearby.NearbyPlaces;
|
||||||
|
import fr.free.nrw.commons.upload.UploadController;
|
||||||
|
|
||||||
|
import static android.content.Context.MODE_PRIVATE;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
|
public class CommonsApplicationModule {
|
||||||
|
private CommonsApplication application;
|
||||||
|
|
||||||
|
public CommonsApplicationModule(CommonsApplication application) {
|
||||||
|
this.application = application;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
public AccountUtil providesAccountUtil() {
|
||||||
|
return new AccountUtil(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("application_preferences")
|
||||||
|
public SharedPreferences providesApplicationSharedPreferences() {
|
||||||
|
return application.getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("default_preferences")
|
||||||
|
public SharedPreferences providesDefaultSharedPreferences() {
|
||||||
|
return PreferenceManager.getDefaultSharedPreferences(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("prefs")
|
||||||
|
public SharedPreferences providesOtherSharedPreferences() {
|
||||||
|
return application.getSharedPreferences("prefs", MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
public UploadController providesUploadController(SessionManager sessionManager, @Named("default_preferences") SharedPreferences sharedPreferences) {
|
||||||
|
return new UploadController(sessionManager, application, sharedPreferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public SessionManager providesSessionManager(MediaWikiApi mediaWikiApi) {
|
||||||
|
return new SessionManager(application, mediaWikiApi);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public MediaWikiApi provideMediaWikiApi() {
|
||||||
|
return new ApacheHttpClientMediaWikiApi(BuildConfig.WIKIMEDIA_API_HOST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public LocationServiceManager provideLocationServiceManager() {
|
||||||
|
return new LocationServiceManager(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public CacheController provideCacheController() {
|
||||||
|
return new CacheController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public DBOpenHelper provideDBOpenHelper() {
|
||||||
|
return new DBOpenHelper(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public NearbyPlaces provideNearbyPlaces() {
|
||||||
|
return new NearbyPlaces();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public LruCache<String, String> provideLruCache() {
|
||||||
|
return new LruCache<>(1024);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package fr.free.nrw.commons.di;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.android.ContributesAndroidInjector;
|
||||||
|
import fr.free.nrw.commons.category.CategoryContentProvider;
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
||||||
|
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
|
public abstract class ContentProviderBuilderModule {
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract ContributionsContentProvider bindContributionsContentProvider();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract ModificationsContentProvider bindModificationsContentProvider();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract CategoryContentProvider bindCategoryContentProvider();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package fr.free.nrw.commons.di;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.android.ContributesAndroidInjector;
|
||||||
|
import fr.free.nrw.commons.category.CategorizationFragment;
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionsListFragment;
|
||||||
|
import fr.free.nrw.commons.media.MediaDetailFragment;
|
||||||
|
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||||
|
import fr.free.nrw.commons.nearby.NearbyListFragment;
|
||||||
|
import fr.free.nrw.commons.nearby.NoPermissionsFragment;
|
||||||
|
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||||
|
import fr.free.nrw.commons.upload.MultipleUploadListFragment;
|
||||||
|
import fr.free.nrw.commons.upload.SingleUploadFragment;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
|
public abstract class FragmentBuilderModule {
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract CategorizationFragment bindCategorizationFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract ContributionsListFragment bindContributionsListFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract MediaDetailFragment bindMediaDetailFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract MediaDetailPagerFragment bindMediaDetailPagerFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract NearbyListFragment bindNearbyListFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract NoPermissionsFragment bindNoPermissionsFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract SettingsFragment bindSettingsFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract MultipleUploadListFragment bindMultipleUploadListFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract SingleUploadFragment bindSingleUploadFragment();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package fr.free.nrw.commons.di;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.android.ContributesAndroidInjector;
|
||||||
|
import fr.free.nrw.commons.auth.WikiAccountAuthenticatorService;
|
||||||
|
import fr.free.nrw.commons.upload.UploadService;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
|
public abstract class ServiceBuilderModule {
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract UploadService bindUploadService();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract WikiAccountAuthenticatorService bindWikiAccountAuthenticatorService();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,6 @@ import javax.inject.Singleton;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class LocationServiceManager implements LocationListener {
|
public class LocationServiceManager implements LocationListener {
|
||||||
public static final int LOCATION_REQUEST = 1;
|
public static final int LOCATION_REQUEST = 1;
|
||||||
|
|
||||||
|
|
@ -32,7 +31,6 @@ public class LocationServiceManager implements LocationListener {
|
||||||
private final List<LocationUpdateListener> locationListeners = new CopyOnWriteArrayList<>();
|
private final List<LocationUpdateListener> locationListeners = new CopyOnWriteArrayList<>();
|
||||||
private boolean isLocationManagerRegistered = false;
|
private boolean isLocationManagerRegistered = false;
|
||||||
|
|
||||||
@Inject
|
|
||||||
public LocationServiceManager(Context context) {
|
public LocationServiceManager(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
@ -22,6 +21,10 @@ import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
import dagger.android.support.DaggerFragment;
|
||||||
import fr.free.nrw.commons.License;
|
import fr.free.nrw.commons.License;
|
||||||
import fr.free.nrw.commons.LicenseList;
|
import fr.free.nrw.commons.LicenseList;
|
||||||
import fr.free.nrw.commons.Media;
|
import fr.free.nrw.commons.Media;
|
||||||
|
|
@ -30,10 +33,11 @@ import fr.free.nrw.commons.MediaWikiImageView;
|
||||||
import fr.free.nrw.commons.PageTitle;
|
import fr.free.nrw.commons.PageTitle;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import fr.free.nrw.commons.ui.widget.CompatTextView;
|
import fr.free.nrw.commons.ui.widget.CompatTextView;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class MediaDetailFragment extends Fragment {
|
public class MediaDetailFragment extends DaggerFragment {
|
||||||
|
|
||||||
private boolean editable;
|
private boolean editable;
|
||||||
private MediaDetailPagerFragment.MediaDetailProvider detailProvider;
|
private MediaDetailPagerFragment.MediaDetailProvider detailProvider;
|
||||||
|
|
@ -53,6 +57,9 @@ public class MediaDetailFragment extends Fragment {
|
||||||
return mf;
|
return mf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Provider<MediaDataExtractor> mediaDataExtractorProvider;
|
||||||
|
|
||||||
private MediaWikiImageView image;
|
private MediaWikiImageView image;
|
||||||
private MediaDetailSpacer spacer;
|
private MediaDetailSpacer spacer;
|
||||||
private int initialListTop = 0;
|
private int initialListTop = 0;
|
||||||
|
|
@ -69,7 +76,7 @@ public class MediaDetailFragment extends Fragment {
|
||||||
private boolean categoriesPresent = false;
|
private boolean categoriesPresent = false;
|
||||||
private ViewTreeObserver.OnGlobalLayoutListener layoutListener; // for layout stuff, only used once!
|
private ViewTreeObserver.OnGlobalLayoutListener layoutListener; // for layout stuff, only used once!
|
||||||
private ViewTreeObserver.OnScrollChangedListener scrollListener;
|
private ViewTreeObserver.OnScrollChangedListener scrollListener;
|
||||||
DataSetObserver dataObserver;
|
private DataSetObserver dataObserver;
|
||||||
private AsyncTask<Void,Void,Boolean> detailFetchTask;
|
private AsyncTask<Void,Void,Boolean> detailFetchTask;
|
||||||
private LicenseList licenseList;
|
private LicenseList licenseList;
|
||||||
|
|
||||||
|
|
@ -188,13 +195,13 @@ public class MediaDetailFragment extends Fragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
extractor = new MediaDataExtractor(media.getFilename(), licenseList);
|
extractor = mediaDataExtractorProvider.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... voids) {
|
protected Boolean doInBackground(Void... voids) {
|
||||||
try {
|
try {
|
||||||
extractor.fetch();
|
extractor.fetch(media.getFilename(), licenseList);
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Timber.d(e);
|
Timber.d(e);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package fr.free.nrw.commons.media;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.DownloadManager;
|
import android.app.DownloadManager;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.database.DataSetObserver;
|
import android.database.DataSetObserver;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
@ -24,12 +25,18 @@ import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import dagger.android.support.DaggerFragment;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
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.SessionManager;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||||
import fr.free.nrw.commons.mwapi.EventLog;
|
import fr.free.nrw.commons.mwapi.EventLog;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
|
||||||
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
||||||
import static android.content.Context.DOWNLOAD_SERVICE;
|
import static android.content.Context.DOWNLOAD_SERVICE;
|
||||||
|
|
@ -37,7 +44,11 @@ import static android.content.Intent.ACTION_VIEW;
|
||||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
import static fr.free.nrw.commons.CommonsApplication.EVENT_SHARE_ATTEMPT;
|
import static fr.free.nrw.commons.CommonsApplication.EVENT_SHARE_ATTEMPT;
|
||||||
|
|
||||||
public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPageChangeListener {
|
public class MediaDetailPagerFragment extends DaggerFragment implements ViewPager.OnPageChangeListener {
|
||||||
|
|
||||||
|
@Inject MediaWikiApi mwApi;
|
||||||
|
@Inject SessionManager sessionManager;
|
||||||
|
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||||
|
|
||||||
private ViewPager pager;
|
private ViewPager pager;
|
||||||
private Boolean editable;
|
private Boolean editable;
|
||||||
|
|
@ -101,8 +112,8 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
||||||
case R.id.menu_share_current_image:
|
case R.id.menu_share_current_image:
|
||||||
// Share - this is just logs it, intent set in onCreateOptionsMenu, around line 252
|
// Share - this is just logs it, intent set in onCreateOptionsMenu, around line 252
|
||||||
CommonsApplication app = (CommonsApplication) getActivity().getApplication();
|
CommonsApplication app = (CommonsApplication) getActivity().getApplication();
|
||||||
EventLog.schema(EVENT_SHARE_ATTEMPT)
|
EventLog.schema(EVENT_SHARE_ATTEMPT, mwApi, prefs)
|
||||||
.param("username", app.getCurrentAccount().name)
|
.param("username", sessionManager.getCurrentAccount().name)
|
||||||
.param("filename", m.getFilename())
|
.param("filename", m.getFilename())
|
||||||
.log();
|
.log();
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -161,9 +172,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
|
||||||
req.allowScanningByMediaScanner();
|
req.allowScanningByMediaScanner();
|
||||||
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !(ContextCompat.checkSelfPermission(getContext(), READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED)) {
|
||||||
&& !(ContextCompat.checkSelfPermission(getContext(),
|
|
||||||
READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED)) {
|
|
||||||
Snackbar.make(getView(), R.string.read_storage_permission_rationale,
|
Snackbar.make(getView(), R.string.read_storage_permission_rationale,
|
||||||
Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok,
|
Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok,
|
||||||
view -> ActivityCompat.requestPermissions(getActivity(),
|
view -> ActivityCompat.requestPermissions(getActivity(),
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,10 @@ import android.net.Uri;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjection;
|
||||||
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class ModificationsContentProvider extends ContentProvider {
|
public class ModificationsContentProvider extends ContentProvider {
|
||||||
|
|
@ -33,10 +36,12 @@ public class ModificationsContentProvider extends ContentProvider {
|
||||||
return Uri.parse(BASE_URI.toString() + "/" + id);
|
return Uri.parse(BASE_URI.toString() + "/" + id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject DBOpenHelper dbOpenHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
return false;
|
AndroidInjection.inject(this);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -53,7 +58,7 @@ public class ModificationsContentProvider extends ContentProvider {
|
||||||
throw new IllegalArgumentException("Unknown URI" + uri);
|
throw new IllegalArgumentException("Unknown URI" + uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLiteDatabase db = CommonsApplication.getInstance().getDBOpenHelper().getReadableDatabase();
|
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||||
|
|
||||||
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
||||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||||
|
|
@ -69,7 +74,7 @@ public class ModificationsContentProvider extends ContentProvider {
|
||||||
@Override
|
@Override
|
||||||
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
||||||
int uriType = uriMatcher.match(uri);
|
int uriType = uriMatcher.match(uri);
|
||||||
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
|
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||||
long id = 0;
|
long id = 0;
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
case MODIFICATIONS:
|
case MODIFICATIONS:
|
||||||
|
|
@ -85,7 +90,7 @@ public class ModificationsContentProvider extends ContentProvider {
|
||||||
@Override
|
@Override
|
||||||
public int delete(@NonNull Uri uri, String s, String[] strings) {
|
public int delete(@NonNull Uri uri, String s, String[] strings) {
|
||||||
int uriType = uriMatcher.match(uri);
|
int uriType = uriMatcher.match(uri);
|
||||||
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
|
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
case MODIFICATIONS_ID:
|
case MODIFICATIONS_ID:
|
||||||
String id = uri.getLastPathSegment();
|
String id = uri.getLastPathSegment();
|
||||||
|
|
@ -103,7 +108,7 @@ public class ModificationsContentProvider extends ContentProvider {
|
||||||
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
|
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
|
||||||
Timber.d("Hello, bulk insert! (ModificationsContentProvider)");
|
Timber.d("Hello, bulk insert! (ModificationsContentProvider)");
|
||||||
int uriType = uriMatcher.match(uri);
|
int uriType = uriMatcher.match(uri);
|
||||||
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
|
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||||
sqlDB.beginTransaction();
|
sqlDB.beginTransaction();
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
case MODIFICATIONS:
|
case MODIFICATIONS:
|
||||||
|
|
@ -131,7 +136,7 @@ public class ModificationsContentProvider extends ContentProvider {
|
||||||
In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise.
|
In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise.
|
||||||
*/
|
*/
|
||||||
int uriType = uriMatcher.match(uri);
|
int uriType = uriMatcher.match(uri);
|
||||||
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
|
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||||
int rowsUpdated = 0;
|
int rowsUpdated = 0;
|
||||||
switch (uriType) {
|
switch (uriType) {
|
||||||
case MODIFICATIONS:
|
case MODIFICATIONS:
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,9 @@ import android.os.RemoteException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.Utils;
|
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
|
@ -23,6 +24,8 @@ import timber.log.Timber;
|
||||||
|
|
||||||
public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
|
public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
|
|
||||||
|
@Inject MediaWikiApi mwApi;
|
||||||
|
|
||||||
public ModificationsSyncAdapter(Context context, boolean autoInitialize) {
|
public ModificationsSyncAdapter(Context context, boolean autoInitialize) {
|
||||||
super(context, autoInitialize);
|
super(context, autoInitialize);
|
||||||
}
|
}
|
||||||
|
|
@ -30,6 +33,7 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
@Override
|
@Override
|
||||||
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
|
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
|
||||||
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
|
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
|
||||||
|
((CommonsApplication)getContext().getApplicationContext()).injector().inject(this);
|
||||||
|
|
||||||
Cursor allModifications;
|
Cursor allModifications;
|
||||||
try {
|
try {
|
||||||
|
|
@ -54,17 +58,16 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils.isNullOrWhiteSpace(authCookie)) {
|
if (isNullOrWhiteSpace(authCookie)) {
|
||||||
Timber.d("Could not authenticate :(");
|
Timber.d("Could not authenticate :(");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
mwApi.setAuthCookie(authCookie);
|
||||||
api.setAuthCookie(authCookie);
|
|
||||||
String editToken;
|
String editToken;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
editToken = api.getEditToken();
|
editToken = mwApi.getEditToken();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Timber.d("Can not retreive edit token!");
|
Timber.d("Can not retreive edit token!");
|
||||||
return;
|
return;
|
||||||
|
|
@ -95,7 +98,7 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
if (contrib.getState() == Contribution.STATE_COMPLETED) {
|
if (contrib.getState() == Contribution.STATE_COMPLETED) {
|
||||||
String pageContent;
|
String pageContent;
|
||||||
try {
|
try {
|
||||||
pageContent = api.revisionsByFilename(contrib.getFilename());
|
pageContent = mwApi.revisionsByFilename(contrib.getFilename());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Timber.d("Network fuckup on modifications sync!");
|
Timber.d("Network fuckup on modifications sync!");
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -106,7 +109,7 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
|
|
||||||
String editResult;
|
String editResult;
|
||||||
try {
|
try {
|
||||||
editResult = api.edit(editToken, processedPageContent, contrib.getFilename(), sequence.getEditSummary());
|
editResult = mwApi.edit(editToken, processedPageContent, contrib.getFilename(), sequence.getEditSummary());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Timber.d("Network fuckup on modifications sync!");
|
Timber.d("Network fuckup on modifications sync!");
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -129,4 +132,8 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isNullOrWhiteSpace(String value) {
|
||||||
|
return value == null || value.trim().isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ import org.mediawiki.api.MWApi;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
@ -34,7 +36,6 @@ import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.PageTitle;
|
import fr.free.nrw.commons.PageTitle;
|
||||||
import fr.free.nrw.commons.Utils;
|
|
||||||
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;
|
||||||
|
|
@ -335,7 +336,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
logEvents.add(new LogEventResult.LogEvent(
|
logEvents.add(new LogEventResult.LogEvent(
|
||||||
image.getString("@pageid"),
|
image.getString("@pageid"),
|
||||||
image.getString("@title"),
|
image.getString("@title"),
|
||||||
Utils.parseMWDate(image.getString("@timestamp")))
|
parseMWDate(image.getString("@timestamp")))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return logEvents;
|
return logEvents;
|
||||||
|
|
@ -402,7 +403,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
String errorCode = result.getString("/api/error/@code");
|
String errorCode = result.getString("/api/error/@code");
|
||||||
return new UploadResult(resultStatus, errorCode);
|
return new UploadResult(resultStatus, errorCode);
|
||||||
} else {
|
} else {
|
||||||
Date dateUploaded = Utils.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");
|
||||||
return new UploadResult(resultStatus, dateUploaded, canonicalFilename, imageUrl);
|
return new UploadResult(resultStatus, dateUploaded, canonicalFilename, imageUrl);
|
||||||
|
|
@ -428,4 +429,13 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
return Integer.parseInt(uploadCount);
|
return Integer.parseInt(uploadCount);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Date parseMWDate(String mwDate) {
|
||||||
|
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC
|
||||||
|
try {
|
||||||
|
return isoFormat.parse(mwDate);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package fr.free.nrw.commons.mwapi;
|
package fr.free.nrw.commons.mwapi;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import fr.free.nrw.commons.Utils;
|
import fr.free.nrw.commons.Utils;
|
||||||
|
|
@ -15,14 +16,14 @@ public class EventLog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LogBuilder schema(String schema, long revision) {
|
private static LogBuilder schema(String schema, long revision, MediaWikiApi mwApi, SharedPreferences prefs) {
|
||||||
return new LogBuilder(schema, revision);
|
return new LogBuilder(schema, revision, mwApi, prefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LogBuilder schema(Object[] scid) {
|
public static LogBuilder schema(Object[] scid, MediaWikiApi mwApi, SharedPreferences prefs) {
|
||||||
if (scid.length != 2) {
|
if (scid.length != 2) {
|
||||||
throw new IllegalArgumentException("Needs an object array with schema as first param and revision as second");
|
throw new IllegalArgumentException("Needs an object array with schema as first param and revision as second");
|
||||||
}
|
}
|
||||||
return schema((String) scid[0], (Long) scid[1]);
|
return schema((String) scid[0], (Long) scid[1], mwApi, prefs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package fr.free.nrw.commons.mwapi;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
@ -12,19 +11,23 @@ import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.Utils;
|
import fr.free.nrw.commons.Utils;
|
||||||
import fr.free.nrw.commons.settings.Prefs;
|
import fr.free.nrw.commons.settings.Prefs;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public class LogBuilder {
|
public class LogBuilder {
|
||||||
private JSONObject data;
|
private final MediaWikiApi mwApi;
|
||||||
private long rev;
|
private final JSONObject data;
|
||||||
private String schema;
|
private final long rev;
|
||||||
|
private final String schema;
|
||||||
|
private final SharedPreferences prefs;
|
||||||
|
|
||||||
LogBuilder(String schema, long revision) {
|
LogBuilder(String schema, long revision, MediaWikiApi mwApi, SharedPreferences prefs) {
|
||||||
data = new JSONObject();
|
this.prefs = prefs;
|
||||||
|
this.data = new JSONObject();
|
||||||
this.schema = schema;
|
this.schema = schema;
|
||||||
this.rev = revision;
|
this.rev = revision;
|
||||||
|
this.mwApi = mwApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LogBuilder param(String key, Object value) {
|
public LogBuilder param(String key, Object value) {
|
||||||
|
|
@ -56,11 +59,10 @@ public class LogBuilder {
|
||||||
// Use *only* for tracking the user preference change for EventLogging
|
// Use *only* for tracking the user preference change for EventLogging
|
||||||
// Attempting to use anywhere else will cause kitten explosions
|
// Attempting to use anywhere else will cause kitten explosions
|
||||||
public void log(boolean force) {
|
public void log(boolean force) {
|
||||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(CommonsApplication.getInstance());
|
if (!prefs.getBoolean(Prefs.TRACKING_ENABLED, true) && !force) {
|
||||||
if (!settings.getBoolean(Prefs.TRACKING_ENABLED, true) && !force) {
|
|
||||||
return; // User has disabled tracking
|
return; // User has disabled tracking
|
||||||
}
|
}
|
||||||
LogTask logTask = new LogTask();
|
LogTask logTask = new LogTask(mwApi);
|
||||||
logTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, this);
|
logTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,16 @@ package fr.free.nrw.commons.mwapi;
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
|
|
||||||
class LogTask extends AsyncTask<LogBuilder, Void, Boolean> {
|
class LogTask extends AsyncTask<LogBuilder, Void, Boolean> {
|
||||||
|
|
||||||
|
private final MediaWikiApi mwApi;
|
||||||
|
|
||||||
|
public LogTask(MediaWikiApi mwApi) {
|
||||||
|
this.mwApi = mwApi;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(LogBuilder... logBuilders) {
|
protected Boolean doInBackground(LogBuilder... logBuilders) {
|
||||||
return CommonsApplication.getInstance().getMWApi().logEvents(logBuilders);
|
return mwApi.logEvents(logBuilders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,6 @@ import javax.inject.Inject;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import dagger.android.AndroidInjection;
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||||
|
|
@ -48,12 +46,17 @@ import static fr.free.nrw.commons.location.LocationServiceManager.LOCATION_REQUE
|
||||||
|
|
||||||
public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener {
|
public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener {
|
||||||
|
|
||||||
|
private static final int LOCATION_REQUEST = 1;
|
||||||
|
private static final String MAP_LAST_USED_PREFERENCE = "mapLastUsed";
|
||||||
|
|
||||||
@BindView(R.id.progressBar)
|
@BindView(R.id.progressBar)
|
||||||
ProgressBar progressBar;
|
ProgressBar progressBar;
|
||||||
private static final String MAP_LAST_USED_PREFERENCE = "mapLastUsed";
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
LocationServiceManager locationManager;
|
LocationServiceManager locationManager;
|
||||||
|
@Inject
|
||||||
|
NearbyController nearbyController;
|
||||||
|
|
||||||
private LatLng curLatLang;
|
private LatLng curLatLang;
|
||||||
private Bundle bundle;
|
private Bundle bundle;
|
||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
@ -64,7 +67,6 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
AndroidInjection.inject(this);
|
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||||
setContentView(R.layout.activity_nearby);
|
setContentView(R.layout.activity_nearby);
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
|
|
@ -279,20 +281,14 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
||||||
}
|
}
|
||||||
|
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
setupPlaceList(this);
|
placesDisposable = Observable.fromCallable(() -> nearbyController
|
||||||
}
|
.loadAttractionsFromLocation(curLatLang, this))
|
||||||
|
|
||||||
private void setupPlaceList(Context context) {
|
|
||||||
placesDisposable = Observable.fromCallable(() -> NearbyController
|
|
||||||
.loadAttractionsFromLocation(curLatLang, CommonsApplication.getInstance()))
|
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe((result) -> {
|
.subscribe(this::populatePlaces);
|
||||||
populatePlaces(context, result);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populatePlaces(Context context, List<Place> placeList) {
|
private void populatePlaces(List<Place> placeList) {
|
||||||
Gson gson = new GsonBuilder()
|
Gson gson = new GsonBuilder()
|
||||||
.registerTypeAdapter(Uri.class, new UriSerializer())
|
.registerTypeAdapter(Uri.class, new UriSerializer())
|
||||||
.create();
|
.create();
|
||||||
|
|
@ -301,7 +297,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
||||||
|
|
||||||
if (placeList.size() == 0) {
|
if (placeList.size() == 0) {
|
||||||
int duration = Toast.LENGTH_SHORT;
|
int duration = Toast.LENGTH_SHORT;
|
||||||
Toast toast = Toast.makeText(context, R.string.no_nearby, duration);
|
Toast toast = Toast.makeText(this, R.string.no_nearby, duration);
|
||||||
toast.show();
|
toast.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package fr.free.nrw.commons.nearby;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.graphics.drawable.VectorDrawableCompat;
|
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||||
|
|
||||||
import com.mapbox.mapboxsdk.annotations.IconFactory;
|
import com.mapbox.mapboxsdk.annotations.IconFactory;
|
||||||
|
|
@ -15,7 +14,9 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
import fr.free.nrw.commons.utils.UiUtils;
|
import fr.free.nrw.commons.utils.UiUtils;
|
||||||
|
|
@ -24,9 +25,17 @@ import timber.log.Timber;
|
||||||
import static fr.free.nrw.commons.utils.LengthUtils.computeDistanceBetween;
|
import static fr.free.nrw.commons.utils.LengthUtils.computeDistanceBetween;
|
||||||
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
|
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
|
||||||
|
|
||||||
|
|
||||||
public class NearbyController {
|
public class NearbyController {
|
||||||
private static final int MAX_RESULTS = 1000;
|
private static final int MAX_RESULTS = 1000;
|
||||||
|
private final NearbyPlaces nearbyPlaces;
|
||||||
|
private final SharedPreferences prefs;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public NearbyController(NearbyPlaces nearbyPlaces,
|
||||||
|
@Named("default_preferences") SharedPreferences prefs) {
|
||||||
|
this.nearbyPlaces = nearbyPlaces;
|
||||||
|
this.prefs = prefs;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares Place list to make their distance information update later.
|
* Prepares Place list to make their distance information update later.
|
||||||
|
|
@ -34,13 +43,11 @@ public class NearbyController {
|
||||||
* @param context context
|
* @param context context
|
||||||
* @return Place list without distance information
|
* @return Place list without distance information
|
||||||
*/
|
*/
|
||||||
public static List<Place> loadAttractionsFromLocation(LatLng curLatLng, Context context) {
|
public List<Place> loadAttractionsFromLocation(LatLng curLatLng, Context context) {
|
||||||
Timber.d("Loading attractions near %s", curLatLng);
|
Timber.d("Loading attractions near %s", curLatLng);
|
||||||
if (curLatLng == null) {
|
if (curLatLng == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
NearbyPlaces nearbyPlaces = CommonsApplication.getInstance().getNearbyPlaces();
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
List<Place> places = prefs.getBoolean("useWikidata", true)
|
List<Place> places = prefs.getBoolean("useWikidata", true)
|
||||||
? nearbyPlaces.getFromWikidataQuery(curLatLng, Locale.getDefault().getLanguage())
|
? nearbyPlaces.getFromWikidataQuery(curLatLng, Locale.getDefault().getLanguage())
|
||||||
: nearbyPlaces.getFromWikiNeedsPictures();
|
: nearbyPlaces.getFromWikiNeedsPictures();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package fr.free.nrw.commons.nearby;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
@ -17,12 +16,13 @@ import java.lang.reflect.Type;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import dagger.android.support.DaggerFragment;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class NearbyListFragment extends Fragment {
|
public class NearbyListFragment extends DaggerFragment {
|
||||||
private static final Type LIST_TYPE = new TypeToken<List<Place>>() {
|
private static final Type LIST_TYPE = new TypeToken<List<Place>>() {
|
||||||
}.getType();
|
}.getType();
|
||||||
private static final Type CUR_LAT_LNG_TYPE = new TypeToken<LatLng>() {
|
private static final Type CUR_LAT_LNG_TYPE = new TypeToken<LatLng>() {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
package fr.free.nrw.commons.nearby;
|
package fr.free.nrw.commons.nearby;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
import dagger.android.support.DaggerFragment;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells user that Nearby Places cannot be displayed if location permissions are denied
|
* Tells user that Nearby Places cannot be displayed if location permissions are denied
|
||||||
*/
|
*/
|
||||||
public class NoPermissionsFragment extends Fragment {
|
public class NoPermissionsFragment extends DaggerFragment {
|
||||||
|
|
||||||
public NoPermissionsFragment() {
|
public NoPermissionsFragment() {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package fr.free.nrw.commons.settings;
|
package fr.free.nrw.commons.settings;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v7.app.AppCompatDelegate;
|
import android.support.v7.app.AppCompatDelegate;
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,17 @@ import android.preference.EditTextPreference;
|
||||||
import android.preference.ListPreference;
|
import android.preference.ListPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.ActivityCompat;
|
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v4.content.FileProvider;
|
import android.support.v4.content.FileProvider;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import dagger.android.AndroidInjection;
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
|
|
@ -34,8 +36,11 @@ public class SettingsFragment extends PreferenceFragment {
|
||||||
|
|
||||||
private static final int REQUEST_CODE_WRITE_EXTERNAL_STORAGE = 100;
|
private static final int REQUEST_CODE_WRITE_EXTERNAL_STORAGE = 100;
|
||||||
|
|
||||||
|
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
AndroidInjection.inject(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Load the preferences from an XML resource
|
// Load the preferences from an XML resource
|
||||||
|
|
@ -58,14 +63,12 @@ public class SettingsFragment extends PreferenceFragment {
|
||||||
});
|
});
|
||||||
|
|
||||||
final EditTextPreference uploadLimit = (EditTextPreference) findPreference("uploads");
|
final EditTextPreference uploadLimit = (EditTextPreference) findPreference("uploads");
|
||||||
final SharedPreferences sharedPref = PreferenceManager
|
int uploads = prefs.getInt(Prefs.UPLOADS_SHOWING, 100);
|
||||||
.getDefaultSharedPreferences(CommonsApplication.getInstance());
|
|
||||||
int uploads = sharedPref.getInt(Prefs.UPLOADS_SHOWING, 100);
|
|
||||||
uploadLimit.setText(uploads + "");
|
uploadLimit.setText(uploads + "");
|
||||||
uploadLimit.setSummary(uploads + "");
|
uploadLimit.setSummary(uploads + "");
|
||||||
uploadLimit.setOnPreferenceChangeListener((preference, newValue) -> {
|
uploadLimit.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
int value = Integer.parseInt(newValue.toString());
|
int value = Integer.parseInt(newValue.toString());
|
||||||
final SharedPreferences.Editor editor = sharedPref.edit();
|
final SharedPreferences.Editor editor = prefs.edit();
|
||||||
if (value > 500) {
|
if (value > 500) {
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
.setTitle(R.string.maximum_limit)
|
.setTitle(R.string.maximum_limit)
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,17 @@ package fr.free.nrw.commons.theme;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
|
|
||||||
|
import dagger.android.support.DaggerAppCompatActivity;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.Utils;
|
|
||||||
|
|
||||||
public class BaseActivity extends AppCompatActivity {
|
public abstract class BaseActivity extends DaggerAppCompatActivity {
|
||||||
boolean currentTheme;
|
boolean currentTheme;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
if (Utils.isDarkTheme(this)) {
|
boolean currentThemeIsDark = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("theme", false);
|
||||||
|
if (currentThemeIsDark){
|
||||||
currentTheme = true;
|
currentTheme = true;
|
||||||
setTheme(R.style.DarkAppTheme);
|
setTheme(R.style.DarkAppTheme);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -27,7 +26,7 @@ public class BaseActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
// Restart activity if theme is changed
|
// Restart activity if theme is changed
|
||||||
boolean newTheme = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("theme",false);
|
boolean newTheme = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("theme", false);
|
||||||
if (currentTheme != newTheme) { //is activity theme changed
|
if (currentTheme != newTheme) { //is activity theme changed
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
finish();
|
finish();
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,10 @@ public abstract class NavigationBaseActivity extends BaseActivity
|
||||||
private void setUserName() {
|
private void setUserName() {
|
||||||
|
|
||||||
View navHeaderView = navigationView.getHeaderView(0);
|
View navHeaderView = navigationView.getHeaderView(0);
|
||||||
TextView username = (TextView) navHeaderView.findViewById(R.id.username);
|
TextView username = navHeaderView.findViewById(R.id.username);
|
||||||
|
|
||||||
AccountManager accountManager = AccountManager.get(this);
|
AccountManager accountManager = AccountManager.get(this);
|
||||||
Account[] allAccounts = accountManager.getAccountsByType(AccountUtil.accountType());
|
Account[] allAccounts = accountManager.getAccountsByType(AccountUtil.ACCOUNT_TYPE);
|
||||||
if (allAccounts.length != 0) {
|
if (allAccounts.length != 0) {
|
||||||
username.setText(allAccounts[0].name);
|
username.setText(allAccounts[0].name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
package fr.free.nrw.commons.ui.widget;
|
package fr.free.nrw.commons.ui.widget;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
import android.support.v7.widget.AppCompatTextView;
|
import android.support.v7.widget.AppCompatTextView;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.text.Spanned;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
import fr.free.nrw.commons.Utils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link AppCompatTextView} which formats the text to HTML displayable text and makes any
|
* An {@link AppCompatTextView} which formats the text to HTML displayable text and makes any
|
||||||
* links clickable.
|
* links clickable.
|
||||||
|
|
@ -17,10 +18,25 @@ public class HtmlTextView extends AppCompatTextView {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
|
|
||||||
setMovementMethod(LinkMovementMethod.getInstance());
|
setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
setText(Utils.fromHtml(getText().toString()));
|
setText(fromHtml(getText().toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHtmlText(String newText) {
|
public void setHtmlText(String newText) {
|
||||||
setText(Utils.fromHtml(newText));
|
setText(fromHtml(newText));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix Html.fromHtml is deprecated problem
|
||||||
|
*
|
||||||
|
* @param source provided Html string
|
||||||
|
* @return returned Spanned of appropriate method according to version check
|
||||||
|
*/
|
||||||
|
private static Spanned fromHtml(String source) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
return Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY);
|
||||||
|
} else {
|
||||||
|
//noinspection deprecation
|
||||||
|
return Html.fromHtml(source);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
|
@ -18,6 +17,7 @@ import timber.log.Timber;
|
||||||
* Displays a warning to the user if the file already exists on Commons
|
* Displays a warning to the user if the file already exists on Commons
|
||||||
*/
|
*/
|
||||||
public class ExistingFileAsync extends AsyncTask<Void, Void, Boolean> {
|
public class ExistingFileAsync extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
void onResult(Result result);
|
void onResult(Result result);
|
||||||
}
|
}
|
||||||
|
|
@ -28,14 +28,16 @@ public class ExistingFileAsync extends AsyncTask<Void, Void, Boolean> {
|
||||||
DUPLICATE_CANCELLED
|
DUPLICATE_CANCELLED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final MediaWikiApi api;
|
||||||
private final String fileSha1;
|
private final String fileSha1;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final Callback callback;
|
private final Callback callback;
|
||||||
|
|
||||||
public ExistingFileAsync(String fileSha1, Context context, Callback callback) {
|
public ExistingFileAsync(String fileSha1, Context context, Callback callback, MediaWikiApi mwApi) {
|
||||||
this.fileSha1 = fileSha1;
|
this.fileSha1 = fileSha1;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
this.api = mwApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -45,7 +47,6 @@ public class ExistingFileAsync extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... voids) {
|
protected Boolean doInBackground(Void... voids) {
|
||||||
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
|
|
||||||
|
|
||||||
// https://commons.wikimedia.org/w/api.php?action=query&list=allimages&format=xml&aisha1=801957214aba50cb63bb6eb1b0effa50188900ba
|
// https://commons.wikimedia.org/w/api.php?action=query&list=allimages&format=xml&aisha1=801957214aba50cb63bb6eb1b0effa50188900ba
|
||||||
boolean fileExists;
|
boolean fileExists;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import android.location.LocationListener;
|
||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.media.ExifInterface;
|
import android.media.ExifInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.RequiresApi;
|
import android.support.annotation.RequiresApi;
|
||||||
|
|
@ -16,7 +15,6 @@ import android.support.annotation.RequiresApi;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -26,6 +24,8 @@ import timber.log.Timber;
|
||||||
*/
|
*/
|
||||||
public class GPSExtractor {
|
public class GPSExtractor {
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private SharedPreferences prefs;
|
||||||
private ExifInterface exif;
|
private ExifInterface exif;
|
||||||
private double decLatitude;
|
private double decLatitude;
|
||||||
private double decLongitude;
|
private double decLongitude;
|
||||||
|
|
@ -38,9 +38,12 @@ public class GPSExtractor {
|
||||||
/**
|
/**
|
||||||
* Construct from the file descriptor of the image (only for API 24 or newer).
|
* Construct from the file descriptor of the image (only for API 24 or newer).
|
||||||
* @param fileDescriptor the file descriptor of the image
|
* @param fileDescriptor the file descriptor of the image
|
||||||
|
* @param context the context
|
||||||
*/
|
*/
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
public GPSExtractor(@NonNull FileDescriptor fileDescriptor) {
|
public GPSExtractor(@NonNull FileDescriptor fileDescriptor, Context context, SharedPreferences prefs) {
|
||||||
|
this.context = context;
|
||||||
|
this.prefs = prefs;
|
||||||
try {
|
try {
|
||||||
exif = new ExifInterface(fileDescriptor);
|
exif = new ExifInterface(fileDescriptor);
|
||||||
} catch (IOException | IllegalArgumentException e) {
|
} catch (IOException | IllegalArgumentException e) {
|
||||||
|
|
@ -51,13 +54,16 @@ public class GPSExtractor {
|
||||||
/**
|
/**
|
||||||
* Construct from the file path of the image.
|
* Construct from the file path of the image.
|
||||||
* @param path file path of the image
|
* @param path file path of the image
|
||||||
|
* @param context the context
|
||||||
*/
|
*/
|
||||||
public GPSExtractor(@NonNull String path) {
|
public GPSExtractor(@NonNull String path, Context context, SharedPreferences prefs) {
|
||||||
|
this.prefs = prefs;
|
||||||
try {
|
try {
|
||||||
exif = new ExifInterface(path);
|
exif = new ExifInterface(path);
|
||||||
} catch (IOException | IllegalArgumentException e) {
|
} catch (IOException | IllegalArgumentException e) {
|
||||||
Timber.w(e);
|
Timber.w(e);
|
||||||
}
|
}
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -65,9 +71,7 @@ public class GPSExtractor {
|
||||||
* @return true if enabled, false if disabled
|
* @return true if enabled, false if disabled
|
||||||
*/
|
*/
|
||||||
private boolean gpsPreferenceEnabled() {
|
private boolean gpsPreferenceEnabled() {
|
||||||
SharedPreferences sharedPref
|
boolean gpsPref = prefs.getBoolean("allowGps", false);
|
||||||
= PreferenceManager.getDefaultSharedPreferences(CommonsApplication.getInstance());
|
|
||||||
boolean gpsPref = sharedPref.getBoolean("allowGps", false);
|
|
||||||
Timber.d("Gps pref set to: %b", gpsPref);
|
Timber.d("Gps pref set to: %b", gpsPref);
|
||||||
return gpsPref;
|
return gpsPref;
|
||||||
}
|
}
|
||||||
|
|
@ -76,8 +80,7 @@ public class GPSExtractor {
|
||||||
* Registers a LocationManager to listen for current location
|
* Registers a LocationManager to listen for current location
|
||||||
*/
|
*/
|
||||||
protected void registerLocationManager() {
|
protected void registerLocationManager() {
|
||||||
locationManager = (LocationManager) CommonsApplication.getInstance()
|
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||||
.getSystemService(Context.LOCATION_SERVICE);
|
|
||||||
Criteria criteria = new Criteria();
|
Criteria criteria = new Criteria();
|
||||||
String provider = locationManager.getBestProvider(criteria, true);
|
String provider = locationManager.getBestProvider(criteria, true);
|
||||||
myLocationListener = new MyLocationListener();
|
myLocationListener = new MyLocationListener();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import android.content.ContentProviderClient;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.database.DataSetObserver;
|
import android.database.DataSetObserver;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
@ -24,11 +25,15 @@ import android.widget.Toast;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
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;
|
||||||
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.category.CategorizationFragment;
|
import fr.free.nrw.commons.category.CategorizationFragment;
|
||||||
import fr.free.nrw.commons.category.OnCategoriesSaveHandler;
|
import fr.free.nrw.commons.category.OnCategoriesSaveHandler;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
|
|
@ -38,24 +43,27 @@ import fr.free.nrw.commons.modifications.ModificationsContentProvider;
|
||||||
import fr.free.nrw.commons.modifications.ModifierSequence;
|
import fr.free.nrw.commons.modifications.ModifierSequence;
|
||||||
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
|
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
|
||||||
import fr.free.nrw.commons.mwapi.EventLog;
|
import fr.free.nrw.commons.mwapi.EventLog;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class MultipleShareActivity
|
public class MultipleShareActivity extends AuthenticatedActivity
|
||||||
extends AuthenticatedActivity
|
implements MediaDetailPagerFragment.MediaDetailProvider,
|
||||||
implements MediaDetailPagerFragment.MediaDetailProvider,
|
AdapterView.OnItemClickListener,
|
||||||
AdapterView.OnItemClickListener,
|
FragmentManager.OnBackStackChangedListener,
|
||||||
FragmentManager.OnBackStackChangedListener,
|
MultipleUploadListFragment.OnMultipleUploadInitiatedHandler,
|
||||||
MultipleUploadListFragment.OnMultipleUploadInitiatedHandler,
|
|
||||||
OnCategoriesSaveHandler {
|
OnCategoriesSaveHandler {
|
||||||
private CommonsApplication app;
|
|
||||||
|
@Inject MediaWikiApi mwApi;
|
||||||
|
@Inject SessionManager sessionManager;
|
||||||
|
@Inject UploadController uploadController;
|
||||||
|
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||||
|
|
||||||
private ArrayList<Contribution> photosList = null;
|
private ArrayList<Contribution> photosList = null;
|
||||||
|
|
||||||
private MultipleUploadListFragment uploadsList;
|
private MultipleUploadListFragment uploadsList;
|
||||||
private MediaDetailPagerFragment mediaDetails;
|
private MediaDetailPagerFragment mediaDetails;
|
||||||
private CategorizationFragment categorizationFragment;
|
private CategorizationFragment categorizationFragment;
|
||||||
|
|
||||||
private UploadController uploadController;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Media getMediaAtPosition(int i) {
|
public Media getMediaAtPosition(int i) {
|
||||||
return photosList.get(i);
|
return photosList.get(i);
|
||||||
|
|
@ -132,11 +140,7 @@ public class MultipleShareActivity
|
||||||
dialog.setProgress(uploadCount);
|
dialog.setProgress(uploadCount);
|
||||||
if (uploadCount == photosList.size()) {
|
if (uploadCount == photosList.size()) {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
Toast startingToast = Toast.makeText(
|
Toast startingToast = Toast.makeText(this, R.string.uploading_started, Toast.LENGTH_LONG);
|
||||||
CommonsApplication.getInstance(),
|
|
||||||
R.string.uploading_started,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
);
|
|
||||||
startingToast.show();
|
startingToast.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -163,8 +167,8 @@ public class MultipleShareActivity
|
||||||
@Override
|
@Override
|
||||||
public void onCategoriesSave(List<String> categories) {
|
public void onCategoriesSave(List<String> categories) {
|
||||||
if (categories.size() > 0) {
|
if (categories.size() > 0) {
|
||||||
ContentProviderClient client = getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY);
|
ContentProviderClient client = getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY);
|
||||||
for (Contribution contribution: photosList) {
|
for (Contribution contribution : photosList) {
|
||||||
ModifierSequence categoriesSequence = new ModifierSequence(contribution.getContentUri());
|
ModifierSequence categoriesSequence = new ModifierSequence(contribution.getContentUri());
|
||||||
|
|
||||||
categoriesSequence.queueModifier(new CategoryModifier(categories.toArray(new String[]{})));
|
categoriesSequence.queueModifier(new CategoryModifier(categories.toArray(new String[]{})));
|
||||||
|
|
@ -176,9 +180,9 @@ public class MultipleShareActivity
|
||||||
}
|
}
|
||||||
// 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(app.getCurrentAccount(), ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
|
ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(), ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
|
||||||
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT)
|
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT, mwApi, prefs)
|
||||||
.param("username", app.getCurrentAccount().name)
|
.param("username", sessionManager.getCurrentAccount().name)
|
||||||
.param("categories-count", categories.size())
|
.param("categories-count", categories.size())
|
||||||
.param("files-count", photosList.size())
|
.param("files-count", photosList.size())
|
||||||
.param("source", Contribution.SOURCE_EXTERNAL)
|
.param("source", Contribution.SOURCE_EXTERNAL)
|
||||||
|
|
@ -202,10 +206,8 @@ public class MultipleShareActivity
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
uploadController = new UploadController();
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_multiple_uploads);
|
setContentView(R.layout.activity_multiple_uploads);
|
||||||
app = CommonsApplication.getInstance();
|
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
initDrawer();
|
initDrawer();
|
||||||
|
|
||||||
|
|
@ -245,7 +247,7 @@ public class MultipleShareActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onAuthCookieAcquired(String authCookie) {
|
protected void onAuthCookieAcquired(String authCookie) {
|
||||||
app.getMWApi().setAuthCookie(authCookie);
|
mwApi.setAuthCookie(authCookie);
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|
||||||
if (intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
|
if (intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
|
||||||
|
|
@ -266,7 +268,7 @@ public class MultipleShareActivity
|
||||||
|
|
||||||
uploadsList = (MultipleUploadListFragment) getSupportFragmentManager().findFragmentByTag("uploadsList");
|
uploadsList = (MultipleUploadListFragment) getSupportFragmentManager().findFragmentByTag("uploadsList");
|
||||||
if (uploadsList == null) {
|
if (uploadsList == null) {
|
||||||
uploadsList = new MultipleUploadListFragment();
|
uploadsList = new MultipleUploadListFragment();
|
||||||
getSupportFragmentManager()
|
getSupportFragmentManager()
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.add(R.id.uploadsFragmentContainer, uploadsList, "uploadsList")
|
.add(R.id.uploadsFragmentContainer, uploadsList, "uploadsList")
|
||||||
|
|
@ -288,16 +290,16 @@ public class MultipleShareActivity
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
if (categorizationFragment != null && categorizationFragment.isVisible()) {
|
if (categorizationFragment != null && categorizationFragment.isVisible()) {
|
||||||
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT)
|
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT, mwApi, prefs)
|
||||||
.param("username", app.getCurrentAccount().name)
|
.param("username", sessionManager.getCurrentAccount().name)
|
||||||
.param("categories-count", categorizationFragment.getCurrentSelectedCount())
|
.param("categories-count", categorizationFragment.getCurrentSelectedCount())
|
||||||
.param("files-count", photosList.size())
|
.param("files-count", photosList.size())
|
||||||
.param("source", Contribution.SOURCE_EXTERNAL)
|
.param("source", Contribution.SOURCE_EXTERNAL)
|
||||||
.param("result", "cancelled")
|
.param("result", "cancelled")
|
||||||
.log();
|
.log();
|
||||||
} else {
|
} else {
|
||||||
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT)
|
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT, mwApi, prefs)
|
||||||
.param("username", app.getCurrentAccount().name)
|
.param("username", sessionManager.getCurrentAccount().name)
|
||||||
.param("source", getIntent().getStringExtra(UploadService.EXTRA_SOURCE))
|
.param("source", getIntent().getStringExtra(UploadService.EXTRA_SOURCE))
|
||||||
.param("multiple", true)
|
.param("multiple", true)
|
||||||
.param("result", "cancelled")
|
.param("result", "cancelled")
|
||||||
|
|
@ -307,11 +309,7 @@ public class MultipleShareActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackStackChanged() {
|
public void onBackStackChanged() {
|
||||||
if (mediaDetails != null && mediaDetails.isVisible()) {
|
getSupportActionBar().setDisplayHomeAsUpEnabled(mediaDetails != null && mediaDetails.isVisible()) ;
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
} else {
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,6 @@ import android.graphics.Point;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.graphics.drawable.VectorDrawableCompat;
|
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
|
@ -28,11 +27,12 @@ import android.widget.TextView;
|
||||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||||
import com.facebook.drawee.view.SimpleDraweeView;
|
import com.facebook.drawee.view.SimpleDraweeView;
|
||||||
|
|
||||||
|
import dagger.android.support.DaggerFragment;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||||
|
|
||||||
public class MultipleUploadListFragment extends Fragment {
|
public class MultipleUploadListFragment extends DaggerFragment {
|
||||||
|
|
||||||
public interface OnMultipleUploadInitiatedHandler {
|
public interface OnMultipleUploadInitiatedHandler {
|
||||||
void OnMultipleUploadInitiated();
|
void OnMultipleUploadInitiated();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package fr.free.nrw.commons.upload;
|
package fr.free.nrw.commons.upload;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import com.android.volley.Cache;
|
import com.android.volley.Cache;
|
||||||
|
|
@ -20,7 +21,6 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,12 +33,14 @@ public class MwVolleyApi {
|
||||||
private static RequestQueue REQUEST_QUEUE;
|
private static RequestQueue REQUEST_QUEUE;
|
||||||
private static final Gson GSON = new GsonBuilder().create();
|
private static final Gson GSON = new GsonBuilder().create();
|
||||||
|
|
||||||
protected static Set<String> categorySet;
|
private static Set<String> categorySet;
|
||||||
private static List<String> categoryList;
|
private static List<String> categoryList;
|
||||||
|
|
||||||
private static final String MWURL = "https://commons.wikimedia.org/";
|
private static final String MWURL = "https://commons.wikimedia.org/";
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
public MwVolleyApi() {
|
public MwVolleyApi(Context context) {
|
||||||
|
this.context = context;
|
||||||
categorySet = new HashSet<>();
|
categorySet = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +95,7 @@ public class MwVolleyApi {
|
||||||
|
|
||||||
private synchronized RequestQueue getQueue() {
|
private synchronized RequestQueue getQueue() {
|
||||||
if (REQUEST_QUEUE == null) {
|
if (REQUEST_QUEUE == null) {
|
||||||
REQUEST_QUEUE = Volley.newRequestQueue(CommonsApplication.getInstance());
|
REQUEST_QUEUE = Volley.newRequestQueue(context);
|
||||||
}
|
}
|
||||||
return REQUEST_QUEUE;
|
return REQUEST_QUEUE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,21 @@ import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.Utils;
|
|
||||||
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
||||||
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
|
import fr.free.nrw.commons.caching.CacheController;
|
||||||
import fr.free.nrw.commons.category.CategorizationFragment;
|
import fr.free.nrw.commons.category.CategorizationFragment;
|
||||||
import fr.free.nrw.commons.category.OnCategoriesSaveHandler;
|
import fr.free.nrw.commons.category.OnCategoriesSaveHandler;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
|
|
@ -46,6 +53,7 @@ import fr.free.nrw.commons.modifications.ModificationsContentProvider;
|
||||||
import fr.free.nrw.commons.modifications.ModifierSequence;
|
import fr.free.nrw.commons.modifications.ModifierSequence;
|
||||||
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
|
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
|
||||||
import fr.free.nrw.commons.mwapi.EventLog;
|
import fr.free.nrw.commons.mwapi.EventLog;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED;
|
import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED;
|
||||||
|
|
@ -66,7 +74,11 @@ public class ShareActivity
|
||||||
private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4;
|
private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4;
|
||||||
private CategorizationFragment categorizationFragment;
|
private CategorizationFragment categorizationFragment;
|
||||||
|
|
||||||
private CommonsApplication app;
|
@Inject MediaWikiApi mwApi;
|
||||||
|
@Inject CacheController cacheController;
|
||||||
|
@Inject SessionManager sessionManager;
|
||||||
|
@Inject UploadController uploadController;
|
||||||
|
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||||
|
|
||||||
private String source;
|
private String source;
|
||||||
private String mimeType;
|
private String mimeType;
|
||||||
|
|
@ -75,8 +87,6 @@ public class ShareActivity
|
||||||
private Contribution contribution;
|
private Contribution contribution;
|
||||||
private SimpleDraweeView backgroundImageView;
|
private SimpleDraweeView backgroundImageView;
|
||||||
|
|
||||||
private UploadController uploadController;
|
|
||||||
|
|
||||||
private boolean cacheFound;
|
private boolean cacheFound;
|
||||||
|
|
||||||
private GPSExtractor imageObj;
|
private GPSExtractor imageObj;
|
||||||
|
|
@ -117,7 +127,7 @@ public class ShareActivity
|
||||||
@RequiresApi(16)
|
@RequiresApi(16)
|
||||||
private boolean needsToRequestStoragePermission() {
|
private boolean needsToRequestStoragePermission() {
|
||||||
// We need to ask storage permission when
|
// We need to ask storage permission when
|
||||||
// the file is not owned by this app, (e.g. shared from the Gallery)
|
// the file is not owned by this application, (e.g. shared from the Gallery)
|
||||||
// and permission is not obtained.
|
// and permission is not obtained.
|
||||||
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)
|
||||||
|
|
@ -127,16 +137,12 @@ public class ShareActivity
|
||||||
private void uploadBegins() {
|
private void uploadBegins() {
|
||||||
getFileMetadata(locationPermitted);
|
getFileMetadata(locationPermitted);
|
||||||
|
|
||||||
Toast startingToast = Toast.makeText(
|
Toast startingToast = Toast.makeText(this, R.string.uploading_started, Toast.LENGTH_LONG);
|
||||||
CommonsApplication.getInstance(),
|
|
||||||
R.string.uploading_started,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
);
|
|
||||||
startingToast.show();
|
startingToast.show();
|
||||||
|
|
||||||
if (!cacheFound) {
|
if (!cacheFound) {
|
||||||
//Has to be called after apiCall.request()
|
//Has to be called after apiCall.request()
|
||||||
app.getCacheData().cacheCategory();
|
cacheController.cacheCategory();
|
||||||
Timber.d("Cache the categories found");
|
Timber.d("Cache the categories found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,10 +174,10 @@ 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(app.getCurrentAccount(), ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
|
ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(), ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
|
||||||
|
|
||||||
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT)
|
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT, mwApi, prefs)
|
||||||
.param("username", app.getCurrentAccount().name)
|
.param("username", sessionManager.getCurrentAccount().name)
|
||||||
.param("categories-count", categories.size())
|
.param("categories-count", categories.size())
|
||||||
.param("files-count", 1)
|
.param("files-count", 1)
|
||||||
.param("source", contribution.getSource())
|
.param("source", contribution.getSource())
|
||||||
|
|
@ -192,16 +198,16 @@ public class ShareActivity
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
if (categorizationFragment != null && categorizationFragment.isVisible()) {
|
if (categorizationFragment != null && categorizationFragment.isVisible()) {
|
||||||
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT)
|
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT, mwApi, prefs)
|
||||||
.param("username", app.getCurrentAccount().name)
|
.param("username", sessionManager.getCurrentAccount().name)
|
||||||
.param("categories-count", categorizationFragment.getCurrentSelectedCount())
|
.param("categories-count", categorizationFragment.getCurrentSelectedCount())
|
||||||
.param("files-count", 1)
|
.param("files-count", 1)
|
||||||
.param("source", contribution.getSource())
|
.param("source", contribution.getSource())
|
||||||
.param("result", "cancelled")
|
.param("result", "cancelled")
|
||||||
.log();
|
.log();
|
||||||
} else {
|
} else {
|
||||||
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT)
|
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT, mwApi, prefs)
|
||||||
.param("username", app.getCurrentAccount().name)
|
.param("username", sessionManager.getCurrentAccount().name)
|
||||||
.param("source", getIntent().getStringExtra(UploadService.EXTRA_SOURCE))
|
.param("source", getIntent().getStringExtra(UploadService.EXTRA_SOURCE))
|
||||||
.param("multiple", true)
|
.param("multiple", true)
|
||||||
.param("result", "cancelled")
|
.param("result", "cancelled")
|
||||||
|
|
@ -211,8 +217,7 @@ public class ShareActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onAuthCookieAcquired(String authCookie) {
|
protected void onAuthCookieAcquired(String authCookie) {
|
||||||
app.getMWApi().setAuthCookie(authCookie);
|
mwApi.setAuthCookie(authCookie);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -225,11 +230,10 @@ public class ShareActivity
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
uploadController = new UploadController();
|
|
||||||
setContentView(R.layout.activity_share);
|
setContentView(R.layout.activity_share);
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
initBack();
|
initBack();
|
||||||
app = CommonsApplication.getInstance();
|
|
||||||
backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage);
|
backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage);
|
||||||
backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder
|
backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder
|
||||||
.newInstance(getResources())
|
.newInstance(getResources())
|
||||||
|
|
@ -379,7 +383,7 @@ public class ShareActivity
|
||||||
try {
|
try {
|
||||||
InputStream inputStream = getContentResolver().openInputStream(mediaUri);
|
InputStream inputStream = getContentResolver().openInputStream(mediaUri);
|
||||||
Timber.d("Input stream created from %s", mediaUri.toString());
|
Timber.d("Input stream created from %s", mediaUri.toString());
|
||||||
String fileSHA1 = Utils.getSHA1(inputStream);
|
String fileSHA1 = getSHA1(inputStream);
|
||||||
Timber.d("File SHA1 is: %s", fileSHA1);
|
Timber.d("File SHA1 is: %s", fileSHA1);
|
||||||
|
|
||||||
ExistingFileAsync fileAsyncTask =
|
ExistingFileAsync fileAsyncTask =
|
||||||
|
|
@ -387,7 +391,7 @@ public class ShareActivity
|
||||||
Timber.d("%s duplicate check: %s", mediaUri.toString(), result);
|
Timber.d("%s duplicate check: %s", mediaUri.toString(), result);
|
||||||
duplicateCheckPassed = (result == DUPLICATE_PROCEED
|
duplicateCheckPassed = (result == DUPLICATE_PROCEED
|
||||||
|| result == NO_DUPLICATE);
|
|| result == NO_DUPLICATE);
|
||||||
});
|
}, mwApi);
|
||||||
fileAsyncTask.execute();
|
fileAsyncTask.execute();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Timber.d(e, "IO Exception: ");
|
Timber.d(e, "IO Exception: ");
|
||||||
|
|
@ -424,9 +428,7 @@ public class ShareActivity
|
||||||
ParcelFileDescriptor descriptor
|
ParcelFileDescriptor descriptor
|
||||||
= getContentResolver().openFileDescriptor(mediaUri, "r");
|
= getContentResolver().openFileDescriptor(mediaUri, "r");
|
||||||
if (descriptor != null) {
|
if (descriptor != null) {
|
||||||
SharedPreferences sharedPref = PreferenceManager
|
boolean useExtStorage = prefs.getBoolean("useExternalStorage", true);
|
||||||
.getDefaultSharedPreferences(CommonsApplication.getInstance());
|
|
||||||
boolean useExtStorage = sharedPref.getBoolean("useExternalStorage", true);
|
|
||||||
if (useExtStorage) {
|
if (useExtStorage) {
|
||||||
copyPath = Environment.getExternalStorageDirectory().toString()
|
copyPath = Environment.getExternalStorageDirectory().toString()
|
||||||
+ "/CommonsApp/" + new Date().getTime() + ".jpg";
|
+ "/CommonsApp/" + new Date().getTime() + ".jpg";
|
||||||
|
|
@ -467,12 +469,12 @@ public class ShareActivity
|
||||||
= getContentResolver().openFileDescriptor(mediaUri, "r");
|
= getContentResolver().openFileDescriptor(mediaUri, "r");
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
if (descriptor != null) {
|
if (descriptor != null) {
|
||||||
imageObj = new GPSExtractor(descriptor.getFileDescriptor());
|
imageObj = new GPSExtractor(descriptor.getFileDescriptor(), this, prefs);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String filePath = getPathOfMediaOrCopy();
|
String filePath = getPathOfMediaOrCopy();
|
||||||
if (filePath != null) {
|
if (filePath != null) {
|
||||||
imageObj = new GPSExtractor(filePath);
|
imageObj = new GPSExtractor(filePath, this, prefs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -499,12 +501,12 @@ public class ShareActivity
|
||||||
if (imageObj.imageCoordsExists) {
|
if (imageObj.imageCoordsExists) {
|
||||||
double decLongitude = imageObj.getDecLongitude();
|
double decLongitude = imageObj.getDecLongitude();
|
||||||
double decLatitude = imageObj.getDecLatitude();
|
double decLatitude = imageObj.getDecLatitude();
|
||||||
app.getCacheData().setQtPoint(decLongitude, decLatitude);
|
cacheController.setQtPoint(decLongitude, decLatitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
MwVolleyApi apiCall = new MwVolleyApi();
|
MwVolleyApi apiCall = new MwVolleyApi(this);
|
||||||
|
|
||||||
List<String> displayCatList = app.getCacheData().findCategory();
|
List<String> displayCatList = cacheController.findCategory();
|
||||||
boolean catListEmpty = displayCatList.isEmpty();
|
boolean catListEmpty = displayCatList.isEmpty();
|
||||||
|
|
||||||
// If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories
|
// If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories
|
||||||
|
|
@ -550,4 +552,41 @@ public class ShareActivity
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get SHA1 of file from input stream
|
||||||
|
private String getSHA1(InputStream is) {
|
||||||
|
|
||||||
|
MessageDigest digest;
|
||||||
|
try {
|
||||||
|
digest = MessageDigest.getInstance("SHA1");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Timber.e(e, "Exception while getting Digest");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int read;
|
||||||
|
try {
|
||||||
|
while ((read = is.read(buffer)) > 0) {
|
||||||
|
digest.update(buffer, 0, read);
|
||||||
|
}
|
||||||
|
byte[] md5sum = digest.digest();
|
||||||
|
BigInteger bigInt = new BigInteger(1, md5sum);
|
||||||
|
String output = bigInt.toString(16);
|
||||||
|
// Fill to 40 chars
|
||||||
|
output = String.format("%40s", output).replace(' ', '0');
|
||||||
|
Timber.i("File SHA1: %s", output);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Timber.e(e, "IO Exception");
|
||||||
|
return "";
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
is.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Timber.e(e, "Exception on closing MD5 input stream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import android.graphics.Color;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
|
@ -28,11 +28,15 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import butterknife.OnClick;
|
import butterknife.OnClick;
|
||||||
import butterknife.OnItemSelected;
|
import butterknife.OnItemSelected;
|
||||||
import butterknife.OnTouch;
|
import butterknife.OnTouch;
|
||||||
|
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.settings.Prefs;
|
import fr.free.nrw.commons.settings.Prefs;
|
||||||
|
|
@ -41,7 +45,7 @@ import timber.log.Timber;
|
||||||
import static android.view.MotionEvent.ACTION_DOWN;
|
import static android.view.MotionEvent.ACTION_DOWN;
|
||||||
import static android.view.MotionEvent.ACTION_UP;
|
import static android.view.MotionEvent.ACTION_UP;
|
||||||
|
|
||||||
public class SingleUploadFragment extends Fragment {
|
public class SingleUploadFragment extends DaggerFragment {
|
||||||
|
|
||||||
@BindView(R.id.titleEdit) EditText titleEdit;
|
@BindView(R.id.titleEdit) EditText titleEdit;
|
||||||
@BindView(R.id.descEdit) EditText descEdit;
|
@BindView(R.id.descEdit) EditText descEdit;
|
||||||
|
|
@ -49,7 +53,8 @@ public class SingleUploadFragment extends Fragment {
|
||||||
@BindView(R.id.share_license_summary) TextView licenseSummaryView;
|
@BindView(R.id.share_license_summary) TextView licenseSummaryView;
|
||||||
@BindView(R.id.licenseSpinner) Spinner licenseSpinner;
|
@BindView(R.id.licenseSpinner) Spinner licenseSpinner;
|
||||||
|
|
||||||
private SharedPreferences prefs;
|
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||||
|
|
||||||
private String license;
|
private String license;
|
||||||
private OnUploadActionInitiated uploadActionInitiatedHandler;
|
private OnUploadActionInitiated uploadActionInitiatedHandler;
|
||||||
private TitleTextWatcher textWatcher = new TitleTextWatcher();
|
private TitleTextWatcher textWatcher = new TitleTextWatcher();
|
||||||
|
|
@ -72,11 +77,10 @@ public class SingleUploadFragment extends Fragment {
|
||||||
String desc = descEdit.getText().toString();
|
String desc = descEdit.getText().toString();
|
||||||
|
|
||||||
//Save the title/desc in short-lived cache so next time this fragment is loaded, we can access these
|
//Save the title/desc in short-lived cache so next time this fragment is loaded, we can access these
|
||||||
SharedPreferences titleDesc = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
prefs.edit()
|
||||||
SharedPreferences.Editor editor = titleDesc.edit();
|
.putString("Title", title)
|
||||||
editor.putString("Title", title);
|
.putString("Desc", desc)
|
||||||
editor.putString("Desc", desc);
|
.apply();
|
||||||
editor.apply();
|
|
||||||
|
|
||||||
uploadActionInitiatedHandler.uploadActionInitiated(title, desc);
|
uploadActionInitiatedHandler.uploadActionInitiated(title, desc);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -91,7 +95,6 @@ public class SingleUploadFragment extends Fragment {
|
||||||
View rootView = inflater.inflate(R.layout.fragment_single_upload, container, false);
|
View rootView = inflater.inflate(R.layout.fragment_single_upload, container, false);
|
||||||
ButterKnife.bind(this, rootView);
|
ButterKnife.bind(this, rootView);
|
||||||
|
|
||||||
|
|
||||||
ArrayList<String> licenseItems = new ArrayList<>();
|
ArrayList<String> licenseItems = new ArrayList<>();
|
||||||
licenseItems.add(getString(R.string.license_name_cc0));
|
licenseItems.add(getString(R.string.license_name_cc0));
|
||||||
licenseItems.add(getString(R.string.license_name_cc_by));
|
licenseItems.add(getString(R.string.license_name_cc_by));
|
||||||
|
|
@ -99,7 +102,6 @@ public class SingleUploadFragment extends Fragment {
|
||||||
licenseItems.add(getString(R.string.license_name_cc_by_four));
|
licenseItems.add(getString(R.string.license_name_cc_by_four));
|
||||||
licenseItems.add(getString(R.string.license_name_cc_by_sa_four));
|
licenseItems.add(getString(R.string.license_name_cc_by_sa_four));
|
||||||
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
|
||||||
license = prefs.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3);
|
license = prefs.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3);
|
||||||
|
|
||||||
// check if this is the first time we have uploaded
|
// check if this is the first time we have uploaded
|
||||||
|
|
@ -172,9 +174,9 @@ public class SingleUploadFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
setLicenseSummary(license);
|
setLicenseSummary(license);
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
prefs.edit()
|
||||||
editor.putString(Prefs.DEFAULT_LICENSE, license);
|
.putString(Prefs.DEFAULT_LICENSE, license)
|
||||||
editor.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnTouch(R.id.share_license_summary)
|
@OnTouch(R.id.share_license_summary)
|
||||||
|
|
@ -182,7 +184,7 @@ public class SingleUploadFragment extends Fragment {
|
||||||
if (motionEvent.getActionMasked() == ACTION_DOWN) {
|
if (motionEvent.getActionMasked() == ACTION_DOWN) {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(Utils.licenseUrlFor(license)));
|
intent.setData(Uri.parse(licenseUrlFor(license)));
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -193,9 +195,8 @@ public class SingleUploadFragment extends Fragment {
|
||||||
@OnClick(R.id.titleDescButton)
|
@OnClick(R.id.titleDescButton)
|
||||||
void setTitleDescButton() {
|
void setTitleDescButton() {
|
||||||
//Retrieve last title and desc entered
|
//Retrieve last title and desc entered
|
||||||
SharedPreferences titleDesc = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
String title = prefs.getString("Title", "");
|
||||||
String title = titleDesc.getString("Title", "");
|
String desc = prefs.getString("Desc", "");
|
||||||
String desc = titleDesc.getString("Desc", "");
|
|
||||||
Timber.d("Title: %s, Desc: %s", title, desc);
|
Timber.d("Title: %s, Desc: %s", title, desc);
|
||||||
|
|
||||||
titleEdit.setText(title);
|
titleEdit.setText(title);
|
||||||
|
|
@ -263,6 +264,23 @@ public class SingleUploadFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String licenseUrlFor(String license) {
|
||||||
|
switch (license) {
|
||||||
|
case Prefs.Licenses.CC_BY_3:
|
||||||
|
return "https://creativecommons.org/licenses/by/3.0/";
|
||||||
|
case Prefs.Licenses.CC_BY_4:
|
||||||
|
return "https://creativecommons.org/licenses/by/4.0/";
|
||||||
|
case Prefs.Licenses.CC_BY_SA_3:
|
||||||
|
return "https://creativecommons.org/licenses/by-sa/3.0/";
|
||||||
|
case Prefs.Licenses.CC_BY_SA_4:
|
||||||
|
return "https://creativecommons.org/licenses/by-sa/4.0/";
|
||||||
|
case Prefs.Licenses.CC0:
|
||||||
|
return "https://creativecommons.org/publicdomain/zero/1.0/";
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unrecognized license value: " + license);
|
||||||
|
}
|
||||||
|
|
||||||
public interface OnUploadActionInitiated {
|
public interface OnUploadActionInitiated {
|
||||||
void uploadActionInitiated(String title, String description);
|
void uploadActionInitiated(String title, String description);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package fr.free.nrw.commons.upload;
|
package fr.free.nrw.commons.upload;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
|
|
@ -9,31 +10,36 @@ import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.HandlerService;
|
import fr.free.nrw.commons.HandlerService;
|
||||||
import fr.free.nrw.commons.Utils;
|
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.settings.Prefs;
|
import fr.free.nrw.commons.settings.Prefs;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class UploadController {
|
public class UploadController {
|
||||||
private UploadService uploadService;
|
private UploadService uploadService;
|
||||||
private final CommonsApplication app;
|
private SessionManager sessionManager;
|
||||||
|
private Context context;
|
||||||
|
private SharedPreferences prefs;
|
||||||
|
|
||||||
public interface ContributionUploadProgress {
|
public interface ContributionUploadProgress {
|
||||||
void onUploadStarted(Contribution contribution);
|
void onUploadStarted(Contribution contribution);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UploadController() {
|
public UploadController(SessionManager sessionManager, Context context, SharedPreferences sharedPreferences) {
|
||||||
app = CommonsApplication.getInstance();
|
this.sessionManager = sessionManager;
|
||||||
|
this.context = context;
|
||||||
|
this.prefs = sharedPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUploadServiceConnected;
|
private boolean isUploadServiceConnected;
|
||||||
|
|
@ -52,15 +58,15 @@ public class UploadController {
|
||||||
};
|
};
|
||||||
|
|
||||||
public void prepareService() {
|
public void prepareService() {
|
||||||
Intent uploadServiceIntent = new Intent(app, UploadService.class);
|
Intent uploadServiceIntent = new Intent(context, UploadService.class);
|
||||||
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
|
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
|
||||||
app.startService(uploadServiceIntent);
|
context.startService(uploadServiceIntent);
|
||||||
app.bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE);
|
context.bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
if (isUploadServiceConnected) {
|
if (isUploadServiceConnected) {
|
||||||
app.unbindService(uploadServiceConnection);
|
context.unbindService(uploadServiceConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,7 +74,9 @@ public class UploadController {
|
||||||
Contribution contribution;
|
Contribution contribution;
|
||||||
|
|
||||||
//TODO: Modify this to include coords
|
//TODO: Modify this to include coords
|
||||||
contribution = new Contribution(mediaUri, null, title, description, -1, null, null, app.getCurrentAccount().name, CommonsApplication.DEFAULT_EDIT_SUMMARY, decimalCoords);
|
contribution = new Contribution(mediaUri, null, title, description, -1,
|
||||||
|
null, null, sessionManager.getCurrentAccount().name,
|
||||||
|
CommonsApplication.DEFAULT_EDIT_SUMMARY, decimalCoords);
|
||||||
|
|
||||||
contribution.setTag("mimeType", mimeType);
|
contribution.setTag("mimeType", mimeType);
|
||||||
contribution.setSource(source);
|
contribution.setSource(source);
|
||||||
|
|
@ -78,12 +86,9 @@ public class UploadController {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startUpload(final Contribution contribution, final ContributionUploadProgress onComplete) {
|
public void startUpload(final Contribution contribution, final ContributionUploadProgress onComplete) {
|
||||||
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app);
|
|
||||||
|
|
||||||
//Set creator, desc, and license
|
//Set creator, desc, and license
|
||||||
if (TextUtils.isEmpty(contribution.getCreator())) {
|
if (TextUtils.isEmpty(contribution.getCreator())) {
|
||||||
contribution.setCreator(app.getCurrentAccount().name);
|
contribution.setCreator(sessionManager.getCurrentAccount().name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contribution.getDescription() == null) {
|
if (contribution.getDescription() == null) {
|
||||||
|
|
@ -102,14 +107,15 @@ public class UploadController {
|
||||||
@Override
|
@Override
|
||||||
protected Contribution doInBackground(Void... voids /* stare into you */) {
|
protected Contribution doInBackground(Void... voids /* stare into you */) {
|
||||||
long length;
|
long length;
|
||||||
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
try {
|
try {
|
||||||
if (contribution.getDataLength() <= 0) {
|
if (contribution.getDataLength() <= 0) {
|
||||||
length = app.getContentResolver()
|
length = contentResolver
|
||||||
.openAssetFileDescriptor(contribution.getLocalUri(), "r")
|
.openAssetFileDescriptor(contribution.getLocalUri(), "r")
|
||||||
.getLength();
|
.getLength();
|
||||||
if (length == -1) {
|
if (length == -1) {
|
||||||
// Let us find out the long way!
|
// Let us find out the long way!
|
||||||
length = Utils.countBytes(app.getContentResolver()
|
length = countBytes(contentResolver
|
||||||
.openInputStream(contribution.getLocalUri()));
|
.openInputStream(contribution.getLocalUri()));
|
||||||
}
|
}
|
||||||
contribution.setDataLength(length);
|
contribution.setDataLength(length);
|
||||||
|
|
@ -126,7 +132,7 @@ public class UploadController {
|
||||||
Boolean imagePrefix = false;
|
Boolean imagePrefix = false;
|
||||||
|
|
||||||
if (mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) {
|
if (mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) {
|
||||||
mimeType = app.getContentResolver().getType(contribution.getLocalUri());
|
mimeType = contentResolver.getType(contribution.getLocalUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mimeType != null) {
|
if (mimeType != null) {
|
||||||
|
|
@ -137,7 +143,7 @@ public class UploadController {
|
||||||
|
|
||||||
if (imagePrefix && contribution.getDateCreated() == null) {
|
if (imagePrefix && contribution.getDateCreated() == null) {
|
||||||
Timber.d("local uri " + contribution.getLocalUri());
|
Timber.d("local uri " + contribution.getLocalUri());
|
||||||
Cursor cursor = app.getContentResolver().query(contribution.getLocalUri(),
|
Cursor cursor = contentResolver.query(contribution.getLocalUri(),
|
||||||
new String[]{MediaStore.Images.ImageColumns.DATE_TAKEN}, null, null, null);
|
new String[]{MediaStore.Images.ImageColumns.DATE_TAKEN}, null, null, null);
|
||||||
if (cursor != null && cursor.getCount() != 0 && cursor.getColumnCount() != 0) {
|
if (cursor != null && cursor.getCount() != 0 && cursor.getColumnCount() != 0) {
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
|
|
@ -165,4 +171,14 @@ public class UploadController {
|
||||||
}
|
}
|
||||||
}.executeOnExecutor(Executors.newFixedThreadPool(1)); // TODO remove this by using a sensible thread handling strategy
|
}.executeOnExecutor(Executors.newFixedThreadPool(1)); // TODO remove this by using a sensible thread handling strategy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private long countBytes(InputStream stream) throws IOException {
|
||||||
|
long count = 0;
|
||||||
|
BufferedInputStream bis = new BufferedInputStream(stream);
|
||||||
|
while (bis.read() != -1) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import android.content.ContentProviderClient;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
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;
|
||||||
|
|
@ -22,10 +23,14 @@ import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
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;
|
||||||
|
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.ContributionsActivity;
|
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
||||||
|
|
@ -45,12 +50,13 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
public static final String EXTRA_SOURCE = EXTRA_PREFIX + ".source";
|
public static final String EXTRA_SOURCE = EXTRA_PREFIX + ".source";
|
||||||
public static final String EXTRA_CAMPAIGN = EXTRA_PREFIX + ".campaign";
|
public static final String EXTRA_CAMPAIGN = EXTRA_PREFIX + ".campaign";
|
||||||
|
|
||||||
|
@Inject MediaWikiApi mwApi;
|
||||||
|
@Inject SessionManager sessionManager;
|
||||||
|
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||||
|
|
||||||
private NotificationManager notificationManager;
|
private NotificationManager notificationManager;
|
||||||
private ContentProviderClient contributionsProviderClient;
|
private ContentProviderClient contributionsProviderClient;
|
||||||
private CommonsApplication app;
|
|
||||||
|
|
||||||
private NotificationCompat.Builder curProgressNotification;
|
private NotificationCompat.Builder curProgressNotification;
|
||||||
|
|
||||||
private int toUpload;
|
private int toUpload;
|
||||||
|
|
||||||
// The file names of unfinished uploads, used to prevent overwriting
|
// The file names of unfinished uploads, used to prevent overwriting
|
||||||
|
|
@ -118,7 +124,6 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||||
app = CommonsApplication.getInstance();
|
|
||||||
contributionsProviderClient = this.getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY);
|
contributionsProviderClient = this.getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,8 +185,6 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
|
|
||||||
@SuppressLint("StringFormatInvalid")
|
@SuppressLint("StringFormatInvalid")
|
||||||
private void uploadContribution(Contribution contribution) {
|
private void uploadContribution(Contribution contribution) {
|
||||||
MediaWikiApi api = app.getMWApi();
|
|
||||||
|
|
||||||
InputStream file;
|
InputStream file;
|
||||||
|
|
||||||
String notificationTag = contribution.getLocalUri().toString();
|
String notificationTag = contribution.getLocalUri().toString();
|
||||||
|
|
@ -228,9 +231,9 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
filename = findUniqueFilename(filename);
|
filename = findUniqueFilename(filename);
|
||||||
unfinishedUploads.add(filename);
|
unfinishedUploads.add(filename);
|
||||||
}
|
}
|
||||||
if (!api.validateLogin()) {
|
if (!mwApi.validateLogin()) {
|
||||||
// Need to revalidate!
|
// Need to revalidate!
|
||||||
if (app.revalidateAuthToken()) {
|
if (sessionManager.revalidateAuthToken()) {
|
||||||
Timber.d("Successfully revalidated token!");
|
Timber.d("Successfully revalidated token!");
|
||||||
} else {
|
} else {
|
||||||
Timber.d("Unable to revalidate :(");
|
Timber.d("Unable to revalidate :(");
|
||||||
|
|
@ -246,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 = api.uploadFile(filename, file, contribution.getDataLength(), contribution.getPageContents(), contribution.getEditSummary(), notificationUpdater);
|
UploadResult uploadResult = mwApi.uploadFile(filename, file, contribution.getDataLength(), contribution.getPageContents(), contribution.getEditSummary(), notificationUpdater);
|
||||||
|
|
||||||
Timber.d("Response is %s", uploadResult.toString());
|
Timber.d("Response is %s", uploadResult.toString());
|
||||||
|
|
||||||
|
|
@ -255,8 +258,8 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
String resultStatus = uploadResult.getResultStatus();
|
String resultStatus = uploadResult.getResultStatus();
|
||||||
if (!resultStatus.equals("Success")) {
|
if (!resultStatus.equals("Success")) {
|
||||||
showFailedNotification(contribution);
|
showFailedNotification(contribution);
|
||||||
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT)
|
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT, mwApi, prefs)
|
||||||
.param("username", app.getCurrentAccount().name)
|
.param("username", sessionManager.getCurrentAccount().name)
|
||||||
.param("source", contribution.getSource())
|
.param("source", contribution.getSource())
|
||||||
.param("multiple", contribution.getMultiple())
|
.param("multiple", contribution.getMultiple())
|
||||||
.param("result", uploadResult.getErrorCode())
|
.param("result", uploadResult.getErrorCode())
|
||||||
|
|
@ -269,8 +272,8 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
contribution.setDateUploaded(uploadResult.getDateUploaded());
|
contribution.setDateUploaded(uploadResult.getDateUploaded());
|
||||||
contribution.save();
|
contribution.save();
|
||||||
|
|
||||||
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT)
|
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT, mwApi, prefs)
|
||||||
.param("username", app.getCurrentAccount().name)
|
.param("username", sessionManager.getCurrentAccount().name)
|
||||||
.param("source", contribution.getSource()) //FIXME
|
.param("source", contribution.getSource()) //FIXME
|
||||||
.param("filename", contribution.getFilename())
|
.param("filename", contribution.getFilename())
|
||||||
.param("multiple", contribution.getMultiple())
|
.param("multiple", contribution.getMultiple())
|
||||||
|
|
@ -287,7 +290,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((CommonsApplication.getInstance()).getCurrentAccount(), ModificationsContentProvider.AUTHORITY, new Bundle());
|
ContentResolver.requestSync(sessionManager.getCurrentAccount(), ModificationsContentProvider.AUTHORITY, new Bundle());
|
||||||
stopForeground(true);
|
stopForeground(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -310,7 +313,6 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String findUniqueFilename(String fileName) throws IOException {
|
private String findUniqueFilename(String fileName) throws IOException {
|
||||||
MediaWikiApi api = app.getMWApi();
|
|
||||||
String sequenceFileName;
|
String sequenceFileName;
|
||||||
for (int sequenceNumber = 1; true; sequenceNumber++) {
|
for (int sequenceNumber = 1; true; sequenceNumber++) {
|
||||||
if (sequenceNumber == 1) {
|
if (sequenceNumber == 1) {
|
||||||
|
|
@ -326,7 +328,7 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
sequenceFileName = regexMatcher.replaceAll("$1 " + sequenceNumber + "$2");
|
sequenceFileName = regexMatcher.replaceAll("$1 " + sequenceNumber + "$2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!api.fileExistsWithName(sequenceFileName)
|
if (!mwApi.fileExistsWithName(sequenceFileName)
|
||||||
&& !unfinishedUploads.contains(sequenceFileName)) {
|
&& !unfinishedUploads.contains(sequenceFileName)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,60 @@
|
||||||
package fr.free.nrw.commons;
|
package fr.free.nrw.commons;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.content.SharedPreferences;
|
||||||
|
import android.support.v4.util.LruCache;
|
||||||
|
|
||||||
import com.squareup.leakcanary.RefWatcher;
|
import com.squareup.leakcanary.RefWatcher;
|
||||||
|
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import dagger.android.AndroidInjector;
|
import fr.free.nrw.commons.auth.AccountUtil;
|
||||||
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
|
import fr.free.nrw.commons.caching.CacheController;
|
||||||
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
|
import fr.free.nrw.commons.di.CommonsApplicationComponent;
|
||||||
|
import fr.free.nrw.commons.di.CommonsApplicationModule;
|
||||||
|
import fr.free.nrw.commons.di.DaggerCommonsApplicationComponent;
|
||||||
|
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import fr.free.nrw.commons.nearby.NearbyPlaces;
|
import fr.free.nrw.commons.nearby.NearbyPlaces;
|
||||||
|
import fr.free.nrw.commons.upload.UploadController;
|
||||||
|
|
||||||
public class TestCommonsApplication extends CommonsApplication {
|
public class TestCommonsApplication extends CommonsApplication {
|
||||||
|
|
||||||
|
CommonsApplicationComponent mockApplicationComponent;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private AndroidInjector<Activity> mockInjector;
|
CommonsApplicationModule commonsApplicationModule;
|
||||||
@Mock
|
@Mock
|
||||||
private NearbyPlaces mockNearbyPlaces;
|
AccountUtil accountUtil;
|
||||||
|
@Mock
|
||||||
|
SharedPreferences appSharedPreferences;
|
||||||
|
@Mock
|
||||||
|
SharedPreferences defaultSharedPreferences;
|
||||||
|
@Mock
|
||||||
|
SharedPreferences otherSharedPreferences;
|
||||||
|
@Mock
|
||||||
|
UploadController uploadController;
|
||||||
|
@Mock
|
||||||
|
SessionManager sessionManager;
|
||||||
|
@Mock
|
||||||
|
MediaWikiApi mediaWikiApi;
|
||||||
|
@Mock
|
||||||
|
LocationServiceManager locationServiceManager;
|
||||||
|
@Mock
|
||||||
|
CacheController cacheController;
|
||||||
|
@Mock
|
||||||
|
DBOpenHelper dbOpenHelper;
|
||||||
|
@Mock
|
||||||
|
NearbyPlaces nearbyPlaces;
|
||||||
|
@Mock
|
||||||
|
LruCache<String, String> lruCache;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
super.onCreate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -29,12 +64,119 @@ public class TestCommonsApplication extends CommonsApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AndroidInjector<Activity> activityInjector() {
|
public CommonsApplicationComponent injector() {
|
||||||
return mockInjector;
|
if (mockApplicationComponent == null) {
|
||||||
|
mockApplicationComponent = DaggerCommonsApplicationComponent.builder()
|
||||||
|
.appModule(new CommonsApplicationModule(this) {
|
||||||
|
@Override
|
||||||
|
public AccountUtil providesAccountUtil() {
|
||||||
|
return accountUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SharedPreferences providesApplicationSharedPreferences() {
|
||||||
|
return appSharedPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SharedPreferences providesDefaultSharedPreferences() {
|
||||||
|
return defaultSharedPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SharedPreferences providesOtherSharedPreferences() {
|
||||||
|
return otherSharedPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UploadController providesUploadController(SessionManager sessionManager, SharedPreferences sharedPreferences) {
|
||||||
|
return uploadController;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionManager providesSessionManager(MediaWikiApi mediaWikiApi) {
|
||||||
|
return sessionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaWikiApi provideMediaWikiApi() {
|
||||||
|
return mediaWikiApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocationServiceManager provideLocationServiceManager() {
|
||||||
|
return locationServiceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CacheController provideCacheController() {
|
||||||
|
return cacheController;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DBOpenHelper provideDBOpenHelper() {
|
||||||
|
return dbOpenHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NearbyPlaces provideNearbyPlaces() {
|
||||||
|
return nearbyPlaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LruCache<String, String> provideLruCache() {
|
||||||
|
return lruCache;
|
||||||
|
}
|
||||||
|
}).build();
|
||||||
|
}
|
||||||
|
return mockApplicationComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public AccountUtil getAccountUtil() {
|
||||||
public synchronized NearbyPlaces getNearbyPlaces() {
|
return accountUtil;
|
||||||
return mockNearbyPlaces;
|
}
|
||||||
|
|
||||||
|
public SharedPreferences getAppSharedPreferences() {
|
||||||
|
return appSharedPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SharedPreferences getDefaultSharedPreferences() {
|
||||||
|
return defaultSharedPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SharedPreferences getOtherSharedPreferences() {
|
||||||
|
return otherSharedPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UploadController getUploadController() {
|
||||||
|
return uploadController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionManager getSessionManager() {
|
||||||
|
return sessionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaWikiApi getMediaWikiApi() {
|
||||||
|
return mediaWikiApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocationServiceManager getLocationServiceManager() {
|
||||||
|
return locationServiceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CacheController getCacheController() {
|
||||||
|
return cacheController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBOpenHelper getDbOpenHelper() {
|
||||||
|
return dbOpenHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NearbyPlaces getNearbyPlaces() {
|
||||||
|
return nearbyPlaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LruCache<String, String> getLruCache() {
|
||||||
|
return lruCache;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
package fr.free.nrw.commons.nearby;
|
package fr.free.nrw.commons.nearby;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.Robolectric;
|
import org.robolectric.Robolectric;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
@ -14,14 +11,11 @@ import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.android.controller.ActivityController;
|
import org.robolectric.android.controller.ActivityController;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
import dagger.android.AndroidInjector;
|
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.TestCommonsApplication;
|
import fr.free.nrw.commons.TestCommonsApplication;
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
|
||||||
|
|
||||||
import static junit.framework.Assert.assertNotNull;
|
import static junit.framework.Assert.assertNotNull;
|
||||||
import static org.mockito.Matchers.isA;
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
|
@ -30,9 +24,6 @@ public class NearbyActivityTest {
|
||||||
|
|
||||||
private static final LatLng ST_LOUIS_MO_LAT_LNG = new LatLng(38.627003, -90.199402, 0);
|
private static final LatLng ST_LOUIS_MO_LAT_LNG = new LatLng(38.627003, -90.199402, 0);
|
||||||
|
|
||||||
@Mock
|
|
||||||
LocationServiceManager mockLocationManager;
|
|
||||||
|
|
||||||
private ActivityController<NearbyActivity> activityController;
|
private ActivityController<NearbyActivity> activityController;
|
||||||
private NearbyActivity nearbyActivity;
|
private NearbyActivity nearbyActivity;
|
||||||
|
|
||||||
|
|
@ -40,13 +31,11 @@ public class NearbyActivityTest {
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
AndroidInjector<Activity> injector = ((TestCommonsApplication) RuntimeEnvironment.application).activityInjector();
|
TestCommonsApplication application = (TestCommonsApplication) RuntimeEnvironment.application;
|
||||||
Mockito.doNothing().when(injector).inject(isA(NearbyActivity.class));
|
when(application.getLocationServiceManager().getLastLocation()).thenReturn(ST_LOUIS_MO_LAT_LNG);
|
||||||
when(mockLocationManager.getLastLocation()).thenReturn(ST_LOUIS_MO_LAT_LNG);
|
|
||||||
|
|
||||||
activityController = Robolectric.buildActivity(NearbyActivity.class);
|
activityController = Robolectric.buildActivity(NearbyActivity.class);
|
||||||
nearbyActivity = activityController.get();
|
nearbyActivity = activityController.get();
|
||||||
nearbyActivity.locationManager = mockLocationManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
package fr.free.nrw.commons
|
package fr.free.nrw.commons
|
||||||
|
|
||||||
import org.hamcrest.CoreMatchers.`is` as _is
|
|
||||||
|
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.hamcrest.CoreMatchers.`is` as _is
|
||||||
|
|
||||||
class UtilsTest {
|
class UtilsTest {
|
||||||
@Test fun `strip nothing from non-localized string`() {
|
@Test fun `strip nothing from non-localized string`() {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ buildscript {
|
||||||
classpath "com.android.tools.build:gradle:${project.gradleVersion}"
|
classpath "com.android.tools.build:gradle:${project.gradleVersion}"
|
||||||
classpath 'com.dicedmelon.gradle:jacoco-android:0.1.1'
|
classpath 'com.dicedmelon.gradle:jacoco-android:0.1.1'
|
||||||
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.7.1'
|
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.7.1'
|
||||||
classpath 'me.tatarka:gradle-retrolambda:3.6.1'
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
41
dependency-injection.md
Normal file
41
dependency-injection.md
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
At its core, dependency injection is just the principle of `"tell, dont ask"` put into practice; for instance, if a class needs to use the `MediaWikiApi`, it should be handed an instance of the classs rather than reaching out to get it. This has the effect of decoupling code, making it easier to test and reuse.
|
||||||
|
|
||||||
|
## Dependency Injection in the Commons app
|
||||||
|
|
||||||
|
We use Dagger 2 as our dependency injection engine. Dagger is a fully static, compile-time dependency injection framework for both Java and Android. Dagger aims to address many of the development and performance issues that have plagued reflection-based solutions that came before it, but it does come at something of a cost in complexity.
|
||||||
|
|
||||||
|
For more information about Dagger, take a look at the [Dagger user guide](https://google.github.io/dagger/users-guide.html).
|
||||||
|
|
||||||
|
## Dagger configuration in the Commons app
|
||||||
|
|
||||||
|
The top level `CommonsApplicationComponent` pulls together configuration for injection across the app. The most important files to understand
|
||||||
|
|
||||||
|
- if you need to add a new Activity, look at `ActivityBuilderModule` and copy how injection is configured. The `BaseActivity` class will take care of the rest.
|
||||||
|
- if you are adding a new Fragment, look at `FragmentBuilderModule`
|
||||||
|
- if you are adding a new ContentProvider, look at `ContentProviderBuilderModule`
|
||||||
|
- if you are adding a new Service, look at `ServiceBuilderModule`
|
||||||
|
- other dependencies are configured in `CommonsApplicationModule`
|
||||||
|
|
||||||
|
## "Provider" methods
|
||||||
|
|
||||||
|
Dagger will resolve the method arguments on provider methods in a module (or the constructor arguments when annotated with `@Inject`) and build the objects accordingly - either by calling another provider method or by looking for a constructor on a class that has the `@Inject` annotation. Dagger takes care of managing singletons, just annotate with the `@Singleton` annotation. For instance,
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public SessionManager providesSessionManager(MediaWikiApi mediaWikiApi) {
|
||||||
|
return new SessionManager(application, mediaWikiApi);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If your code injects an interface (in this case, `MediaWikiApi`) then Dagger needs to know which concrete class to use. This comes by way of a provider method:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public MediaWikiApi provideMediaWikiApi() {
|
||||||
|
return new ApacheHttpClientMediaWikiApi(BuildConfig.WIKIMEDIA_API_HOST);
|
||||||
|
}
|
||||||
|
```
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,6 +1,5 @@
|
||||||
#Thu Nov 02 02:18:35 IST 2017
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue