mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
CommonsApplication migrate to kotlin & some lint fixes (#5879)
* convert to kotlin Signed-off-by: parneet-guraya <gurayaparneet@gmail.com> * use lateinit instead of nullable types Signed-off-by: parneet-guraya <gurayaparneet@gmail.com> * instance property access fix Signed-off-by: parneet-guraya <gurayaparneet@gmail.com> * refactor constants name with uppercased ones Signed-off-by: parneet-guraya <gurayaparneet@gmail.com> * remove unused Signed-off-by: parneet-guraya <gurayaparneet@gmail.com> * fix imports in test Signed-off-by: parneet-guraya <gurayaparneet@gmail.com> * use mockk for kotlin to fix tests Signed-off-by: parneet-guraya <gurayaparneet@gmail.com> --------- Signed-off-by: parneet-guraya <gurayaparneet@gmail.com>
This commit is contained in:
parent
7c58891892
commit
bc065c8792
10 changed files with 436 additions and 445 deletions
|
|
@ -105,7 +105,7 @@ class AboutActivityTest {
|
||||||
fun testLaunchTranslate() {
|
fun testLaunchTranslate() {
|
||||||
Espresso.onView(ViewMatchers.withId(R.id.about_translate)).perform(ViewActions.click())
|
Espresso.onView(ViewMatchers.withId(R.id.about_translate)).perform(ViewActions.click())
|
||||||
Espresso.onView(ViewMatchers.withId(android.R.id.button1)).perform(ViewActions.click())
|
Espresso.onView(ViewMatchers.withId(android.R.id.button1)).perform(ViewActions.click())
|
||||||
val langCode = CommonsApplication.getInstance().languageLookUpTable.codes[0]
|
val langCode = CommonsApplication.instance.languageLookUpTable!!.codes[0]
|
||||||
Intents.intended(
|
Intents.intended(
|
||||||
CoreMatchers.allOf(
|
CoreMatchers.allOf(
|
||||||
IntentMatchers.hasAction(Intent.ACTION_VIEW),
|
IntentMatchers.hasAction(Intent.ACTION_VIEW),
|
||||||
|
|
|
||||||
|
|
@ -1,432 +0,0 @@
|
||||||
package fr.free.nrw.commons;
|
|
||||||
|
|
||||||
import static fr.free.nrw.commons.data.DBOpenHelper.CONTRIBUTIONS_TABLE;
|
|
||||||
import static org.acra.ReportField.ANDROID_VERSION;
|
|
||||||
import static org.acra.ReportField.APP_VERSION_CODE;
|
|
||||||
import static org.acra.ReportField.APP_VERSION_NAME;
|
|
||||||
import static org.acra.ReportField.PHONE_MODEL;
|
|
||||||
import static org.acra.ReportField.STACK_TRACE;
|
|
||||||
import static org.acra.ReportField.USER_COMMENT;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.NotificationChannel;
|
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
import android.database.sqlite.SQLiteException;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Process;
|
|
||||||
import android.util.Log;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.multidex.MultiDexApplication;
|
|
||||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
|
||||||
import com.facebook.imagepipeline.core.ImagePipeline;
|
|
||||||
import com.facebook.imagepipeline.core.ImagePipelineConfig;
|
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table;
|
|
||||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
|
|
||||||
import fr.free.nrw.commons.category.CategoryDao;
|
|
||||||
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler;
|
|
||||||
import fr.free.nrw.commons.concurrency.ThreadPoolService;
|
|
||||||
import fr.free.nrw.commons.contributions.ContributionDao;
|
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
|
||||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
|
||||||
import fr.free.nrw.commons.language.AppLanguageLookUpTable;
|
|
||||||
import fr.free.nrw.commons.logging.FileLoggingTree;
|
|
||||||
import fr.free.nrw.commons.logging.LogUtils;
|
|
||||||
import fr.free.nrw.commons.media.CustomOkHttpNetworkFetcher;
|
|
||||||
import fr.free.nrw.commons.settings.Prefs;
|
|
||||||
import fr.free.nrw.commons.upload.FileUtils;
|
|
||||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
|
||||||
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar;
|
|
||||||
import io.reactivex.Completable;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.internal.functions.Functions;
|
|
||||||
import io.reactivex.plugins.RxJavaPlugins;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import org.acra.ACRA;
|
|
||||||
import org.acra.annotation.AcraCore;
|
|
||||||
import org.acra.annotation.AcraDialog;
|
|
||||||
import org.acra.annotation.AcraMailSender;
|
|
||||||
import org.acra.data.StringFormat;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
@AcraCore(
|
|
||||||
buildConfigClass = BuildConfig.class,
|
|
||||||
resReportSendSuccessToast = R.string.crash_dialog_ok_toast,
|
|
||||||
reportFormat = StringFormat.KEY_VALUE_LIST,
|
|
||||||
reportContent = {USER_COMMENT, APP_VERSION_CODE, APP_VERSION_NAME, ANDROID_VERSION, PHONE_MODEL,
|
|
||||||
STACK_TRACE}
|
|
||||||
)
|
|
||||||
|
|
||||||
@AcraMailSender(
|
|
||||||
mailTo = "commons-app-android-private@googlegroups.com",
|
|
||||||
reportAsFile = false
|
|
||||||
)
|
|
||||||
|
|
||||||
@AcraDialog(
|
|
||||||
resTheme = R.style.Theme_AppCompat_Dialog,
|
|
||||||
resText = R.string.crash_dialog_text,
|
|
||||||
resTitle = R.string.crash_dialog_title,
|
|
||||||
resCommentPrompt = R.string.crash_dialog_comment_prompt
|
|
||||||
)
|
|
||||||
|
|
||||||
public class CommonsApplication extends MultiDexApplication {
|
|
||||||
|
|
||||||
public static final String loginMessageIntentKey = "loginMessage";
|
|
||||||
public static final String loginUsernameIntentKey = "loginUsername";
|
|
||||||
|
|
||||||
public static final String IS_LIMITED_CONNECTION_MODE_ENABLED = "is_limited_connection_mode_enabled";
|
|
||||||
@Inject
|
|
||||||
SessionManager sessionManager;
|
|
||||||
@Inject
|
|
||||||
DBOpenHelper dbOpenHelper;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@Named("default_preferences")
|
|
||||||
JsonKvStore defaultPrefs;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
CommonsCookieJar cookieJar;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
CustomOkHttpNetworkFetcher customOkHttpNetworkFetcher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constants begin
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static final String DEFAULT_EDIT_SUMMARY = "Uploaded using [[COM:MOA|Commons Mobile App]]";
|
|
||||||
|
|
||||||
public static final String FEEDBACK_EMAIL = "commons-app-android@googlegroups.com";
|
|
||||||
|
|
||||||
public static final String FEEDBACK_EMAIL_SUBJECT = "Commons Android App Feedback";
|
|
||||||
|
|
||||||
public static final String REPORT_EMAIL = "commons-app-android-private@googlegroups.com";
|
|
||||||
|
|
||||||
public static final String REPORT_EMAIL_SUBJECT = "Report a violation";
|
|
||||||
|
|
||||||
public static final String NOTIFICATION_CHANNEL_ID_ALL = "CommonsNotificationAll";
|
|
||||||
|
|
||||||
public static final String FEEDBACK_EMAIL_TEMPLATE_HEADER = "-- Technical information --";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constants End
|
|
||||||
*/
|
|
||||||
|
|
||||||
private static CommonsApplication INSTANCE;
|
|
||||||
|
|
||||||
public static CommonsApplication getInstance() {
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AppLanguageLookUpTable languageLookUpTable;
|
|
||||||
|
|
||||||
public AppLanguageLookUpTable getLanguageLookUpTable() {
|
|
||||||
return languageLookUpTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ContributionDao contributionDao;
|
|
||||||
|
|
||||||
public static Boolean isPaused = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to declare and initialize various components and dependencies
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
|
|
||||||
INSTANCE = this;
|
|
||||||
ACRA.init(this);
|
|
||||||
|
|
||||||
ApplicationlessInjection
|
|
||||||
.getInstance(this)
|
|
||||||
.getCommonsApplicationComponent()
|
|
||||||
.inject(this);
|
|
||||||
|
|
||||||
initTimber();
|
|
||||||
|
|
||||||
if (!defaultPrefs.getBoolean("has_user_manually_removed_location")) {
|
|
||||||
Set<String> defaultExifTagsSet = defaultPrefs.getStringSet(Prefs.MANAGED_EXIF_TAGS);
|
|
||||||
if (null == defaultExifTagsSet) {
|
|
||||||
defaultExifTagsSet = new HashSet<>();
|
|
||||||
}
|
|
||||||
defaultExifTagsSet.add(getString(R.string.exif_tag_location));
|
|
||||||
defaultPrefs.putStringSet(Prefs.MANAGED_EXIF_TAGS, defaultExifTagsSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set DownsampleEnabled to True to downsample the image in case it's heavy
|
|
||||||
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(this)
|
|
||||||
.setNetworkFetcher(customOkHttpNetworkFetcher)
|
|
||||||
.setDownsampleEnabled(true)
|
|
||||||
.build();
|
|
||||||
try {
|
|
||||||
Fresco.initialize(this, config);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Timber.e(e);
|
|
||||||
// TODO: Remove when we're able to initialize Fresco in test builds.
|
|
||||||
}
|
|
||||||
|
|
||||||
createNotificationChannel(this);
|
|
||||||
|
|
||||||
languageLookUpTable = new AppLanguageLookUpTable(this);
|
|
||||||
|
|
||||||
// This handler will catch exceptions thrown from Observables after they are disposed,
|
|
||||||
// or from Observables that are (deliberately or not) missing an onError handler.
|
|
||||||
RxJavaPlugins.setErrorHandler(Functions.emptyConsumer());
|
|
||||||
|
|
||||||
// Fire progress callbacks for every 3% of uploaded content
|
|
||||||
System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plants debug and file logging tree. Timber lets you plant your own logging trees.
|
|
||||||
*/
|
|
||||||
private void initTimber() {
|
|
||||||
boolean isBeta = ConfigUtils.isBetaFlavour();
|
|
||||||
String logFileName =
|
|
||||||
isBeta ? "CommonsBetaAppLogs" : "CommonsAppLogs";
|
|
||||||
String logDirectory = LogUtils.getLogDirectory();
|
|
||||||
//Delete stale logs if they have exceeded the specified size
|
|
||||||
deleteStaleLogs(logFileName, logDirectory);
|
|
||||||
|
|
||||||
FileLoggingTree tree = new FileLoggingTree(
|
|
||||||
Log.VERBOSE,
|
|
||||||
logFileName,
|
|
||||||
logDirectory,
|
|
||||||
1000,
|
|
||||||
getFileLoggingThreadPool());
|
|
||||||
|
|
||||||
Timber.plant(tree);
|
|
||||||
Timber.plant(new Timber.DebugTree());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the logs zip file at the specified directory and file locations specified in the
|
|
||||||
* params
|
|
||||||
*
|
|
||||||
* @param logFileName
|
|
||||||
* @param logDirectory
|
|
||||||
*/
|
|
||||||
private void deleteStaleLogs(String logFileName, String logDirectory) {
|
|
||||||
try {
|
|
||||||
File file = new File(logDirectory + "/zip/" + logFileName + ".zip");
|
|
||||||
if (file.exists() && file.getTotalSpace() > 1000000) {// In Kbs
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Timber.e(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isRoboUnitTest() {
|
|
||||||
return "robolectric".equals(Build.FINGERPRINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ThreadPoolService getFileLoggingThreadPool() {
|
|
||||||
return new ThreadPoolService.Builder("file-logging-thread")
|
|
||||||
.setPriority(Process.THREAD_PRIORITY_LOWEST)
|
|
||||||
.setPoolSize(1)
|
|
||||||
.setExceptionHandler(new BackgroundPoolExceptionHandler())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void createNotificationChannel(@NonNull Context context) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
NotificationManager manager = (NotificationManager) context
|
|
||||||
.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
NotificationChannel channel = manager
|
|
||||||
.getNotificationChannel(NOTIFICATION_CHANNEL_ID_ALL);
|
|
||||||
if (channel == null) {
|
|
||||||
channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_ALL,
|
|
||||||
context.getString(R.string.notifications_channel_name_all),
|
|
||||||
NotificationManager.IMPORTANCE_DEFAULT);
|
|
||||||
manager.createNotificationChannel(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserAgent() {
|
|
||||||
return "Commons/" + ConfigUtils.getVersionNameWithSha(this)
|
|
||||||
+ " (https://mediawiki.org/wiki/Apps/Commons) Android/" + Build.VERSION.RELEASE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clears data of current application
|
|
||||||
*
|
|
||||||
* @param context Application context
|
|
||||||
* @param logoutListener Implementation of interface LogoutListener
|
|
||||||
*/
|
|
||||||
@SuppressLint("CheckResult")
|
|
||||||
public void clearApplicationData(Context context, LogoutListener logoutListener) {
|
|
||||||
File cacheDirectory = context.getCacheDir();
|
|
||||||
File applicationDirectory = new File(cacheDirectory.getParent());
|
|
||||||
if (applicationDirectory.exists()) {
|
|
||||||
String[] fileNames = applicationDirectory.list();
|
|
||||||
for (String fileName : fileNames) {
|
|
||||||
if (!fileName.equals("lib")) {
|
|
||||||
FileUtils.deleteFile(new File(applicationDirectory, fileName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionManager.logout()
|
|
||||||
.andThen(Completable.fromAction(() -> cookieJar.clear()))
|
|
||||||
.andThen(Completable.fromAction(() -> {
|
|
||||||
Timber.d("All accounts have been removed");
|
|
||||||
clearImageCache();
|
|
||||||
//TODO: fix preference manager
|
|
||||||
defaultPrefs.clearAll();
|
|
||||||
defaultPrefs.putBoolean("firstrun", false);
|
|
||||||
updateAllDatabases();
|
|
||||||
}
|
|
||||||
))
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(logoutListener::onLogoutComplete, Timber::e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all images cache held by Fresco
|
|
||||||
*/
|
|
||||||
private void clearImageCache() {
|
|
||||||
ImagePipeline imagePipeline = Fresco.getImagePipeline();
|
|
||||||
imagePipeline.clearCaches();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes all tables and re-creates them.
|
|
||||||
*/
|
|
||||||
private void updateAllDatabases() {
|
|
||||||
dbOpenHelper.getReadableDatabase().close();
|
|
||||||
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
|
|
||||||
|
|
||||||
CategoryDao.Table.onDelete(db);
|
|
||||||
dbOpenHelper.deleteTable(db,
|
|
||||||
CONTRIBUTIONS_TABLE);//Delete the contributions table in the existing db on older versions
|
|
||||||
|
|
||||||
try {
|
|
||||||
contributionDao.deleteAll();
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Timber.e(e);
|
|
||||||
}
|
|
||||||
BookmarkPicturesDao.Table.onDelete(db);
|
|
||||||
BookmarkLocationsDao.Table.onDelete(db);
|
|
||||||
Table.onDelete(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface used to get log-out events
|
|
||||||
*/
|
|
||||||
public interface LogoutListener {
|
|
||||||
|
|
||||||
void onLogoutComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This listener is responsible for handling post-logout actions, specifically invoking the LoginActivity
|
|
||||||
* with relevant intent parameters. It does not perform the actual logout operation.
|
|
||||||
*/
|
|
||||||
public static class BaseLogoutListener implements CommonsApplication.LogoutListener {
|
|
||||||
|
|
||||||
Context ctx;
|
|
||||||
String loginMessage, userName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for BaseLogoutListener.
|
|
||||||
*
|
|
||||||
* @param ctx Application context
|
|
||||||
*/
|
|
||||||
public BaseLogoutListener(final Context ctx) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for BaseLogoutListener
|
|
||||||
*
|
|
||||||
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
|
|
||||||
* @param loginMessage Message to be displayed on the login page
|
|
||||||
* @param loginUsername Username to be pre-filled on the login page
|
|
||||||
*/
|
|
||||||
public BaseLogoutListener(final Context ctx, final String loginMessage,
|
|
||||||
final String loginUsername) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.loginMessage = loginMessage;
|
|
||||||
this.userName = loginUsername;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLogoutComplete() {
|
|
||||||
Timber.d("Logout complete callback received.");
|
|
||||||
final Intent loginIntent = new Intent(ctx, LoginActivity.class);
|
|
||||||
loginIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
|
|
||||||
if (loginMessage != null) {
|
|
||||||
loginIntent.putExtra(loginMessageIntentKey, loginMessage);
|
|
||||||
}
|
|
||||||
if (userName != null) {
|
|
||||||
loginIntent.putExtra(loginUsernameIntentKey, userName);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.startActivity(loginIntent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is an extension of BaseLogoutListener, providing additional functionality or customization
|
|
||||||
* for the logout process. It includes specific actions to be taken during logout, such as handling redirection to the login screen.
|
|
||||||
*/
|
|
||||||
public static class ActivityLogoutListener extends BaseLogoutListener {
|
|
||||||
|
|
||||||
Activity activity;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for ActivityLogoutListener.
|
|
||||||
*
|
|
||||||
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
|
|
||||||
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
|
|
||||||
*/
|
|
||||||
public ActivityLogoutListener(final Activity activity, final Context ctx) {
|
|
||||||
super(ctx);
|
|
||||||
this.activity = activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for ActivityLogoutListener with additional parameters for the login screen.
|
|
||||||
*
|
|
||||||
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
|
|
||||||
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
|
|
||||||
* @param loginMessage Message to be displayed on the login page after logout.
|
|
||||||
* @param loginUsername Username to be pre-filled on the login page after logout.
|
|
||||||
*/
|
|
||||||
public ActivityLogoutListener(final Activity activity, final Context ctx,
|
|
||||||
final String loginMessage, final String loginUsername) {
|
|
||||||
super(activity, loginMessage, loginUsername);
|
|
||||||
this.activity = activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLogoutComplete() {
|
|
||||||
super.onLogoutComplete();
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
414
app/src/main/java/fr/free/nrw/commons/CommonsApplication.kt
Normal file
414
app/src/main/java/fr/free/nrw/commons/CommonsApplication.kt
Normal file
|
|
@ -0,0 +1,414 @@
|
||||||
|
package fr.free.nrw.commons
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.database.sqlite.SQLiteException
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Process
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.multidex.MultiDexApplication
|
||||||
|
import com.facebook.drawee.backends.pipeline.Fresco
|
||||||
|
import com.facebook.imagepipeline.core.ImagePipelineConfig
|
||||||
|
import fr.free.nrw.commons.auth.LoginActivity
|
||||||
|
import fr.free.nrw.commons.auth.SessionManager
|
||||||
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao
|
||||||
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
|
||||||
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao
|
||||||
|
import fr.free.nrw.commons.category.CategoryDao
|
||||||
|
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler
|
||||||
|
import fr.free.nrw.commons.concurrency.ThreadPoolService
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionDao
|
||||||
|
import fr.free.nrw.commons.data.DBOpenHelper
|
||||||
|
import fr.free.nrw.commons.di.ApplicationlessInjection
|
||||||
|
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||||
|
import fr.free.nrw.commons.language.AppLanguageLookUpTable
|
||||||
|
import fr.free.nrw.commons.logging.FileLoggingTree
|
||||||
|
import fr.free.nrw.commons.logging.LogUtils
|
||||||
|
import fr.free.nrw.commons.media.CustomOkHttpNetworkFetcher
|
||||||
|
import fr.free.nrw.commons.settings.Prefs
|
||||||
|
import fr.free.nrw.commons.upload.FileUtils
|
||||||
|
import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha
|
||||||
|
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
||||||
|
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar
|
||||||
|
import io.reactivex.Completable
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.internal.functions.Functions
|
||||||
|
import io.reactivex.plugins.RxJavaPlugins
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import org.acra.ACRA.init
|
||||||
|
import org.acra.ReportField
|
||||||
|
import org.acra.annotation.AcraCore
|
||||||
|
import org.acra.annotation.AcraDialog
|
||||||
|
import org.acra.annotation.AcraMailSender
|
||||||
|
import org.acra.data.StringFormat
|
||||||
|
import timber.log.Timber
|
||||||
|
import timber.log.Timber.DebugTree
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Named
|
||||||
|
|
||||||
|
@AcraCore(
|
||||||
|
buildConfigClass = BuildConfig::class,
|
||||||
|
resReportSendSuccessToast = R.string.crash_dialog_ok_toast,
|
||||||
|
reportFormat = StringFormat.KEY_VALUE_LIST,
|
||||||
|
reportContent = [ReportField.USER_COMMENT, ReportField.APP_VERSION_CODE, ReportField.APP_VERSION_NAME, ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL, ReportField.STACK_TRACE]
|
||||||
|
)
|
||||||
|
|
||||||
|
@AcraMailSender(mailTo = "commons-app-android-private@googlegroups.com", reportAsFile = false)
|
||||||
|
|
||||||
|
@AcraDialog(
|
||||||
|
resTheme = R.style.Theme_AppCompat_Dialog,
|
||||||
|
resText = R.string.crash_dialog_text,
|
||||||
|
resTitle = R.string.crash_dialog_title,
|
||||||
|
resCommentPrompt = R.string.crash_dialog_comment_prompt
|
||||||
|
)
|
||||||
|
|
||||||
|
class CommonsApplication : MultiDexApplication() {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var sessionManager: SessionManager
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var dbOpenHelper: DBOpenHelper
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@field:Named("default_preferences")
|
||||||
|
lateinit var defaultPrefs: JsonKvStore
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var cookieJar: CommonsCookieJar
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var customOkHttpNetworkFetcher: CustomOkHttpNetworkFetcher
|
||||||
|
|
||||||
|
var languageLookUpTable: AppLanguageLookUpTable? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var contributionDao: ContributionDao
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to declare and initialize various components and dependencies
|
||||||
|
*/
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
|
||||||
|
instance = this
|
||||||
|
init(this)
|
||||||
|
|
||||||
|
ApplicationlessInjection
|
||||||
|
.getInstance(this)
|
||||||
|
.commonsApplicationComponent
|
||||||
|
.inject(this)
|
||||||
|
|
||||||
|
initTimber()
|
||||||
|
|
||||||
|
if (!defaultPrefs.getBoolean("has_user_manually_removed_location")) {
|
||||||
|
var defaultExifTagsSet = defaultPrefs.getStringSet(Prefs.MANAGED_EXIF_TAGS)
|
||||||
|
if (null == defaultExifTagsSet) {
|
||||||
|
defaultExifTagsSet = HashSet()
|
||||||
|
}
|
||||||
|
defaultExifTagsSet.add(getString(R.string.exif_tag_location))
|
||||||
|
defaultPrefs.putStringSet(Prefs.MANAGED_EXIF_TAGS, defaultExifTagsSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set DownsampleEnabled to True to downsample the image in case it's heavy
|
||||||
|
val config = ImagePipelineConfig.newBuilder(this)
|
||||||
|
.setNetworkFetcher(customOkHttpNetworkFetcher)
|
||||||
|
.setDownsampleEnabled(true)
|
||||||
|
.build()
|
||||||
|
try {
|
||||||
|
Fresco.initialize(this, config)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
// TODO: Remove when we're able to initialize Fresco in test builds.
|
||||||
|
}
|
||||||
|
|
||||||
|
createNotificationChannel(this)
|
||||||
|
|
||||||
|
languageLookUpTable = AppLanguageLookUpTable(this)
|
||||||
|
|
||||||
|
// This handler will catch exceptions thrown from Observables after they are disposed,
|
||||||
|
// or from Observables that are (deliberately or not) missing an onError handler.
|
||||||
|
RxJavaPlugins.setErrorHandler(Functions.emptyConsumer())
|
||||||
|
|
||||||
|
// Fire progress callbacks for every 3% of uploaded content
|
||||||
|
System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plants debug and file logging tree. Timber lets you plant your own logging trees.
|
||||||
|
*/
|
||||||
|
private fun initTimber() {
|
||||||
|
val isBeta = isBetaFlavour
|
||||||
|
val logFileName =
|
||||||
|
if (isBeta) "CommonsBetaAppLogs" else "CommonsAppLogs"
|
||||||
|
val logDirectory = LogUtils.getLogDirectory()
|
||||||
|
//Delete stale logs if they have exceeded the specified size
|
||||||
|
deleteStaleLogs(logFileName, logDirectory)
|
||||||
|
|
||||||
|
val tree = FileLoggingTree(
|
||||||
|
Log.VERBOSE,
|
||||||
|
logFileName,
|
||||||
|
logDirectory,
|
||||||
|
1000,
|
||||||
|
fileLoggingThreadPool
|
||||||
|
)
|
||||||
|
|
||||||
|
Timber.plant(tree)
|
||||||
|
Timber.plant(DebugTree())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the logs zip file at the specified directory and file locations specified in the
|
||||||
|
* params
|
||||||
|
*
|
||||||
|
* @param logFileName
|
||||||
|
* @param logDirectory
|
||||||
|
*/
|
||||||
|
private fun deleteStaleLogs(logFileName: String, logDirectory: String) {
|
||||||
|
try {
|
||||||
|
val file = File("$logDirectory/zip/$logFileName.zip")
|
||||||
|
if (file.exists() && file.totalSpace > 1000000) { // In Kbs
|
||||||
|
file.delete()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val fileLoggingThreadPool: ThreadPoolService
|
||||||
|
get() = ThreadPoolService.Builder("file-logging-thread")
|
||||||
|
.setPriority(Process.THREAD_PRIORITY_LOWEST)
|
||||||
|
.setPoolSize(1)
|
||||||
|
.setExceptionHandler(BackgroundPoolExceptionHandler())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val userAgent: String
|
||||||
|
get() = ("Commons/" + this.getVersionNameWithSha()
|
||||||
|
+ " (https://mediawiki.org/wiki/Apps/Commons) Android/" + Build.VERSION.RELEASE)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clears data of current application
|
||||||
|
*
|
||||||
|
* @param context Application context
|
||||||
|
* @param logoutListener Implementation of interface LogoutListener
|
||||||
|
*/
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
fun clearApplicationData(context: Context, logoutListener: LogoutListener) {
|
||||||
|
val cacheDirectory = context.cacheDir
|
||||||
|
val applicationDirectory = File(cacheDirectory.parent)
|
||||||
|
if (applicationDirectory.exists()) {
|
||||||
|
val fileNames = applicationDirectory.list()
|
||||||
|
for (fileName in fileNames) {
|
||||||
|
if (fileName != "lib") {
|
||||||
|
FileUtils.deleteFile(File(applicationDirectory, fileName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionManager.logout()
|
||||||
|
.andThen(Completable.fromAction { cookieJar.clear() })
|
||||||
|
.andThen(Completable.fromAction {
|
||||||
|
Timber.d("All accounts have been removed")
|
||||||
|
clearImageCache()
|
||||||
|
//TODO: fix preference manager
|
||||||
|
defaultPrefs.clearAll()
|
||||||
|
defaultPrefs.putBoolean("firstrun", false)
|
||||||
|
updateAllDatabases()
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({ logoutListener.onLogoutComplete() }, { t: Throwable? -> Timber.e(t) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all images cache held by Fresco
|
||||||
|
*/
|
||||||
|
private fun clearImageCache() {
|
||||||
|
val imagePipeline = Fresco.getImagePipeline()
|
||||||
|
imagePipeline.clearCaches()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all tables and re-creates them.
|
||||||
|
*/
|
||||||
|
private fun updateAllDatabases() {
|
||||||
|
dbOpenHelper.readableDatabase.close()
|
||||||
|
val db = dbOpenHelper.writableDatabase
|
||||||
|
|
||||||
|
CategoryDao.Table.onDelete(db)
|
||||||
|
dbOpenHelper.deleteTable(
|
||||||
|
db,
|
||||||
|
DBOpenHelper.CONTRIBUTIONS_TABLE
|
||||||
|
) //Delete the contributions table in the existing db on older versions
|
||||||
|
|
||||||
|
try {
|
||||||
|
contributionDao.deleteAll()
|
||||||
|
} catch (e: SQLiteException) {
|
||||||
|
Timber.e(e)
|
||||||
|
}
|
||||||
|
BookmarkPicturesDao.Table.onDelete(db)
|
||||||
|
BookmarkLocationsDao.Table.onDelete(db)
|
||||||
|
BookmarkItemsDao.Table.onDelete(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used to get log-out events
|
||||||
|
*/
|
||||||
|
interface LogoutListener {
|
||||||
|
fun onLogoutComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener is responsible for handling post-logout actions, specifically invoking the LoginActivity
|
||||||
|
* with relevant intent parameters. It does not perform the actual logout operation.
|
||||||
|
*/
|
||||||
|
open class BaseLogoutListener : LogoutListener {
|
||||||
|
var ctx: Context
|
||||||
|
var loginMessage: String? = null
|
||||||
|
var userName: String? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for BaseLogoutListener.
|
||||||
|
*
|
||||||
|
* @param ctx Application context
|
||||||
|
*/
|
||||||
|
constructor(ctx: Context) {
|
||||||
|
this.ctx = ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for BaseLogoutListener
|
||||||
|
*
|
||||||
|
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
|
||||||
|
* @param loginMessage Message to be displayed on the login page
|
||||||
|
* @param loginUsername Username to be pre-filled on the login page
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
ctx: Context, loginMessage: String?,
|
||||||
|
loginUsername: String?
|
||||||
|
) {
|
||||||
|
this.ctx = ctx
|
||||||
|
this.loginMessage = loginMessage
|
||||||
|
this.userName = loginUsername
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLogoutComplete() {
|
||||||
|
Timber.d("Logout complete callback received.")
|
||||||
|
val loginIntent = Intent(ctx, LoginActivity::class.java)
|
||||||
|
loginIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
|
||||||
|
if (loginMessage != null) {
|
||||||
|
loginIntent.putExtra(LOGIN_MESSAGE_INTENT_KEY, loginMessage)
|
||||||
|
}
|
||||||
|
if (userName != null) {
|
||||||
|
loginIntent.putExtra(LOGIN_USERNAME_INTENT_KEY, userName)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.startActivity(loginIntent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is an extension of BaseLogoutListener, providing additional functionality or customization
|
||||||
|
* for the logout process. It includes specific actions to be taken during logout, such as handling redirection to the login screen.
|
||||||
|
*/
|
||||||
|
class ActivityLogoutListener : BaseLogoutListener {
|
||||||
|
var activity: Activity
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ActivityLogoutListener.
|
||||||
|
*
|
||||||
|
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
|
||||||
|
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
|
||||||
|
*/
|
||||||
|
constructor(activity: Activity, ctx: Context) : super(ctx) {
|
||||||
|
this.activity = activity
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ActivityLogoutListener with additional parameters for the login screen.
|
||||||
|
*
|
||||||
|
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
|
||||||
|
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
|
||||||
|
* @param loginMessage Message to be displayed on the login page after logout.
|
||||||
|
* @param loginUsername Username to be pre-filled on the login page after logout.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
activity: Activity, ctx: Context?,
|
||||||
|
loginMessage: String?, loginUsername: String?
|
||||||
|
) : super(activity, loginMessage, loginUsername) {
|
||||||
|
this.activity = activity
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLogoutComplete() {
|
||||||
|
super.onLogoutComplete()
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val LOGIN_MESSAGE_INTENT_KEY: String = "loginMessage"
|
||||||
|
const val LOGIN_USERNAME_INTENT_KEY: String = "loginUsername"
|
||||||
|
|
||||||
|
const val IS_LIMITED_CONNECTION_MODE_ENABLED: String = "is_limited_connection_mode_enabled"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants begin
|
||||||
|
*/
|
||||||
|
const val OPEN_APPLICATION_DETAIL_SETTINGS: Int = 1001
|
||||||
|
|
||||||
|
const val DEFAULT_EDIT_SUMMARY: String = "Uploaded using [[COM:MOA|Commons Mobile App]]"
|
||||||
|
|
||||||
|
const val FEEDBACK_EMAIL: String = "commons-app-android@googlegroups.com"
|
||||||
|
|
||||||
|
const val FEEDBACK_EMAIL_SUBJECT: String = "Commons Android App Feedback"
|
||||||
|
|
||||||
|
const val REPORT_EMAIL: String = "commons-app-android-private@googlegroups.com"
|
||||||
|
|
||||||
|
const val REPORT_EMAIL_SUBJECT: String = "Report a violation"
|
||||||
|
|
||||||
|
const val NOTIFICATION_CHANNEL_ID_ALL: String = "CommonsNotificationAll"
|
||||||
|
|
||||||
|
const val FEEDBACK_EMAIL_TEMPLATE_HEADER: String = "-- Technical information --"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants End
|
||||||
|
*/
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
lateinit var instance: CommonsApplication
|
||||||
|
private set
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
var isPaused: Boolean = false
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun createNotificationChannel(context: Context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val manager = context
|
||||||
|
.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
var channel = manager
|
||||||
|
.getNotificationChannel(NOTIFICATION_CHANNEL_ID_ALL)
|
||||||
|
if (channel == null) {
|
||||||
|
channel = NotificationChannel(
|
||||||
|
NOTIFICATION_CHANNEL_ID_ALL,
|
||||||
|
context.getString(R.string.notifications_channel_name_all),
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT
|
||||||
|
)
|
||||||
|
manager.createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ class ThanksClient
|
||||||
revisionId.toString(), // Rev
|
revisionId.toString(), // Rev
|
||||||
null, // Log
|
null, // Log
|
||||||
csrfTokenClient.getTokenBlocking(), // Token
|
csrfTokenClient.getTokenBlocking(), // Token
|
||||||
CommonsApplication.getInstance().userAgent, // Source
|
CommonsApplication.instance.userAgent, // Source
|
||||||
).map { mwThankPostResponse ->
|
).map { mwThankPostResponse ->
|
||||||
mwThankPostResponse.result?.success == 1
|
mwThankPostResponse.result?.success == 1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ import timber.log.Timber;
|
||||||
import static android.view.KeyEvent.KEYCODE_ENTER;
|
import static android.view.KeyEvent.KEYCODE_ENTER;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||||
import static fr.free.nrw.commons.CommonsApplication.loginMessageIntentKey;
|
import static fr.free.nrw.commons.CommonsApplication.LOGIN_MESSAGE_INTENT_KEY;
|
||||||
import static fr.free.nrw.commons.CommonsApplication.loginUsernameIntentKey;
|
import static fr.free.nrw.commons.CommonsApplication.LOGIN_USERNAME_INTENT_KEY;
|
||||||
|
|
||||||
public class LoginActivity extends AccountAuthenticatorActivity {
|
public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
|
|
||||||
|
|
@ -94,8 +94,8 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
||||||
binding = ActivityLoginBinding.inflate(getLayoutInflater());
|
binding = ActivityLoginBinding.inflate(getLayoutInflater());
|
||||||
setContentView(binding.getRoot());
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
String message = getIntent().getStringExtra(loginMessageIntentKey);
|
String message = getIntent().getStringExtra(LOGIN_MESSAGE_INTENT_KEY);
|
||||||
String username = getIntent().getStringExtra(loginUsernameIntentKey);
|
String username = getIntent().getStringExtra(LOGIN_USERNAME_INTENT_KEY);
|
||||||
|
|
||||||
binding.loginUsername.addTextChangedListener(textWatcher);
|
binding.loginUsername.addTextChangedListener(textWatcher);
|
||||||
binding.loginPassword.addTextChangedListener(textWatcher);
|
binding.loginPassword.addTextChangedListener(textWatcher);
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ class DescriptionEditActivity :
|
||||||
username,
|
username,
|
||||||
)
|
)
|
||||||
|
|
||||||
val commonsApplication = CommonsApplication.getInstance()
|
val commonsApplication = CommonsApplication.instance
|
||||||
if (commonsApplication != null) {
|
if (commonsApplication != null) {
|
||||||
commonsApplication.clearApplicationData(this, logoutListener)
|
commonsApplication.clearApplicationData(this, logoutListener)
|
||||||
}
|
}
|
||||||
|
|
@ -291,7 +291,7 @@ class DescriptionEditActivity :
|
||||||
username,
|
username,
|
||||||
)
|
)
|
||||||
|
|
||||||
val commonsApplication = CommonsApplication.getInstance()
|
val commonsApplication = CommonsApplication.instance
|
||||||
if (commonsApplication != null) {
|
if (commonsApplication != null) {
|
||||||
commonsApplication.clearApplicationData(this, logoutListener)
|
commonsApplication.clearApplicationData(this, logoutListener)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -438,7 +438,7 @@ class UploadWorker(
|
||||||
username,
|
username,
|
||||||
)
|
)
|
||||||
CommonsApplication
|
CommonsApplication
|
||||||
.getInstance()
|
.instance!!
|
||||||
.clearApplicationData(appContext, logoutListener)
|
.clearApplicationData(appContext, logoutListener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import com.nhaarman.mockitokotlin2.eq
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
import com.nhaarman.mockitokotlin2.verify
|
||||||
import fr.free.nrw.commons.CommonsApplication
|
import fr.free.nrw.commons.CommonsApplication
|
||||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockkObject
|
||||||
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
|
||||||
|
|
@ -29,7 +31,6 @@ class ThanksClientTest {
|
||||||
private lateinit var commonsApplication: CommonsApplication
|
private lateinit var commonsApplication: CommonsApplication
|
||||||
|
|
||||||
private lateinit var thanksClient: ThanksClient
|
private lateinit var thanksClient: ThanksClient
|
||||||
private lateinit var mockedApplication: MockedStatic<CommonsApplication>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* initial setup, test environment
|
* initial setup, test environment
|
||||||
|
|
@ -38,8 +39,8 @@ class ThanksClientTest {
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.openMocks(this)
|
MockitoAnnotations.openMocks(this)
|
||||||
mockedApplication = Mockito.mockStatic(CommonsApplication::class.java)
|
mockkObject(CommonsApplication)
|
||||||
`when`(CommonsApplication.getInstance()).thenReturn(commonsApplication)
|
every { CommonsApplication.instance }.returns(commonsApplication)
|
||||||
thanksClient = ThanksClient(csrfTokenClient, service)
|
thanksClient = ThanksClient(csrfTokenClient, service)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import android.os.Looper
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
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.TestCommonsApplication
|
import fr.free.nrw.commons.TestCommonsApplication
|
||||||
|
|
@ -19,6 +20,8 @@ import fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT
|
||||||
import fr.free.nrw.commons.settings.Prefs
|
import fr.free.nrw.commons.settings.Prefs
|
||||||
import fr.free.nrw.commons.upload.UploadMediaDetail
|
import fr.free.nrw.commons.upload.UploadMediaDetail
|
||||||
import fr.free.nrw.commons.upload.UploadMediaDetailAdapter
|
import fr.free.nrw.commons.upload.UploadMediaDetailAdapter
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockkObject
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
|
@ -54,6 +57,9 @@ class DescriptionEditActivityUnitTest {
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var rvDescriptions: RecyclerView
|
private lateinit var rvDescriptions: RecyclerView
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var commonsApplication: CommonsApplication
|
||||||
|
|
||||||
private lateinit var media: Media
|
private lateinit var media: Media
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
|
@ -82,6 +88,8 @@ class DescriptionEditActivityUnitTest {
|
||||||
bundle.putString(Prefs.DESCRIPTION_LANGUAGE, "bn")
|
bundle.putString(Prefs.DESCRIPTION_LANGUAGE, "bn")
|
||||||
bundle.putParcelable("media", media)
|
bundle.putParcelable("media", media)
|
||||||
intent.putExtras(bundle)
|
intent.putExtras(bundle)
|
||||||
|
mockkObject(CommonsApplication)
|
||||||
|
every { CommonsApplication.instance }.returns(commonsApplication)
|
||||||
activity =
|
activity =
|
||||||
Robolectric.buildActivity(DescriptionEditActivity::class.java, intent).create().get()
|
Robolectric.buildActivity(DescriptionEditActivity::class.java, intent).create().get()
|
||||||
binding = ActivityDescriptionEditBinding.inflate(LayoutInflater.from(activity))
|
binding = ActivityDescriptionEditBinding.inflate(LayoutInflater.from(activity))
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import com.nhaarman.mockitokotlin2.eq
|
||||||
import com.nhaarman.mockitokotlin2.mock
|
import com.nhaarman.mockitokotlin2.mock
|
||||||
import com.nhaarman.mockitokotlin2.times
|
import com.nhaarman.mockitokotlin2.times
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
import fr.free.nrw.commons.CommonsApplication.DEFAULT_EDIT_SUMMARY
|
import fr.free.nrw.commons.CommonsApplication.Companion.DEFAULT_EDIT_SUMMARY
|
||||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
||||||
import fr.free.nrw.commons.contributions.ChunkInfo
|
import fr.free.nrw.commons.contributions.ChunkInfo
|
||||||
import fr.free.nrw.commons.contributions.Contribution
|
import fr.free.nrw.commons.contributions.Contribution
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue