mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-30 14:23:55 +01:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
b3e0c10afd
81 changed files with 1385 additions and 1075 deletions
7
.idea/inspectionProfiles/Project_Default.xml
generated
7
.idea/inspectionProfiles/Project_Default.xml
generated
|
|
@ -1,16 +1,12 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="AndroidLintNewerVersionAvailable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ClassWithOnlyPrivateConstructors" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ConfusingElse" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="reportWhenNoStatementFollow" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ControlFlowStatementWithoutBraces" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="DefaultNotLastCaseInSwitch" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ExplicitThis" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="FieldMayBeFinal" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="REPORT_VARIABLES" value="true" />
|
||||
<option name="REPORT_PARAMETERS" value="true" />
|
||||
|
|
@ -25,13 +21,11 @@
|
|||
<option name="ignoreInMatchingInstanceof" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ProtectedMemberInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="RedundantFieldInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="RedundantImplements" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoreSerializable" value="false" />
|
||||
<option name="ignoreCloneable" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="RedundantMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="SimplifiableEqualsExpression" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TypeParameterExtendsFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnnecessarilyQualifiedStaticUsage" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
|
|
@ -47,6 +41,5 @@
|
|||
<inspection_tool class="UnnecessaryQualifierForThis" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnnecessarySuperConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnnecessaryThis" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnnecessaryToStringCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
|
|
@ -93,6 +93,7 @@ dependencies {
|
|||
implementation "org.jetbrains.kotlin:kotlin-reflect:$KOTLIN_VERSION"
|
||||
|
||||
//Mocking
|
||||
testImplementation("io.mockk:mockk:1.13.4")
|
||||
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
|
||||
testImplementation 'org.mockito:mockito-inline:5.2.0'
|
||||
testImplementation 'org.mockito:mockito-core:5.6.0'
|
||||
|
|
@ -226,7 +227,7 @@ android {
|
|||
excludes += ['META-INF/androidx.*']
|
||||
}
|
||||
resources {
|
||||
excludes += ['META-INF/androidx.*', 'META-INF/proguard/androidx-annotations.pro']
|
||||
excludes += ['META-INF/androidx.*', 'META-INF/proguard/androidx-annotations.pro', '/META-INF/LICENSE.md', '/META-INF/LICENSE-notice.md']
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -380,7 +381,7 @@ android {
|
|||
compose true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion '1.3.2'
|
||||
kotlinCompilerExtensionVersion '1.5.8'
|
||||
}
|
||||
namespace 'fr.free.nrw.commons'
|
||||
lint {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ class AboutActivityTest {
|
|||
fun testLaunchTranslate() {
|
||||
Espresso.onView(ViewMatchers.withId(R.id.about_translate)).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(
|
||||
CoreMatchers.allOf(
|
||||
IntentMatchers.hasAction(Intent.ACTION_VIEW),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import org.junit.Before
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.CoreMatchers.equalTo
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
|
|
@ -59,7 +61,7 @@ class WelcomeActivityTest {
|
|||
.perform(ViewActions.click())
|
||||
onView(withId(R.id.finishTutorialButton))
|
||||
.perform(ViewActions.click())
|
||||
assert(activityRule.activity.isDestroyed)
|
||||
assertThat(activityRule.activity.isDestroyed, equalTo(true))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,10 +71,10 @@ class WelcomeActivityTest {
|
|||
.perform(ViewActions.click())
|
||||
onView(withId(R.id.welcomePager))
|
||||
.perform(ViewActions.swipeLeft())
|
||||
assert(true)
|
||||
assertThat(true, equalTo(true))
|
||||
onView(withId(R.id.welcomePager))
|
||||
.perform(ViewActions.swipeRight())
|
||||
assert(true)
|
||||
assertThat(true, equalTo(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -84,13 +86,13 @@ class WelcomeActivityTest {
|
|||
.perform(ViewActions.swipeLeft())
|
||||
.perform(ViewActions.swipeLeft())
|
||||
.perform(ViewActions.swipeLeft())
|
||||
assert(true)
|
||||
assertThat(true, equalTo(true))
|
||||
onView(withId(R.id.welcomePager))
|
||||
.perform(ViewActions.swipeRight())
|
||||
.perform(ViewActions.swipeRight())
|
||||
.perform(ViewActions.swipeRight())
|
||||
.perform(ViewActions.swipeRight())
|
||||
assert(true)
|
||||
assertThat(true, equalTo(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -101,10 +103,10 @@ class WelcomeActivityTest {
|
|||
if (viewPager.currentItem == 3) {
|
||||
onView(withId(R.id.welcomePager))
|
||||
.perform(ViewActions.swipeLeft())
|
||||
assert(true)
|
||||
assertThat(true, equalTo(true))
|
||||
onView(withId(R.id.welcomePager))
|
||||
.perform(ViewActions.swipeRight())
|
||||
assert(false)
|
||||
assertThat(true, equalTo(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -119,7 +121,7 @@ class WelcomeActivityTest {
|
|||
.perform(ViewActions.click())
|
||||
onView(withId(R.id.finishTutorialButton))
|
||||
.perform(ViewActions.click())
|
||||
assert(activityRule.activity.isDestroyed)
|
||||
assertThat(activityRule.activity.isDestroyed, equalTo(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@
|
|||
android:maxSdkVersion="29"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<!-- Permission needed up to Android 5.1, see https://github.com/commons-app/apps-android-commons/pull/5863 -->
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS"
|
||||
android:maxSdkVersion="22"/>
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
|
|
|
|||
|
|
@ -1,433 +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 int OPEN_APPLICATION_DETAIL_SETTINGS = 1001;
|
||||
|
||||
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
|
||||
null, // Log
|
||||
csrfTokenClient.getTokenBlocking(), // Token
|
||||
CommonsApplication.getInstance().userAgent, // Source
|
||||
CommonsApplication.instance.userAgent, // Source
|
||||
).map { mwThankPostResponse ->
|
||||
mwThankPostResponse.result?.success == 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ import timber.log.Timber;
|
|||
import static android.view.KeyEvent.KEYCODE_ENTER;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||
import static fr.free.nrw.commons.CommonsApplication.loginMessageIntentKey;
|
||||
import static fr.free.nrw.commons.CommonsApplication.loginUsernameIntentKey;
|
||||
import static fr.free.nrw.commons.CommonsApplication.LOGIN_MESSAGE_INTENT_KEY;
|
||||
import static fr.free.nrw.commons.CommonsApplication.LOGIN_USERNAME_INTENT_KEY;
|
||||
|
||||
public class LoginActivity extends AccountAuthenticatorActivity {
|
||||
|
||||
|
|
@ -94,8 +94,8 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
|||
binding = ActivityLoginBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
String message = getIntent().getStringExtra(loginMessageIntentKey);
|
||||
String username = getIntent().getStringExtra(loginUsernameIntentKey);
|
||||
String message = getIntent().getStringExtra(LOGIN_MESSAGE_INTENT_KEY);
|
||||
String username = getIntent().getStringExtra(LOGIN_USERNAME_INTENT_KEY);
|
||||
|
||||
binding.loginUsername.addTextChangedListener(textWatcher);
|
||||
binding.loginPassword.addTextChangedListener(textWatcher);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.view.ViewGroup;
|
|||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
|
@ -33,6 +34,23 @@ public class BookmarkLocationsFragment extends DaggerFragment {
|
|||
@Inject BookmarkLocationsDao bookmarkLocationDao;
|
||||
@Inject CommonPlaceClickActions commonPlaceClickActions;
|
||||
private PlaceAdapter adapter;
|
||||
|
||||
private final ActivityResultLauncher<Intent> cameraPickLauncherForResult =
|
||||
registerForActivityResult(new StartActivityForResult(),
|
||||
result -> {
|
||||
contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> {
|
||||
contributionController.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
|
||||
});
|
||||
});
|
||||
|
||||
private final ActivityResultLauncher<Intent> galleryPickLauncherForResult =
|
||||
registerForActivityResult(new StartActivityForResult(),
|
||||
result -> {
|
||||
contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> {
|
||||
contributionController.onPictureReturnedFromGallery(result, requireActivity(), callbacks);
|
||||
});
|
||||
});
|
||||
|
||||
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
|
||||
@Override
|
||||
public void onActivityResult(Map<String, Boolean> result) {
|
||||
|
|
@ -45,7 +63,7 @@ public class BookmarkLocationsFragment extends DaggerFragment {
|
|||
contributionController.locationPermissionCallback.onLocationPermissionGranted();
|
||||
} else {
|
||||
if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
|
||||
contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher);
|
||||
contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
|
||||
} else {
|
||||
contributionController.locationPermissionCallback.onLocationPermissionDenied(getActivity().getString(R.string.in_app_camera_location_permission_denied));
|
||||
}
|
||||
|
|
@ -83,7 +101,9 @@ public class BookmarkLocationsFragment extends DaggerFragment {
|
|||
return Unit.INSTANCE;
|
||||
},
|
||||
commonPlaceClickActions,
|
||||
inAppCameraLocationPermissionLauncher
|
||||
inAppCameraLocationPermissionLauncher,
|
||||
galleryPickLauncherForResult,
|
||||
cameraPickLauncherForResult
|
||||
);
|
||||
binding.listView.setAdapter(adapter);
|
||||
}
|
||||
|
|
@ -109,11 +129,6 @@ public class BookmarkLocationsFragment extends DaggerFragment {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
contributionController.handleActivityResult(getActivity(), requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import android.app.Activity;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
|
@ -64,10 +65,11 @@ public class ContributionController {
|
|||
* Check for permissions and initiate camera click
|
||||
*/
|
||||
public void initiateCameraPick(Activity activity,
|
||||
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
|
||||
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher,
|
||||
ActivityResultLauncher<Intent> resultLauncher) {
|
||||
boolean useExtStorage = defaultKvStore.getBoolean("useExternalStorage", true);
|
||||
if (!useExtStorage) {
|
||||
initiateCameraUpload(activity);
|
||||
initiateCameraUpload(activity, resultLauncher);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -75,12 +77,12 @@ public class ContributionController {
|
|||
() -> {
|
||||
if (defaultKvStore.getBoolean("inAppCameraFirstRun")) {
|
||||
defaultKvStore.putBoolean("inAppCameraFirstRun", false);
|
||||
askUserToAllowLocationAccess(activity, inAppCameraLocationPermissionLauncher);
|
||||
askUserToAllowLocationAccess(activity, inAppCameraLocationPermissionLauncher, resultLauncher);
|
||||
} else if (defaultKvStore.getBoolean("inAppCameraLocationPref")) {
|
||||
createDialogsAndHandleLocationPermissions(activity,
|
||||
inAppCameraLocationPermissionLauncher);
|
||||
inAppCameraLocationPermissionLauncher, resultLauncher);
|
||||
} else {
|
||||
initiateCameraUpload(activity);
|
||||
initiateCameraUpload(activity, resultLauncher);
|
||||
}
|
||||
},
|
||||
R.string.storage_permission_title,
|
||||
|
|
@ -94,7 +96,8 @@ public class ContributionController {
|
|||
* @param activity
|
||||
*/
|
||||
private void createDialogsAndHandleLocationPermissions(Activity activity,
|
||||
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
|
||||
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher,
|
||||
ActivityResultLauncher<Intent> resultLauncher) {
|
||||
locationPermissionCallback = new LocationPermissionCallback() {
|
||||
@Override
|
||||
public void onLocationPermissionDenied(String toastMessage) {
|
||||
|
|
@ -103,16 +106,16 @@ public class ContributionController {
|
|||
toastMessage,
|
||||
Toast.LENGTH_LONG
|
||||
).show();
|
||||
initiateCameraUpload(activity);
|
||||
initiateCameraUpload(activity, resultLauncher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationPermissionGranted() {
|
||||
if (!locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) {
|
||||
showLocationOffDialog(activity, R.string.in_app_camera_needs_location,
|
||||
R.string.in_app_camera_location_unavailable);
|
||||
R.string.in_app_camera_location_unavailable, resultLauncher);
|
||||
} else {
|
||||
initiateCameraUpload(activity);
|
||||
initiateCameraUpload(activity, resultLauncher);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -135,9 +138,10 @@ public class ContributionController {
|
|||
* @param activity Activity reference
|
||||
* @param dialogTextResource Resource id of text to be shown in dialog
|
||||
* @param toastTextResource Resource id of text to be shown in toast
|
||||
* @param resultLauncher
|
||||
*/
|
||||
private void showLocationOffDialog(Activity activity, int dialogTextResource,
|
||||
int toastTextResource) {
|
||||
int toastTextResource, ActivityResultLauncher<Intent> resultLauncher) {
|
||||
DialogUtil
|
||||
.showAlertDialog(activity,
|
||||
activity.getString(R.string.ask_to_turn_location_on),
|
||||
|
|
@ -148,20 +152,21 @@ public class ContributionController {
|
|||
() -> {
|
||||
Toast.makeText(activity, activity.getString(toastTextResource),
|
||||
Toast.LENGTH_LONG).show();
|
||||
initiateCameraUpload(activity);
|
||||
initiateCameraUpload(activity, resultLauncher);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void handleShowRationaleFlowCameraLocation(Activity activity,
|
||||
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
|
||||
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher,
|
||||
ActivityResultLauncher<Intent> resultLauncher) {
|
||||
DialogUtil.showAlertDialog(activity, activity.getString(R.string.location_permission_title),
|
||||
activity.getString(R.string.in_app_camera_location_permission_rationale),
|
||||
activity.getString(android.R.string.ok),
|
||||
activity.getString(android.R.string.cancel),
|
||||
() -> {
|
||||
createDialogsAndHandleLocationPermissions(activity,
|
||||
inAppCameraLocationPermissionLauncher);
|
||||
inAppCameraLocationPermissionLauncher, resultLauncher);
|
||||
},
|
||||
() -> locationPermissionCallback.onLocationPermissionDenied(
|
||||
activity.getString(R.string.in_app_camera_location_permission_denied)),
|
||||
|
|
@ -181,7 +186,8 @@ public class ContributionController {
|
|||
* @param activity
|
||||
*/
|
||||
private void askUserToAllowLocationAccess(Activity activity,
|
||||
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
|
||||
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher,
|
||||
ActivityResultLauncher<Intent> resultLauncher) {
|
||||
DialogUtil.showAlertDialog(activity,
|
||||
activity.getString(R.string.in_app_camera_location_permission_title),
|
||||
activity.getString(R.string.in_app_camera_location_access_explanation),
|
||||
|
|
@ -190,12 +196,12 @@ public class ContributionController {
|
|||
() -> {
|
||||
defaultKvStore.putBoolean("inAppCameraLocationPref", true);
|
||||
createDialogsAndHandleLocationPermissions(activity,
|
||||
inAppCameraLocationPermissionLauncher);
|
||||
inAppCameraLocationPermissionLauncher, resultLauncher);
|
||||
},
|
||||
() -> {
|
||||
ViewUtil.showLongToast(activity, R.string.in_app_camera_location_permission_denied);
|
||||
defaultKvStore.putBoolean("inAppCameraLocationPref", false);
|
||||
initiateCameraUpload(activity);
|
||||
initiateCameraUpload(activity, resultLauncher);
|
||||
},
|
||||
null,
|
||||
true);
|
||||
|
|
@ -204,18 +210,18 @@ public class ContributionController {
|
|||
/**
|
||||
* Initiate gallery picker
|
||||
*/
|
||||
public void initiateGalleryPick(final Activity activity, final boolean allowMultipleUploads) {
|
||||
initiateGalleryUpload(activity, allowMultipleUploads);
|
||||
public void initiateGalleryPick(final Activity activity, ActivityResultLauncher<Intent> resultLauncher, final boolean allowMultipleUploads) {
|
||||
initiateGalleryUpload(activity, resultLauncher, allowMultipleUploads);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate gallery picker with permission
|
||||
*/
|
||||
public void initiateCustomGalleryPickWithPermission(final Activity activity) {
|
||||
public void initiateCustomGalleryPickWithPermission(final Activity activity, ActivityResultLauncher<Intent> resultLauncher) {
|
||||
setPickerConfiguration(activity, true);
|
||||
|
||||
PermissionUtils.checkPermissionsAndPerformAction(activity,
|
||||
() -> FilePicker.openCustomSelector(activity, 0),
|
||||
() -> FilePicker.openCustomSelector(activity, resultLauncher, 0),
|
||||
R.string.storage_permission_title,
|
||||
R.string.write_storage_permission_rationale,
|
||||
PermissionUtils.PERMISSIONS_STORAGE);
|
||||
|
|
@ -225,12 +231,10 @@ public class ContributionController {
|
|||
/**
|
||||
* Open chooser for gallery uploads
|
||||
*/
|
||||
private void initiateGalleryUpload(final Activity activity,
|
||||
private void initiateGalleryUpload(final Activity activity, ActivityResultLauncher<Intent> resultLauncher,
|
||||
final boolean allowMultipleUploads) {
|
||||
setPickerConfiguration(activity, allowMultipleUploads);
|
||||
boolean openDocumentIntentPreferred = defaultKvStore.getBoolean(
|
||||
"openDocumentPhotoPickerPref", true);
|
||||
FilePicker.openGallery(activity, 0, openDocumentIntentPreferred);
|
||||
FilePicker.openGallery(activity, resultLauncher, 0, isDocumentPhotoPickerPreferred());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -247,22 +251,43 @@ public class ContributionController {
|
|||
/**
|
||||
* Initiate camera upload by opening camera
|
||||
*/
|
||||
private void initiateCameraUpload(Activity activity) {
|
||||
private void initiateCameraUpload(Activity activity, ActivityResultLauncher<Intent> resultLauncher) {
|
||||
setPickerConfiguration(activity, false);
|
||||
if (defaultKvStore.getBoolean("inAppCameraLocationPref", false)) {
|
||||
locationBeforeImageCapture = locationManager.getLastLocation();
|
||||
}
|
||||
isInAppCameraUpload = true;
|
||||
FilePicker.openCameraForImage(activity, 0);
|
||||
FilePicker.openCameraForImage(activity, resultLauncher, 0);
|
||||
}
|
||||
|
||||
private boolean isDocumentPhotoPickerPreferred(){
|
||||
return defaultKvStore.getBoolean(
|
||||
"openDocumentPhotoPickerPref", true);
|
||||
}
|
||||
|
||||
public void onPictureReturnedFromGallery(ActivityResult result, Activity activity, FilePicker.Callbacks callbacks){
|
||||
|
||||
if(isDocumentPhotoPickerPreferred()){
|
||||
FilePicker.onPictureReturnedFromDocuments(result, activity, callbacks);
|
||||
} else {
|
||||
FilePicker.onPictureReturnedFromGallery(result, activity, callbacks);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPictureReturnedFromCustomSelector(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
FilePicker.onPictureReturnedFromCustomSelector(result, activity, callbacks);
|
||||
}
|
||||
|
||||
public void onPictureReturnedFromCamera(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
FilePicker.onPictureReturnedFromCamera(result, activity, callbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches callback for file picker.
|
||||
*/
|
||||
public void handleActivityResult(Activity activity, int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
FilePicker.handleActivityResult(requestCode, resultCode, data, activity,
|
||||
new DefaultCallback() {
|
||||
public void handleActivityResultWithCallback(Activity activity, FilePicker.HandleActivityResult handleActivityResult) {
|
||||
|
||||
handleActivityResult.onHandleActivityResult(new DefaultCallback() {
|
||||
|
||||
@Override
|
||||
public void onCanceled(final ImageSource source, final int type) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_
|
|||
|
||||
import android.Manifest.permission;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
|
@ -20,6 +21,7 @@ import android.widget.LinearLayout;
|
|||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
|
@ -96,6 +98,30 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
private int contributionsSize;
|
||||
private String userName;
|
||||
|
||||
private final ActivityResultLauncher<Intent> galleryPickLauncherForResult =
|
||||
registerForActivityResult(new StartActivityForResult(),
|
||||
result -> {
|
||||
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
|
||||
controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks);
|
||||
});
|
||||
});
|
||||
|
||||
private final ActivityResultLauncher<Intent> customSelectorLauncherForResult =
|
||||
registerForActivityResult(new StartActivityForResult(),
|
||||
result -> {
|
||||
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
|
||||
controller.onPictureReturnedFromCustomSelector(result, requireActivity(), callbacks);
|
||||
});
|
||||
});
|
||||
|
||||
private final ActivityResultLauncher<Intent> cameraPickLauncherForResult =
|
||||
registerForActivityResult(new StartActivityForResult(),
|
||||
result -> {
|
||||
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
|
||||
controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
|
||||
});
|
||||
});
|
||||
|
||||
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(
|
||||
new RequestMultiplePermissions(),
|
||||
new ActivityResultCallback<Map<String, Boolean>>() {
|
||||
|
|
@ -111,7 +137,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
} else {
|
||||
if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
|
||||
controller.handleShowRationaleFlowCameraLocation(getActivity(),
|
||||
inAppCameraLocationPermissionLauncher);
|
||||
inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
|
||||
} else {
|
||||
controller.locationPermissionCallback.onLocationPermissionDenied(
|
||||
getActivity().getString(
|
||||
|
|
@ -322,7 +348,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
private void setListeners() {
|
||||
binding.fabPlus.setOnClickListener(view -> animateFAB(isFabOpen));
|
||||
binding.fabCamera.setOnClickListener(view -> {
|
||||
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher);
|
||||
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
|
||||
animateFAB(isFabOpen);
|
||||
});
|
||||
binding.fabCamera.setOnLongClickListener(view -> {
|
||||
|
|
@ -330,7 +356,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
return true;
|
||||
});
|
||||
binding.fabGallery.setOnClickListener(view -> {
|
||||
controller.initiateGalleryPick(getActivity(), true);
|
||||
controller.initiateGalleryPick(getActivity(), galleryPickLauncherForResult, true);
|
||||
animateFAB(isFabOpen);
|
||||
});
|
||||
binding.fabGallery.setOnLongClickListener(view -> {
|
||||
|
|
@ -343,7 +369,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
* Launch Custom Selector.
|
||||
*/
|
||||
protected void launchCustomSelector() {
|
||||
controller.initiateCustomGalleryPickWithPermission(getActivity());
|
||||
controller.initiateCustomGalleryPickWithPermission(getActivity(), customSelectorLauncherForResult);
|
||||
animateFAB(isFabOpen);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -438,13 +438,6 @@ public class MainActivity extends BaseActivity
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
Timber.d(data != null ? data.toString() : "onActivityResult data is null");
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
controller.handleActivityResult(this, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
|
|
|||
|
|
@ -15,19 +15,19 @@ abstract class NotForUploadStatusDao {
|
|||
* Insert into Not For Upload status.
|
||||
*/
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insert(notForUploadStatus: NotForUploadStatus)
|
||||
abstract fun insert(notForUploadStatus: NotForUploadStatus)
|
||||
|
||||
/**
|
||||
* Delete Not For Upload status entry.
|
||||
*/
|
||||
@Delete
|
||||
abstract suspend fun delete(notForUploadStatus: NotForUploadStatus)
|
||||
abstract fun delete(notForUploadStatus: NotForUploadStatus)
|
||||
|
||||
/**
|
||||
* Query Not For Upload status with image sha1.
|
||||
*/
|
||||
@Query("SELECT * FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ")
|
||||
abstract suspend fun getFromImageSHA1(imageSHA1: String): NotForUploadStatus?
|
||||
abstract fun getFromImageSHA1(imageSHA1: String): NotForUploadStatus?
|
||||
|
||||
/**
|
||||
* Asynchronous image sha1 query.
|
||||
|
|
@ -38,7 +38,7 @@ abstract class NotForUploadStatusDao {
|
|||
* Deletion Not For Upload status with image sha1.
|
||||
*/
|
||||
@Query("DELETE FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ")
|
||||
abstract suspend fun deleteWithImageSHA1(imageSHA1: String)
|
||||
abstract fun deleteWithImageSHA1(imageSHA1: String)
|
||||
|
||||
/**
|
||||
* Asynchronous image sha1 deletion.
|
||||
|
|
@ -49,5 +49,5 @@ abstract class NotForUploadStatusDao {
|
|||
* Check whether the imageSHA1 is present in database
|
||||
*/
|
||||
@Query("SELECT COUNT() FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ")
|
||||
abstract suspend fun find(imageSHA1: String): Int
|
||||
abstract fun find(imageSHA1: String): Int
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,31 +17,31 @@ abstract class UploadedStatusDao {
|
|||
* Insert into uploaded status.
|
||||
*/
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insert(uploadedStatus: UploadedStatus)
|
||||
abstract fun insert(uploadedStatus: UploadedStatus)
|
||||
|
||||
/**
|
||||
* Update uploaded status entry.
|
||||
*/
|
||||
@Update
|
||||
abstract suspend fun update(uploadedStatus: UploadedStatus)
|
||||
abstract fun update(uploadedStatus: UploadedStatus)
|
||||
|
||||
/**
|
||||
* Delete uploaded status entry.
|
||||
*/
|
||||
@Delete
|
||||
abstract suspend fun delete(uploadedStatus: UploadedStatus)
|
||||
abstract fun delete(uploadedStatus: UploadedStatus)
|
||||
|
||||
/**
|
||||
* Query uploaded status with image sha1.
|
||||
*/
|
||||
@Query("SELECT * FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) ")
|
||||
abstract suspend fun getFromImageSHA1(imageSHA1: String): UploadedStatus?
|
||||
abstract fun getFromImageSHA1(imageSHA1: String): UploadedStatus?
|
||||
|
||||
/**
|
||||
* Query uploaded status with modified image sha1.
|
||||
*/
|
||||
@Query("SELECT * FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) ")
|
||||
abstract suspend fun getFromModifiedImageSHA1(modifiedImageSHA1: String): UploadedStatus?
|
||||
abstract fun getFromModifiedImageSHA1(modifiedImageSHA1: String): UploadedStatus?
|
||||
|
||||
/**
|
||||
* Asynchronous insert into uploaded status table.
|
||||
|
|
@ -55,7 +55,7 @@ abstract class UploadedStatusDao {
|
|||
* Check whether the imageSHA1 is present in database
|
||||
*/
|
||||
@Query("SELECT COUNT() FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) AND imageResult = (:imageResult) ")
|
||||
abstract suspend fun findByImageSHA1(
|
||||
abstract fun findByImageSHA1(
|
||||
imageSHA1: String,
|
||||
imageResult: Boolean,
|
||||
): Int
|
||||
|
|
@ -66,7 +66,7 @@ abstract class UploadedStatusDao {
|
|||
@Query(
|
||||
"SELECT COUNT() FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) AND modifiedImageResult = (:modifiedImageResult) ",
|
||||
)
|
||||
abstract suspend fun findByModifiedImageSHA1(
|
||||
abstract fun findByModifiedImageSHA1(
|
||||
modifiedImageSHA1: String,
|
||||
modifiedImageResult: Boolean,
|
||||
): Int
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import android.view.Window
|
|||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
|
@ -41,7 +43,6 @@ import fr.free.nrw.commons.R
|
|||
import fr.free.nrw.commons.customselector.database.NotForUploadStatus
|
||||
import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao
|
||||
import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants
|
||||
import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants.SHOULD_REFRESH
|
||||
import fr.free.nrw.commons.customselector.listeners.FolderClickListener
|
||||
import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
|
|
@ -147,6 +148,10 @@ class CustomSelectorActivity :
|
|||
|
||||
private var showPartialAccessIndicator by mutableStateOf(false)
|
||||
|
||||
private val startForResult = registerForActivityResult(StartActivityForResult()){ result ->
|
||||
onFullScreenDataReceived(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* onCreate Activity, sets theme, initialises the view model, setup view.
|
||||
*/
|
||||
|
|
@ -225,20 +230,12 @@ class CustomSelectorActivity :
|
|||
/**
|
||||
* When data will be send from full screen mode, it will be passed to fragment
|
||||
*/
|
||||
override fun onActivityResult(
|
||||
requestCode: Int,
|
||||
resultCode: Int,
|
||||
data: Intent?,
|
||||
) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == Constants.RequestCodes.RECEIVE_DATA_FROM_FULL_SCREEN_MODE &&
|
||||
resultCode == Activity.RESULT_OK
|
||||
) {
|
||||
private fun onFullScreenDataReceived(result: ActivityResult){
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
val selectedImages: ArrayList<Image> =
|
||||
data!!
|
||||
result.data!!
|
||||
.getParcelableArrayListExtra(CustomSelectorConstants.NEW_SELECTED_IMAGES)!!
|
||||
val shouldRefresh = data.getBooleanExtra(SHOULD_REFRESH, false)
|
||||
imageFragment?.passSelectedImages(selectedImages, shouldRefresh)
|
||||
viewModel?.selectedImages?.value = selectedImages
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -511,7 +508,7 @@ class CustomSelectorActivity :
|
|||
selectedImages,
|
||||
)
|
||||
intent.putExtra(CustomSelectorConstants.BUCKET_ID, bucketId)
|
||||
startActivityForResult(intent, Constants.RequestCodes.RECEIVE_DATA_FROM_FULL_SCREEN_MODE)
|
||||
startForResult.launch(intent)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -279,11 +279,17 @@ class ImageFragment :
|
|||
filteredImages = ImageHelper.filterImages(images, bucketId)
|
||||
allImages = ArrayList(filteredImages)
|
||||
imageAdapter.init(filteredImages, allImages, TreeMap(), uploadingContributions)
|
||||
viewModel?.selectedImages?.value?.let { selectedImages ->
|
||||
imageAdapter.setSelectedImages(selectedImages)
|
||||
}
|
||||
imageAdapter.notifyDataSetChanged()
|
||||
selectorRV?.let {
|
||||
it.visibility = View.VISIBLE
|
||||
lastItemId?.let { pos ->
|
||||
(it.layoutManager as GridLayoutManager)
|
||||
.scrollToPosition(ImageHelper.getIndexFromId(filteredImages, pos))
|
||||
if (switch?.isChecked == false) {
|
||||
lastItemId?.let { pos ->
|
||||
(it.layoutManager as GridLayoutManager)
|
||||
.scrollToPosition(ImageHelper.getIndexFromId(filteredImages, pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -382,14 +388,6 @@ class ImageFragment :
|
|||
selectedImages: ArrayList<Image>,
|
||||
shouldRefresh: Boolean,
|
||||
) {
|
||||
imageAdapter.setSelectedImages(selectedImages)
|
||||
|
||||
val uploadingContributions = getUploadingContributions()
|
||||
|
||||
if (!showAlreadyActionedImages && shouldRefresh) {
|
||||
imageAdapter.init(filteredImages, allImages, TreeMap(), uploadingContributions)
|
||||
imageAdapter.setSelectedImages(selectedImages)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import android.os.Bundle
|
|||
import android.os.Parcelable
|
||||
import android.speech.RecognizerIntent
|
||||
import android.view.View
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
|
|
@ -70,10 +72,14 @@ class DescriptionEditActivity :
|
|||
|
||||
private lateinit var binding: ActivityDescriptionEditBinding
|
||||
|
||||
private val requestCodeForVoiceInput = 1213
|
||||
|
||||
private var descriptionAndCaptions: ArrayList<UploadMediaDetail>? = null
|
||||
|
||||
private val voiceInputResultLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result: ActivityResult ->
|
||||
onVoiceInput(result)
|
||||
}
|
||||
|
||||
@Inject lateinit var descriptionEditHelper: DescriptionEditHelper
|
||||
|
||||
@Inject lateinit var sessionManager: SessionManager
|
||||
|
|
@ -115,6 +121,7 @@ class DescriptionEditActivity :
|
|||
savedLanguageValue,
|
||||
descriptionAndCaptions,
|
||||
recentLanguagesDao,
|
||||
voiceInputResultLauncher
|
||||
)
|
||||
uploadMediaDetailAdapter.setCallback { titleStringID: Int, messageStringId: Int ->
|
||||
showInfoAlert(
|
||||
|
|
@ -149,6 +156,15 @@ class DescriptionEditActivity :
|
|||
|
||||
override fun onPrimaryCaptionTextChange(isNotEmpty: Boolean) {}
|
||||
|
||||
private fun onVoiceInput(result: ActivityResult) {
|
||||
if (result.resultCode == RESULT_OK && result.data != null) {
|
||||
val resultData = result.data!!.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
|
||||
uploadMediaDetailAdapter.handleSpeechResult(resultData!![0])
|
||||
} else {
|
||||
Timber.e("Error %s", result.resultCode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new language item to RecyclerView
|
||||
*/
|
||||
|
|
@ -242,7 +258,7 @@ class DescriptionEditActivity :
|
|||
username,
|
||||
)
|
||||
|
||||
val commonsApplication = CommonsApplication.getInstance()
|
||||
val commonsApplication = CommonsApplication.instance
|
||||
if (commonsApplication != null) {
|
||||
commonsApplication.clearApplicationData(this, logoutListener)
|
||||
}
|
||||
|
|
@ -275,7 +291,7 @@ class DescriptionEditActivity :
|
|||
username,
|
||||
)
|
||||
|
||||
val commonsApplication = CommonsApplication.getInstance()
|
||||
val commonsApplication = CommonsApplication.instance
|
||||
if (commonsApplication != null) {
|
||||
commonsApplication.clearApplicationData(this, logoutListener)
|
||||
}
|
||||
|
|
@ -292,22 +308,6 @@ class DescriptionEditActivity :
|
|||
progressDialog!!.show()
|
||||
}
|
||||
|
||||
override fun onActivityResult(
|
||||
requestCode: Int,
|
||||
resultCode: Int,
|
||||
data: Intent?,
|
||||
) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == requestCodeForVoiceInput) {
|
||||
if (resultCode == RESULT_OK && data != null) {
|
||||
val result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
|
||||
uploadMediaDetailAdapter.handleSpeechResult(result!![0])
|
||||
} else {
|
||||
Timber.e("Error %s", resultCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class MediaConverter
|
|||
metadata.licenseShortName(),
|
||||
metadata.prefixedLicenseUrl,
|
||||
getAuthor(metadata),
|
||||
imageInfo.user,
|
||||
getAuthor(metadata),
|
||||
MediaDataExtractorUtil.extractCategoriesFromList(metadata.categories),
|
||||
metadata.latLng,
|
||||
entity.labels().mapValues { it.value.value() },
|
||||
|
|
|
|||
|
|
@ -4,21 +4,11 @@ public interface Constants {
|
|||
String DEFAULT_FOLDER_NAME = "CommonsContributions";
|
||||
|
||||
/**
|
||||
* Provides the request codes utilised by the FilePicker
|
||||
* Provides the request codes for permission handling
|
||||
*/
|
||||
interface RequestCodes {
|
||||
int LOCATION = 1;
|
||||
int STORAGE = 2;
|
||||
int FILE_PICKER_IMAGE_IDENTIFICATOR = 0b1101101100; //876
|
||||
int SOURCE_CHOOSER = 1 << 15;
|
||||
|
||||
int PICK_PICTURE_FROM_CUSTOM_SELECTOR = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 10);
|
||||
int PICK_PICTURE_FROM_DOCUMENTS = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 11);
|
||||
int PICK_PICTURE_FROM_GALLERY = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 12);
|
||||
int TAKE_PICTURE = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 13);
|
||||
int CAPTURE_VIDEO = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 14);
|
||||
|
||||
int RECEIVE_DATA_FROM_FULL_SCREEN_MODE = 1 << 9;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import android.content.pm.ResolveInfo;
|
|||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
|
@ -107,25 +109,25 @@ public class FilePicker implements Constants {
|
|||
*
|
||||
* @param type Custom type of your choice, which will be returned with the images
|
||||
*/
|
||||
public static void openGallery(Activity activity, int type, boolean openDocumentIntentPreferred) {
|
||||
public static void openGallery(Activity activity, ActivityResultLauncher<Intent> resultLauncher, int type, boolean openDocumentIntentPreferred) {
|
||||
Intent intent = createGalleryIntent(activity, type, openDocumentIntentPreferred);
|
||||
activity.startActivityForResult(intent, RequestCodes.PICK_PICTURE_FROM_GALLERY);
|
||||
resultLauncher.launch(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens Custom Selector
|
||||
*/
|
||||
public static void openCustomSelector(Activity activity, int type) {
|
||||
public static void openCustomSelector(Activity activity, ActivityResultLauncher<Intent> resultLauncher, int type) {
|
||||
Intent intent = createCustomSelectorIntent(activity, type);
|
||||
activity.startActivityForResult(intent, RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR);
|
||||
resultLauncher.launch(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the camera app to pick image clicked by user
|
||||
*/
|
||||
public static void openCameraForImage(Activity activity, int type) {
|
||||
public static void openCameraForImage(Activity activity, ActivityResultLauncher<Intent> resultLauncher, int type) {
|
||||
Intent intent = createCameraForImageIntent(activity, type);
|
||||
activity.startActivityForResult(intent, RequestCodes.TAKE_PICTURE);
|
||||
resultLauncher.launch(intent);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
@ -148,47 +150,6 @@ public class FilePicker implements Constants {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Any activity can use this method to attach their callback to the file picker
|
||||
*/
|
||||
public static void handleActivityResult(int requestCode, int resultCode, Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
boolean isHandledPickedFile = (requestCode & RequestCodes.FILE_PICKER_IMAGE_IDENTIFICATOR) > 0;
|
||||
if (isHandledPickedFile) {
|
||||
requestCode &= ~RequestCodes.SOURCE_CHOOSER;
|
||||
if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY ||
|
||||
requestCode == RequestCodes.TAKE_PICTURE ||
|
||||
requestCode == RequestCodes.CAPTURE_VIDEO ||
|
||||
requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS ||
|
||||
requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS && !isPhoto(data)) {
|
||||
onPictureReturnedFromDocuments(data, activity, callbacks);
|
||||
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY && !isPhoto(data)) {
|
||||
onPictureReturnedFromGallery(data, activity, callbacks);
|
||||
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) {
|
||||
onPictureReturnedFromCustomSelector(data, activity, callbacks);
|
||||
} else if (requestCode == RequestCodes.TAKE_PICTURE) {
|
||||
onPictureReturnedFromCamera(activity, callbacks);
|
||||
} else if (requestCode == RequestCodes.CAPTURE_VIDEO) {
|
||||
onVideoReturnedFromCamera(activity, callbacks);
|
||||
} else if (isPhoto(data)) {
|
||||
onPictureReturnedFromCamera(activity, callbacks);
|
||||
} else {
|
||||
onPictureReturnedFromDocuments(data, activity, callbacks);
|
||||
}
|
||||
} else {
|
||||
if (requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS) {
|
||||
callbacks.onCanceled(FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
|
||||
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY) {
|
||||
callbacks.onCanceled(FilePicker.ImageSource.GALLERY, restoreType(activity));
|
||||
} else {
|
||||
callbacks.onCanceled(FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<UploadableFile> handleExternalImagesPicked(Intent data, Activity activity) {
|
||||
try {
|
||||
return getFilesFromGalleryPictures(data, activity);
|
||||
|
|
@ -241,18 +202,22 @@ public class FilePicker implements Constants {
|
|||
return intent;
|
||||
}
|
||||
|
||||
private static void onPictureReturnedFromDocuments(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
try {
|
||||
Uri photoPath = data.getData();
|
||||
UploadableFile photoFile = PickedFiles.pickedExistingPicture(activity, photoPath);
|
||||
callbacks.onImagesPicked(singleFileList(photoFile), FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
|
||||
public static void onPictureReturnedFromDocuments(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
if(result.getResultCode() == Activity.RESULT_OK && !isPhoto(result.getData())){
|
||||
try {
|
||||
Uri photoPath = result.getData().getData();
|
||||
UploadableFile photoFile = PickedFiles.pickedExistingPicture(activity, photoPath);
|
||||
callbacks.onImagesPicked(singleFileList(photoFile), FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
|
||||
|
||||
if (configuration(activity).shouldCopyPickedImagesToPublicGalleryAppFolder()) {
|
||||
PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile));
|
||||
if (configuration(activity).shouldCopyPickedImagesToPublicGalleryAppFolder()) {
|
||||
PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbacks.onImagePickerError(e, FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbacks.onImagePickerError(e, FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
|
||||
} else {
|
||||
callbacks.onCanceled(FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -260,14 +225,18 @@ public class FilePicker implements Constants {
|
|||
* onPictureReturnedFromCustomSelector.
|
||||
* Retrieve and forward the images to upload wizard through callback.
|
||||
*/
|
||||
private static void onPictureReturnedFromCustomSelector(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
try {
|
||||
List<UploadableFile> files = getFilesFromCustomSelector(data, activity);
|
||||
callbacks.onImagesPicked(files, ImageSource.CUSTOM_SELECTOR, restoreType(activity));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbacks.onImagePickerError(e, ImageSource.CUSTOM_SELECTOR, restoreType(activity));
|
||||
}
|
||||
public static void onPictureReturnedFromCustomSelector(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
if(result.getResultCode() == Activity.RESULT_OK){
|
||||
try {
|
||||
List<UploadableFile> files = getFilesFromCustomSelector(result.getData(), activity);
|
||||
callbacks.onImagesPicked(files, ImageSource.CUSTOM_SELECTOR, restoreType(activity));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbacks.onImagePickerError(e, ImageSource.CUSTOM_SELECTOR, restoreType(activity));
|
||||
}
|
||||
} else {
|
||||
callbacks.onCanceled(ImageSource.CUSTOM_SELECTOR, restoreType(activity));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -290,13 +259,17 @@ public class FilePicker implements Constants {
|
|||
return files;
|
||||
}
|
||||
|
||||
private static void onPictureReturnedFromGallery(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
try {
|
||||
List<UploadableFile> files = getFilesFromGalleryPictures(data, activity);
|
||||
callbacks.onImagesPicked(files, FilePicker.ImageSource.GALLERY, restoreType(activity));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbacks.onImagePickerError(e, FilePicker.ImageSource.GALLERY, restoreType(activity));
|
||||
public static void onPictureReturnedFromGallery(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
if(result.getResultCode() == Activity.RESULT_OK && !isPhoto(result.getData())){
|
||||
try {
|
||||
List<UploadableFile> files = getFilesFromGalleryPictures(result.getData(), activity);
|
||||
callbacks.onImagesPicked(files, FilePicker.ImageSource.GALLERY, restoreType(activity));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbacks.onImagePickerError(e, FilePicker.ImageSource.GALLERY, restoreType(activity));
|
||||
}
|
||||
} else{
|
||||
callbacks.onCanceled(FilePicker.ImageSource.GALLERY, restoreType(activity));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -322,69 +295,40 @@ public class FilePicker implements Constants {
|
|||
return files;
|
||||
}
|
||||
|
||||
private static void onPictureReturnedFromCamera(Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
try {
|
||||
String lastImageUri = PreferenceManager.getDefaultSharedPreferences(activity).getString(KEY_PHOTO_URI, null);
|
||||
if (!TextUtils.isEmpty(lastImageUri)) {
|
||||
revokeWritePermission(activity, Uri.parse(lastImageUri));
|
||||
}
|
||||
|
||||
UploadableFile photoFile = FilePicker.takenCameraPicture(activity);
|
||||
List<UploadableFile> files = new ArrayList<>();
|
||||
files.add(photoFile);
|
||||
|
||||
if (photoFile == null) {
|
||||
Exception e = new IllegalStateException("Unable to get the picture returned from camera");
|
||||
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
|
||||
} else {
|
||||
if (configuration(activity).shouldCopyTakenPhotosToPublicGalleryAppFolder()) {
|
||||
PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile));
|
||||
public static void onPictureReturnedFromCamera(ActivityResult activityResult, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
if(activityResult.getResultCode() == Activity.RESULT_OK){
|
||||
try {
|
||||
String lastImageUri = PreferenceManager.getDefaultSharedPreferences(activity).getString(KEY_PHOTO_URI, null);
|
||||
if (!TextUtils.isEmpty(lastImageUri)) {
|
||||
revokeWritePermission(activity, Uri.parse(lastImageUri));
|
||||
}
|
||||
|
||||
callbacks.onImagesPicked(files, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
|
||||
}
|
||||
UploadableFile photoFile = FilePicker.takenCameraPicture(activity);
|
||||
List<UploadableFile> files = new ArrayList<>();
|
||||
files.add(photoFile);
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
if (photoFile == null) {
|
||||
Exception e = new IllegalStateException("Unable to get the picture returned from camera");
|
||||
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
|
||||
} else {
|
||||
if (configuration(activity).shouldCopyTakenPhotosToPublicGalleryAppFolder()) {
|
||||
PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile));
|
||||
}
|
||||
|
||||
callbacks.onImagesPicked(files, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
|
||||
}
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
.edit()
|
||||
.remove(KEY_LAST_CAMERA_PHOTO)
|
||||
.remove(KEY_PHOTO_URI)
|
||||
.apply();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
|
||||
}
|
||||
}
|
||||
|
||||
private static void onVideoReturnedFromCamera(Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||
try {
|
||||
String lastVideoUri = PreferenceManager.getDefaultSharedPreferences(activity).getString(KEY_VIDEO_URI, null);
|
||||
if (!TextUtils.isEmpty(lastVideoUri)) {
|
||||
revokeWritePermission(activity, Uri.parse(lastVideoUri));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
|
||||
}
|
||||
|
||||
UploadableFile photoFile = FilePicker.takenCameraVideo(activity);
|
||||
List<UploadableFile> files = new ArrayList<>();
|
||||
files.add(photoFile);
|
||||
|
||||
if (photoFile == null) {
|
||||
Exception e = new IllegalStateException("Unable to get the video returned from camera");
|
||||
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_VIDEO, restoreType(activity));
|
||||
} else {
|
||||
if (configuration(activity).shouldCopyTakenPhotosToPublicGalleryAppFolder()) {
|
||||
PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile));
|
||||
}
|
||||
|
||||
callbacks.onImagesPicked(files, FilePicker.ImageSource.CAMERA_VIDEO, restoreType(activity));
|
||||
}
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
.edit()
|
||||
.remove(KEY_LAST_CAMERA_VIDEO)
|
||||
.remove(KEY_VIDEO_URI)
|
||||
.apply();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_VIDEO, restoreType(activity));
|
||||
} else {
|
||||
callbacks.onCanceled(FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -404,4 +348,8 @@ public class FilePicker implements Constants {
|
|||
|
||||
void onCanceled(FilePicker.ImageSource source, int type);
|
||||
}
|
||||
|
||||
public interface HandleActivityResult{
|
||||
void onHandleActivityResult(FilePicker.Callbacks callbacks);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
package fr.free.nrw.commons.media;
|
||||
|
||||
import static android.app.Activity.RESULT_CANCELED;
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_NEEDING_CATEGORIES;
|
||||
import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_UNCATEGORISED;
|
||||
import static fr.free.nrw.commons.description.EditDescriptionConstants.LIST_OF_DESCRIPTION_AND_CAPTION;
|
||||
import static fr.free.nrw.commons.description.EditDescriptionConstants.UPDATED_WIKITEXT;
|
||||
import static fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT;
|
||||
import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_LOCATION;
|
||||
import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources;
|
||||
|
|
@ -112,8 +109,6 @@ import timber.log.Timber;
|
|||
public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
||||
CategoryEditHelper.Callback {
|
||||
|
||||
private static final int REQUEST_CODE = 1001;
|
||||
private static final int REQUEST_CODE_EDIT_DESCRIPTION = 1002;
|
||||
private static final String IMAGE_BACKGROUND_COLOR = "image_background_color";
|
||||
static final int DEFAULT_IMAGE_BACKGROUND_COLOR = 0;
|
||||
|
||||
|
|
@ -1065,81 +1060,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
return captionList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result from another activity and act accordingly.
|
||||
* @param requestCode
|
||||
* @param resultCode
|
||||
* @param data
|
||||
*/
|
||||
@Override
|
||||
public void onActivityResult(final int requestCode, final int resultCode,
|
||||
@Nullable final Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (requestCode == REQUEST_CODE_EDIT_DESCRIPTION && resultCode == RESULT_OK) {
|
||||
final String updatedWikiText = data.getStringExtra(UPDATED_WIKITEXT);
|
||||
|
||||
try {
|
||||
compositeDisposable.add(descriptionEditHelper.addDescription(getContext(), media,
|
||||
updatedWikiText)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(s -> {
|
||||
Timber.d("Descriptions are added.");
|
||||
}));
|
||||
} catch (Exception e) {
|
||||
if (e.getLocalizedMessage().equals(CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE)) {
|
||||
final String username = sessionManager.getUserName();
|
||||
final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener(
|
||||
getActivity(),
|
||||
requireActivity().getString(R.string.invalid_login_message),
|
||||
username
|
||||
);
|
||||
|
||||
CommonsApplication.getInstance().clearApplicationData(
|
||||
requireActivity(), logoutListener);
|
||||
}
|
||||
}
|
||||
|
||||
final ArrayList<UploadMediaDetail> uploadMediaDetails
|
||||
= data.getParcelableArrayListExtra(LIST_OF_DESCRIPTION_AND_CAPTION);
|
||||
|
||||
LinkedHashMap<String, String> updatedCaptions = new LinkedHashMap<>();
|
||||
for (UploadMediaDetail mediaDetail:
|
||||
uploadMediaDetails) {
|
||||
try {
|
||||
compositeDisposable.add(descriptionEditHelper.addCaption(getContext(), media,
|
||||
mediaDetail.getLanguageCode(), mediaDetail.getCaptionText())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(s -> {
|
||||
updateCaptions(mediaDetail, updatedCaptions);
|
||||
Timber.d("Caption is added.");
|
||||
}));
|
||||
|
||||
} catch (Exception e) {
|
||||
if (e.getLocalizedMessage().equals(CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE)) {
|
||||
final String username = sessionManager.getUserName();
|
||||
final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener(
|
||||
getActivity(),
|
||||
requireActivity().getString(R.string.invalid_login_message),
|
||||
username
|
||||
);
|
||||
|
||||
CommonsApplication.getInstance().clearApplicationData(
|
||||
requireActivity(), logoutListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.progressBarEdit.setVisibility(GONE);
|
||||
binding.descriptionEdit.setVisibility(VISIBLE);
|
||||
|
||||
} else if (requestCode == REQUEST_CODE_EDIT_DESCRIPTION && resultCode == RESULT_CANCELED) {
|
||||
binding.progressBarEdit.setVisibility(GONE);
|
||||
binding.descriptionEdit.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds caption to the map and updates captions
|
||||
* @param mediaDetail UploadMediaDetail
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package fr.free.nrw.commons.nearby
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
|
|
@ -17,9 +18,9 @@ import fr.free.nrw.commons.databinding.ItemPlaceBinding
|
|||
fun placeAdapterDelegate(
|
||||
bookmarkLocationDao: BookmarkLocationsDao,
|
||||
onItemClick: ((Place) -> Unit)? = null,
|
||||
onCameraClicked: (Place, ActivityResultLauncher<Array<String>>) -> Unit,
|
||||
onCameraClicked: (Place, ActivityResultLauncher<Array<String>>, ActivityResultLauncher<Intent>) -> Unit,
|
||||
onCameraLongPressed: () -> Boolean,
|
||||
onGalleryClicked: (Place) -> Unit,
|
||||
onGalleryClicked: (Place, ActivityResultLauncher<Intent>) -> Unit,
|
||||
onGalleryLongPressed: () -> Boolean,
|
||||
onBookmarkClicked: (Place, Boolean) -> Unit,
|
||||
onBookmarkLongPressed: () -> Boolean,
|
||||
|
|
@ -28,6 +29,8 @@ fun placeAdapterDelegate(
|
|||
onDirectionsClicked: (Place) -> Unit,
|
||||
onDirectionsLongPressed: () -> Boolean,
|
||||
inAppCameraLocationPermissionLauncher: ActivityResultLauncher<Array<String>>,
|
||||
cameraPickLauncherForResult: ActivityResultLauncher<Intent>,
|
||||
galleryPickLauncherForResult: ActivityResultLauncher<Intent>
|
||||
) = adapterDelegateViewBinding<Place, Place, ItemPlaceBinding>({ layoutInflater, parent ->
|
||||
ItemPlaceBinding.inflate(layoutInflater, parent, false)
|
||||
}) {
|
||||
|
|
@ -44,10 +47,10 @@ fun placeAdapterDelegate(
|
|||
onItemClick?.invoke(item)
|
||||
}
|
||||
}
|
||||
nearbyButtonLayout.cameraButton.setOnClickListener { onCameraClicked(item, inAppCameraLocationPermissionLauncher) }
|
||||
nearbyButtonLayout.cameraButton.setOnClickListener { onCameraClicked(item, inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult) }
|
||||
nearbyButtonLayout.cameraButton.setOnLongClickListener { onCameraLongPressed() }
|
||||
|
||||
nearbyButtonLayout.galleryButton.setOnClickListener { onGalleryClicked(item) }
|
||||
nearbyButtonLayout.galleryButton.setOnClickListener { onGalleryClicked(item, galleryPickLauncherForResult) }
|
||||
nearbyButtonLayout.galleryButton.setOnLongClickListener { onGalleryLongPressed() }
|
||||
bookmarkButtonImage.setOnClickListener {
|
||||
val isBookmarked = bookmarkLocationDao.updateBookmarkLocation(item)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import androidx.room.Dao;
|
|||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import io.reactivex.Completable;
|
||||
|
||||
/**
|
||||
|
|
@ -42,4 +41,23 @@ public abstract class PlaceDao {
|
|||
saveSynchronous(place);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all Place objects from the database.
|
||||
*
|
||||
* @return A Completable that completes once the deletion operation is done.
|
||||
*/
|
||||
@Query("DELETE FROM place")
|
||||
public abstract void deleteAllSynchronous();
|
||||
|
||||
/**
|
||||
* Deletes all Place objects from the database.
|
||||
*
|
||||
*/
|
||||
public Completable deleteAll() {
|
||||
return Completable
|
||||
.fromAction(() -> {
|
||||
deleteAllSynchronous();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import io.reactivex.Completable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
|
@ -36,4 +35,8 @@ public class PlacesLocalDataSource {
|
|||
public Completable savePlace(Place place) {
|
||||
return placeDao.save(place);
|
||||
}
|
||||
|
||||
public Completable clearCache() {
|
||||
return placeDao.deleteAll();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package fr.free.nrw.commons.nearby;
|
|||
import fr.free.nrw.commons.contributions.Contribution;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
|
|
@ -38,4 +39,13 @@ public class PlacesRepository {
|
|||
return localDataSource.fetchPlace(entityID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the Nearby cache on an IO thread.
|
||||
*
|
||||
* @return A Completable that completes once the cache has been successfully cleared.
|
||||
*/
|
||||
public Completable clearCache() {
|
||||
return localDataSource.clearCache()
|
||||
.subscribeOn(Schedulers.io()); // Ensure it runs on IO thread
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,14 +28,14 @@ class CommonPlaceClickActions
|
|||
private val activity: Activity,
|
||||
private val contributionController: ContributionController,
|
||||
) {
|
||||
fun onCameraClicked(): (Place, ActivityResultLauncher<Array<String>>) -> Unit =
|
||||
{ place, launcher ->
|
||||
fun onCameraClicked(): (Place, ActivityResultLauncher<Array<String>>, ActivityResultLauncher<Intent>) -> Unit =
|
||||
{ place, launcher, resultLauncher ->
|
||||
if (applicationKvStore.getBoolean("login_skipped", false)) {
|
||||
showLoginDialog()
|
||||
} else {
|
||||
Timber.d("Camera button tapped. Image title: ${place.getName()}Image desc: ${place.longDescription}")
|
||||
storeSharedPrefs(place)
|
||||
contributionController.initiateCameraPick(activity, launcher)
|
||||
contributionController.initiateCameraPick(activity, launcher, resultLauncher)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,14 +72,14 @@ class CommonPlaceClickActions
|
|||
true
|
||||
}
|
||||
|
||||
fun onGalleryClicked(): (Place) -> Unit =
|
||||
{
|
||||
fun onGalleryClicked(): (Place, ActivityResultLauncher<Intent>) -> Unit =
|
||||
{place, galleryPickLauncherForResult ->
|
||||
if (applicationKvStore.getBoolean("login_skipped", false)) {
|
||||
showLoginDialog()
|
||||
} else {
|
||||
Timber.d("Gallery button tapped. Image title: ${it.getName()}Image desc: ${it.getLongDescription()}")
|
||||
storeSharedPrefs(it)
|
||||
contributionController.initiateGalleryPick(activity, false)
|
||||
Timber.d("Gallery button tapped. Image title: ${place.getName()}Image desc: ${place.getLongDescription()}")
|
||||
storeSharedPrefs(place)
|
||||
contributionController.initiateGalleryPick(activity, galleryPickLauncherForResult, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,15 +43,18 @@ import android.view.ViewGroup;
|
|||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog.Builder;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
|
|
@ -105,6 +108,7 @@ import fr.free.nrw.commons.utils.NetworkUtils;
|
|||
import fr.free.nrw.commons.utils.SystemThemeUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListener;
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
|
@ -218,9 +222,36 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
private LatLng updatedLatLng;
|
||||
private boolean searchable;
|
||||
|
||||
private ConstraintLayout nearbyLegend;
|
||||
|
||||
private GridLayoutManager gridLayoutManager;
|
||||
private List<BottomSheetItem> dataList;
|
||||
private BottomSheetAdapter bottomSheetAdapter;
|
||||
|
||||
private final ActivityResultLauncher<Intent> galleryPickLauncherForResult =
|
||||
registerForActivityResult(new StartActivityForResult(),
|
||||
result -> {
|
||||
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
|
||||
controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks);
|
||||
});
|
||||
});
|
||||
|
||||
private final ActivityResultLauncher<Intent> customSelectorLauncherForResult =
|
||||
registerForActivityResult(new StartActivityForResult(),
|
||||
result -> {
|
||||
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
|
||||
controller.onPictureReturnedFromCustomSelector(result, requireActivity(), callbacks);
|
||||
});
|
||||
});
|
||||
|
||||
private final ActivityResultLauncher<Intent> cameraPickLauncherForResult =
|
||||
registerForActivityResult(new StartActivityForResult(),
|
||||
result -> {
|
||||
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
|
||||
controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
|
||||
});
|
||||
});
|
||||
|
||||
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(
|
||||
new RequestMultiplePermissions(),
|
||||
new ActivityResultCallback<Map<String, Boolean>>() {
|
||||
|
|
@ -236,7 +267,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
} else {
|
||||
if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
|
||||
controller.handleShowRationaleFlowCameraLocation(getActivity(),
|
||||
inAppCameraLocationPermissionLauncher);
|
||||
inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
|
||||
} else {
|
||||
controller.locationPermissionCallback.onLocationPermissionDenied(
|
||||
getActivity().getString(
|
||||
|
|
@ -302,6 +333,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
progressDialog.setCancelable(false);
|
||||
progressDialog.setMessage("Saving in progress...");
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
// Inflate the layout for this fragment
|
||||
return view;
|
||||
|
||||
|
|
@ -311,9 +343,21 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
public void onCreateOptionsMenu(@NonNull final Menu menu,
|
||||
@NonNull final MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.nearby_fragment_menu, menu);
|
||||
MenuItem refreshButton = menu.findItem(R.id.item_refresh);
|
||||
MenuItem listMenu = menu.findItem(R.id.list_sheet);
|
||||
MenuItem saveAsGPXButton = menu.findItem(R.id.list_item_gpx);
|
||||
MenuItem saveAsKMLButton = menu.findItem(R.id.list_item_kml);
|
||||
refreshButton.setOnMenuItemClickListener(new OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
try {
|
||||
emptyCache();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
listMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
|
|
@ -362,6 +406,16 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
}
|
||||
locationPermissionsHelper = new LocationPermissionsHelper(getActivity(), locationManager,
|
||||
this);
|
||||
|
||||
// Set up the floating activity button to toggle the visibility of the legend
|
||||
binding.fabLegend.setOnClickListener(v -> {
|
||||
if (binding.nearbyLegendLayout.getRoot().getVisibility() == View.VISIBLE) {
|
||||
binding.nearbyLegendLayout.getRoot().setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.nearbyLegendLayout.getRoot().setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
presenter.attachView(this);
|
||||
isPermissionDenied = false;
|
||||
recenterToUserLocation = false;
|
||||
|
|
@ -555,7 +609,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
return Unit.INSTANCE;
|
||||
},
|
||||
commonPlaceClickActions,
|
||||
inAppCameraLocationPermissionLauncher
|
||||
inAppCameraLocationPermissionLauncher,
|
||||
galleryPickLauncherForResult,
|
||||
cameraPickLauncherForResult
|
||||
);
|
||||
binding.bottomSheetNearby.rvNearbyList.setAdapter(adapter);
|
||||
}
|
||||
|
|
@ -1115,6 +1171,48 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the Nearby map
|
||||
* Clears all location markers, refreshes them, reinserts them into the map.
|
||||
*
|
||||
*/
|
||||
private void reloadMap() {
|
||||
clearAllMarkers(); // Clear the list of markers
|
||||
binding.map.getController().setZoom(ZOOM_LEVEL); // Reset the zoom level
|
||||
binding.map.getController().setCenter(lastMapFocus); // Recenter the focus
|
||||
if (locationPermissionsHelper.checkLocationPermission(getActivity())) {
|
||||
locationPermissionGranted(); // Reload map with user's location
|
||||
} else {
|
||||
startMapWithoutPermission(); // Reload map without user's location
|
||||
}
|
||||
binding.map.invalidate(); // Invalidate the map
|
||||
presenter.updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED); // Restart the map
|
||||
Timber.d("Reloaded Map Successfully");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears the Nearby local cache and then calls for the map to be reloaded
|
||||
*
|
||||
*/
|
||||
private void emptyCache() {
|
||||
// reload the map once the cache is cleared
|
||||
compositeDisposable.add(
|
||||
placesRepository.clearCache()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.andThen(Completable.fromAction(this::reloadMap))
|
||||
.subscribe(
|
||||
() -> {
|
||||
Timber.d("Nearby Cache cleared successfully.");
|
||||
},
|
||||
throwable -> {
|
||||
Timber.e(throwable, "Failed to clear the Nearby Cache");
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void savePlacesAsKML() {
|
||||
final Observable<String> savePlacesObservable = Observable
|
||||
.fromCallable(() -> nearbyController
|
||||
|
|
@ -2186,7 +2284,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
if (binding.fabCamera.isShown()) {
|
||||
Timber.d("Camera button tapped. Place: %s", selectedPlace.toString());
|
||||
storeSharedPrefs(selectedPlace);
|
||||
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher);
|
||||
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -2195,6 +2293,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
Timber.d("Gallery button tapped. Place: %s", selectedPlace.toString());
|
||||
storeSharedPrefs(selectedPlace);
|
||||
controller.initiateGalleryPick(getActivity(),
|
||||
galleryPickLauncherForResult,
|
||||
false);
|
||||
}
|
||||
});
|
||||
|
|
@ -2203,7 +2302,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
if (binding.fabCustomGallery.isShown()) {
|
||||
Timber.d("Gallery button tapped. Place: %s", selectedPlace.toString());
|
||||
storeSharedPrefs(selectedPlace);
|
||||
controller.initiateCustomGalleryPickWithPermission(getActivity());
|
||||
controller.initiateCustomGalleryPickWithPermission(getActivity(), customSelectorLauncherForResult);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package fr.free.nrw.commons.nearby.fragments
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
|
|
@ -12,6 +13,8 @@ class PlaceAdapter(
|
|||
onBookmarkClicked: (Place, Boolean) -> Unit,
|
||||
commonPlaceClickActions: CommonPlaceClickActions,
|
||||
inAppCameraLocationPermissionLauncher: ActivityResultLauncher<Array<String>>,
|
||||
galleryPickLauncherForResult: ActivityResultLauncher<Intent>,
|
||||
cameraPickLauncherForResult: ActivityResultLauncher<Intent>
|
||||
) : BaseDelegateAdapter<Place>(
|
||||
placeAdapterDelegate(
|
||||
bookmarkLocationsDao,
|
||||
|
|
@ -27,6 +30,8 @@ class PlaceAdapter(
|
|||
commonPlaceClickActions.onDirectionsClicked(),
|
||||
commonPlaceClickActions.onDirectionsLongPressed(),
|
||||
inAppCameraLocationPermissionLauncher,
|
||||
cameraPickLauncherForResult,
|
||||
galleryPickLauncherForResult
|
||||
),
|
||||
areItemsTheSame = { oldItem, newItem -> oldItem.wikiDataEntityId == newItem.wikiDataEntityId },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import fr.free.nrw.commons.databinding.ActivityNotificationBinding;
|
|||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException;
|
||||
import fr.free.nrw.commons.notification.models.Notification;
|
||||
import fr.free.nrw.commons.notification.models.NotificationType;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
|
|
@ -148,7 +149,11 @@ public class NotificationActivity extends BaseActivity {
|
|||
}
|
||||
adapter = new NotificatinAdapter(item -> {
|
||||
Timber.d("Notification clicked %s", item.getLink());
|
||||
handleUrl(item.getLink());
|
||||
if (item.getNotificationType() == NotificationType.EMAIL){
|
||||
ViewUtil.showLongSnackbar(binding.container,getString(R.string.check_your_email_inbox));
|
||||
} else {
|
||||
handleUrl(item.getLink());
|
||||
}
|
||||
removeNotification(item);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -51,13 +51,23 @@ class NotificationClient
|
|||
}
|
||||
}
|
||||
|
||||
private fun WikimediaNotification.toCommonsNotification() =
|
||||
Notification(
|
||||
notificationType = NotificationType.UNKNOWN,
|
||||
notificationText = contents?.compactHeader ?: "",
|
||||
date = DateUtil.getMonthOnlyDateString(timestamp),
|
||||
link = contents?.links?.primary?.url ?: "",
|
||||
iconUrl = "",
|
||||
notificationId = id().toString(),
|
||||
)
|
||||
private fun WikimediaNotification.toCommonsNotification() :
|
||||
Notification {
|
||||
val notificationText = contents?.compactHeader ?: ""
|
||||
val notificationType =
|
||||
if (notificationText.contains("Sent you an email", ignoreCase = true)) {
|
||||
NotificationType.EMAIL
|
||||
} else {
|
||||
NotificationType.UNKNOWN
|
||||
}
|
||||
|
||||
return Notification(
|
||||
notificationType = notificationType,
|
||||
notificationText = notificationText,
|
||||
date = DateUtil.getMonthOnlyDateString(timestamp),
|
||||
link = contents?.links?.primary?.url ?: "",
|
||||
iconUrl = "",
|
||||
notificationId = id().toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ public enum NotificationType {
|
|||
THANK_YOU_EDIT("thank-you-edit"),
|
||||
EDIT_USER_TALK("edit-user-talk"),
|
||||
MENTION("mention"),
|
||||
EMAIL("email"),
|
||||
WELCOME("welcome"),
|
||||
UNKNOWN("unknown");
|
||||
private String type;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import android.widget.TextView;
|
|||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.MultiSelectListPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
|
@ -85,6 +86,15 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||
private View separator;
|
||||
private ListView languageHistoryListView;
|
||||
private static final String GET_CONTENT_PICKER_HELP_URL = "https://commons-app.github.io/docs.html#get-content";
|
||||
|
||||
private final ActivityResultLauncher<Intent> cameraPickLauncherForResult =
|
||||
registerForActivityResult(new StartActivityForResult(),
|
||||
result -> {
|
||||
contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> {
|
||||
contributionController.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
|
||||
});
|
||||
});
|
||||
|
||||
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
|
||||
@Override
|
||||
public void onActivityResult(Map<String, Boolean> result) {
|
||||
|
|
@ -93,7 +103,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||
areAllGranted = areAllGranted && b;
|
||||
}
|
||||
if (!areAllGranted && shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
|
||||
contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher);
|
||||
contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -320,6 +320,14 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* go to the uploadProgress activity to check the status of uploading
|
||||
*/
|
||||
@Override
|
||||
public void goToUploadProgressActivity() {
|
||||
startActivity(new Intent(this, UploadProgressActivity.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show/Hide the progress dialog
|
||||
*/
|
||||
|
|
@ -433,14 +441,6 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS) {
|
||||
//TODO: Confirm if handling manual permission enabled is required
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flag indicating whether the upload is of a specific place.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@ public interface UploadContract {
|
|||
|
||||
void returnToMainActivity();
|
||||
|
||||
/**
|
||||
* When submission successful, go to the loadProgressActivity to hint the user this
|
||||
* submission is valid. And the user will see the upload progress in this activity;
|
||||
* Fixes: <a href="https://github.com/commons-app/apps-android-commons/issues/5846">Issue</a>
|
||||
*/
|
||||
void goToUploadProgressActivity();
|
||||
|
||||
void askUserToLogIn();
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import android.widget.ImageView;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
|
@ -57,27 +58,29 @@ public class UploadMediaDetailAdapter extends
|
|||
private int currentPosition;
|
||||
private Fragment fragment;
|
||||
private Activity activity;
|
||||
private final ActivityResultLauncher<Intent> voiceInputResultLauncher;
|
||||
private SelectedVoiceIcon selectedVoiceIcon;
|
||||
private static final int REQUEST_CODE_FOR_VOICE_INPUT = 1213;
|
||||
|
||||
private RowItemDescriptionBinding binding;
|
||||
|
||||
public UploadMediaDetailAdapter(Fragment fragment, String savedLanguageValue,
|
||||
RecentLanguagesDao recentLanguagesDao) {
|
||||
RecentLanguagesDao recentLanguagesDao, ActivityResultLauncher<Intent> voiceInputResultLauncher) {
|
||||
uploadMediaDetails = new ArrayList<>();
|
||||
selectedLanguages = new HashMap<>();
|
||||
this.savedLanguageValue = savedLanguageValue;
|
||||
this.recentLanguagesDao = recentLanguagesDao;
|
||||
this.fragment = fragment;
|
||||
this.voiceInputResultLauncher = voiceInputResultLauncher;
|
||||
}
|
||||
|
||||
public UploadMediaDetailAdapter(Activity activity, final String savedLanguageValue,
|
||||
List<UploadMediaDetail> uploadMediaDetails, RecentLanguagesDao recentLanguagesDao) {
|
||||
List<UploadMediaDetail> uploadMediaDetails, RecentLanguagesDao recentLanguagesDao, ActivityResultLauncher<Intent> voiceInputResultLauncher) {
|
||||
this.uploadMediaDetails = uploadMediaDetails;
|
||||
selectedLanguages = new HashMap<>();
|
||||
this.savedLanguageValue = savedLanguageValue;
|
||||
this.recentLanguagesDao = recentLanguagesDao;
|
||||
this.activity = activity;
|
||||
this.voiceInputResultLauncher = voiceInputResultLauncher;
|
||||
}
|
||||
|
||||
public void setCallback(Callback callback) {
|
||||
|
|
@ -150,11 +153,7 @@ public class UploadMediaDetailAdapter extends
|
|||
);
|
||||
|
||||
try {
|
||||
if (activity == null) {
|
||||
fragment.startActivityForResult(intent, REQUEST_CODE_FOR_VOICE_INPUT);
|
||||
} else {
|
||||
activity.startActivityForResult(intent, REQUEST_CODE_FOR_VOICE_INPUT);
|
||||
}
|
||||
voiceInputResultLauncher.launch(intent);
|
||||
} catch (Exception e) {
|
||||
Timber.e(e.getMessage());
|
||||
}
|
||||
|
|
@ -407,7 +406,7 @@ public class UploadMediaDetailAdapter extends
|
|||
recentLanguagesDao
|
||||
.addRecentLanguage(new Language(languageName, languageCode));
|
||||
|
||||
selectedLanguages.remove(position);
|
||||
selectedLanguages.clear();
|
||||
selectedLanguages.put(position, languageCode);
|
||||
((LanguagesAdapter) adapterView
|
||||
.getAdapter()).setSelectedLangCode(languageCode);
|
||||
|
|
@ -497,7 +496,7 @@ public class UploadMediaDetailAdapter extends
|
|||
}
|
||||
recentLanguagesDao.addRecentLanguage(new Language(languageName, languageCode));
|
||||
|
||||
selectedLanguages.remove(position);
|
||||
selectedLanguages.clear();
|
||||
selectedLanguages.put(position, languageCode);
|
||||
((RecentLanguagesAdapter) adapterView
|
||||
.getAdapter()).setSelectedLangCode(languageCode);
|
||||
|
|
|
|||
|
|
@ -123,6 +123,9 @@ public class UploadPresenter implements UploadContract.UserActionListener {
|
|||
view.returnToMainActivity();
|
||||
compositeDisposable.clear();
|
||||
Timber.e("failed to upload: " + e.getMessage());
|
||||
|
||||
//is submission error, not need to go to the uploadActivity
|
||||
//not start the uploading progress
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -131,6 +134,10 @@ public class UploadPresenter implements UploadContract.UserActionListener {
|
|||
repository.cleanup();
|
||||
view.returnToMainActivity();
|
||||
compositeDisposable.clear();
|
||||
|
||||
//after finish the uploadActivity, if successful,
|
||||
//directly go to the upload progress activity
|
||||
view.goToUploadProgressActivity();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -10,15 +10,13 @@ abstract class BaseDelegateAdapter<T>(
|
|||
areContentsTheSame: (T, T) -> Boolean = { old, new -> old == new },
|
||||
) : AsyncListDifferDelegationAdapter<T>(
|
||||
object : DiffUtil.ItemCallback<T>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: T,
|
||||
newItem: T,
|
||||
) = areItemsTheSame(oldItem, newItem)
|
||||
override fun areItemsTheSame(oldItem: T & Any, newItem: T & Any): Boolean {
|
||||
return areItemsTheSame(oldItem, newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: T,
|
||||
newItem: T,
|
||||
) = areContentsTheSame(oldItem, newItem)
|
||||
override fun areContentsTheSame(oldItem: T & Any, newItem: T & Any): Boolean {
|
||||
return areContentsTheSame(oldItem, newItem)
|
||||
}
|
||||
},
|
||||
*delegates,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -22,16 +22,16 @@ abstract class DepictsDao {
|
|||
private val maxItemsAllowed = 10
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun insert(depictedItem: Depicts)
|
||||
abstract fun insert(depictedItem: Depicts)
|
||||
|
||||
@Query("Select * From depicts_table order by lastUsed DESC")
|
||||
abstract suspend fun getAllDepicts(): List<Depicts>
|
||||
abstract fun getAllDepicts(): List<Depicts>
|
||||
|
||||
@Query("Select * From depicts_table order by lastUsed DESC LIMIT :n OFFSET 10")
|
||||
abstract suspend fun getDepictsForDeletion(n: Int): List<Depicts>
|
||||
abstract fun getDepictsForDeletion(n: Int): List<Depicts>
|
||||
|
||||
@Delete
|
||||
abstract suspend fun delete(depicts: Depicts)
|
||||
abstract fun delete(depicts: Depicts)
|
||||
|
||||
/**
|
||||
* Gets all Depicts objects from the database, ordered by lastUsed in descending order.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ import android.view.ViewGroup;
|
|||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
|
|
@ -58,9 +61,24 @@ import timber.log.Timber;
|
|||
public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
||||
UploadMediaDetailsContract.View, UploadMediaDetailAdapter.EventListener {
|
||||
|
||||
private static final int REQUEST_CODE = 1211;
|
||||
private static final int REQUEST_CODE_FOR_EDIT_ACTIVITY = 1212;
|
||||
private static final int REQUEST_CODE_FOR_VOICE_INPUT = 1213;
|
||||
private UploadMediaDetailAdapter uploadMediaDetailAdapter;
|
||||
|
||||
private final ActivityResultLauncher<Intent> startForResult = registerForActivityResult(
|
||||
new StartActivityForResult(), result -> {
|
||||
onCameraPosition(result);
|
||||
});
|
||||
|
||||
private final ActivityResultLauncher<Intent> startForEditActivityResult = registerForActivityResult(
|
||||
new StartActivityForResult(), result -> {
|
||||
onEditActivityResult(result);
|
||||
}
|
||||
);
|
||||
|
||||
private final ActivityResultLauncher<Intent> voiceInputResultLauncher = registerForActivityResult(
|
||||
new StartActivityForResult(), result -> {
|
||||
onVoiceInput(result);
|
||||
}
|
||||
);
|
||||
|
||||
public static Activity activity ;
|
||||
|
||||
|
|
@ -84,8 +102,6 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
private boolean hasUserRemovedLocation;
|
||||
|
||||
|
||||
private UploadMediaDetailAdapter uploadMediaDetailAdapter;
|
||||
|
||||
@Inject
|
||||
UploadMediaDetailsContract.UserActionListener presenter;
|
||||
|
||||
|
|
@ -279,7 +295,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
*/
|
||||
private void initRecyclerView() {
|
||||
uploadMediaDetailAdapter = new UploadMediaDetailAdapter(this,
|
||||
defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, ""), recentLanguagesDao);
|
||||
defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, ""), recentLanguagesDao, voiceInputResultLauncher);
|
||||
uploadMediaDetailAdapter.setCallback(this::showInfoAlert);
|
||||
uploadMediaDetailAdapter.setEventListener(this);
|
||||
binding.rvDescriptions.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
|
|
@ -593,14 +609,14 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
* This method is called to start the image editing activity for a specific UploadItem.
|
||||
* It sets the UploadItem as the currently editable item, creates an intent to launch the
|
||||
* EditActivity, and passes the image file path as an extra in the intent. The activity
|
||||
* is started with a request code, allowing the result to be handled in onActivityResult.
|
||||
* is started using resultLauncher that handles the result in respective callback.
|
||||
*/
|
||||
@Override
|
||||
public void showEditActivity(UploadItem uploadItem) {
|
||||
editableUploadItem = uploadItem;
|
||||
Intent intent = new Intent(getContext(), EditActivity.class);
|
||||
intent.putExtra("image", uploadableFile.getFilePath().toString());
|
||||
startActivityForResult(intent, REQUEST_CODE_FOR_EDIT_ACTIVITY);
|
||||
startForEditActivityResult.launch(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -615,6 +631,8 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
double defaultLongitude = -122.431297;
|
||||
double defaultZoom = 16.0;
|
||||
|
||||
final Intent locationPickerIntent;
|
||||
|
||||
/* Retrieve image location from EXIF if present or
|
||||
check if user has provided location while using the in-app camera.
|
||||
Use location of last UploadItem if none of them is available */
|
||||
|
|
@ -624,10 +642,11 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
.getDecLatitude();
|
||||
defaultLongitude = uploadItem.getGpsCoords().getDecLongitude();
|
||||
defaultZoom = uploadItem.getGpsCoords().getZoomLevel();
|
||||
startActivityForResult(new LocationPicker.IntentBuilder()
|
||||
|
||||
locationPickerIntent = new LocationPicker.IntentBuilder()
|
||||
.defaultLocation(new CameraPosition(defaultLatitude,defaultLongitude,defaultZoom))
|
||||
.activityKey("UploadActivity")
|
||||
.build(getActivity()), REQUEST_CODE);
|
||||
.build(getActivity());
|
||||
} else {
|
||||
if (defaultKvStore.getString(LAST_LOCATION) != null) {
|
||||
final String[] locationLatLng
|
||||
|
|
@ -638,27 +657,20 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
if (defaultKvStore.getString(LAST_ZOOM) != null) {
|
||||
defaultZoom = Double.parseDouble(defaultKvStore.getString(LAST_ZOOM));
|
||||
}
|
||||
startActivityForResult(new LocationPicker.IntentBuilder()
|
||||
|
||||
locationPickerIntent = new LocationPicker.IntentBuilder()
|
||||
.defaultLocation(new CameraPosition(defaultLatitude,defaultLongitude,defaultZoom))
|
||||
.activityKey("NoLocationUploadActivity")
|
||||
.build(getActivity()), REQUEST_CODE);
|
||||
.build(getActivity());
|
||||
}
|
||||
startForResult.launch(locationPickerIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the coordinates and update the existing coordinates.
|
||||
* @param requestCode code of request
|
||||
* @param resultCode code of result
|
||||
* @param data intent
|
||||
*/
|
||||
@Override
|
||||
public void onActivityResult(final int requestCode, final int resultCode,
|
||||
@Nullable final Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
|
||||
private void onCameraPosition(ActivityResult result){
|
||||
if (result.getResultCode() == RESULT_OK) {
|
||||
|
||||
assert data != null;
|
||||
final CameraPosition cameraPosition = LocationPicker.getCameraPosition(data);
|
||||
assert result.getData() != null;
|
||||
final CameraPosition cameraPosition = LocationPicker.getCameraPosition(result.getData());
|
||||
|
||||
if (cameraPosition != null) {
|
||||
|
||||
|
|
@ -678,8 +690,21 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
removeLocation();
|
||||
}
|
||||
}
|
||||
if (requestCode == REQUEST_CODE_FOR_EDIT_ACTIVITY && resultCode == RESULT_OK) {
|
||||
String result = data.getStringExtra("editedImageFilePath");
|
||||
}
|
||||
|
||||
private void onVoiceInput(ActivityResult result) {
|
||||
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
|
||||
ArrayList<String> resultData = result.getData().getStringArrayListExtra(
|
||||
RecognizerIntent.EXTRA_RESULTS);
|
||||
uploadMediaDetailAdapter.handleSpeechResult(resultData.get(0));
|
||||
}else {
|
||||
Timber.e("Error %s", result.getResultCode());
|
||||
}
|
||||
}
|
||||
|
||||
private void onEditActivityResult(ActivityResult result){
|
||||
if (result.getResultCode() == RESULT_OK) {
|
||||
String path = result.getData().getStringExtra("editedImageFilePath");
|
||||
|
||||
if (Objects.equals(result, "Error")) {
|
||||
Timber.e("Error in rotating image");
|
||||
|
|
@ -687,24 +712,15 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
}
|
||||
try {
|
||||
if (binding != null){
|
||||
binding.backgroundImage.setImageURI(Uri.fromFile(new File(result)));
|
||||
binding.backgroundImage.setImageURI(Uri.fromFile(new File(path)));
|
||||
}
|
||||
editableUploadItem.setContentUri(Uri.fromFile(new File(result)));
|
||||
editableUploadItem.setContentUri(Uri.fromFile(new File(path)));
|
||||
callback.changeThumbnail(indexOfFragment,
|
||||
result);
|
||||
path);
|
||||
} catch (Exception e) {
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
else if (requestCode == REQUEST_CODE_FOR_VOICE_INPUT) {
|
||||
if (resultCode == RESULT_OK && data != null) {
|
||||
ArrayList<String> result = data.getStringArrayListExtra(
|
||||
RecognizerIntent.EXTRA_RESULTS);
|
||||
uploadMediaDetailAdapter.handleSpeechResult(result.get(0));
|
||||
}else {
|
||||
Timber.e("Error %s", resultCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import androidx.work.Data
|
|||
import androidx.work.ForegroundInfo
|
||||
import androidx.work.WorkerParameters
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import fr.free.nrw.commons.BuildConfig.HOME_URL
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
|
|
@ -30,6 +31,7 @@ import fr.free.nrw.commons.customselector.database.UploadedStatus
|
|||
import fr.free.nrw.commons.customselector.database.UploadedStatusDao
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection
|
||||
import fr.free.nrw.commons.media.MediaClient
|
||||
import fr.free.nrw.commons.nearby.PlacesRepository
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.upload.FileUtilsWrapper
|
||||
import fr.free.nrw.commons.upload.StashUploadResult
|
||||
|
|
@ -38,6 +40,7 @@ import fr.free.nrw.commons.upload.UploadClient
|
|||
import fr.free.nrw.commons.upload.UploadProgressActivity
|
||||
import fr.free.nrw.commons.upload.UploadResult
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditService
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -74,6 +77,9 @@ class UploadWorker(
|
|||
@Inject
|
||||
lateinit var fileUtilsWrapper: FileUtilsWrapper
|
||||
|
||||
@Inject
|
||||
lateinit var placesRepository: PlacesRepository
|
||||
|
||||
private val processingUploadsNotificationTag = BuildConfig.APPLICATION_ID + " : upload_tag"
|
||||
|
||||
private val processingUploadsNotificationId = 101
|
||||
|
|
@ -379,7 +385,7 @@ class UploadWorker(
|
|||
saveCompletedContribution(contribution, uploadResult)
|
||||
} else {
|
||||
Timber.d(
|
||||
"WikiDataEdit not required, making wikidata edit",
|
||||
"WikiDataEdit required, making wikidata edit",
|
||||
)
|
||||
makeWikiDataEdit(uploadResult, contribution)
|
||||
}
|
||||
|
|
@ -432,7 +438,7 @@ class UploadWorker(
|
|||
username,
|
||||
)
|
||||
CommonsApplication
|
||||
.getInstance()
|
||||
.instance!!
|
||||
.clearApplicationData(appContext, logoutListener)
|
||||
}
|
||||
}
|
||||
|
|
@ -471,6 +477,16 @@ class UploadWorker(
|
|||
contribution.media.captions,
|
||||
)
|
||||
if (null != revisionID) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val place = placesRepository.fetchPlace(wikiDataPlace.id);
|
||||
place.name = wikiDataPlace.name;
|
||||
place.pic = HOME_URL + uploadResult.createCanonicalFileName()
|
||||
placesRepository
|
||||
.save(place)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.blockingAwait()
|
||||
Timber.d("Updated WikiItem place ${place.name} with image ${place.pic}")
|
||||
}
|
||||
showSuccessNotification(contribution)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
|
|
|
|||
|
|
@ -54,8 +54,7 @@ public class PermissionUtils {
|
|||
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
final Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
|
||||
intent.setData(uri);
|
||||
activity.startActivityForResult(intent,
|
||||
CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
18
app/src/main/res/drawable/ic_refresh_24dp_nearby.xml
Normal file
18
app/src/main/res/drawable/ic_refresh_24dp_nearby.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="@dimen/half_standard_height"
|
||||
android:height="@dimen/half_standard_height"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
|
||||
<group
|
||||
android:scaleX="1.0"
|
||||
android:scaleY="1.0"
|
||||
android:translateX="-0.0"
|
||||
android:translateY="-0.0">
|
||||
|
||||
<path
|
||||
android:fillColor="?attr/menu_item_tint"
|
||||
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
|
||||
|
||||
</group>
|
||||
</vector>
|
||||
|
|
@ -124,6 +124,33 @@
|
|||
app:srcCompat="@drawable/ic_my_location_black_24dp"
|
||||
app:useCompatPadding="true" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_legend"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/fab_recenter"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:clickable="true"
|
||||
android:visibility="visible"
|
||||
app:backgroundTint="@color/main_background_light"
|
||||
app:elevation="@dimen/dimen_6"
|
||||
app:fabSize="normal"
|
||||
app:layout_anchorGravity="top|right|end"
|
||||
app:srcCompat="@drawable/ic_info_outline_24dp"
|
||||
app:useCompatPadding="true" />
|
||||
|
||||
<include
|
||||
android:id="@+id/nearby_legend_layout"
|
||||
layout="@layout/nearby_legend"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/rl_container_wlm_month_message"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginStart="5dp"
|
||||
/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<FrameLayout
|
||||
|
|
|
|||
74
app/src/main/res/layout/nearby_legend.xml
Normal file
74
app/src/main/res/layout/nearby_legend.xml
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#BFFFFFFF"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageRed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/imageGreen"
|
||||
app:layout_constraintEnd_toEndOf="@+id/imageGreen"
|
||||
app:layout_constraintStart_toStartOf="@+id/imageGreen"
|
||||
app:srcCompat="@drawable/ic_custom_map_marker_red" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textRed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
android:text="@string/red_pin"
|
||||
android:textColor="#F74D4D"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageRed"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageRed"
|
||||
app:layout_constraintTop_toTopOf="@+id/imageRed" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageGreen"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/imageGrey"
|
||||
app:layout_constraintEnd_toEndOf="@+id/imageGrey"
|
||||
app:layout_constraintStart_toStartOf="@+id/imageGrey"
|
||||
app:srcCompat="@drawable/ic_custom_map_marker_green" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textGreen"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
android:text="@string/green_pin"
|
||||
android:textColor="#1F7123"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageGreen"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageGreen"
|
||||
app:layout_constraintTop_toTopOf="@+id/imageGreen" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageGrey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:srcCompat="@drawable/ic_custom_map_marker_grey" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textGrey"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/grey_pin"
|
||||
android:textColor="#454547"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageGrey"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageGrey"
|
||||
app:layout_constraintTop_toTopOf="@+id/imageGrey" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -1,17 +1,25 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:id="@+id/item_refresh"
|
||||
android:title="Refresh"
|
||||
app:showAsAction="ifRoom"
|
||||
android:icon="@drawable/ic_refresh_24dp_nearby" />
|
||||
|
||||
<item android:id="@+id/list_sheet"
|
||||
android:title="@string/list_sheet"
|
||||
app:showAsAction="ifRoom|withText"
|
||||
android:icon="@drawable/ic_list_white_24dp"
|
||||
/>
|
||||
|
||||
<item android:id="@+id/list_item_gpx"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:title="Save as GPX file" />
|
||||
|
||||
<item android:id="@+id/list_item_kml"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:title="Save as KML file" />
|
||||
|
||||
</menu>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
<!-- Authors:
|
||||
* Dağlı95
|
||||
* Khan27
|
||||
* Nemoralis
|
||||
-->
|
||||
<resources>
|
||||
<string name="crash_dialog_title">Nasazlıq</string>
|
||||
<string name="crash_dialog_text">Uups. Nəsə düzgün çalışmır!</string>
|
||||
<string name="crash_dialog_comment_prompt">Nə etdiyinizi dəqiqləşdirib, bizə bildirin və sonra e-poçtla bizə göndərin. Bu problemi həll etməyə bizə kömək edin.</string>
|
||||
<string name="crash_dialog_ok_toast">Təşəkkür!</string>
|
||||
<string name="crash_dialog_comment_prompt">Nə etdiyinizi bizə deyin, sonra e-poçt vasitəsilə bizimlə paylaşın. Bu, bizə bunu düzəltməyə kömək edəcək!</string>
|
||||
<string name="crash_dialog_ok_toast">Təşəkkürlər!</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -12,34 +12,60 @@
|
|||
* Şeyx Şamil
|
||||
-->
|
||||
<resources>
|
||||
<string name="commons_facebook">Commons Facebook səhifəsi</string>
|
||||
<string name="commons_github">Commons Github Mənbə Kodu</string>
|
||||
<string name="commons_logo">Commons Loqotipi</string>
|
||||
<string name="commons_website">Commons Veb-saytı</string>
|
||||
<string name="exit_location_picker">Məkan seçicidən çıxın</string>
|
||||
<string name="submit">Göndər</string>
|
||||
<string name="add_another_description">Başqa təsvir əlavə et</string>
|
||||
<string name="add_new_contribution">Yeni töhfə</string>
|
||||
<string name="add_contribution_from_camera">Kamera ilə töhfə ver</string>
|
||||
<string name="add_contribution_from_photos">Fotolar ilə töhfə ver</string>
|
||||
<string name="add_contribution_from_contributions_gallery">Əvvəlki töhfələr qalereyasından töhfə əlavə et</string>
|
||||
<string name="show_captions">Başlıqlar</string>
|
||||
<string name="row_item_language_description">Dil təsviri</string>
|
||||
<string name="row_item_caption">Başlıq</string>
|
||||
<string name="show_captions_description">Təsvir</string>
|
||||
<string name="nearby_row_image">Şəkil</string>
|
||||
<string name="nearby_all">Hamısı</string>
|
||||
<string name="nearby_filter_toggle">Aç/Bağla</string>
|
||||
<string name="nearby_filter_search">Axtarış Görünüşü</string>
|
||||
<string name="nearby_filter_state">Məkanın Vəziyyəti</string>
|
||||
<string name="appwidget_img">Günün Şəkli</string>
|
||||
<plurals name="uploads_pending_notification_indicator">
|
||||
<item quantity="one">%1$d fayl yüklənir</item>
|
||||
<item quantity="other">%1$d fayllar yüklənir</item>
|
||||
</plurals>
|
||||
<plurals name="contributions_subtitle">
|
||||
<item quantity="one">(%1$d)</item>
|
||||
<item quantity="other">(%1$d)</item>
|
||||
</plurals>
|
||||
<string name="starting_uploads">Yükləmələrə Başlanılır</string>
|
||||
<string name="preference_category_general">Ümumi</string>
|
||||
<string name="preference_category_privacy">Məxfilik</string>
|
||||
<string name="app_name">Vikimedia Commons</string>
|
||||
<string name="app_name">Vikianbar</string>
|
||||
<string name="menu_settings">Tənzimləmələr</string>
|
||||
<string name="username">Ləqəb</string>
|
||||
<string name="username">İstifadəçi adı</string>
|
||||
<string name="password">Parol</string>
|
||||
<string name="login">Daxil ol</string>
|
||||
<string name="signup">Qeydiyyatdan keç</string>
|
||||
<string name="logging_in_title">Giriş edilir</string>
|
||||
<string name="logging_in_message">Lütfən gözləyin…</string>
|
||||
<string name="logging_in_message">Zəhmət olmasa, gözləyin…</string>
|
||||
<string name="login_success" fuzzy="true">Daxil oldunuz!</string>
|
||||
<string name="login_failed">Giriş baş tutmadı!</string>
|
||||
<string name="upload_failed">Fayl tapılmadı. Xahiş edirik başqa bir fayl üzərində cəhd edin.</string>
|
||||
<string name="authentication_failed" fuzzy="true">Doğrulama alınmadı, xahiş edirəm yenidən daxil olun</string>
|
||||
<string name="uploading_started">Yükləmə başladı!</string>
|
||||
<string name="upload_completed_notification_title">%1$s yükləndi!</string>
|
||||
<string name="upload_completed_notification_text">Yüklədiyini izlə</string>
|
||||
<string name="upload_completed_notification_text">Yükləmənizə baxmaq üçün toxunun</string>
|
||||
<string name="upload_progress_notification_title_start" fuzzy="true">%1$s yüklənməsi başlanır</string>
|
||||
<string name="upload_progress_notification_title_in_progress">%1$s yüklənir</string>
|
||||
<string name="upload_progress_notification_title_finishing">%1$s yüklənməsi başa çatdı</string>
|
||||
<string name="upload_failed_notification_title" fuzzy="true">%1$s faylının yüklənməsi alınmadı</string>
|
||||
<string name="upload_failed_notification_subtitle">Baxmaq üçün toxunun</string>
|
||||
<string name="title_activity_contributions">Yükləmələrim</string>
|
||||
<string name="contribution_state_queued">Sırada</string>
|
||||
<string name="upload_failed_notification_subtitle">Baxmaq üçün toxun</string>
|
||||
<string name="title_activity_contributions">Son Yükləmələrim</string>
|
||||
<string name="contribution_state_queued">Növbəyə alındı</string>
|
||||
<string name="contribution_state_failed">Uğursuz</string>
|
||||
<string name="contribution_state_in_progress">%1$d%% tamamlandı</string>
|
||||
<string name="contribution_state_starting">Yüklənir</string>
|
||||
|
|
@ -52,15 +78,15 @@
|
|||
<string name="share_description_hint">Açıqlama</string>
|
||||
<string name="login_failed_network" fuzzy="true">Daxil olmaq olmur — şəbəkə xətası</string>
|
||||
<string name="login_failed_throttled">Çox sayda uğursuz daxil olma. Xahiş edirik bir neçə dəqiqə sonra yenidən cəhd edin.</string>
|
||||
<string name="login_failed_blocked">Bağışlayın, bu istifadəçi Commons-da bloklanmışdır.</string>
|
||||
<string name="login_failed_2fa_needed">İki faktorlu giriş doğrulama kodunu verməlisiniz.</string>
|
||||
<string name="login_failed_blocked">Bağışlayın, bu istifadəçi Vikianbardan bloklanıb</string>
|
||||
<string name="login_failed_2fa_needed">Siz iki faktorlu autentifikasiya kodunuzu təqdim etməlisiniz.</string>
|
||||
<string name="login_failed_generic" fuzzy="true">Daxil olma uğursuz oldu</string>
|
||||
<string name="share_upload_button">Yüklə</string>
|
||||
<string name="multiple_share_base_title">Bu dəsti adlandırın</string>
|
||||
<string name="provider_modifications">Bildirişlər</string>
|
||||
<string name="provider_modifications">Dəyişikliklər</string>
|
||||
<string name="menu_upload_single">Yüklə</string>
|
||||
<string name="categories_search_text_hint">Kateqoriyaları axtar</string>
|
||||
<string name="menu_save_categories">Qeyd et</string>
|
||||
<string name="categories_search_text_hint">Kateqoriyalarda axtar</string>
|
||||
<string name="menu_save_categories">Yadda saxla</string>
|
||||
<string name="refresh_button">Yenilə</string>
|
||||
<string name="display_list_button">Siyahı</string>
|
||||
<string name="contributions_subtitle_zero">(Hələ yükləmə yoxdur)</string>
|
||||
|
|
|
|||
|
|
@ -307,4 +307,5 @@
|
|||
<string name="please_wait">Моля, изчакайте...</string>
|
||||
<string name="delete_helper_ask_spam_blurry">напълно размазано</string>
|
||||
<string name="leaderboard_nearby">Наблизо</string>
|
||||
<string name="read_help_link">Прочетете повече</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -481,6 +481,7 @@
|
|||
<string name="no_notification">Du har ingen ulæste notifikationer</string>
|
||||
<string name="no_read_notification">Du har ingen læste notifikationer</string>
|
||||
<string name="share_logs_using">Del logs ved hjælp af</string>
|
||||
<string name="check_your_email_inbox">Tjek din e-mail-indbakke</string>
|
||||
<string name="menu_option_read">Vis læste</string>
|
||||
<string name="menu_option_unread">Vis ulæste</string>
|
||||
<string name="error_occurred_in_picking_images">Der opstod en fejl under udvælgelse af billeder</string>
|
||||
|
|
@ -788,4 +789,7 @@
|
|||
<string name="pending">Afventer</string>
|
||||
<string name="failed">Mislykkedes</string>
|
||||
<string name="could_not_load_place_data">Kunne ikke indlæse steddata</string>
|
||||
<string name="red_pin">Dette sted har endnu ikke noget billede, så gå hen og tag et!</string>
|
||||
<string name="green_pin">Dette sted har allerede et billede.</string>
|
||||
<string name="grey_pin">Tjekker nu, om dette sted har et billede.</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
* JenyxGym
|
||||
* KATRINE1992
|
||||
* Koreller
|
||||
* Mahabarata
|
||||
* McDutchie
|
||||
* Melissadeba95
|
||||
* Metroitendo
|
||||
|
|
@ -516,6 +517,7 @@
|
|||
<string name="no_notification">Vous n’avez aucune notification non lue</string>
|
||||
<string name="no_read_notification">Vous n’avez aucune notification lue</string>
|
||||
<string name="share_logs_using">Partager les journaux en utilisant</string>
|
||||
<string name="check_your_email_inbox">Vérifiez votre boîte de réception</string>
|
||||
<string name="menu_option_read">Afficher les lus</string>
|
||||
<string name="menu_option_unread">Afficher les non lus</string>
|
||||
<string name="error_occurred_in_picking_images">Une erreur est survenue lors de la sélection des images</string>
|
||||
|
|
|
|||
|
|
@ -760,4 +760,7 @@
|
|||
<item quantity="one">%d immagine selezionata</item>
|
||||
<item quantity="other">%d immagini selezionate</item>
|
||||
</plurals>
|
||||
<string name="red_pin">Questo posto non ha ancora una foto, scattane una!</string>
|
||||
<string name="green_pin">Questo posto ha già una foto.</string>
|
||||
<string name="grey_pin">Ora controlliamo se questo posto ha una foto.</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -462,6 +462,7 @@
|
|||
<string name="no_notification">未読の通知はありません</string>
|
||||
<string name="no_read_notification">既読のお知らせはありません</string>
|
||||
<string name="share_logs_using">ログの共有に使うアプリ</string>
|
||||
<string name="check_your_email_inbox">メールをご確認ください</string>
|
||||
<string name="menu_option_read">既読を表示</string>
|
||||
<string name="menu_option_unread">未読を見る</string>
|
||||
<string name="error_occurred_in_picking_images">画像の選択中にエラーが発生しました</string>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
<string name="show_captions_description">Aprašymas</string>
|
||||
<string name="nearby_row_image">Paveikslėlis</string>
|
||||
<string name="nearby_all">Visi</string>
|
||||
<string name="nearby_filter_toggle">Perjungti aukštyn</string>
|
||||
<string name="nearby_filter_search">Paieškos rodinys</string>
|
||||
<string name="appwidget_img">Dienos nuotrauka</string>
|
||||
<plurals name="uploads_pending_notification_indicator" fuzzy="true">
|
||||
|
|
@ -57,6 +58,7 @@
|
|||
<string name="app_name">Vikiteka</string>
|
||||
<string name="menu_settings">Nustatymai</string>
|
||||
<string name="intent_share_upload_label">Įkelti į Vikiteką</string>
|
||||
<string name="upload_in_progress">Vyksta įkėlimas</string>
|
||||
<string name="username">Naudotojo vardas</string>
|
||||
<string name="password">Slaptažodis</string>
|
||||
<string name="login_credential">Prisijunkite prie savo Commons Beta paskyros</string>
|
||||
|
|
@ -67,10 +69,13 @@
|
|||
<string name="logging_in_message">Prašome palaukti…</string>
|
||||
<string name="updating_caption_title">Antraštės ir aprašymai atnaujinami</string>
|
||||
<string name="updating_caption_message">Prašome palaukti...</string>
|
||||
<string name="login_success" fuzzy="true">Sėkmingai prisijungėte!</string>
|
||||
<string name="login_failed" fuzzy="true">Prisijungti nepavyko!</string>
|
||||
<string name="login_success">Sėkmingai prisijungėte!</string>
|
||||
<string name="login_failed">Prisijungti nepavyko!</string>
|
||||
<string name="upload_failed">Failas nerastas. Prašome pabandyti kitą failą.</string>
|
||||
<string name="authentication_failed" fuzzy="true">Autentifikavimas nepavyko, prašome prisijungti dar kartą</string>
|
||||
<string name="retry_limit_reached">Pasiektas maksimalus pakartotinių bandymų limitas! Atšaukite įkėlimą ir bandykite dar kartą</string>
|
||||
<string name="unrestricted_battery_mode">Išjungti akumuliatoriaus optimizavimą?</string>
|
||||
<string name="suggest_unrestricted_mode">Daugiau nei 3 paveikslėlių įkėlimas veikia patikimiau, kai akumuliatoriaus optimizavimas išjungtas. Išjunkite Vikitekos programėlės akumuliatoriaus optimizavimą nustatymuose, kad įkėlimas būtų sklandus. \n\nGalimi akumuliatoriaus optimizavimo išjungimo veiksmai:\n\n1 veiksmas: bakstelėkite toliau esantį mygtuką „Nustatymai“.\n\n2 veiksmas: perjunkite iš „Neoptimizuota“ į „Visos programėlės“.\n\n3 veiksmas: ieškokite „Vikiteka“ arba „fr.free.nrw.commons“.\n\n4 veiksmas: spustelėkite jį ir pasirinkite „Neoptimizuoti“.\n\n5 veiksmas: paspauskite „Atlikta“.</string>
|
||||
<string name="authentication_failed">Autentifikavimas nepavyko. Prašome prisijungti dar kartą.</string>
|
||||
<string name="uploading_started">Įkėlimas prasidėjo!</string>
|
||||
<string name="uploading_queued">Įkėlimas eilėje (įgalintas riboto ryšio režimas)</string>
|
||||
<string name="upload_completed_notification_title">%1$s įkelta!</string>
|
||||
|
|
@ -97,11 +102,11 @@
|
|||
<string name="add_caption_toast">Pateikite šio failo antraštę</string>
|
||||
<string name="share_description_hint">Aprašymas</string>
|
||||
<string name="share_caption_hint">Antraštė</string>
|
||||
<string name="login_failed_network" fuzzy="true">Negalima prisijungti - tinklo klaida</string>
|
||||
<string name="login_failed_network">Negalima prisijungti - tinklo klaida</string>
|
||||
<string name="login_failed_throttled">Per daug nesėkmingų bandymų. Pabandykite dar kartą po keleto minučių.</string>
|
||||
<string name="login_failed_blocked">Atsiprašome, šis vartotojas buvo užblokuotas Commons</string>
|
||||
<string name="login_failed_2fa_needed">Turite pateikti savo dviejų žingsnių patvirtinimo kodą.</string>
|
||||
<string name="login_failed_generic" fuzzy="true">Prisijungti nepavyko</string>
|
||||
<string name="login_failed_generic">Prisijungti nepavyko</string>
|
||||
<string name="share_upload_button">Įkelti</string>
|
||||
<string name="multiple_share_base_title">Pavadinkite šį rinkinį</string>
|
||||
<string name="provider_modifications">Pakeitimai</string>
|
||||
|
|
@ -123,7 +128,7 @@
|
|||
<string name="title_activity_category_details">Kategorija</string>
|
||||
<string name="menu_about">Apie</string>
|
||||
<string name="about_license">Vikitekos programėlė yra atviro kodo programėlė, kurią sukūrė ir prižiūri Vikitekos bendruomenės dotacijų gavėjai ir savanoriai. „Wikimedia Foundation“ nedalyvauja kuriant, plėtojant ar prižiūrint programėlę.</string>
|
||||
<string name="about_improve" fuzzy="true">Sukurkite naują <a href=\"%1$s\">GitHub pranešimą</a>, siekiant pranešti apie klaidas ir pateikti siūlymus.</string>
|
||||
<string name="about_improve">Sukurkite naują <a href=\"%1$s\">GitHub pranešimą</a>, siekiant pranešti apie klaidas ir pateikti siūlymus.</string>
|
||||
<string name="about_privacy_policy">Privatumo politika</string>
|
||||
<string name="about_credits">Kūrėjai</string>
|
||||
<string name="title_activity_about">Apie</string>
|
||||
|
|
@ -178,6 +183,8 @@
|
|||
<string name="read_storage_permission_rationale">Reikalinga teisė: Skaityti išorinę talpyklą. Programėle be to negali prieiti prie jūsų galerijos.</string>
|
||||
<string name="write_storage_permission_rationale">Reikalingas leidimas: rašyti į išorinę saugyklą. Programėlė be to negali pasiekti jūsų fotoaparato/galerijos.</string>
|
||||
<string name="location_permission_title">Prašoma vietovės leidimo</string>
|
||||
<string name="in_app_camera_location_permission_title">Įrašyti vietovę nuotraukoms, kurios fotografuotos programėlėje</string>
|
||||
<string name="in_app_camera_location_switch_pref_summary">Įjunkite šią funkciją, kad įrašytumėte vietovę nuotraukoms, jei jūsų įrenginio kamera to nepadaro</string>
|
||||
<string name="ok">Gerai</string>
|
||||
<string name="warning">Įspėjimas</string>
|
||||
<string name="duplicate_file_name">Rastas pasikartojantis failo pavadinimas</string>
|
||||
|
|
@ -198,6 +205,7 @@
|
|||
<string name="become_a_tester_description">Prisijunkite prie beta kanalo Google Play ir gaukite išankstinę prieigą prie naujų funkcijų bei klaidų pataisymų</string>
|
||||
<string name="_2fa_code">2FA kodas</string>
|
||||
<string name="logout_verification">Ar tikrai norite atsijungti?</string>
|
||||
<string name="mediaimage_failed">Medijos paveikslėlis nepavyko</string>
|
||||
<string name="no_subcategory_found">Subkategorijų nerasta</string>
|
||||
<string name="welcome_image_mount_zao">Zao kalnas</string>
|
||||
<string name="welcome_image_llamas">Lamos</string>
|
||||
|
|
@ -214,6 +222,7 @@
|
|||
<string name="navigation_item_about">Apie</string>
|
||||
<string name="navigation_item_settings">Nustatymai</string>
|
||||
<string name="navigation_item_feedback">Atsiliepimai</string>
|
||||
<string name="navigation_item_feedback_github">Atsiliepimai per GitHub</string>
|
||||
<string name="navigation_item_logout">Atsijungti</string>
|
||||
<string name="navigation_item_info">Pamoka</string>
|
||||
<string name="navigation_item_notification">Pranešimai</string>
|
||||
|
|
@ -247,13 +256,15 @@
|
|||
<string name="nominated_see_more">Žiūrėkite tinklapį dėl daugiau informacijos</string>
|
||||
<string name="skip_login">Praleisti</string>
|
||||
<string name="navigation_item_login">Prisijungti</string>
|
||||
<string name="skip_login_title" fuzzy="true">Ar tikrai norite praleisti prisijungimą?</string>
|
||||
<string name="skip_login_message" fuzzy="true">Norėdami ateityje įkelti nuotraukas, turėsite prisijungti.</string>
|
||||
<string name="skip_login_title">Ar tikrai norite praleisti prisijungimą?</string>
|
||||
<string name="skip_login_message">Norėdami ateityje įkelti nuotraukas, turėsite prisijungti.</string>
|
||||
<string name="login_alert_message">Jei norite naudotis šia funkcija, prisijunkite</string>
|
||||
<string name="copy_wikicode">Nukopijuokite vikitekstą į mainų sritį</string>
|
||||
<string name="wikicode_copied">Vikitekstas buvo nukopijuotas į mainų sritį</string>
|
||||
<string name="nearby_location_not_available">Netoliese gali tinkamai neveikti, vieta nepasiekiama.</string>
|
||||
<string name="upload_location_access_denied">Prieiga prie vietos uždrausta. Norėdami naudotis šia funkcija, nustatykite savo vietą rankiniu būdu.</string>
|
||||
<string name="location_permission_rationale_nearby">Norint rodyti netoliese esančių vietų sąrašą, reikalingas leidimas</string>
|
||||
<string name="location_permission_rationale_explore">Norint rodyti netoliese esančių paveikslėlių sąrašą, reikalingas leidimas</string>
|
||||
<string name="nearby_directions">Nurodymai</string>
|
||||
<string name="nearby_wikidata">Vikiduomenys</string>
|
||||
<string name="nearby_wikipedia">Vikipedija</string>
|
||||
|
|
@ -313,14 +324,16 @@
|
|||
<string name="construction_event_answer">Nuotraukos, kuriose pavaizduotos technologijos ar kultūra, yra labai laukiamos Vikitekoje.</string>
|
||||
<string name="congratulatory_message_quiz">Surinkote %1$s teisingų atsakymų. Sveikiname!</string>
|
||||
<string name="warning_for_no_answer">Norėdami atsakyti į klausimą, pasirinkite vieną iš dviejų variantų</string>
|
||||
<string name="user_not_logged_in" fuzzy="true">Prisijungimo sesija baigėsi, prisijunkite dar kartą.</string>
|
||||
<string name="user_not_logged_in">Prisijungimo sesija baigėsi, prašome prisijungti dar kartą.</string>
|
||||
<string name="quiz_result_share_message">Pasidalinkite savo apkalusa su draugais!</string>
|
||||
<string name="continue_message">Tęsti</string>
|
||||
<string name="correct">Teisingas atsakymas</string>
|
||||
<string name="wrong">Atsakymas neteisingas</string>
|
||||
<string name="quiz_screenshot_question">Ar šią ekrano kopiją galima įkelti?</string>
|
||||
<string name="share_app_title">Dalintis programėle</string>
|
||||
<string name="error_fetching_nearby_places" fuzzy="true">Klaida gaunant netoliese esančias vietas.</string>
|
||||
<string name="rotate">Pasukti</string>
|
||||
<string name="error_fetching_nearby_places">Nepavyko įkelti netoliese esančių vietų</string>
|
||||
<string name="no_pictures_in_this_area">Šioje vietovėje nuotraukų nėra</string>
|
||||
<string name="no_nearby_places_around">Nėra šalia esančių vietų</string>
|
||||
<string name="error_fetching_nearby_monuments">Gaunant netoliese esančius paminklus įvyko klaida.</string>
|
||||
<string name="no_recent_searches">Nėra naujausių paieškų</string>
|
||||
|
|
@ -338,6 +351,7 @@
|
|||
<string name="statistics_wikidata_edits">Vaizdai per „Netoliese esančios vietos“</string>
|
||||
<string name="level">Lygis</string>
|
||||
<string name="images_uploaded">Vaizdai įkelti</string>
|
||||
<string name="image_reverts">Paveikslėliai negrąžinti</string>
|
||||
<string name="images_used_by_wiki">Naudoti vaizdai</string>
|
||||
<string name="achievements_share_message">Pasidalinkite savo pasiekimais su draugais!</string>
|
||||
<string name="achievements_info_message">Jūsų lygis kyla, kai atitinkate šiuos reikalavimus. Skiltyje „statistika“ esantys elementai neįskaičiuojami į jūsų lygį.</string>
|
||||
|
|
@ -394,12 +408,22 @@
|
|||
<string name="never_ask_again">Niekada daugiau to neklausti</string>
|
||||
<string name="display_location_permission_title">Paprašyti vietos leidimo</string>
|
||||
<string name="display_location_permission_explanation">Jei reikia, kad būtų galima naudoti netoliese esančio pranešimų kortelės peržiūros funkciją, paprašykite leidimo nustatyti vietą.</string>
|
||||
<string name="achievements_fetch_failed" fuzzy="true">Kažkas ne taip. Nepavyko gauti jūsų pasiekimų</string>
|
||||
<string name="achievements_fetch_failed">Kažkas ne taip, mums nepavyko gauti pasiekimų</string>
|
||||
<string name="achievements_fetch_failed_ultimate_achievement">Prisidėjote tiek daug, kad mūsų pasiekimų skaičiavimo sistema negali susidoroti. Tai yra didžiausias pasiekimas.</string>
|
||||
<string name="ends_on">Baigiasi:</string>
|
||||
<string name="display_campaigns_explanation">Peržiūrėkite vykstančias kampanijas</string>
|
||||
<string name="in_app_camera_location_access_explanation">Leiskite programėlei nuskaityti vietą, jei fotoaparatas jos neįrašo. Kai kurių įrenginių kameros neįrašo vietos. Tokiais atvejais leidus programai gauti ir pridėti vietą, jūsų indėlis bus naudingesnis. Tai galite bet kada pakeisti nustatymuose</string>
|
||||
<string name="option_allow">Leisti</string>
|
||||
<string name="option_dismiss">Paslėpti</string>
|
||||
<string name="in_app_camera_needs_location">Nustatymuose įjunkite prieigą prie vietos ir bandykite dar kartą. \n\nPastaba: įkėlimas gali neturėti vietos, jei programėlė negali per trumpą laiką nuskaityti vietos iš įrenginio.</string>
|
||||
<string name="in_app_camera_location_permission_rationale">Programėlėje esančiam fotoaparatui reikalingas vietos leidimas, kad jis būtų pridėtas prie vaizdų, jei EXIF nėra vietos. Leiskite programėlei pasiekti jūsų buvimo vietą ir bandykite dar kartą.\n\nPastaba: įkėlimas gali neturėti vietos, jei programėlė negali per trumpą laiką nuskaityti vietos iš įrenginio.</string>
|
||||
<string name="in_app_camera_location_permission_denied">Programėlė neįrašys vietos kartu su kadrais, nes neturi vietos leidimo</string>
|
||||
<string name="in_app_camera_location_unavailable">Programa neįrašys vietos kartu su kadrais, nes GPS išjungtas</string>
|
||||
<string name="open_document_photo_picker_title">Naudokite dokumentais pagrįstą nuotraukų rinkiklį</string>
|
||||
<string name="open_document_photo_picker_explanation">Naujasis „Android“ nuotraukų rinkiklis gali prarasti vietos informaciją. Įjunkite, jei atrodo, kad jį naudojate.</string>
|
||||
<string name="location_loss_warning">Išjungus tai gali suaktyvinti naująjį „Android“ nuotraukų rinkiklį. Dėl to kyla pavojus prarasti vietos informaciją.\n\nNorėdami gauti daugiau informacijos, bakstelėkite „Skaityti daugiau“.</string>
|
||||
<string name="nearby_campaign_dismiss_message">Kampanijų nebematysite. Tačiau, jei norite, galite iš naujo įjungti šį pranešimą nustatymuose.</string>
|
||||
<string name="this_function_needs_network_connection" fuzzy="true">Šiai funkcijai reikalingas tinklo ryšys, patikrinkite ryšio nustatymus.</string>
|
||||
<string name="this_function_needs_network_connection">Šiai funkcijai reikalingas tinklo ryšys. Prašome patikrinti savo ryšio nustatymus.</string>
|
||||
<string name="error_processing_image">Apdorojant vaizdą įvyko klaida. Pabandykite dar kartą!</string>
|
||||
<string name="getting_edit_token">Gaunamas redagavimo prieigos raktas</string>
|
||||
<string name="check_category_adding_template">Kategorijos tikrinimo šablonas pridedamas</string>
|
||||
|
|
@ -418,22 +442,26 @@
|
|||
<string name="send_thank_toast">Siunčiama padėka už %1$s</string>
|
||||
<string name="review_copyright">Ar tai atitinka autorines teises?</string>
|
||||
<string name="review_category">Ar tai teisingai priskirta kategorijoms?</string>
|
||||
<string name="review_spam">Ar tai taikytina?</string>
|
||||
<string name="review_thanks">Ar norėtumėte padėkoti prisidėjusiam?</string>
|
||||
<string name="review_spam_explanation">Spustelėkite NE, kad pasiūlytumėte šį vaizdą ištrinti, jei jis visai nenaudingas.</string>
|
||||
<string name="review_copyright_explanation">Logotipai, ekrano kopijos, filmų plakatai dažnai pažeidžia autorines teises. Spustelėkite NE, jei norite pasiūlyti šį vaizdą ištrinti</string>
|
||||
<string name="review_thanks_explanation">%1$s bus padrąsintas jūsų dėkingumu</string>
|
||||
<string name="review_no_category">Oi, tai net nėra priskirta kategorijai!</string>
|
||||
<string name="review_category_explanation">Šis vaizdas priklauso %1$s kategorijoms.</string>
|
||||
<string name="review_spam_report_question">Tai nėra taikytina, nes</string>
|
||||
<string name="review_c_violation_report_question">Tai yra autorių teisių pažeidimas, nes</string>
|
||||
<string name="review_thanks_yes_button_text">Kitas vaizdas</string>
|
||||
<string name="review_thanks_no_button_text">Taip, kodėl gi ne</string>
|
||||
<string name="skip_image_explanation">Spustelėję šį mygtuką pamatysite kitą neseniai įkeltą vaizdą iš Vikitekos</string>
|
||||
<string name="review_image_explanation">Galite peržiūrėti vaizdus, kad pagerintumėte Vikitekos kokybę.\nTrys peržiūros parametrai yra:\n\n- Ar šis vaizdas tinkamas?\nKai paliesite Ne (nepatenka į sritį), jūs prie šio paveikslėlio pridedate ištrynimo nominacijos šabloną.\n\n- Ar šis vaizdas atitinka autorių teisių taisykles?\nKai paliesite Ne (neatitinka autorių teisių taisyklių), pridedate ištrynimo nominacijos šabloną prie šio paveikslėlio.\n\n- Ar šis vaizdas teisingai suskirstytas į kategorijas?\nKai paliesite Ne (neteisingai suskirstytas į kategorijas), prie šio paveikslėlio pridedate kategorizavimo užklausos šabloną.\n\nJei viskas yra gerai, prie paveikslėlio nepridedamas joks šablonas, ir jūs turite galimybę padėkoti bendraautoriui.</string>
|
||||
<string name="no_image">Nenaudojami jokie vaizdai</string>
|
||||
<string name="no_image_reverted">Jokie vaizdai negrąžinti</string>
|
||||
<string name="no_image_uploaded">Neįkelta jokių vaizdų</string>
|
||||
<string name="no_notification">Neturite neskaitytų pranešimų</string>
|
||||
<string name="no_read_notification">Neturite perskaitytų pranešimų</string>
|
||||
<string name="share_logs_using">Dalinkitės žurnalus naudodami</string>
|
||||
<string name="check_your_email_inbox">Patikrinkite savo el. pašto dėžutę</string>
|
||||
<string name="menu_option_read">Žiūrėti perskaitytus</string>
|
||||
<string name="menu_option_unread">Žiūrėti neperskaitytus</string>
|
||||
<string name="error_occurred_in_picking_images">Renkant vaizdus įvyko klaida</string>
|
||||
|
|
@ -478,6 +506,7 @@
|
|||
<string name="delete_helper_ask_reason_copyright_press_photo">Žiniasklaidos nuotrauka</string>
|
||||
<string name="delete_helper_ask_reason_copyright_internet_photo">Atsitiktinė nuotrauka iš interneto</string>
|
||||
<string name="delete_helper_ask_reason_copyright_logo">Logotipas</string>
|
||||
<string name="delete_helper_ask_reason_copyright_no_freedom_of_panorama">Panoramos laisvės pažeidimas</string>
|
||||
<string name="delete_helper_ask_alert_set_positive_button_reason">Nes</string>
|
||||
<string name="category_edit_helper_make_edit_toast">Bandoma atnaujinti kategorijas.</string>
|
||||
<string name="category_edit_helper_show_edit_title">Kategorijos atnaujinimas</string>
|
||||
|
|
@ -498,7 +527,7 @@
|
|||
<string name="coordinates_edit_helper_edit_message_else">Nepavyko pridėti koordinačių.</string>
|
||||
<string name="description_edit_helper_edit_message_else">Nepavyko pridėti aprašymų.</string>
|
||||
<string name="caption_edit_helper_edit_message_else">Nepavyko pridėti antraštę.</string>
|
||||
<string name="coordinates_picking_unsuccessful" fuzzy="true">Nepavyko gauti koordinačių.</string>
|
||||
<string name="coordinates_picking_unsuccessful">Paveikslėlio koordinatės neatnaujintos</string>
|
||||
<string name="descriptions_picking_unsuccessful">Nepavyko gauti aprašymų.</string>
|
||||
<string name="description_activity_title">Redaguokite aprašymus ir antraštes</string>
|
||||
<string name="share_image_via">Dalintis vaizdu per</string>
|
||||
|
|
@ -513,10 +542,11 @@
|
|||
<string name="place_state_needs_photo">Reikia Nuotraukos</string>
|
||||
<string name="place_type">Vietos tipas:</string>
|
||||
<string name="nearby_search_hint">Tiltas, muziejus, viešbutis ir t.t.</string>
|
||||
<string name="you_must_reset_your_passsword" fuzzy="true">Kažkas nepavyko prisijungiant, turite iš naujo nustatyti slaptažodį !!</string>
|
||||
<string name="you_must_reset_your_passsword">Kažkas nepavyko prisijungiant. Turite iš naujo nustatyti slaptažodį!</string>
|
||||
<string name="title_for_media">MEDIJA</string>
|
||||
<string name="upload_nearby_place_found_title">Netoliese rasta vieta</string>
|
||||
<string name="upload_nearby_place_found_description_singular" fuzzy="true">Ar tai vietos %1$s nuotrauka?</string>
|
||||
<string name="upload_nearby_place_found_description_plural">Ar tai %1$s nuotraukos?</string>
|
||||
<string name="upload_nearby_place_found_description_singular">Ar tai %1$s nuotrauka?</string>
|
||||
<string name="title_app_shortcut_bookmark">Žymės</string>
|
||||
<string name="title_app_shortcut_setting">Nustatymai</string>
|
||||
<string name="remove_bookmark">Pašalinta iš žymių</string>
|
||||
|
|
@ -524,12 +554,16 @@
|
|||
<string name="wallpaper_set_unsuccessfully">Kažkas nepavyko. Nepavyko nustatyti fono paveikslėlio</string>
|
||||
<string name="setting_wallpaper_dialog_title">Nustatyti kaip fono paveikslėlį</string>
|
||||
<string name="setting_wallpaper_dialog_message">Fono paveikslėlis nustatomas. Prašome palaukti…</string>
|
||||
<string name="theme_default_name">Sekti sistemos</string>
|
||||
<string name="theme_dark_name">Tamsus</string>
|
||||
<string name="theme_light_name">Šviesus</string>
|
||||
<string name="cannot_open_location_settings">Nepavyko atidaryti vietos nustatymų. Įjunkite vietą rankiniu būdu</string>
|
||||
<string name="recommend_high_accuracy_mode">Norėdami gauti geriausius rezultatus, pasirinkite didelio tikslumo režimą.</string>
|
||||
<string name="ask_to_turn_location_on">Įjungti vietą?</string>
|
||||
<string name="ask_to_turn_location_on_text">Įjunkite vietos nustatymo paslaugas, kad programa parodytų jūsų dabartinę vietą</string>
|
||||
<string name="nearby_needs_location">Kad tinkamai veiktų, Netoliese turi būti įjungta vieta</string>
|
||||
<string name="explore_map_needs_location">Žemėlapio naršymui reikia vietos leidimo, kad būtų rodomi netoliese esantys paveikslėliai</string>
|
||||
<string name="upload_map_location_access">Norėdami automatiškai nustatyti vietą, turite suteikti vietos leidimą.</string>
|
||||
<string name="use_location_from_similar_image">Ar nufotografavote šias dvi nuotraukas toje pačioje vietoje? Ar norite naudoti dešinėje esančio paveikslėlio platumą/ilgumą?</string>
|
||||
<string name="load_more">Įkelti daugiau</string>
|
||||
<string name="nearby_no_results">Vietų nerasta, pabandykite pakeisti paieškos kriterijus.</string>
|
||||
|
|
@ -617,6 +651,10 @@
|
|||
<string name="custom_selector_info_text2">Skirtingai nuo paveikslėlio kairėje, paveikslėlyje dešinėje yra Vikitekos logotipas, nurodantis, kad jis jau įkeltas. \n Palieskite ir palaikykite, kad peržiūrėtumėte vaizdą.</string>
|
||||
<string name="welcome_custom_selector_ok">Puiku</string>
|
||||
<string name="custom_selector_already_uploaded_image_text">Šis vaizdas jau buvo įkeltas į Vikiteką.</string>
|
||||
<string name="custom_selector_over_limit_warning">Dėl techninių priežasčių programėlė negali patikimai įkelti daugiau nei %1$d nuotraukos vienu metu. %1$d įkėlimo limitas buvo viršytas %2$d.</string>
|
||||
<string name="custom_selector_dismiss_limit_warning_button_text">Paslėpti</string>
|
||||
<string name="custom_selector_button_limit_text">Maksimalus: %1$d</string>
|
||||
<string name="custom_selector_limit_error_desc">Klaida: viršytas įkėlimo limitas</string>
|
||||
<string name="wlm_upload_info">Šis vaizdas bus įtrauktas į konkursą \"Wiki Loves Monuments\" (\"Wiki\" mėgsta paminklus)</string>
|
||||
<string name="display_monuments">Rodyti paminklus</string>
|
||||
<string name="wlm_month_message">Vyksta Viki myli paminklus mėnuo!</string>
|
||||
|
|
@ -626,7 +664,7 @@
|
|||
<string name="read_phone_state_permission_message">Netoliese žemėlapiai turi perskaityti TELENFONO BŪSENĄ, kad tinkamai funkcionuotų</string>
|
||||
<string name="contributions_of_user">Naudotojo indėlis: %s</string>
|
||||
<string name="achievements_of_user">Naudotojo pasiekimai: %s</string>
|
||||
<string name="menu_view_user_page" fuzzy="true">Žiūrėti naudotojo puslapį</string>
|
||||
<string name="menu_view_user_page">Žiūrėti naudotojo puslapį</string>
|
||||
<string name="edit_depictions">Redaguoti vaizdus</string>
|
||||
<string name="edit_categories">Redaguoti kategorijas</string>
|
||||
<string name="advanced_options">Išplėstiniai nustatymai</string>
|
||||
|
|
@ -652,6 +690,8 @@
|
|||
<string name="your_feedback">Jūsų atsiliepimas</string>
|
||||
<string name="mark_as_not_for_upload">Pažymėti kaip neskirtą įkėlimui</string>
|
||||
<string name="unmark_as_not_for_upload">Panaikinkite žymėjimą kaip neskirto įkėlimui</string>
|
||||
<string name="marking_as_not_for_upload">Žymima kaip neįkėlimui</string>
|
||||
<string name="unmarking_as_not_for_upload">Naikinamas žymėjimas kaip neįkėlimui</string>
|
||||
<string name="show_already_actioned_pictures">Rodyti jau padarytas nuotraukas</string>
|
||||
<string name="hiding_already_actioned_pictures">Slepiamos jau padarytos nuotraukos</string>
|
||||
<string name="no_more_images_found">Daugiau paveikslėlių nerasta</string>
|
||||
|
|
@ -660,6 +700,8 @@
|
|||
<string name="image_selected">Paveiklėlis pasirinktas</string>
|
||||
<string name="image_marked_as_not_for_upload">Paveikslėlis pažymėtas kaip neskirtas įkėlimui</string>
|
||||
<string name="menu_view_report">Pranešti</string>
|
||||
<string name="menu_view_set_white_background">Nustatyti baltą foną</string>
|
||||
<string name="menu_view_set_black_background">Nustatyti juodą foną</string>
|
||||
<string name="report_violation">Pranešti apie pažeidimą</string>
|
||||
<string name="report_user">Pranešti apie šį nauodotoją</string>
|
||||
<string name="report_content">Pranešti apie šį turinį</string>
|
||||
|
|
@ -669,4 +711,44 @@
|
|||
<string name="full_screen_mode_features_info">Norėdami atlikti šiuos veiksmus, braukite greitai ir ilgai: \n- Kairėn/dešinėn: Pereikite prie ankstesnio/kito\n- Į viršų: Pasirinkite\n- Žemyn: Pažymėti kaip neskirtą įkėlimui.</string>
|
||||
<string name="set_up_avatar_toast_string">Norėdami nustatyti pirmaujančiųjų sąrašo avatarą, bet kurio vaizdo trijų taškų meniu palieskite „Nustatyti kaip avatarą“.</string>
|
||||
<string name="similar_coordinate_description_auto_set">Koordinatės nėra tikslios koordinatės, bet asmuo, kuris įkėlė šią nuotrauką, mano, kad jos yra pakankamai arti.</string>
|
||||
<string name="storage_permissions_denied">Saugyklos leidimai atmesti</string>
|
||||
<string name="unable_to_share_upload_item">Nepavyko bendrinti šio elemento</string>
|
||||
<string name="permissions_are_required_for_functionality">Funkcionalumui reikalingi leidimai</string>
|
||||
<string name="learn_how_to_write_a_useful_description">Sužinokite, kaip parašyti naudingą aprašymą</string>
|
||||
<string name="learn_how_to_write_a_useful_caption">Sužinokite, kaip parašyti naudingą antraštę</string>
|
||||
<string name="see_your_achievements">Pamatykite savo pasiekimus</string>
|
||||
<string name="edit_image">Redaguoti paveikslėlį</string>
|
||||
<string name="edit_location">Redaguoti vietą</string>
|
||||
<string name="location_updated">Vieta atnaujinta!</string>
|
||||
<string name="remove_location">Pašalinti vietą</string>
|
||||
<string name="remove_location_warning_title">Pašalinti vietos įspėjimą</string>
|
||||
<string name="remove_location_warning_desc">Vieta daro nuotraukas naudingesnes ir lengviau randamas. Ar tikrai norite pašalinti vietą iš šios nuotraukos?</string>
|
||||
<string name="location_removed">Vieta pašalinta!</string>
|
||||
<string name="send_thanks_to_author">Padėkoti autoriui</string>
|
||||
<string name="error_sending_thanks">Klaida siunčiant padėką autoriui.</string>
|
||||
<string name="invalid_login_message">Jūsų prisijungimo sesija baigėsi. Prašome prisijungti dar kartą.</string>
|
||||
<string name="no_application_available_to_open_gpx_files">Nėra jokios programos GPX failams atidaryti</string>
|
||||
<string name="file_saved_successfully">Failas sėkmingai išsaugotas</string>
|
||||
<string name="do_you_want_to_open_gpx_file">Ar norite atidaryti GPX failą?</string>
|
||||
<string name="do_you_want_to_open_kml_file">Ar norite atidaryti KML failą?</string>
|
||||
<string name="failed_to_save_kml_file">Nepavyko išsaugoti KML failo.</string>
|
||||
<string name="failed_to_save_gpx_file">Nepavyko išsaugoti GPX failo.</string>
|
||||
<string name="saving_kml_file">Išsaugomas KML failas</string>
|
||||
<string name="saving_gpx_file">Išsaugomas GPX failas</string>
|
||||
<string name="multiple_files_depiction">Atminkite, kad visi paveikslėliai, įkeliant kelis, turi tas pačias kategorijas ir vaizdus. Jei paveikslėliuose vaizdai ir kategorijos skiriasi, prašome atlikti kelis atskirus įkėlimus.</string>
|
||||
<string name="multiple_files_depiction_header">Pastaba apie kelis įkėlimus</string>
|
||||
<string name="nearby_wikitalk">Praneškite apie problemą dėl šio elemento Vikiduomenims.</string>
|
||||
<string name="please_enter_some_comments">Įveskite keletą komentarų</string>
|
||||
<string name="talk">Aptarimas</string>
|
||||
<string name="write_something_about_the_item">Parašykite ką nors apie \'%1$s\' elementą. Tai bus matoma viešai.</string>
|
||||
<string name="does_not_exist_anymore_no_picture_can_ever_be_taken_of_it">\'%1$s\' nebeegzistuoja, niekada nebegalima jo nufotografuoti.</string>
|
||||
<string name="is_at_a_different_place_please_specify_the_correct_place_below_if_possible_tell_us_the_correct_latitude_longitude">\'%1$s\' yra kitoje vietoje. Toliau nurodykite teisingą vietą ir, jei įmanoma, parašykite teisingą platumą ir ilgumą.</string>
|
||||
<string name="other_problem_or_information_please_explain_below">Kita problema arba informacija (paaiškinkite toliau).</string>
|
||||
<string name="feedback_destination_note">Jūsų atsiliepimai bus paskelbti šiame viki puslapyje: <a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\">Commons:Mobile App/Feedback</a></string>
|
||||
<string name="are_you_sure_that_you_want_cancel_all_the_uploads">Ar tikrai norite atšaukti visus įkėlimus?</string>
|
||||
<string name="cancelling_all_the_uploads">Atšaukiami visi įkėlimai...</string>
|
||||
<string name="uploads">Įkėlimai</string>
|
||||
<string name="pending">Laukiama</string>
|
||||
<string name="failed">Nepavyko</string>
|
||||
<string name="could_not_load_place_data">Nepavyko įkelti vietos duomenų</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -475,6 +475,7 @@
|
|||
<string name="no_notification">Немате непрочитани известувања</string>
|
||||
<string name="no_read_notification">Немате прочитани известувања</string>
|
||||
<string name="share_logs_using">Споделувај дневници користејќи</string>
|
||||
<string name="check_your_email_inbox">Проверете си ја дојдовната е-пошта</string>
|
||||
<string name="menu_option_read">Погл. прочитани</string>
|
||||
<string name="menu_option_unread">Погл. непрочитани</string>
|
||||
<string name="error_occurred_in_picking_images">Се јави грешка при избирањето на сликите</string>
|
||||
|
|
@ -784,4 +785,7 @@
|
|||
<string name="pending">Во исчекување</string>
|
||||
<string name="failed">Неуспешно</string>
|
||||
<string name="could_not_load_place_data">Не можев да ги вчитам податоците за место</string>
|
||||
<string name="red_pin">Местово сè уште нема слика. Направете ја!</string>
|
||||
<string name="green_pin">Местово веќе има слика.</string>
|
||||
<string name="grey_pin">Проверувам дали местово има слика.</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -496,6 +496,7 @@
|
|||
<string name="no_notification">U heeft geen ongelezen meldingen</string>
|
||||
<string name="no_read_notification">U heeft geen gelezen meldingen</string>
|
||||
<string name="share_logs_using">Logboeken delen via</string>
|
||||
<string name="check_your_email_inbox">Bekijk uw e-mailinbox</string>
|
||||
<string name="menu_option_read">Bekijk gelezen</string>
|
||||
<string name="menu_option_unread">Ongelezen bekijken</string>
|
||||
<string name="error_occurred_in_picking_images">Er is een fout opgetreden bij het kiezen van afbeeldingen</string>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
<string name="username">ਵਰਤੋਂਕਾਰ ਨਾਂ</string>
|
||||
<string name="password">ਲੰਘ-ਸ਼ਬਦ</string>
|
||||
<string name="login">ਦਾਖ਼ਲ ਹੋਵੋ</string>
|
||||
<string name="forgot_password">ਪਾਰਸ਼ਬਦ ਭੁੱਲ ਗਏ?</string>
|
||||
<string name="logging_in_title">ਦਾਖ਼ਲਾ ਹੋ ਰਿਹਾ ਹੈ</string>
|
||||
<string name="logging_in_message">ਉਡੀਕੋ ਜੀ…</string>
|
||||
<string name="updating_caption_message">ਕਿਰਪਾ ਕਰਕੇ ਉਡੀਕੋ...</string>
|
||||
|
|
@ -67,7 +68,7 @@
|
|||
<string name="menu_nearby">ਨੇੜੇ-ਤੇੜੇ</string>
|
||||
<string name="provider_contributions">ਮੇਰੇ ਅੱਪਲੋਡ</string>
|
||||
<string name="menu_share">ਸਾਂਝਾ ਕਰੋ</string>
|
||||
<string name="share_title_hint" fuzzy="true">ਸਿਰਲੇਖ</string>
|
||||
<string name="share_title_hint">ਸੁਰਖੀ (ਲੋੜੀਂਦੀ)</string>
|
||||
<string name="share_description_hint">ਵੇਰਵਾ</string>
|
||||
<string name="login_failed_network">ਦਾਖ਼ਲ ਹੋਣ ਵਿੱਚ ਅਸਮਰੱਥ - ਨੈੱਟਵਰਕ ਫੇਲ੍ਹ ਹੋਇਆ ਹੈ</string>
|
||||
<string name="login_failed_throttled">ਬਹੁਤ ਸਾਰੀਆਂ ਅਸਫ਼ਲ ਕੋਸ਼ਿਸ਼ਾਂ। ਥੋੜ੍ਹੀ ਦੇਰ ਬਾਅਦ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ।</string>
|
||||
|
|
@ -86,7 +87,7 @@
|
|||
<string name="categories_skip_explanation" fuzzy="true">ਆਪਣੀਆਂ ਤਸਵੀਰਾਂ ਨੂੰ ਵਿਕੀਮੀਡੀਆ ਕਾਮਨਜ਼ ਵਿਚ ਜ਼ਿਆਦਾ ਲੱਭਣਯੋਗ ਬਣਾਉਣ ਲਈ ਸ਼੍ਰੇਣੀਆਂ ਜੋੜੋ।\n\nਸ਼੍ਰੇਣੀਆਂ ਜੋੜਨ ਲਈ ਟਾਈਪ ਕਰਨ ਅਰੰਭ ਕਰੋ।\nਇਸ ਕਾਰਜ ਨੂੰ ਅਣਡਿੱਠਾ ਕਰਨ ਲਈ ਇਹ ਸੁਨੇਹਾ ਥਪੇੜੋ (ਜਾਂ ਵਾਪਸੀ ਬਟਨ ਦਬਾਓ)।</string>
|
||||
<string name="categories_activity_title">ਸ਼੍ਰੇਣੀਆਂ</string>
|
||||
<string name="title_activity_settings">ਪਸੰਦਾਂ</string>
|
||||
<string name="title_activity_signup">ਸਾਈਨ ਅੱਪ</string>
|
||||
<string name="title_activity_signup">ਖਾਤਾ ਬਣਾਓ</string>
|
||||
<string name="title_activity_category_details">ਸ਼੍ਰੇਣੀ</string>
|
||||
<string name="menu_about">ਇਸ ਬਾਰੇ</string>
|
||||
<string name="about_license" fuzzy="true">ਅਜ਼ਾਦ ਸਰੋਤ ਸਾਫ਼ਟਵੇਅਰ ਨੂੰ <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache License v2</a> ਅਧੀਨ ਜਾਰੀ ਕੀਤਾ ਗਿਆ ਹੈ</string>
|
||||
|
|
@ -149,8 +150,8 @@
|
|||
<string name="media_detail_coordinates_empty">ਕੋਈ ਉਪਲਬਧ ਨਹੀਂ</string>
|
||||
<string name="_2fa_code">2FA ਕੋਡ</string>
|
||||
<string name="logout_verification">ਕੀ ਤੁਸੀਂ ਸੱਚੀਂ ਬੰਦ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?</string>
|
||||
<string name="welcome_image_welcome_wikipedia">ਵਿਕੀਪੀਡੀਆ \'ਤੇ ਸੁਆਗਤ</string>
|
||||
<string name="welcome_image_welcome_copyright">ਕਾਪੀਰਾਈਟ ਸੁਆਗਤ</string>
|
||||
<string name="welcome_image_welcome_wikipedia">ਵਿਕੀਪੀਡੀਆ \'ਤੇ ਜੀ ਆਇਆਂ ਨੂੰ</string>
|
||||
<string name="welcome_image_welcome_copyright">ਜੀ ਆਇਆਂ ਨੂੰ ਕਾਪੀਰਾਈਟ</string>
|
||||
<string name="cancel">ਰੱਦ ਕਰੋ</string>
|
||||
<string name="navigation_drawer_open">ਖੋਲ੍ਹੋ</string>
|
||||
<string name="navigation_drawer_close">ਬੰਦ ਕਰੋ</string>
|
||||
|
|
@ -179,6 +180,7 @@
|
|||
<string name="menu_search_button">ਲੱਭੋ</string>
|
||||
<string name="title_activity_search">ਲੱਭੋ</string>
|
||||
<string name="search_recent_header">ਹਾਲੀਆ ਖੋਜਾਂ:</string>
|
||||
<string name="provider_searches">ਹਾਲ ਦੀਆਂ ਪੁੱਛਗਿੱਛ ਖੋਜਾਂ</string>
|
||||
<string name="provider_recent_languages">ਹਾਲ ਹੀ ਵਿੱਚ ਬੋਲੀਆਂ ਬਾਰੇ ਪੁੱਛਗਿੱਛ</string>
|
||||
<string name="search_tab_title_categories">ਸ਼੍ਰੇਣੀਆਂ</string>
|
||||
<string name="explore_tab_title_map">ਨਕਸ਼ਾ</string>
|
||||
|
|
|
|||
|
|
@ -473,6 +473,7 @@
|
|||
<string name="no_notification">A l\'ha gnun-e notìfiche nen lesùe</string>
|
||||
<string name="no_read_notification">A l\'ha gnun-e notìfiche lesùe</string>
|
||||
<string name="share_logs_using">Partagé j\'argistr dovrand</string>
|
||||
<string name="check_your_email_inbox">Ch\'a contròla soa casela ëd pòsta eletrònica</string>
|
||||
<string name="menu_option_read">Vëdde lòn ch\'a l\'é stàit lesù</string>
|
||||
<string name="menu_option_unread">Vëdde lòn ch\'a l\'é ancor nen ëstàit lesù</string>
|
||||
<string name="error_occurred_in_picking_images">A-i é staje n\'eror an selessionand le plance</string>
|
||||
|
|
|
|||
|
|
@ -530,6 +530,7 @@
|
|||
<string name="no_notification">У вас нет непрочитанных уведомлений</string>
|
||||
<string name="no_read_notification">Нет прочитанных уведомлений</string>
|
||||
<string name="share_logs_using">Поделиться лог-файлами</string>
|
||||
<string name="check_your_email_inbox">Проверьте свой почтовый ящик</string>
|
||||
<string name="menu_option_read">Просмотр прочитанного</string>
|
||||
<string name="menu_option_unread">См. непрочитанные</string>
|
||||
<string name="error_occurred_in_picking_images">Произошла ошибка при загрузке изображений</string>
|
||||
|
|
|
|||
|
|
@ -111,4 +111,5 @@
|
|||
<string name="detail_description_empty">没有说明</string>
|
||||
<string name="detail_license_empty">未知授权协议</string>
|
||||
<string name="menu_refresh">刷新</string>
|
||||
<string name="check_your_email_inbox">请查看你的电子邮箱</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@
|
|||
<string name="nearby_info_menu_wikidata_article">維基數據項目</string>
|
||||
<string name="nearby_info_menu_wikipedia_article">維基百科條目</string>
|
||||
<string name="description_info">請盡可能描述媒體內容:拍攝於何處?是顯示什麼事物?有什麼脈絡?請描述對象或人物。透露出一些較不易猜測的訊息,例如是風景的話,可以是一天裡的時間。如果媒體顯示出了一些不尋常的事物,請說明不尋常原因。</string>
|
||||
<string name="check_your_email_inbox">請查看你的電子郵件信箱</string>
|
||||
<string name="learn_how_to_write_a_useful_description">學習如何編寫有用的描述</string>
|
||||
<string name="learn_how_to_write_a_useful_caption">學習如何編寫有用的標題</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -496,6 +496,7 @@
|
|||
<string name="no_notification">您有尚未讀取的通知</string>
|
||||
<string name="no_read_notification">您沒有已讀的通知</string>
|
||||
<string name="share_logs_using">分享日誌使用</string>
|
||||
<string name="check_your_email_inbox">請查看你的電子郵件信箱</string>
|
||||
<string name="menu_option_read">檢視已讀</string>
|
||||
<string name="menu_option_unread">檢視未讀</string>
|
||||
<string name="error_occurred_in_picking_images">選擇圖片時發生錯誤</string>
|
||||
|
|
@ -805,4 +806,7 @@
|
|||
<string name="pending">待處理</string>
|
||||
<string name="failed">失敗</string>
|
||||
<string name="could_not_load_place_data">無法載入地點資料</string>
|
||||
<string name="red_pin">這個地點還沒有照片,來拍一張吧!</string>
|
||||
<string name="green_pin">這個地點已有照片。</string>
|
||||
<string name="grey_pin">現在檢查這個地點是否有照片。</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -526,6 +526,7 @@
|
|||
<string name="no_notification">您没有任何未读通知</string>
|
||||
<string name="no_read_notification">您没有已读通知</string>
|
||||
<string name="share_logs_using">分享日志于</string>
|
||||
<string name="check_your_email_inbox">请查看你的电子邮箱</string>
|
||||
<string name="menu_option_read">查看已读</string>
|
||||
<string name="menu_option_unread">查看未读</string>
|
||||
<string name="error_occurred_in_picking_images">选择图片时出错</string>
|
||||
|
|
|
|||
|
|
@ -504,6 +504,7 @@ Upload your first media by tapping on the add button.</string>
|
|||
<string name="no_notification">You have no unread notifications</string>
|
||||
<string name="no_read_notification">You have no read notifications</string>
|
||||
<string name="share_logs_using">Share logs using</string>
|
||||
<string name="check_your_email_inbox">Check your email inbox</string>
|
||||
<string name="menu_option_read">View read</string>
|
||||
<string name="menu_option_unread">View unread</string>
|
||||
|
||||
|
|
@ -831,4 +832,7 @@ Upload your first media by tapping on the add button.</string>
|
|||
<string name="pending">Pending</string>
|
||||
<string name="failed">Failed</string>
|
||||
<string name="could_not_load_place_data">Could not load place data</string>
|
||||
<string name="red_pin">This place has no picture yet, go take one!</string>
|
||||
<string name="green_pin">This place has a picture already.</string>
|
||||
<string name="grey_pin">Now checking whether this place has a picture.</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ WHERE {
|
|||
}
|
||||
|
||||
# Get the label in the preferred language of the user, or any other language if no label is available in that language.
|
||||
OPTIONAL {?item rdfs:label ?itemLabelPreferredLanguage. FILTER (lang(?itemLabelPreferredLanguage) = "en")}
|
||||
OPTIONAL {?item rdfs:label ?itemLabelPreferredLanguage. FILTER (lang(?itemLabelPreferredLanguage) = "${LANG}")}
|
||||
OPTIONAL {?item rdfs:label ?itemLabelAnyLanguage}
|
||||
BIND(COALESCE(?itemLabelPreferredLanguage, ?itemLabelAnyLanguage, "?") as ?label)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import com.nhaarman.mockitokotlin2.eq
|
|||
import com.nhaarman.mockitokotlin2.verify
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkObject
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
|
@ -29,7 +31,6 @@ class ThanksClientTest {
|
|||
private lateinit var commonsApplication: CommonsApplication
|
||||
|
||||
private lateinit var thanksClient: ThanksClient
|
||||
private lateinit var mockedApplication: MockedStatic<CommonsApplication>
|
||||
|
||||
/**
|
||||
* initial setup, test environment
|
||||
|
|
@ -38,8 +39,8 @@ class ThanksClientTest {
|
|||
@Throws(Exception::class)
|
||||
fun setUp() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
mockedApplication = Mockito.mockStatic(CommonsApplication::class.java)
|
||||
`when`(CommonsApplication.getInstance()).thenReturn(commonsApplication)
|
||||
mockkObject(CommonsApplication)
|
||||
every { CommonsApplication.instance }.returns(commonsApplication)
|
||||
thanksClient = ThanksClient(csrfTokenClient, service)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -340,20 +340,6 @@ class MainActivityUnitTests {
|
|||
method.invoke(activity, null, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnActivityResult() {
|
||||
val method: Method =
|
||||
MainActivity::class.java.getDeclaredMethod(
|
||||
"onActivityResult",
|
||||
Int::class.java,
|
||||
Int::class.java,
|
||||
Intent::class.java,
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(activity, 0, 0, null)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnResume() {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package fr.free.nrw.commons.customselector.ui.selector
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.ActivityResult
|
||||
import fr.free.nrw.commons.OkHttpConnectionFactory
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import fr.free.nrw.commons.contributions.ContributionDao
|
||||
|
|
@ -98,20 +100,20 @@ class CustomSelectorActivityTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* Test onActivityResult function.
|
||||
* Test callback when result received.
|
||||
*/
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnActivityResult() {
|
||||
fun testResultLauncher() {
|
||||
val intent = Mockito.mock(Intent::class.java)
|
||||
val activityResult = ActivityResult(Activity.RESULT_OK, intent)
|
||||
val func =
|
||||
activity.javaClass.getDeclaredMethod(
|
||||
"onActivityResult",
|
||||
Int::class.java,
|
||||
Int::class.java,
|
||||
Intent::class.java,
|
||||
"onFullScreenDataReceived",
|
||||
ActivityResult::class.java,
|
||||
)
|
||||
func.isAccessible = true
|
||||
func.invoke(activity, 512, -1, Mockito.mock(Intent::class.java))
|
||||
func.invoke(activity, activityResult)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import android.os.Looper
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
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.upload.UploadMediaDetail
|
||||
import fr.free.nrw.commons.upload.UploadMediaDetailAdapter
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkObject
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
|
|
@ -54,6 +57,9 @@ class DescriptionEditActivityUnitTest {
|
|||
@Mock
|
||||
private lateinit var rvDescriptions: RecyclerView
|
||||
|
||||
@Mock
|
||||
private lateinit var commonsApplication: CommonsApplication
|
||||
|
||||
private lateinit var media: Media
|
||||
|
||||
@Before
|
||||
|
|
@ -82,6 +88,8 @@ class DescriptionEditActivityUnitTest {
|
|||
bundle.putString(Prefs.DESCRIPTION_LANGUAGE, "bn")
|
||||
bundle.putParcelable("media", media)
|
||||
intent.putExtras(bundle)
|
||||
mockkObject(CommonsApplication)
|
||||
every { CommonsApplication.instance }.returns(commonsApplication)
|
||||
activity =
|
||||
Robolectric.buildActivity(DescriptionEditActivity::class.java, intent).create().get()
|
||||
binding = ActivityDescriptionEditBinding.inflate(LayoutInflater.from(activity))
|
||||
|
|
|
|||
|
|
@ -6,18 +6,19 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.provider.MediaStore
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.nhaarman.mockitokotlin2.KArgumentCaptor
|
||||
import com.nhaarman.mockitokotlin2.argumentCaptor
|
||||
import com.nhaarman.mockitokotlin2.verify
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import fr.free.nrw.commons.filepicker.Constants.RequestCodes
|
||||
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.`when`
|
||||
|
|
@ -48,8 +49,10 @@ class FilePickerTest {
|
|||
@Mock
|
||||
var unit: Unit? = null
|
||||
|
||||
@Captor
|
||||
var requestCodeCaptor: ArgumentCaptor<Integer>? = null
|
||||
@Mock
|
||||
private lateinit var mockResultLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
private val intentCaptor: KArgumentCaptor<Intent> = argumentCaptor()
|
||||
|
||||
private lateinit var context: Context
|
||||
|
||||
|
|
@ -64,12 +67,19 @@ class FilePickerTest {
|
|||
`when`(PreferenceManager.getDefaultSharedPreferences(activity)).thenReturn(sharedPref)
|
||||
`when`(sharedPref.edit()).thenReturn(sharedPreferencesEditor)
|
||||
`when`(sharedPref.edit().putInt("type", 0)).thenReturn(sharedPreferencesEditor)
|
||||
FilePicker.openGallery(activity, 0, nextBoolean())
|
||||
verify(activity).startActivityForResult(
|
||||
ArgumentMatchers.any(),
|
||||
requestCodeCaptor?.capture()?.toInt()!!,
|
||||
)
|
||||
assertEquals(requestCodeCaptor?.value, RequestCodes.PICK_PICTURE_FROM_GALLERY)
|
||||
val openDocumentPreferred = nextBoolean()
|
||||
|
||||
FilePicker.openGallery(activity, mockResultLauncher, 0, openDocumentPreferred)
|
||||
|
||||
verify(mockResultLauncher).launch(intentCaptor.capture())
|
||||
|
||||
val capturedIntent = intentCaptor.firstValue
|
||||
|
||||
if (openDocumentPreferred) {
|
||||
assertEquals(Intent.ACTION_OPEN_DOCUMENT, capturedIntent.action)
|
||||
} else {
|
||||
assertEquals(Intent.ACTION_GET_CONTENT, capturedIntent.action)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -79,12 +89,13 @@ class FilePickerTest {
|
|||
`when`(sharedPref.edit().putInt("type", 0)).thenReturn(sharedPreferencesEditor)
|
||||
val mockApplication = mock(Application::class.java)
|
||||
`when`(activity.applicationContext).thenReturn(mockApplication)
|
||||
FilePicker.openCameraForImage(activity, 0)
|
||||
verify(activity).startActivityForResult(
|
||||
ArgumentMatchers.any(),
|
||||
requestCodeCaptor?.capture()?.toInt()!!,
|
||||
)
|
||||
assertEquals(requestCodeCaptor?.value, RequestCodes.TAKE_PICTURE)
|
||||
FilePicker.openCameraForImage(activity, mockResultLauncher, 0)
|
||||
|
||||
verify(mockResultLauncher).launch(intentCaptor.capture())
|
||||
|
||||
val capturedIntent = intentCaptor.firstValue
|
||||
|
||||
assertEquals(MediaStore.ACTION_IMAGE_CAPTURE, capturedIntent.action)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -165,32 +176,6 @@ class FilePickerTest {
|
|||
method.invoke(mockFilePicker, activity)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTakenCameraVideo() {
|
||||
val mockFilePicker = mock(FilePicker::class.java)
|
||||
val method: Method =
|
||||
FilePicker::class.java.getDeclaredMethod(
|
||||
"takenCameraVideo",
|
||||
Context::class.java,
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(mockFilePicker, context)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTakenCameraVideoCaseTrue() {
|
||||
val mockFilePicker = mock(FilePicker::class.java)
|
||||
`when`(PreferenceManager.getDefaultSharedPreferences(activity)).thenReturn(sharedPref)
|
||||
`when`(sharedPref.getString("last_video", null)).thenReturn("")
|
||||
val method: Method =
|
||||
FilePicker::class.java.getDeclaredMethod(
|
||||
"takenCameraVideo",
|
||||
Context::class.java,
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(mockFilePicker, activity)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsPhoto() {
|
||||
val mockFilePicker = mock(FilePicker::class.java)
|
||||
|
|
@ -204,46 +189,20 @@ class FilePickerTest {
|
|||
method.invoke(mockFilePicker, mockIntent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHandleActivityResultCaseOne() {
|
||||
val mockIntent = mock(Intent::class.java)
|
||||
FilePicker.handleActivityResult(
|
||||
RequestCodes.FILE_PICKER_IMAGE_IDENTIFICATOR,
|
||||
Activity.RESULT_OK,
|
||||
mockIntent,
|
||||
activity,
|
||||
object : DefaultCallback() {
|
||||
override fun onCanceled(
|
||||
source: FilePicker.ImageSource,
|
||||
type: Int,
|
||||
) {
|
||||
super.onCanceled(source, type)
|
||||
}
|
||||
|
||||
override fun onImagePickerError(
|
||||
e: Exception,
|
||||
source: FilePicker.ImageSource,
|
||||
type: Int,
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onImagesPicked(
|
||||
imagesFiles: List<UploadableFile>,
|
||||
source: FilePicker.ImageSource,
|
||||
type: Int,
|
||||
) {
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOpenCustomSelectorRequestCode() {
|
||||
`when`(PreferenceManager.getDefaultSharedPreferences(activity)).thenReturn(sharedPref)
|
||||
`when`(sharedPref.edit()).thenReturn(sharedPreferencesEditor)
|
||||
`when`(sharedPref.edit().putInt("type", 0)).thenReturn(sharedPreferencesEditor)
|
||||
FilePicker.openCustomSelector(activity, 0)
|
||||
verify(activity).startActivityForResult(ArgumentMatchers.any(), requestCodeCaptor?.capture()?.toInt()!!)
|
||||
assertEquals(requestCodeCaptor?.value, RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR)
|
||||
FilePicker.openCustomSelector(activity, mockResultLauncher, 0)
|
||||
|
||||
verify(mockResultLauncher).launch(intentCaptor.capture())
|
||||
|
||||
val capturedIntent = intentCaptor.firstValue
|
||||
|
||||
assertEquals(
|
||||
CustomSelectorActivity.Companion::class.java.declaringClass.name,
|
||||
capturedIntent.component?.className
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ package fr.free.nrw.commons.location
|
|||
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.CoreMatchers.equalTo
|
||||
import org.hamcrest.CoreMatchers.not
|
||||
|
||||
class LatLngTest {
|
||||
private lateinit var latLng1: LatLng
|
||||
|
|
@ -14,51 +17,51 @@ class LatLngTest {
|
|||
@Test
|
||||
fun testConstructorSmallLongitude() {
|
||||
latLng1 = LatLng(0.0, -181.0, 0.0f)
|
||||
assert(latLng1.longitude == 179.0)
|
||||
assertThat(latLng1.longitude, equalTo(179.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConstructorBigLongitude() {
|
||||
latLng1 = LatLng(0.0, 181.0, 0.0f)
|
||||
assert(latLng1.longitude == -179.0)
|
||||
assertThat(latLng1.longitude, equalTo(-179.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConstructorSmallLatitude() {
|
||||
latLng1 = LatLng(-91.0, 0.0, 0.0f)
|
||||
assert(latLng1.latitude == -90.0)
|
||||
assertThat(latLng1.latitude, equalTo(-90.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConstructorBigLatitude() {
|
||||
latLng1 = LatLng(91.0, 0.0, 0.0f)
|
||||
assert(latLng1.latitude == 90.0)
|
||||
assertThat(latLng1.latitude, equalTo(90.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHashCodeDiffersWenLngZero() {
|
||||
latLng1 = LatLng(2.0, 0.0, 0.0f)
|
||||
latLng2 = LatLng(1.0, 0.0, 0.0f)
|
||||
assert(latLng1.hashCode() != latLng2.hashCode())
|
||||
assertThat(latLng1.hashCode(), not(equalTo(latLng2.hashCode())))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHashCodeDiffersWenLatZero() {
|
||||
latLng1 = LatLng(0.0, 1.0, 0.0f)
|
||||
latLng2 = LatLng(0.0, 2.0, 0.0f)
|
||||
assert(latLng1.hashCode() != latLng2.hashCode())
|
||||
assertThat(latLng1.hashCode(), not(equalTo(latLng2.hashCode())))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEqualsWorks() {
|
||||
latLng1 = LatLng(1.0, 2.0, 5.0f)
|
||||
latLng2 = LatLng(1.0, 2.0, 0.0f)
|
||||
assert(latLng1.equals(latLng2))
|
||||
assertThat(latLng1, equalTo(latLng2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToString() {
|
||||
latLng1 = LatLng(1.0, 2.0, 5.0f)
|
||||
assert(latLng1.toString().equals("lat/lng: (1.0,2.0)"))
|
||||
assertThat(latLng1.toString(), equalTo("lat/lng: (1.0,2.0)"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import android.widget.ProgressBar
|
|||
import android.widget.ScrollView
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
|
|
@ -76,7 +77,6 @@ import java.util.Locale
|
|||
@Config(sdk = [21], application = TestCommonsApplication::class)
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
class MediaDetailFragmentUnitTests {
|
||||
private val requestCode = 1001
|
||||
private val lastLocation = "last_location_while_uploading"
|
||||
private lateinit var fragment: MediaDetailFragment
|
||||
private lateinit var fragmentManager: FragmentManager
|
||||
|
|
@ -231,24 +231,6 @@ class MediaDetailFragmentUnitTests {
|
|||
fragment.onCreateView(layoutInflater, null, savedInstanceState)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnActivityResultLocationPickerActivity() {
|
||||
fragment.onActivityResult(requestCode, Activity.RESULT_CANCELED, intent)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `test OnActivity Result Cancelled LocationPickerActivity`() {
|
||||
fragment.onActivityResult(requestCode, Activity.RESULT_CANCELED, intent)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `test OnActivity Result Cancelled DescriptionEditActivity`() {
|
||||
fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnSaveInstanceState() {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package fr.free.nrw.commons.nearby
|
|||
import fr.free.nrw.commons.R
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.CoreMatchers.equalTo
|
||||
|
||||
class LabelTest {
|
||||
private lateinit var label: Label
|
||||
|
|
@ -21,7 +23,7 @@ class LabelTest {
|
|||
*/
|
||||
@Test
|
||||
fun testLabelIcon() {
|
||||
assert(label.icon.equals(R.drawable.round_icon_church))
|
||||
assertThat(label.icon, equalTo(R.drawable.round_icon_church))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -30,6 +32,6 @@ class LabelTest {
|
|||
@Test
|
||||
fun testNullLabelIcon() {
|
||||
var nullLabel: Label = Label.fromText("a random text not exist in label texts")
|
||||
assert(nullLabel.icon.equals(R.drawable.round_icon_unknown))
|
||||
assertThat(nullLabel.icon, equalTo(R.drawable.round_icon_unknown))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ class ReviewHelperTest {
|
|||
mock<MwQueryPage>().apply {
|
||||
whenever(title()).thenReturn(file)
|
||||
if (revision.isNotEmpty()) {
|
||||
whenever(revisions()).thenReturn(*revision.toMutableList())
|
||||
whenever(revisions()).thenReturn(revision.toMutableList())
|
||||
}
|
||||
|
||||
val media =
|
||||
|
|
|
|||
|
|
@ -167,20 +167,6 @@ class UploadActivityUnitTests {
|
|||
activity.makeUploadRequest()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnActivityResult() {
|
||||
val method: Method =
|
||||
UploadActivity::class.java.getDeclaredMethod(
|
||||
"onActivityResult",
|
||||
Int::class.java,
|
||||
Int::class.java,
|
||||
Intent::class.java,
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(activity, CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS, 0, Intent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testReceiveSharedItems() {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import com.nhaarman.mockitokotlin2.eq
|
|||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.times
|
||||
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.contributions.ChunkInfo
|
||||
import fr.free.nrw.commons.contributions.Contribution
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ package fr.free.nrw.commons.upload
|
|||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.GridLayout
|
||||
import android.widget.ListView
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.nhaarman.mockitokotlin2.any
|
||||
import com.nhaarman.mockitokotlin2.times
|
||||
|
|
@ -67,13 +69,16 @@ class UploadMediaDetailAdapterUnitTest {
|
|||
@Mock
|
||||
private lateinit var adapterView: AdapterView<RecentLanguagesAdapter>
|
||||
|
||||
@Mock
|
||||
private lateinit var mockResultLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
uploadMediaDetails = mutableListOf(uploadMediaDetail, uploadMediaDetail)
|
||||
activity = Robolectric.buildActivity(UploadActivity::class.java).get()
|
||||
fragment = mock(UploadMediaDetailFragment::class.java)
|
||||
adapter = UploadMediaDetailAdapter(fragment, "", recentLanguagesDao)
|
||||
adapter = UploadMediaDetailAdapter(fragment, "", recentLanguagesDao, mockResultLauncher)
|
||||
context = ApplicationProvider.getApplicationContext()
|
||||
Whitebox.setInternalState(adapter, "uploadMediaDetails", uploadMediaDetails)
|
||||
Whitebox.setInternalState(adapter, "eventListener", eventListener)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import android.view.View
|
|||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.appcompat.widget.AppCompatButton
|
||||
import androidx.appcompat.widget.AppCompatImageButton
|
||||
import androidx.fragment.app.FragmentManager
|
||||
|
|
@ -348,7 +349,7 @@ class UploadMediaDetailFragmentUnitTest {
|
|||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnActivityResultOnMapIconClicked() {
|
||||
fun testOnCameraPositionCallbackOnMapIconClicked() {
|
||||
shadowOf(Looper.getMainLooper()).idle()
|
||||
Mockito.mock(LocationPicker::class.java)
|
||||
val intent = Mockito.mock(Intent::class.java)
|
||||
|
|
@ -363,13 +364,18 @@ class UploadMediaDetailFragmentUnitTest {
|
|||
`when`(latLng.latitude).thenReturn(0.0)
|
||||
`when`(latLng.longitude).thenReturn(0.0)
|
||||
`when`(uploadItem.gpsCoords).thenReturn(imageCoordinates)
|
||||
fragment.onActivityResult(1211, Activity.RESULT_OK, intent)
|
||||
val activityResult = ActivityResult(Activity.RESULT_OK, intent)
|
||||
|
||||
val handleResultMethod = UploadMediaDetailFragment::class.java.getDeclaredMethod("onCameraPosition", ActivityResult::class.java)
|
||||
handleResultMethod.isAccessible = true
|
||||
|
||||
handleResultMethod.invoke(fragment, activityResult)
|
||||
Mockito.verify(presenter, Mockito.times(0)).getImageQuality(0, location, activity)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnActivityResultAddLocationDialog() {
|
||||
fun testOnCameraPositionCallbackAddLocationDialog() {
|
||||
shadowOf(Looper.getMainLooper()).idle()
|
||||
Mockito.mock(LocationPicker::class.java)
|
||||
val intent = Mockito.mock(Intent::class.java)
|
||||
|
|
@ -387,7 +393,13 @@ class UploadMediaDetailFragmentUnitTest {
|
|||
`when`(latLng.latitude).thenReturn(0.0)
|
||||
`when`(latLng.longitude).thenReturn(0.0)
|
||||
`when`(uploadItem.gpsCoords).thenReturn(imageCoordinates)
|
||||
fragment.onActivityResult(1211, Activity.RESULT_OK, intent)
|
||||
|
||||
val activityResult = ActivityResult(Activity.RESULT_OK,intent)
|
||||
|
||||
val handleResultMethod = UploadMediaDetailFragment::class.java.getDeclaredMethod("onCameraPosition", ActivityResult::class.java)
|
||||
handleResultMethod.isAccessible = true
|
||||
|
||||
handleResultMethod.invoke(fragment, activityResult)
|
||||
Mockito.verify(presenter, Mockito.times(1)).displayLocDialog(0, null, false)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ buildscript {
|
|||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.5.0'
|
||||
classpath 'com.android.tools.build:gradle:8.7.0'
|
||||
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
|
||||
classpath 'org.codehaus.groovy:groovy-all:2.4.15'
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ org.gradle.jvmargs=-Xmx1536M
|
|||
org.gradle.caching=true
|
||||
android.enableR8.fullMode=false
|
||||
|
||||
KOTLIN_VERSION=1.7.20
|
||||
KOTLIN_VERSION=1.9.22
|
||||
LEAK_CANARY_VERSION=2.10
|
||||
DAGGER_VERSION=2.23
|
||||
ROOM_VERSION=2.5.0
|
||||
|
|
|
|||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,6 +1,6 @@
|
|||
#Sun Apr 23 18:22:54 IST 2023
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
Loading…
Add table
Add a link
Reference in a new issue